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_read.c,v 10.44 2001/06/25 15:19:19 skimo 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 <limits.h>
2419304Speter#include <stdio.h>
2519304Speter#include <stdlib.h>
2619304Speter#include <string.h>
2719304Speter
2819304Speter#include "../common/common.h"
2919304Speter#include "../vi/vi.h"
3019304Speter
3119304Speter/*
3219304Speter * ex_read --	:read [file]
3319304Speter *		:read [!cmd]
3419304Speter *	Read from a file or utility.
3519304Speter *
3619304Speter * !!!
3719304Speter * Historical vi wouldn't undo a filter read, for no apparent reason.
3819304Speter *
3919304Speter * PUBLIC: int ex_read __P((SCR *, EXCMD *));
4019304Speter */
4119304Speterint
42254225Speterex_read(SCR *sp, EXCMD *cmdp)
4319304Speter{
4419304Speter	enum { R_ARG, R_EXPANDARG, R_FILTER } which;
4519304Speter	struct stat sb;
46254225Speter	CHAR_T *arg = NULL;
47254225Speter	char *name = NULL;
48254225Speter	size_t nlen;
4919304Speter	EX_PRIVATE *exp;
5019304Speter	FILE *fp;
5119304Speter	FREF *frp;
5219304Speter	GS *gp;
5319304Speter	MARK rm;
5419304Speter	recno_t nlines;
55254225Speter	size_t arglen = 0;
5619304Speter	int argc, rval;
5719304Speter	char *p;
5819304Speter
5919304Speter	gp = sp->gp;
6019304Speter
6119304Speter	/*
6219304Speter	 * 0 args: read the current pathname.
6319304Speter	 * 1 args: check for "read !arg".
6419304Speter	 */
6519304Speter	switch (cmdp->argc) {
6619304Speter	case 0:
6719304Speter		which = R_ARG;
6819304Speter		break;
6919304Speter	case 1:
7019304Speter		arg = cmdp->argv[0]->bp;
7119304Speter		arglen = cmdp->argv[0]->len;
7219304Speter		if (*arg == '!') {
7319304Speter			++arg;
7419304Speter			--arglen;
7519304Speter			which = R_FILTER;
7619304Speter
7719304Speter			/* Secure means no shell access. */
7819304Speter			if (O_ISSET(sp, O_SECURE)) {
79254225Speter				ex_wemsg(sp, cmdp->cmd->name, EXM_SECURE_F);
8019304Speter				return (1);
8119304Speter			}
8219304Speter		} else
8319304Speter			which = R_EXPANDARG;
8419304Speter		break;
8519304Speter	default:
8619304Speter		abort();
8719304Speter		/* NOTREACHED */
8819304Speter	}
8919304Speter
9019304Speter	/* Load a temporary file if no file being edited. */
9119304Speter	if (sp->ep == NULL) {
9219304Speter		if ((frp = file_add(sp, NULL)) == NULL)
9319304Speter			return (1);
9419304Speter		if (file_init(sp, frp, NULL, 0))
9519304Speter			return (1);
9619304Speter	}
9719304Speter
9819304Speter	switch (which) {
9919304Speter	case R_FILTER:
10019304Speter		/*
10119304Speter		 * File name and bang expand the user's argument.  If
10219304Speter		 * we don't get an additional argument, it's illegal.
10319304Speter		 */
10419304Speter		argc = cmdp->argc;
10519304Speter		if (argv_exp1(sp, cmdp, arg, arglen, 1))
10619304Speter			return (1);
10719304Speter		if (argc == cmdp->argc) {
10819304Speter			ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
10919304Speter			return (1);
11019304Speter		}
11119304Speter		argc = cmdp->argc - 1;
11219304Speter
11319304Speter		/* Set the last bang command. */
11419304Speter		exp = EXP(sp);
11519304Speter		if (exp->lastbcomm != NULL)
11619304Speter			free(exp->lastbcomm);
11719304Speter		if ((exp->lastbcomm =
118254225Speter		    v_wstrdup(sp, cmdp->argv[argc]->bp,
119254225Speter				cmdp->argv[argc]->len)) == NULL) {
12019304Speter			msgq(sp, M_SYSERR, NULL);
12119304Speter			return (1);
12219304Speter		}
12319304Speter
12419304Speter		/*
12519304Speter		 * Vi redisplayed the user's argument if it changed, ex
12619304Speter		 * always displayed a !, plus the user's argument if it
12719304Speter		 * changed.
12819304Speter		 */
12919304Speter		if (F_ISSET(sp, SC_VI)) {
13019304Speter			if (F_ISSET(cmdp, E_MODIFY))
13119304Speter				(void)vs_update(sp, "!", cmdp->argv[argc]->bp);
13219304Speter		} else {
13319304Speter			if (F_ISSET(cmdp, E_MODIFY))
13419304Speter				(void)ex_printf(sp,
135254225Speter				    "!"WS"\n", cmdp->argv[argc]->bp);
13619304Speter			else
13719304Speter				(void)ex_puts(sp, "!\n");
13819304Speter			(void)ex_fflush(sp);
13919304Speter		}
14019304Speter
14119304Speter		/*
14219304Speter		 * Historically, filter reads as the first ex command didn't
14319304Speter		 * wait for the user. If SC_SCR_EXWROTE not already set, set
14419304Speter		 * the don't-wait flag.
14519304Speter		 */
14619304Speter		if (!F_ISSET(sp, SC_SCR_EXWROTE))
14719304Speter			F_SET(sp, SC_EX_WAIT_NO);
14819304Speter
14919304Speter		/*
15019304Speter		 * Switch into ex canonical mode.  The reason to restore the
15119304Speter		 * original terminal modes for read filters is so that users
15219304Speter		 * can do things like ":r! cat /dev/tty".
15319304Speter		 *
15419304Speter		 * !!!
15519304Speter		 * We do not output an extra <newline>, so that we don't touch
15619304Speter		 * the screen on a normal read.
15719304Speter		 */
15819304Speter		if (F_ISSET(sp, SC_VI)) {
15919304Speter			if (gp->scr_screen(sp, SC_EX)) {
160254225Speter				ex_wemsg(sp, cmdp->cmd->name, EXM_NOCANON_F);
16119304Speter				return (1);
16219304Speter			}
16319304Speter			/*
16419304Speter			 * !!!
16519304Speter			 * Historically, the read command doesn't switch to
16619304Speter			 * the alternate X11 xterm screen, if doing a filter
16719304Speter			 * read -- don't set SA_ALTERNATE.
16819304Speter			 */
16919304Speter			F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE);
17019304Speter		}
17119304Speter
17219304Speter		if (ex_filter(sp, cmdp, &cmdp->addr1,
17319304Speter		    NULL, &rm, cmdp->argv[argc]->bp, FILTER_READ))
17419304Speter			return (1);
17519304Speter
17619304Speter		/* The filter version of read set the autoprint flag. */
17719304Speter		F_SET(cmdp, E_AUTOPRINT);
17819304Speter
17919304Speter		/*
18019304Speter		 * If in vi mode, move to the first nonblank.  Might have
18119304Speter		 * switched into ex mode, so saved the original SC_VI value.
18219304Speter		 */
18319304Speter		sp->lno = rm.lno;
18419304Speter		if (F_ISSET(sp, SC_VI)) {
18519304Speter			sp->cno = 0;
18619304Speter			(void)nonblank(sp, sp->lno, &sp->cno);
18719304Speter		}
18819304Speter		return (0);
18919304Speter	case R_ARG:
19019304Speter		name = sp->frp->name;
19119304Speter		break;
19219304Speter	case R_EXPANDARG:
19319304Speter		if (argv_exp2(sp, cmdp, arg, arglen))
19419304Speter			return (1);
19519304Speter		/*
19619304Speter		 *  0 args: impossible.
19719304Speter		 *  1 args: impossible (I hope).
19819304Speter		 *  2 args: read it.
19919304Speter		 * >2 args: object, too many args.
20019304Speter		 *
20119304Speter		 * The 1 args case depends on the argv_sexp() function refusing
20219304Speter		 * to return success without at least one non-blank character.
20319304Speter		 */
20419304Speter		switch (cmdp->argc) {
20519304Speter		case 0:
20619304Speter		case 1:
20719304Speter			abort();
20819304Speter			/* NOTREACHED */
20919304Speter		case 2:
210254225Speter			INT2CHAR(sp, cmdp->argv[1]->bp, cmdp->argv[1]->len + 1,
211254225Speter				 name, nlen);
21219304Speter			/*
21319304Speter			 * !!!
21419304Speter			 * Historically, the read and write commands renamed
21519304Speter			 * "unnamed" files, or, if the file had a name, set
21619304Speter			 * the alternate file name.
21719304Speter			 */
21819304Speter			if (F_ISSET(sp->frp, FR_TMPFILE) &&
21919304Speter			    !F_ISSET(sp->frp, FR_EXNAMED)) {
220254225Speter				if ((p = strdup(name)) != NULL) {
22119304Speter					free(sp->frp->name);
22219304Speter					sp->frp->name = p;
22319304Speter				}
22419304Speter				/*
22519304Speter				 * The file has a real name, it's no longer a
22619304Speter				 * temporary, clear the temporary file flags.
22719304Speter				 */
22819304Speter				F_CLR(sp->frp, FR_TMPEXIT | FR_TMPFILE);
22919304Speter				F_SET(sp->frp, FR_NAMECHANGE | FR_EXNAMED);
23019304Speter
23119304Speter				/* Notify the screen. */
23219304Speter				(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
233254225Speter				name = sp->frp->name;
234254225Speter			} else {
23519304Speter				set_alt_name(sp, name);
236254225Speter				name = sp->alt_name;
237254225Speter			}
23819304Speter			break;
23919304Speter		default:
240254225Speter			ex_wemsg(sp, cmdp->argv[0]->bp, EXM_FILECOUNT);
24119304Speter			return (1);
24219304Speter
24319304Speter		}
24419304Speter		break;
24519304Speter	}
24619304Speter
24719304Speter	/*
24819304Speter	 * !!!
24919304Speter	 * Historically, vi did not permit reads from non-regular files, nor
25019304Speter	 * did it distinguish between "read !" and "read!", so there was no
25119304Speter	 * way to "force" it.  We permit reading from named pipes too, since
25219304Speter	 * they didn't exist when the original implementation of vi was done
25319304Speter	 * and they seem a reasonable addition.
25419304Speter	 */
25519304Speter	if ((fp = fopen(name, "r")) == NULL || fstat(fileno(fp), &sb)) {
25619304Speter		msgq_str(sp, M_SYSERR, name, "%s");
25719304Speter		return (1);
25819304Speter	}
25919304Speter	if (!S_ISFIFO(sb.st_mode) && !S_ISREG(sb.st_mode)) {
26019304Speter		(void)fclose(fp);
26119304Speter		msgq(sp, M_ERR,
26219304Speter		    "145|Only regular files and named pipes may be read");
26319304Speter		return (1);
26419304Speter	}
26519304Speter
26619304Speter	/* Try and get a lock. */
267254225Speter	if (file_lock(sp, NULL, fileno(fp), 0) == LOCK_UNAVAIL)
26819304Speter		msgq(sp, M_ERR, "146|%s: read lock was unavailable", name);
26919304Speter
27019304Speter	rval = ex_readfp(sp, name, fp, &cmdp->addr1, &nlines, 0);
27119304Speter
27219304Speter	/*
27319304Speter	 * In vi, set the cursor to the first line read in, if anything read
27419304Speter	 * in, otherwise, the address.  (Historic vi set it to the line after
27519304Speter	 * the address regardless, but since that line may not exist we don't
27619304Speter	 * bother.)
27719304Speter	 *
27819304Speter	 * In ex, set the cursor to the last line read in, if anything read in,
27919304Speter	 * otherwise, the address.
28019304Speter	 */
28119304Speter	if (F_ISSET(sp, SC_VI)) {
28219304Speter		sp->lno = cmdp->addr1.lno;
28319304Speter		if (nlines)
28419304Speter			++sp->lno;
28519304Speter	} else
28619304Speter		sp->lno = cmdp->addr1.lno + nlines;
28719304Speter	return (rval);
28819304Speter}
28919304Speter
29019304Speter/*
29119304Speter * ex_readfp --
29219304Speter *	Read lines into the file.
29319304Speter *
29419304Speter * PUBLIC: int ex_readfp __P((SCR *, char *, FILE *, MARK *, recno_t *, int));
29519304Speter */
29619304Speterint
297254225Speterex_readfp(SCR *sp, char *name, FILE *fp, MARK *fm, recno_t *nlinesp, int silent)
29819304Speter{
29919304Speter	EX_PRIVATE *exp;
30019304Speter	GS *gp;
30119304Speter	recno_t lcnt, lno;
30219304Speter	size_t len;
30319304Speter	u_long ccnt;			/* XXX: can't print off_t portably. */
30419304Speter	int nf, rval;
30519304Speter	char *p;
306254225Speter	size_t wlen;
307254225Speter	CHAR_T *wp;
30819304Speter
30919304Speter	gp = sp->gp;
31019304Speter	exp = EXP(sp);
31119304Speter
31219304Speter	/*
31319304Speter	 * Add in the lines from the output.  Insertion starts at the line
31419304Speter	 * following the address.
31519304Speter	 */
31619304Speter	ccnt = 0;
31719304Speter	lcnt = 0;
31819304Speter	p = "147|Reading...";
31919304Speter	for (lno = fm->lno; !ex_getline(sp, fp, &len); ++lno, ++lcnt) {
32019304Speter		if ((lcnt + 1) % INTERRUPT_CHECK == 0) {
32119304Speter			if (INTERRUPTED(sp))
32219304Speter				break;
32319304Speter			if (!silent) {
32419304Speter				gp->scr_busy(sp, p,
32519304Speter				    p == NULL ? BUSY_UPDATE : BUSY_ON);
32619304Speter				p = NULL;
32719304Speter			}
32819304Speter		}
329254225Speter		FILE2INT5(sp, exp->ibcw, exp->ibp, len, wp, wlen);
330254225Speter		if (db_append(sp, 1, lno, wp, wlen))
33119304Speter			goto err;
33219304Speter		ccnt += len;
33319304Speter	}
33419304Speter
33519304Speter	if (ferror(fp) || fclose(fp))
33619304Speter		goto err;
33719304Speter
33819304Speter	/* Return the number of lines read in. */
33919304Speter	if (nlinesp != NULL)
34019304Speter		*nlinesp = lcnt;
34119304Speter
34219304Speter	if (!silent) {
34319304Speter		p = msg_print(sp, name, &nf);
34419304Speter		msgq(sp, M_INFO,
345254225Speter		    "148|%s: %lu lines, %lu characters", p,
346254225Speter		    (u_long)lcnt, ccnt);
34719304Speter		if (nf)
34819304Speter			FREE_SPACE(sp, p, 0);
34919304Speter	}
35019304Speter
35119304Speter	rval = 0;
35219304Speter	if (0) {
35319304Spetererr:		msgq_str(sp, M_SYSERR, name, "%s");
35419304Speter		(void)fclose(fp);
35519304Speter		rval = 1;
35619304Speter	}
35719304Speter
35819304Speter	if (!silent)
35919304Speter		gp->scr_busy(sp, NULL, BUSY_OFF);
36019304Speter	return (rval);
36119304Speter}
362