xref: /dragonfly/contrib/nvi2/ex/ex_read.c (revision 07bc39c2f4bbca56f12568e06d89da17f2eeb965)
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
ex_read(SCR * sp,EXCMD * cmdp)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
ex_readfp(SCR * sp,char * name,FILE * fp,MARK * fm,recno_t * nlinesp,int silent)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