1/*	$NetBSD: ex_read.c,v 1.2 2013/11/22 15:52:05 christos Exp $ */
2/*-
3 * Copyright (c) 1992, 1993, 1994
4 *	The Regents of the University of California.  All rights reserved.
5 * Copyright (c) 1992, 1993, 1994, 1995, 1996
6 *	Keith Bostic.  All rights reserved.
7 *
8 * See the LICENSE file for redistribution information.
9 */
10
11#include "config.h"
12
13#include <sys/cdefs.h>
14#if 0
15#ifndef lint
16static const char sccsid[] = "Id: ex_read.c,v 10.44 2001/06/25 15:19:19 skimo Exp  (Berkeley) Date: 2001/06/25 15:19:19 ";
17#endif /* not lint */
18#else
19__RCSID("$NetBSD$");
20#endif
21
22#include <sys/types.h>
23#include <sys/queue.h>
24#include <sys/stat.h>
25#include <sys/time.h>
26
27#include <bitstring.h>
28#include <ctype.h>
29#include <errno.h>
30#include <limits.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34
35#include "../common/common.h"
36#include "../vi/vi.h"
37
38/*
39 * ex_read --	:read [file]
40 *		:read [!cmd]
41 *	Read from a file or utility.
42 *
43 * !!!
44 * Historical vi wouldn't undo a filter read, for no apparent reason.
45 *
46 * PUBLIC: int ex_read __P((SCR *, EXCMD *));
47 */
48int
49ex_read(SCR *sp, EXCMD *cmdp)
50{
51	enum { R_ARG, R_EXPANDARG, R_FILTER } which;
52	struct stat sb;
53	CHAR_T *arg = NULL;
54	const char *name;
55	size_t nlen;
56	EX_PRIVATE *exp;
57	FILE *fp;
58	FREF *frp;
59	GS *gp;
60	MARK rm;
61	db_recno_t nlines;
62	size_t arglen = 0;
63	int argc, rval;
64	char *p;
65
66	gp = sp->gp;
67
68	/*
69	 * 0 args: read the current pathname.
70	 * 1 args: check for "read !arg".
71	 */
72	switch (cmdp->argc) {
73	case 0:
74		which = R_ARG;
75		break;
76	case 1:
77		arg = cmdp->argv[0]->bp;
78		arglen = cmdp->argv[0]->len;
79		if (*arg == '!') {
80			++arg;
81			--arglen;
82			which = R_FILTER;
83
84			/* Secure means no shell access. */
85			if (O_ISSET(sp, O_SECURE)) {
86				ex_wemsg(sp, cmdp->cmd->name, EXM_SECURE_F);
87				return (1);
88			}
89		} else
90			which = R_EXPANDARG;
91		break;
92	default:
93		abort();
94		/* NOTREACHED */
95	}
96
97	/* Load a temporary file if no file being edited. */
98	if (sp->ep == NULL) {
99		if ((frp = file_add(sp, NULL)) == NULL)
100			return (1);
101		if (file_init(sp, frp, NULL, 0))
102			return (1);
103	}
104
105	switch (which) {
106	case R_FILTER:
107		/*
108		 * File name and bang expand the user's argument.  If
109		 * we don't get an additional argument, it's illegal.
110		 */
111		argc = cmdp->argc;
112		if (argv_exp1(sp, cmdp, arg, arglen, 1))
113			return (1);
114		if (argc == cmdp->argc) {
115			ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
116			return (1);
117		}
118		argc = cmdp->argc - 1;
119
120		/* Set the last bang command. */
121		exp = EXP(sp);
122		if (exp->lastbcomm != NULL)
123			free(exp->lastbcomm);
124		if ((exp->lastbcomm =
125		    v_wstrdup(sp, cmdp->argv[argc]->bp,
126				cmdp->argv[argc]->len)) == NULL) {
127			msgq(sp, M_SYSERR, NULL);
128			return (1);
129		}
130
131		/*
132		 * Vi redisplayed the user's argument if it changed, ex
133		 * always displayed a !, plus the user's argument if it
134		 * changed.
135		 */
136		if (F_ISSET(sp, SC_VI)) {
137			if (F_ISSET(cmdp, E_MODIFY))
138				(void)vs_update(sp, "!", cmdp->argv[argc]->bp);
139		} else {
140			if (F_ISSET(cmdp, E_MODIFY)) {
141				INT2CHAR(sp, cmdp->argv[argc]->bp,
142				    cmdp->argv[argc]->len + 1, name, nlen);
143				(void)ex_printf(sp,
144				    "!%s\n", name);
145			} else
146				(void)ex_puts(sp, "!\n");
147			(void)ex_fflush(sp);
148		}
149
150		/*
151		 * Historically, filter reads as the first ex command didn't
152		 * wait for the user. If SC_SCR_EXWROTE not already set, set
153		 * the don't-wait flag.
154		 */
155		if (!F_ISSET(sp, SC_SCR_EXWROTE))
156			F_SET(sp, SC_EX_WAIT_NO);
157
158		/*
159		 * Switch into ex canonical mode.  The reason to restore the
160		 * original terminal modes for read filters is so that users
161		 * can do things like ":r! cat /dev/tty".
162		 *
163		 * !!!
164		 * We do not output an extra <newline>, so that we don't touch
165		 * the screen on a normal read.
166		 */
167		if (F_ISSET(sp, SC_VI)) {
168			if (gp->scr_screen(sp, SC_EX)) {
169				ex_wemsg(sp, cmdp->cmd->name, EXM_NOCANON_F);
170				return (1);
171			}
172			/*
173			 * !!!
174			 * Historically, the read command doesn't switch to
175			 * the alternate X11 xterm screen, if doing a filter
176			 * read -- don't set SA_ALTERNATE.
177			 */
178			F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE);
179		}
180
181		if (ex_filter(sp, cmdp, &cmdp->addr1,
182		    NULL, &rm, cmdp->argv[argc]->bp, FILTER_READ))
183			return (1);
184
185		/* The filter version of read set the autoprint flag. */
186		F_SET(cmdp, E_AUTOPRINT);
187
188		/*
189		 * If in vi mode, move to the first nonblank.  Might have
190		 * switched into ex mode, so saved the original SC_VI value.
191		 */
192		sp->lno = rm.lno;
193		if (F_ISSET(sp, SC_VI)) {
194			sp->cno = 0;
195			(void)nonblank(sp, sp->lno, &sp->cno);
196		}
197		return (0);
198	case R_ARG:
199		name = sp->frp->name;
200		break;
201	case R_EXPANDARG:
202		if (argv_exp2(sp, cmdp, arg, arglen))
203			return (1);
204		/*
205		 *  0 args: impossible.
206		 *  1 args: impossible (I hope).
207		 *  2 args: read it.
208		 * >2 args: object, too many args.
209		 *
210		 * The 1 args case depends on the argv_sexp() function refusing
211		 * to return success without at least one non-blank character.
212		 */
213		switch (cmdp->argc) {
214		case 0:
215		case 1:
216			abort();
217			/* NOTREACHED */
218		case 2:
219			INT2CHAR(sp, cmdp->argv[1]->bp, cmdp->argv[1]->len + 1,
220				 name, nlen);
221			/*
222			 * !!!
223			 * Historically, the read and write commands renamed
224			 * "unnamed" files, or, if the file had a name, set
225			 * the alternate file name.
226			 */
227			if (F_ISSET(sp->frp, FR_TMPFILE) &&
228			    !F_ISSET(sp->frp, FR_EXNAMED)) {
229				if ((p = strdup(name)) != NULL) {
230					free(sp->frp->name);
231					sp->frp->name = p;
232				}
233				/*
234				 * The file has a real name, it's no longer a
235				 * temporary, clear the temporary file flags.
236				 */
237				F_CLR(sp->frp, FR_TMPEXIT | FR_TMPFILE);
238				F_SET(sp->frp, FR_NAMECHANGE | FR_EXNAMED);
239
240				/* Notify the screen. */
241				(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
242				name = sp->frp->name;
243			} else {
244				set_alt_name(sp, name);
245				name = sp->alt_name;
246			}
247			break;
248		default:
249			ex_wemsg(sp, cmdp->argv[0]->bp, EXM_FILECOUNT);
250			return (1);
251
252		}
253		break;
254	}
255
256	/*
257	 * !!!
258	 * Historically, vi did not permit reads from non-regular files, nor
259	 * did it distinguish between "read !" and "read!", so there was no
260	 * way to "force" it.  We permit reading from named pipes too, since
261	 * they didn't exist when the original implementation of vi was done
262	 * and they seem a reasonable addition.
263	 */
264	if ((fp = fopen(name, "r")) == NULL || fstat(fileno(fp), &sb)) {
265		msgq_str(sp, M_SYSERR, name, "%s");
266		return (1);
267	}
268	if (!S_ISFIFO(sb.st_mode) && !S_ISREG(sb.st_mode)) {
269		(void)fclose(fp);
270		msgq(sp, M_ERR,
271		    "145|Only regular files and named pipes may be read");
272		return (1);
273	}
274
275	/* Try and get a lock. */
276	if (file_lock(sp, NULL, NULL, fileno(fp), 0) == LOCK_UNAVAIL)
277		msgq(sp, M_ERR, "146|%s: read lock was unavailable", name);
278
279	rval = ex_readfp(sp, name, fp, &cmdp->addr1, &nlines, 0);
280
281	/*
282	 * In vi, set the cursor to the first line read in, if anything read
283	 * in, otherwise, the address.  (Historic vi set it to the line after
284	 * the address regardless, but since that line may not exist we don't
285	 * bother.)
286	 *
287	 * In ex, set the cursor to the last line read in, if anything read in,
288	 * otherwise, the address.
289	 */
290	if (F_ISSET(sp, SC_VI)) {
291		sp->lno = cmdp->addr1.lno;
292		if (nlines)
293			++sp->lno;
294	} else
295		sp->lno = cmdp->addr1.lno + nlines;
296	return (rval);
297}
298
299/*
300 * ex_readfp --
301 *	Read lines into the file.
302 *
303 * PUBLIC: int ex_readfp __P((SCR *, const char *, FILE *, MARK *, db_recno_t *, int));
304 */
305int
306ex_readfp(SCR *sp, const char *name, FILE *fp, MARK *fm, db_recno_t *nlinesp, int silent)
307{
308	EX_PRIVATE *exp;
309	GS *gp;
310	db_recno_t lcnt, lno;
311	size_t len;
312	u_long ccnt;			/* XXX: can't print off_t portably. */
313	int nf, rval;
314	const char *p;
315	size_t wlen;
316	const CHAR_T *wp;
317
318	gp = sp->gp;
319	exp = EXP(sp);
320
321	/*
322	 * Add in the lines from the output.  Insertion starts at the line
323	 * following the address.
324	 */
325	ccnt = 0;
326	lcnt = 0;
327	p = "147|Reading...";
328	for (lno = fm->lno; !ex_getline(sp, fp, &len); ++lno, ++lcnt) {
329		if ((lcnt + 1) % INTERRUPT_CHECK == 0) {
330			if (INTERRUPTED(sp))
331				break;
332			if (!silent) {
333				gp->scr_busy(sp, p,
334				    p == NULL ? BUSY_UPDATE : BUSY_ON);
335				p = NULL;
336			}
337		}
338		FILE2INT5(sp, exp->ibcw, exp->ibp, len, wp, wlen);
339		if (db_append(sp, 1, lno, wp, wlen))
340			goto err;
341		ccnt += len;
342	}
343
344	if (ferror(fp) || fclose(fp))
345		goto err;
346
347	/* Return the number of lines read in. */
348	if (nlinesp != NULL)
349		*nlinesp = lcnt;
350
351	if (!silent) {
352		char *q = msg_print(sp, name, &nf);
353		msgq(sp, M_INFO,
354		    "148|%s: %lu lines, %lu characters", q, (unsigned long)lcnt,
355		    (unsigned long)ccnt);
356		if (nf)
357			FREE_SPACE(sp, q, 0);
358	}
359
360	rval = 0;
361	if (0) {
362err:		msgq_str(sp, M_SYSERR, name, "%s");
363		(void)fclose(fp);
364		rval = 1;
365	}
366
367	if (!silent)
368		gp->scr_busy(sp, NULL, BUSY_OFF);
369	return (rval);
370}
371