119304Speter/*-
219304Speter * Copyright (c) 1992, 1993, 1994
319304Speter *	The Regents of the University of California.  All rights reserved.
419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996
519304Speter *	Keith Bostic.  All rights reserved.
619304Speter *
719304Speter * See the LICENSE file for redistribution information.
819304Speter */
919304Speter
1019304Speter#include "config.h"
1119304Speter
1219304Speter#ifndef lint
13281373Sbaptstatic const char sccsid[] = "$Id: ex_write.c,v 10.43 2015/04/03 15:18:45 zy Exp $";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/queue.h>
1819304Speter#include <sys/stat.h>
1919304Speter
2019304Speter#include <bitstring.h>
2119304Speter#include <ctype.h>
2219304Speter#include <errno.h>
2319304Speter#include <fcntl.h>
2419304Speter#include <limits.h>
2519304Speter#include <stdio.h>
2619304Speter#include <stdlib.h>
2719304Speter#include <string.h>
2819304Speter#include <unistd.h>
2919304Speter
3019304Speter#include "../common/common.h"
3119304Speter
3219304Speterenum which {WN, WQ, WRITE, XIT};
33281373Sbaptstatic int exwr(SCR *, EXCMD *, enum which);
3419304Speter
3519304Speter/*
3619304Speter * ex_wn --	:wn[!] [>>] [file]
3719304Speter *	Write to a file and switch to the next one.
3819304Speter *
39281373Sbapt * PUBLIC: int ex_wn(SCR *, EXCMD *);
4019304Speter */
4119304Speterint
42254225Speterex_wn(SCR *sp, EXCMD *cmdp)
4319304Speter{
4419304Speter	if (exwr(sp, cmdp, WN))
4519304Speter		return (1);
4619304Speter	if (file_m3(sp, 0))
4719304Speter		return (1);
4819304Speter
4919304Speter	/* The file name isn't a new file to edit. */
5019304Speter	cmdp->argc = 0;
5119304Speter
5219304Speter	return (ex_next(sp, cmdp));
5319304Speter}
5419304Speter
5519304Speter/*
5619304Speter * ex_wq --	:wq[!] [>>] [file]
5719304Speter *	Write to a file and quit.
5819304Speter *
59281373Sbapt * PUBLIC: int ex_wq(SCR *, EXCMD *);
6019304Speter */
6119304Speterint
62254225Speterex_wq(SCR *sp, EXCMD *cmdp)
6319304Speter{
6419304Speter	int force;
6519304Speter
6619304Speter	if (exwr(sp, cmdp, WQ))
6719304Speter		return (1);
6819304Speter	if (file_m3(sp, 0))
6919304Speter		return (1);
7019304Speter
7119304Speter	force = FL_ISSET(cmdp->iflags, E_C_FORCE);
7219304Speter
7319304Speter	if (ex_ncheck(sp, force))
7419304Speter		return (1);
7519304Speter
7619304Speter	F_SET(sp, force ? SC_EXIT_FORCE : SC_EXIT);
7719304Speter	return (0);
7819304Speter}
7919304Speter
8019304Speter/*
8119304Speter * ex_write --	:write[!] [>>] [file]
8219304Speter *		:write [!] [cmd]
8319304Speter *	Write to a file.
8419304Speter *
85281373Sbapt * PUBLIC: int ex_write(SCR *, EXCMD *);
8619304Speter */
8719304Speterint
88254225Speterex_write(SCR *sp, EXCMD *cmdp)
8919304Speter{
9019304Speter	return (exwr(sp, cmdp, WRITE));
9119304Speter}
9219304Speter
9319304Speter
9419304Speter/*
9519304Speter * ex_xit -- :x[it]! [file]
9619304Speter *	Write out any modifications and quit.
9719304Speter *
98281373Sbapt * PUBLIC: int ex_xit(SCR *, EXCMD *);
9919304Speter */
10019304Speterint
101254225Speterex_xit(SCR *sp, EXCMD *cmdp)
10219304Speter{
10319304Speter	int force;
10419304Speter
10519304Speter	NEEDFILE(sp, cmdp);
10619304Speter
10719304Speter	if (F_ISSET(sp->ep, F_MODIFIED) && exwr(sp, cmdp, XIT))
10819304Speter		return (1);
10919304Speter	if (file_m3(sp, 0))
11019304Speter		return (1);
11119304Speter
11219304Speter	force = FL_ISSET(cmdp->iflags, E_C_FORCE);
11319304Speter
11419304Speter	if (ex_ncheck(sp, force))
11519304Speter		return (1);
11619304Speter
11719304Speter	F_SET(sp, force ? SC_EXIT_FORCE : SC_EXIT);
11819304Speter	return (0);
11919304Speter}
12019304Speter
12119304Speter/*
12219304Speter * exwr --
12319304Speter *	The guts of the ex write commands.
12419304Speter */
12519304Speterstatic int
126254225Speterexwr(SCR *sp, EXCMD *cmdp, enum which cmd)
12719304Speter{
12819304Speter	MARK rm;
12919304Speter	int flags;
130254225Speter	char *name;
131254225Speter	CHAR_T *p = NULL;
132254225Speter	size_t nlen;
133254225Speter	char *n;
134254225Speter	int rc;
135254225Speter	EX_PRIVATE *exp;
13619304Speter
13719304Speter	NEEDFILE(sp, cmdp);
13819304Speter
13919304Speter	/* All write commands can have an associated '!'. */
14019304Speter	LF_INIT(FS_POSSIBLE);
14119304Speter	if (FL_ISSET(cmdp->iflags, E_C_FORCE))
14219304Speter		LF_SET(FS_FORCE);
14319304Speter
14419304Speter	/* Skip any leading whitespace. */
14519304Speter	if (cmdp->argc != 0)
146254225Speter		for (p = cmdp->argv[0]->bp; *p != '\0' && cmdskip(*p); ++p);
14719304Speter
14819304Speter	/* If "write !" it's a pipe to a utility. */
14919304Speter	if (cmdp->argc != 0 && cmd == WRITE && *p == '!') {
15019304Speter		/* Secure means no shell access. */
15119304Speter		if (O_ISSET(sp, O_SECURE)) {
152254225Speter			ex_wemsg(sp, cmdp->cmd->name, EXM_SECURE_F);
15319304Speter			return (1);
15419304Speter		}
15519304Speter
15619304Speter		/* Expand the argument. */
157254225Speter		for (++p; *p && cmdskip(*p); ++p);
15819304Speter		if (*p == '\0') {
15919304Speter			ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
16019304Speter			return (1);
16119304Speter		}
162254225Speter		if (argv_exp1(sp, cmdp, p, STRLEN(p), 1))
16319304Speter			return (1);
16419304Speter
165254225Speter		/* Set the last bang command */
166254225Speter		exp = EXP(sp);
167254225Speter		free(exp->lastbcomm);
168254225Speter		exp->lastbcomm = v_wstrdup(sp, cmdp->argv[1]->bp,
169254225Speter		    cmdp->argv[1]->len);
170254225Speter
17119304Speter		/*
17219304Speter		 * Historically, vi waited after a write filter even if there
17319304Speter		 * wasn't any output from the command.  People complained when
17419304Speter		 * nvi waited only if there was output, wanting the visual cue
17519304Speter		 * that the program hadn't written anything.
17619304Speter		 */
17719304Speter		F_SET(sp, SC_EX_WAIT_YES);
17819304Speter
17919304Speter		/*
18019304Speter		 * !!!
18119304Speter		 * Ignore the return cursor position, the cursor doesn't
18219304Speter		 * move.
18319304Speter		 */
18419304Speter		if (ex_filter(sp, cmdp, &cmdp->addr1,
18519304Speter		    &cmdp->addr2, &rm, cmdp->argv[1]->bp, FILTER_WRITE))
18619304Speter			return (1);
18719304Speter
18819304Speter		/* Ex terminates with a bang, even if the command fails. */
18919304Speter		if (!F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_EX_SILENT))
19019304Speter			(void)ex_puts(sp, "!\n");
19119304Speter
19219304Speter		return (0);
19319304Speter	}
19419304Speter
19519304Speter	/* Set the FS_ALL flag if we're writing the entire file. */
19619304Speter	if (cmdp->addr1.lno <= 1 && !db_exist(sp, cmdp->addr2.lno + 1))
19719304Speter		LF_SET(FS_ALL);
19819304Speter
19919304Speter	/* If "write >>" it's an append to a file. */
20019304Speter	if (cmdp->argc != 0 && cmd != XIT && p[0] == '>' && p[1] == '>') {
20119304Speter		LF_SET(FS_APPEND);
20219304Speter
20319304Speter		/* Skip ">>" and whitespace. */
204254225Speter		for (p += 2; *p && cmdskip(*p); ++p);
20519304Speter	}
20619304Speter
20719304Speter	/* If no other arguments, just write the file back. */
20819304Speter	if (cmdp->argc == 0 || *p == '\0')
20919304Speter		return (file_write(sp,
21019304Speter		    &cmdp->addr1, &cmdp->addr2, NULL, flags));
21119304Speter
21219304Speter	/* Build an argv so we get an argument count and file expansion. */
213254225Speter	if (argv_exp2(sp, cmdp, p, STRLEN(p)))
21419304Speter		return (1);
21519304Speter
21619304Speter	/*
21719304Speter	 *  0 args: impossible.
21819304Speter	 *  1 args: impossible (I hope).
21919304Speter	 *  2 args: read it.
22019304Speter	 * >2 args: object, too many args.
22119304Speter	 *
22219304Speter	 * The 1 args case depends on the argv_sexp() function refusing
22319304Speter	 * to return success without at least one non-blank character.
22419304Speter	 */
22519304Speter	switch (cmdp->argc) {
22619304Speter	case 0:
22719304Speter	case 1:
22819304Speter		abort();
22919304Speter		/* NOTREACHED */
23019304Speter	case 2:
231254225Speter		INT2CHAR(sp, cmdp->argv[1]->bp, cmdp->argv[1]->len+1,
232254225Speter			 n, nlen);
233254225Speter		name = v_strdup(sp, n, nlen - 1);
23419304Speter
23519304Speter		/*
23619304Speter		 * !!!
23719304Speter		 * Historically, the read and write commands renamed
23819304Speter		 * "unnamed" files, or, if the file had a name, set
23919304Speter		 * the alternate file name.
24019304Speter		 */
24119304Speter		if (F_ISSET(sp->frp, FR_TMPFILE) &&
24219304Speter		    !F_ISSET(sp->frp, FR_EXNAMED)) {
243254225Speter			if ((n = v_strdup(sp, name, nlen - 1)) != NULL) {
24419304Speter				free(sp->frp->name);
245254225Speter				sp->frp->name = n;
24619304Speter			}
24719304Speter			/*
24819304Speter			 * The file has a real name, it's no longer a
24919304Speter			 * temporary, clear the temporary file flags.
25019304Speter			 *
25119304Speter			 * !!!
25219304Speter			 * If we're writing the whole file, FR_NAMECHANGE
25319304Speter			 * will be cleared by the write routine -- this is
25419304Speter			 * historic practice.
25519304Speter			 */
25619304Speter			F_CLR(sp->frp, FR_TMPEXIT | FR_TMPFILE);
25719304Speter			F_SET(sp->frp, FR_NAMECHANGE | FR_EXNAMED);
25819304Speter
25919304Speter			/* Notify the screen. */
26019304Speter			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
26119304Speter		} else
26219304Speter			set_alt_name(sp, name);
26319304Speter		break;
26419304Speter	default:
265254225Speter		INT2CHAR(sp, p, STRLEN(p) + 1, n, nlen);
266254225Speter		ex_emsg(sp, n, EXM_FILECOUNT);
26719304Speter		return (1);
26819304Speter	}
26919304Speter
270254225Speter	rc = file_write(sp, &cmdp->addr1, &cmdp->addr2, name, flags);
271254225Speter
272254225Speter	free(name);
273254225Speter
274254225Speter	return rc;
27519304Speter}
27619304Speter
27719304Speter/*
27819304Speter * ex_writefp --
27919304Speter *	Write a range of lines to a FILE *.
28019304Speter *
281281373Sbapt * PUBLIC: int ex_writefp(SCR *,
282281373Sbapt * PUBLIC:    char *, FILE *, MARK *, MARK *, u_long *, u_long *, int);
28319304Speter */
28419304Speterint
285254225Speterex_writefp(SCR *sp, char *name, FILE *fp, MARK *fm, MARK *tm, u_long *nlno, u_long *nch, int silent)
28619304Speter{
28719304Speter	struct stat sb;
28819304Speter	GS *gp;
28919304Speter	u_long ccnt;			/* XXX: can't print off_t portably. */
29019304Speter	recno_t fline, tline, lcnt;
29119304Speter	size_t len;
29219304Speter	int rval;
293281373Sbapt	char *msg, *p;
29419304Speter
29519304Speter	gp = sp->gp;
29619304Speter	fline = fm->lno;
29719304Speter	tline = tm->lno;
29819304Speter
29919304Speter	if (nlno != NULL) {
30019304Speter		*nch = 0;
30119304Speter		*nlno = 0;
30219304Speter	}
30319304Speter
30419304Speter	/*
30519304Speter	 * The vi filter code has multiple processes running simultaneously,
30619304Speter	 * and one of them calls ex_writefp().  The "unsafe" function calls
30719304Speter	 * in this code are to db_get() and msgq().  Db_get() is safe, see
30819304Speter	 * the comment in ex_filter.c:ex_filter() for details.  We don't call
30919304Speter	 * msgq if the multiple process bit in the EXF is set.
31019304Speter	 *
31119304Speter	 * !!!
31219304Speter	 * Historic vi permitted files of 0 length to be written.  However,
31319304Speter	 * since the way vi got around dealing with "empty" files was to
31419304Speter	 * always have a line in the file no matter what, it wrote them as
31519304Speter	 * files of a single, empty line.  We write empty files.
31619304Speter	 *
31719304Speter	 * "Alex, I'll take vi trivia for $1000."
31819304Speter	 */
31919304Speter	ccnt = 0;
32019304Speter	lcnt = 0;
32119304Speter	msg = "253|Writing...";
322281373Sbapt	if (tline != 0)
32319304Speter		for (; fline <= tline; ++fline, ++lcnt) {
32419304Speter			/* Caller has to provide any interrupt message. */
32519304Speter			if ((lcnt + 1) % INTERRUPT_CHECK == 0) {
32619304Speter				if (INTERRUPTED(sp))
32719304Speter					break;
32819304Speter				if (!silent) {
32919304Speter					gp->scr_busy(sp, msg, msg == NULL ?
33019304Speter					    BUSY_UPDATE : BUSY_ON);
33119304Speter					msg = NULL;
33219304Speter				}
33319304Speter			}
334281373Sbapt			if (db_rget(sp, fline, &p, &len))
33519304Speter				goto err;
336281373Sbapt			if (fwrite(p, 1, len, fp) != len)
33719304Speter				goto err;
33819304Speter			ccnt += len;
339281373Sbapt			if (putc('\n', fp) != '\n')
34019304Speter				break;
34119304Speter			++ccnt;
34219304Speter		}
34319304Speter
344281373Sbapt	if (fflush(fp))
34519304Speter		goto err;
34619304Speter	/*
34719304Speter	 * XXX
34819304Speter	 * I don't trust NFS -- check to make sure that we're talking to
34919304Speter	 * a regular file and sync so that NFS is forced to flush.
35019304Speter	 */
35119304Speter	if (!fstat(fileno(fp), &sb) &&
35219304Speter	    S_ISREG(sb.st_mode) && fsync(fileno(fp)))
35319304Speter		goto err;
35419304Speter
35519304Speter	if (fclose(fp))
35619304Speter		goto err;
35719304Speter
35819304Speter	rval = 0;
35919304Speter	if (0) {
36019304Spetererr:		if (!F_ISSET(sp->ep, F_MULTILOCK))
36119304Speter			msgq_str(sp, M_SYSERR, name, "%s");
36219304Speter		(void)fclose(fp);
36319304Speter		rval = 1;
36419304Speter	}
36519304Speter
36619304Speter	if (!silent)
36719304Speter		gp->scr_busy(sp, NULL, BUSY_OFF);
36819304Speter
36919304Speter	/* Report the possibly partial transfer. */
37019304Speter	if (nlno != NULL) {
37119304Speter		*nch = ccnt;
37219304Speter		*nlno = lcnt;
37319304Speter	}
37419304Speter	return (rval);
37519304Speter}
376