This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / usr.bin / vi / ex / ex_write.c
CommitLineData
395c4480
AM
1/*-
2 * Copyright (c) 1992, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static char sccsid[] = "@(#)ex_write.c 8.27 (Berkeley) 3/23/94";
36#endif /* not lint */
37
38#include <sys/types.h>
39#include <queue.h>
40#include <sys/stat.h>
41#include <sys/time.h>
42
43#include <bitstring.h>
44#include <ctype.h>
45#include <errno.h>
46#include <fcntl.h>
47#include <limits.h>
48#include <signal.h>
49#include <stdio.h>
50#include <string.h>
51#include <termios.h>
52#include <unistd.h>
53
54#include <db.h>
55#include <regex.h>
56
57#include "vi.h"
58#include "excmd.h"
59
60enum which {WQ, WRITE, XIT};
61
62static int exwr __P((SCR *, EXF *, EXCMDARG *, enum which));
63
64/*
65 * ex_wq -- :wq[!] [>>] [file]
66 * Write to a file.
67 */
68int
69ex_wq(sp, ep, cmdp)
70 SCR *sp;
71 EXF *ep;
72 EXCMDARG *cmdp;
73{
74 int force;
75
76 if (exwr(sp, ep, cmdp, WQ))
77 return (1);
78
79 force = F_ISSET(cmdp, E_FORCE);
80 if (!force && ep->refcnt <= 1 && file_unedited(sp) != NULL) {
81 msgq(sp, M_ERR,
82 "More files to edit; use \":n\" to go to the next file");
83 return (1);
84 }
85
86 F_SET(sp, force ? S_EXIT_FORCE : S_EXIT);
87 return (0);
88}
89
90/*
91 * ex_write -- :write[!] [>>] [file]
92 * :write [!] [cmd]
93 * Write to a file.
94 */
95int
96ex_write(sp, ep, cmdp)
97 SCR *sp;
98 EXF *ep;
99 EXCMDARG *cmdp;
100{
101 return (exwr(sp, ep, cmdp, WRITE));
102}
103
104
105/*
106 * ex_xit -- :x[it]! [file]
107 *
108 * Write out any modifications and quit.
109 */
110int
111ex_xit(sp, ep, cmdp)
112 SCR *sp;
113 EXF *ep;
114 EXCMDARG *cmdp;
115{
116 int force;
117
118 if (F_ISSET((ep), F_MODIFIED) && exwr(sp, ep, cmdp, XIT))
119 return (1);
120
121 force = F_ISSET(cmdp, E_FORCE);
122 if (!force && ep->refcnt <= 1 && file_unedited(sp) != NULL) {
123 msgq(sp, M_ERR,
124 "More files to edit; use \":n\" to go to the next file");
125 return (1);
126 }
127
128 F_SET(sp, force ? S_EXIT_FORCE : S_EXIT);
129 return (0);
130}
131
132/*
133 * exwr --
134 * The guts of the ex write commands.
135 */
136static int
137exwr(sp, ep, cmdp, cmd)
138 SCR *sp;
139 EXF *ep;
140 EXCMDARG *cmdp;
141 enum which cmd;
142{
143 EX_PRIVATE *exp;
144 MARK rm;
145 int flags;
146 char *name, *p;
147
148 /* All write commands can have an associated '!'. */
149 LF_INIT(FS_POSSIBLE);
150 if (F_ISSET(cmdp, E_FORCE))
151 LF_SET(FS_FORCE);
152
153 /* Skip any leading whitespace. */
154 if (cmdp->argc != 0)
155 for (p = cmdp->argv[0]->bp; *p && isblank(*p); ++p);
156
157 /* If no arguments, just write the file back. */
158 if (cmdp->argc == 0 || *p == '\0') {
159 if (F_ISSET(cmdp, E_ADDR2_ALL))
160 LF_SET(FS_ALL);
161 return (file_write(sp, ep,
162 &cmdp->addr1, &cmdp->addr2, NULL, flags));
163 }
164
165 /* If "write !" it's a pipe to a utility. */
166 exp = EXP(sp);
167 if (cmd == WRITE && *p == '!') {
168 for (++p; *p && isblank(*p); ++p);
169 if (*p == '\0') {
170 msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage);
171 return (1);
172 }
173 /* Expand the argument. */
174 if (argv_exp1(sp, ep, cmdp, p, strlen(p), 0))
175 return (1);
176 if (filtercmd(sp, ep, &cmdp->addr1, &cmdp->addr2,
177 &rm, cmdp->argv[1]->bp, FILTER_WRITE))
178 return (1);
179 sp->lno = rm.lno;
180 return (0);
181 }
182
183 /* If "write >>" it's an append to a file. */
184 if (cmd != XIT && p[0] == '>' && p[1] == '>') {
185 LF_SET(FS_APPEND);
186
187 /* Skip ">>" and whitespace. */
188 for (p += 2; *p && isblank(*p); ++p);
189 }
190
191 /* Build an argv so we get an argument count and file expansion. */
192 if (argv_exp2(sp, ep, cmdp, p, strlen(p), 0))
193 return (1);
194
195 switch (cmdp->argc) {
196 case 1:
197 /*
198 * Nothing to expand, write the current file.
199 * XXX
200 * Should never happen, already checked this case.
201 */
202 name = NULL;
203 break;
204 case 2:
205 /* One new argument, write it. */
206 name = cmdp->argv[exp->argsoff - 1]->bp;
207 set_alt_name(sp, name);
208 break;
209 default:
210 /* If expanded to more than one argument, object. */
211 msgq(sp, M_ERR, "%s expanded into too many file names",
212 cmdp->argv[0]->bp);
213 msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage);
214 return (1);
215 }
216
217 if (F_ISSET(cmdp, E_ADDR2_ALL))
218 LF_SET(FS_ALL);
219 return (file_write(sp, ep, &cmdp->addr1, &cmdp->addr2, name, flags));
220}
221
222/*
223 * ex_writefp --
224 * Write a range of lines to a FILE *.
225 */
226int
227ex_writefp(sp, ep, name, fp, fm, tm, nlno, nch)
228 SCR *sp;
229 EXF *ep;
230 char *name;
231 FILE *fp;
232 MARK *fm, *tm;
233 u_long *nlno, *nch;
234{
235 struct stat sb;
236 u_long ccnt; /* XXX: can't print off_t portably. */
237 recno_t fline, tline, lcnt;
238 size_t len;
239 int sv_errno;
240 char *p;
241
242 fline = fm->lno;
243 tline = tm->lno;
244
245 if (nlno != NULL) {
246 *nch = 0;
247 *nlno = 0;
248 }
249
250 /*
251 * The vi filter code has multiple processes running simultaneously,
252 * and one of them calls ex_writefp(). The "unsafe" function calls
253 * in this code are to file_gline() and msgq(). File_gline() is safe,
254 * see the comment in filter.c:filtercmd() for details. We don't call
255 * msgq if the multiple process bit in the EXF is set.
256 *
257 * !!!
258 * Historic vi permitted files of 0 length to be written. However,
259 * since the way vi got around dealing with "empty" files was to
260 * always have a line in the file no matter what, it wrote them as
261 * files of a single, empty line. We write empty files.
262 *
263 * "Alex, I'll take vi trivia for $1000."
264 */
265 ccnt = 0;
266 lcnt = 0;
267 if (tline != 0) {
268 for (; fline <= tline; ++fline, ++lcnt) {
269 if (F_ISSET(sp, S_INTERRUPTED)) {
270 msgq(sp, M_INFO, "Interrupted.");
271 break;
272 }
273 if ((p = file_gline(sp, ep, fline, &len)) == NULL)
274 break;
275 if (fwrite(p, 1, len, fp) != len) {
276 msgq(sp, M_SYSERR, name);
277 (void)fclose(fp);
278 return (1);
279 }
280 ccnt += len;
281 if (putc('\n', fp) != '\n')
282 break;
283 ++ccnt;
284 }
285 }
286
287 /* If it's a regular file, sync it so that NFS is forced to flush. */
288 if (!fstat(fileno(fp), &sb) &&
289 S_ISREG(sb.st_mode) && fsync(fileno(fp))) {
290 sv_errno = errno;
291 (void)fclose(fp);
292 errno = sv_errno;
293 goto err;
294 }
295 if (fclose(fp))
296 goto err;
297 if (nlno != NULL) {
298 *nch = ccnt;
299 *nlno = lcnt;
300 }
301 return (0);
302
303err: if (!F_ISSET(ep, F_MULTILOCK))
304 msgq(sp, M_SYSERR, name);
305 return (1);
306}