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
1319304Speterstatic const char sccsid[] = "@(#)ex_write.c	10.30 (Berkeley) 7/12/96";
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};
3319304Speterstatic int exwr __P((SCR *, EXCMD *, enum which));
3419304Speter
3519304Speter/*
3619304Speter * ex_wn --	:wn[!] [>>] [file]
3719304Speter *	Write to a file and switch to the next one.
3819304Speter *
3919304Speter * PUBLIC: int ex_wn __P((SCR *, EXCMD *));
4019304Speter */
4119304Speterint
4219304Speterex_wn(sp, cmdp)
4319304Speter	SCR *sp;
4419304Speter	EXCMD *cmdp;
4519304Speter{
4619304Speter	if (exwr(sp, cmdp, WN))
4719304Speter		return (1);
4819304Speter	if (file_m3(sp, 0))
4919304Speter		return (1);
5019304Speter
5119304Speter	/* The file name isn't a new file to edit. */
5219304Speter	cmdp->argc = 0;
5319304Speter
5419304Speter	return (ex_next(sp, cmdp));
5519304Speter}
5619304Speter
5719304Speter/*
5819304Speter * ex_wq --	:wq[!] [>>] [file]
5919304Speter *	Write to a file and quit.
6019304Speter *
6119304Speter * PUBLIC: int ex_wq __P((SCR *, EXCMD *));
6219304Speter */
6319304Speterint
6419304Speterex_wq(sp, cmdp)
6519304Speter	SCR *sp;
6619304Speter	EXCMD *cmdp;
6719304Speter{
6819304Speter	int force;
6919304Speter
7019304Speter	if (exwr(sp, cmdp, WQ))
7119304Speter		return (1);
7219304Speter	if (file_m3(sp, 0))
7319304Speter		return (1);
7419304Speter
7519304Speter	force = FL_ISSET(cmdp->iflags, E_C_FORCE);
7619304Speter
7719304Speter	if (ex_ncheck(sp, force))
7819304Speter		return (1);
7919304Speter
8019304Speter	F_SET(sp, force ? SC_EXIT_FORCE : SC_EXIT);
8119304Speter	return (0);
8219304Speter}
8319304Speter
8419304Speter/*
8519304Speter * ex_write --	:write[!] [>>] [file]
8619304Speter *		:write [!] [cmd]
8719304Speter *	Write to a file.
8819304Speter *
8919304Speter * PUBLIC: int ex_write __P((SCR *, EXCMD *));
9019304Speter */
9119304Speterint
9219304Speterex_write(sp, cmdp)
9319304Speter	SCR *sp;
9419304Speter	EXCMD *cmdp;
9519304Speter{
9619304Speter	return (exwr(sp, cmdp, WRITE));
9719304Speter}
9819304Speter
9919304Speter
10019304Speter/*
10119304Speter * ex_xit -- :x[it]! [file]
10219304Speter *	Write out any modifications and quit.
10319304Speter *
10419304Speter * PUBLIC: int ex_xit __P((SCR *, EXCMD *));
10519304Speter */
10619304Speterint
10719304Speterex_xit(sp, cmdp)
10819304Speter	SCR *sp;
10919304Speter	EXCMD *cmdp;
11019304Speter{
11119304Speter	int force;
11219304Speter
11319304Speter	NEEDFILE(sp, cmdp);
11419304Speter
11519304Speter	if (F_ISSET(sp->ep, F_MODIFIED) && exwr(sp, cmdp, XIT))
11619304Speter		return (1);
11719304Speter	if (file_m3(sp, 0))
11819304Speter		return (1);
11919304Speter
12019304Speter	force = FL_ISSET(cmdp->iflags, E_C_FORCE);
12119304Speter
12219304Speter	if (ex_ncheck(sp, force))
12319304Speter		return (1);
12419304Speter
12519304Speter	F_SET(sp, force ? SC_EXIT_FORCE : SC_EXIT);
12619304Speter	return (0);
12719304Speter}
12819304Speter
12919304Speter/*
13019304Speter * exwr --
13119304Speter *	The guts of the ex write commands.
13219304Speter */
13319304Speterstatic int
13419304Speterexwr(sp, cmdp, cmd)
13519304Speter	SCR *sp;
13619304Speter	EXCMD *cmdp;
13719304Speter	enum which cmd;
13819304Speter{
13919304Speter	MARK rm;
14019304Speter	int flags;
14119304Speter	char *name, *p;
14219304Speter
14319304Speter	NEEDFILE(sp, cmdp);
14419304Speter
14519304Speter	/* All write commands can have an associated '!'. */
14619304Speter	LF_INIT(FS_POSSIBLE);
14719304Speter	if (FL_ISSET(cmdp->iflags, E_C_FORCE))
14819304Speter		LF_SET(FS_FORCE);
14919304Speter
15019304Speter	/* Skip any leading whitespace. */
15119304Speter	if (cmdp->argc != 0)
15219304Speter		for (p = cmdp->argv[0]->bp; *p != '\0' && isblank(*p); ++p);
15319304Speter
15419304Speter	/* If "write !" it's a pipe to a utility. */
15519304Speter	if (cmdp->argc != 0 && cmd == WRITE && *p == '!') {
15619304Speter		/* Secure means no shell access. */
15719304Speter		if (O_ISSET(sp, O_SECURE)) {
15819304Speter			ex_emsg(sp, cmdp->cmd->name, EXM_SECURE_F);
15919304Speter			return (1);
16019304Speter		}
16119304Speter
16219304Speter		/* Expand the argument. */
16319304Speter		for (++p; *p && isblank(*p); ++p);
16419304Speter		if (*p == '\0') {
16519304Speter			ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
16619304Speter			return (1);
16719304Speter		}
16819304Speter		if (argv_exp1(sp, cmdp, p, strlen(p), 1))
16919304Speter			return (1);
17019304Speter
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. */
20419304Speter		for (p += 2; *p && isblank(*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. */
21319304Speter	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:
23119304Speter		name = cmdp->argv[1]->bp;
23219304Speter
23319304Speter		/*
23419304Speter		 * !!!
23519304Speter		 * Historically, the read and write commands renamed
23619304Speter		 * "unnamed" files, or, if the file had a name, set
23719304Speter		 * the alternate file name.
23819304Speter		 */
23919304Speter		if (F_ISSET(sp->frp, FR_TMPFILE) &&
24019304Speter		    !F_ISSET(sp->frp, FR_EXNAMED)) {
24119304Speter			if ((p = v_strdup(sp,
24219304Speter			    cmdp->argv[1]->bp, cmdp->argv[1]->len)) != NULL) {
24319304Speter				free(sp->frp->name);
24419304Speter				sp->frp->name = p;
24519304Speter			}
24619304Speter			/*
24719304Speter			 * The file has a real name, it's no longer a
24819304Speter			 * temporary, clear the temporary file flags.
24919304Speter			 *
25019304Speter			 * !!!
25119304Speter			 * If we're writing the whole file, FR_NAMECHANGE
25219304Speter			 * will be cleared by the write routine -- this is
25319304Speter			 * historic practice.
25419304Speter			 */
25519304Speter			F_CLR(sp->frp, FR_TMPEXIT | FR_TMPFILE);
25619304Speter			F_SET(sp->frp, FR_NAMECHANGE | FR_EXNAMED);
25719304Speter
25819304Speter			/* Notify the screen. */
25919304Speter			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
26019304Speter		} else
26119304Speter			set_alt_name(sp, name);
26219304Speter		break;
26319304Speter	default:
26419304Speter		ex_emsg(sp, p, EXM_FILECOUNT);
26519304Speter		return (1);
26619304Speter	}
26719304Speter
26819304Speter	return (file_write(sp, &cmdp->addr1, &cmdp->addr2, name, flags));
26919304Speter}
27019304Speter
27119304Speter/*
27219304Speter * ex_writefp --
27319304Speter *	Write a range of lines to a FILE *.
27419304Speter *
27519304Speter * PUBLIC: int ex_writefp __P((SCR *,
27619304Speter * PUBLIC:    char *, FILE *, MARK *, MARK *, u_long *, u_long *, int));
27719304Speter */
27819304Speterint
27919304Speterex_writefp(sp, name, fp, fm, tm, nlno, nch, silent)
28019304Speter	SCR *sp;
28119304Speter	char *name;
28219304Speter	FILE *fp;
28319304Speter	MARK *fm, *tm;
28419304Speter	u_long *nlno, *nch;
28519304Speter	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;
29319304Speter	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...";
32219304Speter	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			}
33419304Speter			if (db_get(sp, fline, DBG_FATAL, &p, &len))
33519304Speter				goto err;
33619304Speter			if (fwrite(p, 1, len, fp) != len)
33719304Speter				goto err;
33819304Speter			ccnt += len;
33919304Speter			if (putc('\n', fp) != '\n')
34019304Speter				break;
34119304Speter			++ccnt;
34219304Speter		}
34319304Speter
34419304Speter	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