]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - ex/ex_read.c
Import nvi 2.2.0
[FreeBSD/FreeBSD.git] / ex / ex_read.c
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 }