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
13254225Speterstatic const char sccsid[] = "$Id: ex_write.c,v 10.41 2011/12/02 01:07:06 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>
28254225Speter#include <strings.h>
2919304Speter#include <unistd.h>
3019304Speter
3119304Speter#include "../common/common.h"
3219304Speter
3319304Speterenum which {WN, WQ, WRITE, XIT};
3419304Speterstatic int exwr __P((SCR *, EXCMD *, enum which));
3519304Speter
3619304Speter/*
3719304Speter * ex_wn --	:wn[!] [>>] [file]
3819304Speter *	Write to a file and switch to the next one.
3919304Speter *
4019304Speter * PUBLIC: int ex_wn __P((SCR *, EXCMD *));
4119304Speter */
4219304Speterint
43254225Speterex_wn(SCR *sp, EXCMD *cmdp)
4419304Speter{
4519304Speter	if (exwr(sp, cmdp, WN))
4619304Speter		return (1);
4719304Speter	if (file_m3(sp, 0))
4819304Speter		return (1);
4919304Speter
5019304Speter	/* The file name isn't a new file to edit. */
5119304Speter	cmdp->argc = 0;
5219304Speter
5319304Speter	return (ex_next(sp, cmdp));
5419304Speter}
5519304Speter
5619304Speter/*
5719304Speter * ex_wq --	:wq[!] [>>] [file]
5819304Speter *	Write to a file and quit.
5919304Speter *
6019304Speter * PUBLIC: int ex_wq __P((SCR *, EXCMD *));
6119304Speter */
6219304Speterint
63254225Speterex_wq(SCR *sp, EXCMD *cmdp)
6419304Speter{
6519304Speter	int force;
6619304Speter
6719304Speter	if (exwr(sp, cmdp, WQ))
6819304Speter		return (1);
6919304Speter	if (file_m3(sp, 0))
7019304Speter		return (1);
7119304Speter
7219304Speter	force = FL_ISSET(cmdp->iflags, E_C_FORCE);
7319304Speter
7419304Speter	if (ex_ncheck(sp, force))
7519304Speter		return (1);
7619304Speter
7719304Speter	F_SET(sp, force ? SC_EXIT_FORCE : SC_EXIT);
7819304Speter	return (0);
7919304Speter}
8019304Speter
8119304Speter/*
8219304Speter * ex_write --	:write[!] [>>] [file]
8319304Speter *		:write [!] [cmd]
8419304Speter *	Write to a file.
8519304Speter *
8619304Speter * PUBLIC: int ex_write __P((SCR *, EXCMD *));
8719304Speter */
8819304Speterint
89254225Speterex_write(SCR *sp, EXCMD *cmdp)
9019304Speter{
9119304Speter	return (exwr(sp, cmdp, WRITE));
9219304Speter}
9319304Speter
9419304Speter
9519304Speter/*
9619304Speter * ex_xit -- :x[it]! [file]
9719304Speter *	Write out any modifications and quit.
9819304Speter *
9919304Speter * PUBLIC: int ex_xit __P((SCR *, EXCMD *));
10019304Speter */
10119304Speterint
102254225Speterex_xit(SCR *sp, EXCMD *cmdp)
10319304Speter{
10419304Speter	int force;
10519304Speter
10619304Speter	NEEDFILE(sp, cmdp);
10719304Speter
10819304Speter	if (F_ISSET(sp->ep, F_MODIFIED) && exwr(sp, cmdp, XIT))
10919304Speter		return (1);
11019304Speter	if (file_m3(sp, 0))
11119304Speter		return (1);
11219304Speter
11319304Speter	force = FL_ISSET(cmdp->iflags, E_C_FORCE);
11419304Speter
11519304Speter	if (ex_ncheck(sp, force))
11619304Speter		return (1);
11719304Speter
11819304Speter	F_SET(sp, force ? SC_EXIT_FORCE : SC_EXIT);
11919304Speter	return (0);
12019304Speter}
12119304Speter
12219304Speter/*
12319304Speter * exwr --
12419304Speter *	The guts of the ex write commands.
12519304Speter */
12619304Speterstatic int
127254225Speterexwr(SCR *sp, EXCMD *cmdp, enum which cmd)
12819304Speter{
12919304Speter	MARK rm;
13019304Speter	int flags;
131254225Speter	char *name;
132254225Speter	CHAR_T *p = NULL;
133254225Speter	size_t nlen;
134254225Speter	char *n;
135254225Speter	int rc;
136254225Speter	EX_PRIVATE *exp;
13719304Speter
13819304Speter	NEEDFILE(sp, cmdp);
13919304Speter
14019304Speter	/* All write commands can have an associated '!'. */
14119304Speter	LF_INIT(FS_POSSIBLE);
14219304Speter	if (FL_ISSET(cmdp->iflags, E_C_FORCE))
14319304Speter		LF_SET(FS_FORCE);
14419304Speter
14519304Speter	/* Skip any leading whitespace. */
14619304Speter	if (cmdp->argc != 0)
147254225Speter		for (p = cmdp->argv[0]->bp; *p != '\0' && cmdskip(*p); ++p);
14819304Speter
14919304Speter	/* If "write !" it's a pipe to a utility. */
15019304Speter	if (cmdp->argc != 0 && cmd == WRITE && *p == '!') {
15119304Speter		/* Secure means no shell access. */
15219304Speter		if (O_ISSET(sp, O_SECURE)) {
153254225Speter			ex_wemsg(sp, cmdp->cmd->name, EXM_SECURE_F);
15419304Speter			return (1);
15519304Speter		}
15619304Speter
15719304Speter		/* Expand the argument. */
158254225Speter		for (++p; *p && cmdskip(*p); ++p);
15919304Speter		if (*p == '\0') {
16019304Speter			ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
16119304Speter			return (1);
16219304Speter		}
163254225Speter		if (argv_exp1(sp, cmdp, p, STRLEN(p), 1))
16419304Speter			return (1);
16519304Speter
166254225Speter		/* Set the last bang command */
167254225Speter		exp = EXP(sp);
168254225Speter		free(exp->lastbcomm);
169254225Speter		exp->lastbcomm = v_wstrdup(sp, cmdp->argv[1]->bp,
170254225Speter		    cmdp->argv[1]->len);
171254225Speter
17219304Speter		/*
17319304Speter		 * Historically, vi waited after a write filter even if there
17419304Speter		 * wasn't any output from the command.  People complained when
17519304Speter		 * nvi waited only if there was output, wanting the visual cue
17619304Speter		 * that the program hadn't written anything.
17719304Speter		 */
17819304Speter		F_SET(sp, SC_EX_WAIT_YES);
17919304Speter
18019304Speter		/*
18119304Speter		 * !!!
18219304Speter		 * Ignore the return cursor position, the cursor doesn't
18319304Speter		 * move.
18419304Speter		 */
18519304Speter		if (ex_filter(sp, cmdp, &cmdp->addr1,
18619304Speter		    &cmdp->addr2, &rm, cmdp->argv[1]->bp, FILTER_WRITE))
18719304Speter			return (1);
18819304Speter
18919304Speter		/* Ex terminates with a bang, even if the command fails. */
19019304Speter		if (!F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_EX_SILENT))
19119304Speter			(void)ex_puts(sp, "!\n");
19219304Speter
19319304Speter		return (0);
19419304Speter	}
19519304Speter
19619304Speter	/* Set the FS_ALL flag if we're writing the entire file. */
19719304Speter	if (cmdp->addr1.lno <= 1 && !db_exist(sp, cmdp->addr2.lno + 1))
19819304Speter		LF_SET(FS_ALL);
19919304Speter
20019304Speter	/* If "write >>" it's an append to a file. */
20119304Speter	if (cmdp->argc != 0 && cmd != XIT && p[0] == '>' && p[1] == '>') {
20219304Speter		LF_SET(FS_APPEND);
20319304Speter
20419304Speter		/* Skip ">>" and whitespace. */
205254225Speter		for (p += 2; *p && cmdskip(*p); ++p);
20619304Speter	}
20719304Speter
20819304Speter	/* If no other arguments, just write the file back. */
20919304Speter	if (cmdp->argc == 0 || *p == '\0')
21019304Speter		return (file_write(sp,
21119304Speter		    &cmdp->addr1, &cmdp->addr2, NULL, flags));
21219304Speter
21319304Speter	/* Build an argv so we get an argument count and file expansion. */
214254225Speter	if (argv_exp2(sp, cmdp, p, STRLEN(p)))
21519304Speter		return (1);
21619304Speter
21719304Speter	/*
21819304Speter	 *  0 args: impossible.
21919304Speter	 *  1 args: impossible (I hope).
22019304Speter	 *  2 args: read it.
22119304Speter	 * >2 args: object, too many args.
22219304Speter	 *
22319304Speter	 * The 1 args case depends on the argv_sexp() function refusing
22419304Speter	 * to return success without at least one non-blank character.
22519304Speter	 */
22619304Speter	switch (cmdp->argc) {
22719304Speter	case 0:
22819304Speter	case 1:
22919304Speter		abort();
23019304Speter		/* NOTREACHED */
23119304Speter	case 2:
232254225Speter		INT2CHAR(sp, cmdp->argv[1]->bp, cmdp->argv[1]->len+1,
233254225Speter			 n, nlen);
234254225Speter		name = v_strdup(sp, n, nlen - 1);
23519304Speter
23619304Speter		/*
23719304Speter		 * !!!
23819304Speter		 * Historically, the read and write commands renamed
23919304Speter		 * "unnamed" files, or, if the file had a name, set
24019304Speter		 * the alternate file name.
24119304Speter		 */
24219304Speter		if (F_ISSET(sp->frp, FR_TMPFILE) &&
24319304Speter		    !F_ISSET(sp->frp, FR_EXNAMED)) {
244254225Speter			if ((n = v_strdup(sp, name, nlen - 1)) != NULL) {
24519304Speter				free(sp->frp->name);
246254225Speter				sp->frp->name = n;
24719304Speter			}
24819304Speter			/*
24919304Speter			 * The file has a real name, it's no longer a
25019304Speter			 * temporary, clear the temporary file flags.
25119304Speter			 *
25219304Speter			 * !!!
25319304Speter			 * If we're writing the whole file, FR_NAMECHANGE
25419304Speter			 * will be cleared by the write routine -- this is
25519304Speter			 * historic practice.
25619304Speter			 */
25719304Speter			F_CLR(sp->frp, FR_TMPEXIT | FR_TMPFILE);
25819304Speter			F_SET(sp->frp, FR_NAMECHANGE | FR_EXNAMED);
25919304Speter
26019304Speter			/* Notify the screen. */
26119304Speter			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
26219304Speter		} else
26319304Speter			set_alt_name(sp, name);
26419304Speter		break;
26519304Speter	default:
266254225Speter		INT2CHAR(sp, p, STRLEN(p) + 1, n, nlen);
267254225Speter		ex_emsg(sp, n, EXM_FILECOUNT);
26819304Speter		return (1);
26919304Speter	}
27019304Speter
271254225Speter	rc = file_write(sp, &cmdp->addr1, &cmdp->addr2, name, flags);
272254225Speter
273254225Speter	free(name);
274254225Speter
275254225Speter	return rc;
27619304Speter}
27719304Speter
27819304Speter/*
27919304Speter * ex_writefp --
28019304Speter *	Write a range of lines to a FILE *.
28119304Speter *
28219304Speter * PUBLIC: int ex_writefp __P((SCR *,
28319304Speter * PUBLIC:    char *, FILE *, MARK *, MARK *, u_long *, u_long *, int));
28419304Speter */
28519304Speterint
286254225Speterex_writefp(SCR *sp, char *name, FILE *fp, MARK *fm, MARK *tm, u_long *nlno, u_long *nch, int silent)
28719304Speter{
28819304Speter	struct stat sb;
28919304Speter	GS *gp;
29019304Speter	u_long ccnt;			/* XXX: can't print off_t portably. */
29119304Speter	recno_t fline, tline, lcnt;
29219304Speter	size_t len;
29319304Speter	int rval;
294254225Speter	char *msg;
295254225Speter	CHAR_T *p;
296254225Speter	char *f;
297254225Speter	size_t flen;
298254225Speter	int isutf16;
29919304Speter
30019304Speter	gp = sp->gp;
30119304Speter	fline = fm->lno;
30219304Speter	tline = tm->lno;
30319304Speter
30419304Speter	if (nlno != NULL) {
30519304Speter		*nch = 0;
30619304Speter		*nlno = 0;
30719304Speter	}
30819304Speter
30919304Speter	/*
31019304Speter	 * The vi filter code has multiple processes running simultaneously,
31119304Speter	 * and one of them calls ex_writefp().  The "unsafe" function calls
31219304Speter	 * in this code are to db_get() and msgq().  Db_get() is safe, see
31319304Speter	 * the comment in ex_filter.c:ex_filter() for details.  We don't call
31419304Speter	 * msgq if the multiple process bit in the EXF is set.
31519304Speter	 *
31619304Speter	 * !!!
31719304Speter	 * Historic vi permitted files of 0 length to be written.  However,
31819304Speter	 * since the way vi got around dealing with "empty" files was to
31919304Speter	 * always have a line in the file no matter what, it wrote them as
32019304Speter	 * files of a single, empty line.  We write empty files.
32119304Speter	 *
32219304Speter	 * "Alex, I'll take vi trivia for $1000."
32319304Speter	 */
32419304Speter	ccnt = 0;
32519304Speter	lcnt = 0;
32619304Speter	msg = "253|Writing...";
327254225Speter
328254225Speter	if (O_ISSET(sp, O_FILEENCODING)) {
329254225Speter		isutf16 = !strncasecmp(O_STR(sp, O_FILEENCODING), "utf-16", 6);
330254225Speter		isutf16 += !strncasecmp(O_STR(sp, O_FILEENCODING), "utf-16le", 8);
331254225Speter	} else isutf16 = 0;
332254225Speter
333254225Speter	if (tline != 0) {
334254225Speter		if (isutf16 == 1 && fwrite("\xfe\xff", 1, 2, fp) != 2)
335254225Speter			goto err;
336254225Speter		if (isutf16 == 2 && fwrite("\xff\xfe", 1, 2, fp) != 2)
337254225Speter			goto err;
33819304Speter		for (; fline <= tline; ++fline, ++lcnt) {
33919304Speter			/* Caller has to provide any interrupt message. */
34019304Speter			if ((lcnt + 1) % INTERRUPT_CHECK == 0) {
34119304Speter				if (INTERRUPTED(sp))
34219304Speter					break;
34319304Speter				if (!silent) {
34419304Speter					gp->scr_busy(sp, msg, msg == NULL ?
34519304Speter					    BUSY_UPDATE : BUSY_ON);
34619304Speter					msg = NULL;
34719304Speter				}
34819304Speter			}
34919304Speter			if (db_get(sp, fline, DBG_FATAL, &p, &len))
35019304Speter				goto err;
351254225Speter			INT2FILE(sp, p, len, f, flen);
352254225Speter			if (fwrite(f, 1, flen, fp) != flen)
35319304Speter				goto err;
35419304Speter			ccnt += len;
355254225Speter			/* UTF-16 w/o BOM is big-endian */
356254225Speter			switch (isutf16) {
357254225Speter			case 1:		/* UTF-16BE */
358254225Speter				if (fwrite("\0\x0a", 1, 2, fp) != 2)
359254225Speter					goto done;
36019304Speter				break;
361254225Speter			case 2:		/* UTF-16LE */
362254225Speter				if (fwrite("\x0a\0", 1, 2, fp) != 2)
363254225Speter					goto done;
364254225Speter				break;
365254225Speter			default:
366254225Speter				if (putc('\n', fp) != '\n')
367254225Speter					goto done;
368254225Speter			}
36919304Speter			++ccnt;
37019304Speter		}
371254225Speter	}
37219304Speter
373254225Speterdone:	if (fflush(fp))
37419304Speter		goto err;
37519304Speter	/*
37619304Speter	 * XXX
37719304Speter	 * I don't trust NFS -- check to make sure that we're talking to
37819304Speter	 * a regular file and sync so that NFS is forced to flush.
37919304Speter	 */
38019304Speter	if (!fstat(fileno(fp), &sb) &&
38119304Speter	    S_ISREG(sb.st_mode) && fsync(fileno(fp)))
38219304Speter		goto err;
38319304Speter
38419304Speter	if (fclose(fp))
38519304Speter		goto err;
38619304Speter
38719304Speter	rval = 0;
38819304Speter	if (0) {
38919304Spetererr:		if (!F_ISSET(sp->ep, F_MULTILOCK))
39019304Speter			msgq_str(sp, M_SYSERR, name, "%s");
39119304Speter		(void)fclose(fp);
39219304Speter		rval = 1;
39319304Speter	}
39419304Speter
39519304Speter	if (!silent)
39619304Speter		gp->scr_busy(sp, NULL, BUSY_OFF);
39719304Speter
39819304Speter	/* Report the possibly partial transfer. */
39919304Speter	if (nlno != NULL) {
40019304Speter		*nch = ccnt;
40119304Speter		*nlno = lcnt;
40219304Speter	}
40319304Speter	return (rval);
40419304Speter}
405