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