ex_write.c revision 19304
1/*-
2 * Copyright (c) 1992, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 * Copyright (c) 1992, 1993, 1994, 1995, 1996
5 *	Keith Bostic.  All rights reserved.
6 *
7 * See the LICENSE file for redistribution information.
8 */
9
10#include "config.h"
11
12#ifndef lint
13static const char sccsid[] = "@(#)ex_write.c	10.30 (Berkeley) 7/12/96";
14#endif /* not lint */
15
16#include <sys/types.h>
17#include <sys/queue.h>
18#include <sys/stat.h>
19
20#include <bitstring.h>
21#include <ctype.h>
22#include <errno.h>
23#include <fcntl.h>
24#include <limits.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <unistd.h>
29
30#include "../common/common.h"
31
32enum which {WN, WQ, WRITE, XIT};
33static int exwr __P((SCR *, EXCMD *, enum which));
34
35/*
36 * ex_wn --	:wn[!] [>>] [file]
37 *	Write to a file and switch to the next one.
38 *
39 * PUBLIC: int ex_wn __P((SCR *, EXCMD *));
40 */
41int
42ex_wn(sp, cmdp)
43	SCR *sp;
44	EXCMD *cmdp;
45{
46	if (exwr(sp, cmdp, WN))
47		return (1);
48	if (file_m3(sp, 0))
49		return (1);
50
51	/* The file name isn't a new file to edit. */
52	cmdp->argc = 0;
53
54	return (ex_next(sp, cmdp));
55}
56
57/*
58 * ex_wq --	:wq[!] [>>] [file]
59 *	Write to a file and quit.
60 *
61 * PUBLIC: int ex_wq __P((SCR *, EXCMD *));
62 */
63int
64ex_wq(sp, cmdp)
65	SCR *sp;
66	EXCMD *cmdp;
67{
68	int force;
69
70	if (exwr(sp, cmdp, WQ))
71		return (1);
72	if (file_m3(sp, 0))
73		return (1);
74
75	force = FL_ISSET(cmdp->iflags, E_C_FORCE);
76
77	if (ex_ncheck(sp, force))
78		return (1);
79
80	F_SET(sp, force ? SC_EXIT_FORCE : SC_EXIT);
81	return (0);
82}
83
84/*
85 * ex_write --	:write[!] [>>] [file]
86 *		:write [!] [cmd]
87 *	Write to a file.
88 *
89 * PUBLIC: int ex_write __P((SCR *, EXCMD *));
90 */
91int
92ex_write(sp, cmdp)
93	SCR *sp;
94	EXCMD *cmdp;
95{
96	return (exwr(sp, cmdp, WRITE));
97}
98
99
100/*
101 * ex_xit -- :x[it]! [file]
102 *	Write out any modifications and quit.
103 *
104 * PUBLIC: int ex_xit __P((SCR *, EXCMD *));
105 */
106int
107ex_xit(sp, cmdp)
108	SCR *sp;
109	EXCMD *cmdp;
110{
111	int force;
112
113	NEEDFILE(sp, cmdp);
114
115	if (F_ISSET(sp->ep, F_MODIFIED) && exwr(sp, cmdp, XIT))
116		return (1);
117	if (file_m3(sp, 0))
118		return (1);
119
120	force = FL_ISSET(cmdp->iflags, E_C_FORCE);
121
122	if (ex_ncheck(sp, force))
123		return (1);
124
125	F_SET(sp, force ? SC_EXIT_FORCE : SC_EXIT);
126	return (0);
127}
128
129/*
130 * exwr --
131 *	The guts of the ex write commands.
132 */
133static int
134exwr(sp, cmdp, cmd)
135	SCR *sp;
136	EXCMD *cmdp;
137	enum which cmd;
138{
139	MARK rm;
140	int flags;
141	char *name, *p;
142
143	NEEDFILE(sp, cmdp);
144
145	/* All write commands can have an associated '!'. */
146	LF_INIT(FS_POSSIBLE);
147	if (FL_ISSET(cmdp->iflags, E_C_FORCE))
148		LF_SET(FS_FORCE);
149
150	/* Skip any leading whitespace. */
151	if (cmdp->argc != 0)
152		for (p = cmdp->argv[0]->bp; *p != '\0' && isblank(*p); ++p);
153
154	/* If "write !" it's a pipe to a utility. */
155	if (cmdp->argc != 0 && cmd == WRITE && *p == '!') {
156		/* Secure means no shell access. */
157		if (O_ISSET(sp, O_SECURE)) {
158			ex_emsg(sp, cmdp->cmd->name, EXM_SECURE_F);
159			return (1);
160		}
161
162		/* Expand the argument. */
163		for (++p; *p && isblank(*p); ++p);
164		if (*p == '\0') {
165			ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
166			return (1);
167		}
168		if (argv_exp1(sp, cmdp, p, strlen(p), 1))
169			return (1);
170
171		/*
172		 * Historically, vi waited after a write filter even if there
173		 * wasn't any output from the command.  People complained when
174		 * nvi waited only if there was output, wanting the visual cue
175		 * that the program hadn't written anything.
176		 */
177		F_SET(sp, SC_EX_WAIT_YES);
178
179		/*
180		 * !!!
181		 * Ignore the return cursor position, the cursor doesn't
182		 * move.
183		 */
184		if (ex_filter(sp, cmdp, &cmdp->addr1,
185		    &cmdp->addr2, &rm, cmdp->argv[1]->bp, FILTER_WRITE))
186			return (1);
187
188		/* Ex terminates with a bang, even if the command fails. */
189		if (!F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_EX_SILENT))
190			(void)ex_puts(sp, "!\n");
191
192		return (0);
193	}
194
195	/* Set the FS_ALL flag if we're writing the entire file. */
196	if (cmdp->addr1.lno <= 1 && !db_exist(sp, cmdp->addr2.lno + 1))
197		LF_SET(FS_ALL);
198
199	/* If "write >>" it's an append to a file. */
200	if (cmdp->argc != 0 && cmd != XIT && p[0] == '>' && p[1] == '>') {
201		LF_SET(FS_APPEND);
202
203		/* Skip ">>" and whitespace. */
204		for (p += 2; *p && isblank(*p); ++p);
205	}
206
207	/* If no other arguments, just write the file back. */
208	if (cmdp->argc == 0 || *p == '\0')
209		return (file_write(sp,
210		    &cmdp->addr1, &cmdp->addr2, NULL, flags));
211
212	/* Build an argv so we get an argument count and file expansion. */
213	if (argv_exp2(sp, cmdp, p, strlen(p)))
214		return (1);
215
216	/*
217	 *  0 args: impossible.
218	 *  1 args: impossible (I hope).
219	 *  2 args: read it.
220	 * >2 args: object, too many args.
221	 *
222	 * The 1 args case depends on the argv_sexp() function refusing
223	 * to return success without at least one non-blank character.
224	 */
225	switch (cmdp->argc) {
226	case 0:
227	case 1:
228		abort();
229		/* NOTREACHED */
230	case 2:
231		name = cmdp->argv[1]->bp;
232
233		/*
234		 * !!!
235		 * Historically, the read and write commands renamed
236		 * "unnamed" files, or, if the file had a name, set
237		 * the alternate file name.
238		 */
239		if (F_ISSET(sp->frp, FR_TMPFILE) &&
240		    !F_ISSET(sp->frp, FR_EXNAMED)) {
241			if ((p = v_strdup(sp,
242			    cmdp->argv[1]->bp, cmdp->argv[1]->len)) != NULL) {
243				free(sp->frp->name);
244				sp->frp->name = p;
245			}
246			/*
247			 * The file has a real name, it's no longer a
248			 * temporary, clear the temporary file flags.
249			 *
250			 * !!!
251			 * If we're writing the whole file, FR_NAMECHANGE
252			 * will be cleared by the write routine -- this is
253			 * historic practice.
254			 */
255			F_CLR(sp->frp, FR_TMPEXIT | FR_TMPFILE);
256			F_SET(sp->frp, FR_NAMECHANGE | FR_EXNAMED);
257
258			/* Notify the screen. */
259			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
260		} else
261			set_alt_name(sp, name);
262		break;
263	default:
264		ex_emsg(sp, p, EXM_FILECOUNT);
265		return (1);
266	}
267
268	return (file_write(sp, &cmdp->addr1, &cmdp->addr2, name, flags));
269}
270
271/*
272 * ex_writefp --
273 *	Write a range of lines to a FILE *.
274 *
275 * PUBLIC: int ex_writefp __P((SCR *,
276 * PUBLIC:    char *, FILE *, MARK *, MARK *, u_long *, u_long *, int));
277 */
278int
279ex_writefp(sp, name, fp, fm, tm, nlno, nch, silent)
280	SCR *sp;
281	char *name;
282	FILE *fp;
283	MARK *fm, *tm;
284	u_long *nlno, *nch;
285	int silent;
286{
287	struct stat sb;
288	GS *gp;
289	u_long ccnt;			/* XXX: can't print off_t portably. */
290	recno_t fline, tline, lcnt;
291	size_t len;
292	int rval;
293	char *msg, *p;
294
295	gp = sp->gp;
296	fline = fm->lno;
297	tline = tm->lno;
298
299	if (nlno != NULL) {
300		*nch = 0;
301		*nlno = 0;
302	}
303
304	/*
305	 * The vi filter code has multiple processes running simultaneously,
306	 * and one of them calls ex_writefp().  The "unsafe" function calls
307	 * in this code are to db_get() and msgq().  Db_get() is safe, see
308	 * the comment in ex_filter.c:ex_filter() for details.  We don't call
309	 * msgq if the multiple process bit in the EXF is set.
310	 *
311	 * !!!
312	 * Historic vi permitted files of 0 length to be written.  However,
313	 * since the way vi got around dealing with "empty" files was to
314	 * always have a line in the file no matter what, it wrote them as
315	 * files of a single, empty line.  We write empty files.
316	 *
317	 * "Alex, I'll take vi trivia for $1000."
318	 */
319	ccnt = 0;
320	lcnt = 0;
321	msg = "253|Writing...";
322	if (tline != 0)
323		for (; fline <= tline; ++fline, ++lcnt) {
324			/* Caller has to provide any interrupt message. */
325			if ((lcnt + 1) % INTERRUPT_CHECK == 0) {
326				if (INTERRUPTED(sp))
327					break;
328				if (!silent) {
329					gp->scr_busy(sp, msg, msg == NULL ?
330					    BUSY_UPDATE : BUSY_ON);
331					msg = NULL;
332				}
333			}
334			if (db_get(sp, fline, DBG_FATAL, &p, &len))
335				goto err;
336			if (fwrite(p, 1, len, fp) != len)
337				goto err;
338			ccnt += len;
339			if (putc('\n', fp) != '\n')
340				break;
341			++ccnt;
342		}
343
344	if (fflush(fp))
345		goto err;
346	/*
347	 * XXX
348	 * I don't trust NFS -- check to make sure that we're talking to
349	 * a regular file and sync so that NFS is forced to flush.
350	 */
351	if (!fstat(fileno(fp), &sb) &&
352	    S_ISREG(sb.st_mode) && fsync(fileno(fp)))
353		goto err;
354
355	if (fclose(fp))
356		goto err;
357
358	rval = 0;
359	if (0) {
360err:		if (!F_ISSET(sp->ep, F_MULTILOCK))
361			msgq_str(sp, M_SYSERR, name, "%s");
362		(void)fclose(fp);
363		rval = 1;
364	}
365
366	if (!silent)
367		gp->scr_busy(sp, NULL, BUSY_OFF);
368
369	/* Report the possibly partial transfer. */
370	if (nlno != NULL) {
371		*nch = ccnt;
372		*nlno = lcnt;
373	}
374	return (rval);
375}
376