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