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