1 /***********************************************************************
2  *                                                                      *
3  *               This software is part of the ast package               *
4  *          Copyright (c) 1982-2014 AT&T Intellectual Property          *
5  *                      and is licensed under the                       *
6  *                 Eclipse Public License, Version 1.0                  *
7  *                    by AT&T Intellectual Property                     *
8  *                                                                      *
9  *                A copy of the License is available at                 *
10  *          http://www.eclipse.org/org/documents/epl-v10.html           *
11  *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12  *                                                                      *
13  *              Information and Software Systems Research               *
14  *                            AT&T Research                             *
15  *                           Florham Park NJ                            *
16  *                                                                      *
17  *                    David Korn <dgkorn@gmail.com>                     *
18  *                                                                      *
19  ***********************************************************************/
20 #include "config_ast.h"  // IWYU pragma: keep
21 
22 #include <ctype.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <setjmp.h>
26 #include <stdbool.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <termios.h>
31 
32 #include "ast.h"
33 #include "ast_assert.h"
34 #include "builtins.h"
35 #include "cdt.h"
36 #include "defs.h"
37 #include "edit.h"
38 #include "error.h"
39 #include "fault.h"
40 #include "history.h"
41 #include "io.h"
42 #include "lexstates.h"
43 #include "name.h"
44 #include "optget_long.h"
45 #include "sfio.h"
46 #include "shcmd.h"
47 #include "stk.h"
48 #include "terminal.h"
49 #include "variables.h"
50 
51 #define R_FLAG 1      // raw mode
52 #define S_FLAG 2      // save in history file
53 #define A_FLAG 4      // read into array
54 #define N_FLAG 8      // fixed size read at most
55 #define NN_FLAG 0x10  // fixed size read exact
56 #define V_FLAG 0x20   // use default value
57 #define C_FLAG 0x40   // read into compound variable
58 #define D_FLAG 8      // must be number of bits for all flags
59 #define SS_FLAG 0x80  // read .csv format file
60 
61 struct read_save {
62     char **argv;
63     char *prompt;
64     short fd;
65     short plen;
66     int flags;
67     int mindex;
68     ssize_t len;
69     long timeout;
70 };
71 
72 struct Method {
73     char *name;
74     void *fun;
75 };
76 
77 // Support for json in `read` builtin seems very unstable, so this code is kept disabled.
78 // See https://github.com/att/ast/issues/820
79 #if SUPPORT_JSON
json2sh(Shell_t * shp,Sfio_t * in,Sfio_t * out)80 static_fn int json2sh(Shell_t *shp, Sfio_t *in, Sfio_t *out) {
81     int c, state = 0, lastc = 0, level = 0, line = 1;
82     size_t here, offset = stktell(shp->stk);
83     char *start;
84     bool isname, isnull = false;
85 
86     while ((c = sfgetc(in)) > 0) {
87         if (c == '\n') line++;
88         if (state == 0) {
89             switch (c) {
90                 case '\t':
91                 case ' ': {
92                     if (lastc == ' ' || lastc == '\t') continue;
93                     break;
94                 }
95                 case ',': {
96                     continue;
97                 }
98                 case '[':
99                 case '{': {
100                     c = '(';
101                     level++;
102                     break;
103                 }
104                 case ']':
105                 case '}': {
106                     c = ')';
107                     level--;
108                     break;
109                 }
110                 case '"': {
111                     state = 1;
112                     isname = true;
113                     sfputc(shp->stk, c);
114                     continue;
115                 }
116                 default: { break; }
117             }
118             sfputc(out, c);
119             if (level == 0) break;
120         } else if (state == 1) {
121             if (c == '"' && lastc != '\\') {
122                 state = 2;
123             } else if (state == 1 && !isalnum(c) && c != '_') {
124                 isname = false;
125             }
126             sfputc(shp->stk, c);
127         } else if (state == 2) {
128             if (c == ' ' || c == '\t') continue;
129             if (c == ':') {
130                 int len;
131                 while ((c = sfgetc(in)) && iswblank(c)) {
132                     ;  // empty loop
133                 }
134                 sfungetc(in, c);
135                 if (!strchr("[{,\"", c)) {
136                     if (isdigit(c) || c == '.' || c == '-') {
137                         sfwrite(out, "float ", 6);
138                     } else if (c == 't' || c == 'f') {
139                         sfwrite(out, "bool ", 5);
140                     } else if (c == 'n') {
141                         char buff[4];
142                         isnull = true;
143                         sfread(in, buff, 4);
144                         sfwrite(out, "typeset  ", 8);
145                     }
146                 }
147                 start = stkptr(shp->stk, offset);
148                 here = stktell(shp->stk);
149                 if (isname && !iswalpha(*(start + 1)) && c != '_') isname = false;
150                 len = here - offset - 2;
151                 if (!isname) {
152                     char *sp;
153                     sfwrite(out, ".[", 2);
154                     for (sp = start + 1; len-- > 0; sp++) {
155                         if (*sp == '$') {
156                             if (sp > start + 1) sfwrite(out, start, sp - start);
157                             sfputc(out, '\\');
158                             sfputc(out, '$');
159                             start = sp;
160                         }
161                     }
162                     len = (sp - start) - 1;
163                 }
164                 sfwrite(out, start + 1, len);
165                 if (!isname) sfputc(out, ']');
166                 if (isnull) {
167                     isnull = false;
168                 } else {
169                     sfputc(out, '=');
170                 }
171                 stkseek(shp->stk, offset);
172                 if (c == '{') c = ' ';
173             }
174             if (c == ',' || c == '\n' || c == '}' || c == ']' || c == '{') {
175                 start = stkptr(shp->stk, offset);
176                 here = stktell(shp->stk);
177                 if (here > 1) {
178                     *stkptr(shp->stk, here - 1) = 0;
179                     stresc(start + 1);
180                     sfputr(out, sh_fmtq(start + 1), -1);
181                     stkseek(shp->stk, offset);
182                 }
183                 if (c == '{') {
184                     sfputc(out, '=');
185                 } else {
186                     sfputc(out, ' ');
187                 }
188                 if (c == '}' || c == ']' || c == '{') sfungetc(in, c);
189             }
190             c = ' ';
191             state = 0;
192         }
193         lastc = c;
194     }
195     return 0;
196 }
197 #endif
198 
199 static struct Method methods[] = {
200 #if SUPPORT_JSON
201     {"json", json2sh},
202 #endif
203     {"ksh", NULL},
204     {NULL, NULL}};
205 
206 static const char *short_options = "ad:n:p:rsu:t:vACN:S";
207 static const struct optget_option long_options[] = {
208     {"help", optget_no_arg, NULL, 1},  // all builtins support --help
209     {NULL, 0, NULL, 0}};
210 
211 //
212 // Builtin `read` command.
213 //
b_read(int argc,char * argv[],Shbltin_t * context)214 int b_read(int argc, char *argv[], Shbltin_t *context) {
215     Sfdouble_t sec;
216     char *name = NULL;
217     int opt, r, flags = 0, fd = 0;
218     Shell_t *shp = context->shp;
219     char *cmd = argv[0];
220     ssize_t len = 0;
221     long timeout = 1000 * shp->st.tmout;
222     int save_prompt, fixargs = context->invariant;
223     struct read_save *rp;
224     int mindex = 0;
225     char *method = NULL;
226     void *readfn = NULL;
227     static char default_prompt[3] = {ESC, ESC};
228 
229     rp = (struct read_save *)(context->data);
230     if (argc == 0) {
231         if (rp) free(rp);
232         return 0;
233     }
234     if (rp) {
235         flags = rp->flags;
236         timeout = rp->timeout;
237         fd = rp->fd;
238         argv = rp->argv;
239         name = rp->prompt;
240         mindex = rp->mindex;
241         r = rp->plen;
242         goto bypass;
243     }
244 
245     optget_ind = 0;
246     while ((opt = optget_long(argc, argv, short_options, long_options)) != -1) {
247         switch (opt) {
248             case 1: {
249                 builtin_print_help(shp, cmd);
250                 return 0;
251             }
252             case 'a': {
253                 // `read -a` is an alias for `read -A`
254                 flags |= A_FLAG;
255                 break;
256             }
257             case 'A': {
258                 flags |= A_FLAG;
259                 break;
260             }
261             case 'C': {
262                 flags |= C_FLAG;
263                 method = "ksh";
264                 break;
265             }
266             case 't': {
267                 sec = sh_strnum(shp, optget_arg, NULL, 1);
268                 timeout = sec ? 1000 * sec : 1;
269                 break;
270             }
271             case 'd': {
272                 flags &= ((1 << (D_FLAG + 1)) - 1);
273                 flags |= (mb1char(&optget_arg) << (D_FLAG + 1)) | (1 << D_FLAG);
274                 break;
275             }
276             case 'p': {
277                 if (shp->cpipe[0] <= 0 || (*optget_arg != '-' && (!strmatch(optget_arg, "+(\\w)") ||
278                                                                   isdigit(*optget_arg)))) {
279                     name = optget_arg;
280                 } else {
281                     // For backward compatibility.
282                     fd = shp->cpipe[0];
283                     argv--;
284                     argc--;
285                 }
286                 break;
287             }
288 #if SUPPORT_JSON
289             case 'm': {
290                 method = optget_arg;
291                 flags |= C_FLAG;
292                 break;
293             }
294 #endif
295             case 'n':
296             case 'N': {
297                 char *cp;
298                 int64_t n = strton64(optget_arg, &cp, NULL, 0);
299                 if (*cp || n < 0 || n > INT_MAX) {
300                     builtin_usage_error(shp, cmd, "%s: invalid -%c value", optget_arg, optget_opt);
301                     return 2;
302                 }
303                 len = n;
304                 flags &= ((1 << D_FLAG) - 1);
305                 flags |= (opt == 'n' ? N_FLAG : NN_FLAG);
306                 break;
307             }
308             case 'r': {
309                 flags |= R_FLAG;
310                 break;
311             }
312             case 's': {
313                 // Save in history file.
314                 flags |= S_FLAG;
315                 break;
316             }
317             case 'S': {
318                 flags |= SS_FLAG;
319                 break;
320             }
321             case 'u': {
322                 if (optget_arg[0] == 'p' && optget_arg[1] == 0) {
323                     fd = shp->cpipe[0];
324                     if (fd <= 0) {
325                         errormsg(SH_DICT, ERROR_exit(1), e_query);
326                         __builtin_unreachable();
327                     }
328                     break;
329                 }
330 
331                 char *cp;
332                 int64_t n = strton64(optget_arg, &cp, NULL, 0);
333                 if (*cp || n < 0 || n > INT_MAX || !sh_iovalidfd(shp, n)) {
334                     builtin_usage_error(shp, cmd, "%s: invalid -u value", optget_arg);
335                     return 2;
336                 }
337                 fd = n;
338                 break;
339             }
340             case 'v': {
341                 flags |= V_FLAG;
342                 break;
343             }
344             case ':': {
345                 if (optget_opt == 'p' && shp->cpipe[0] > 0) {
346                     fd = shp->cpipe[0];
347                     break;
348                 }
349                 builtin_missing_argument(shp, cmd, argv[optget_ind - 1]);
350                 return 2;
351             }
352             case '?': {
353                 builtin_unknown_option(shp, cmd, argv[optget_ind - 1]);
354                 return 2;
355             }
356             default: { abort(); }
357         }
358     }
359     argv += optget_ind;
360 
361     if (method) {
362         for (mindex = 0; methods[mindex].name; mindex++) {
363             if (strcmp(method, methods[mindex].name) == 0) break;
364         }
365         if (!methods[mindex].name) {
366             errormsg(SH_DICT, ERROR_system(1), "%s method not supported", method);
367             __builtin_unreachable();
368         }
369     }
370 
371     if (!((r = shp->fdstatus[fd]) & IOREAD) || !(r & (IOSEEK | IONOSEEK))) {
372         assert(fd >= 0);  // there is a theoretical path which could reach here with fd == -1
373         r = sh_iocheckfd(shp, fd, fd);
374     }
375     if (fd < 0 || !(r & IOREAD)) {
376         errormsg(SH_DICT, ERROR_system(1), e_file + 4);
377         __builtin_unreachable();
378     }
379     // Look for prompt.
380     if (!name && *argv && (name = strchr(*argv, '?'))) name++;
381     if (name && (r & IOTTY)) {
382         r = strlen(name) + 1;
383     } else {
384         r = 0;
385     }
386     if (argc == fixargs && (rp = calloc(1, sizeof(struct read_save)))) {
387         context->data = rp;
388         rp->fd = fd;
389         rp->flags = flags;
390         rp->timeout = timeout;
391         rp->argv = argv;
392         rp->prompt = name;
393         rp->plen = r;
394         rp->len = len;
395         rp->mindex = mindex;
396     }
397 
398 bypass:
399     shp->prompt = default_prompt;
400     if (r && (shp->prompt = (char *)sfreserve(sfstderr, r, SF_LOCKR))) {
401         memcpy(shp->prompt, name, r);
402         sfwrite(sfstderr, shp->prompt, r - 1);
403     }
404     shp->timeout = 0;
405     save_prompt = shp->nextprompt;
406     shp->nextprompt = 0;
407     readfn = (flags & C_FLAG) ? methods[mindex].fun : 0;
408     r = sh_readline(shp, argv, readfn, fd, flags, len, timeout);
409     shp->nextprompt = save_prompt;
410     if (r == 0) {
411         r = (sfeof(shp->sftable[fd]) || sferror(shp->sftable[fd]));
412         if (r && fd == shp->cpipe[0] && errno != EINTR) sh_pclose(shp->cpipe);
413     }
414     return r;
415 }
416 
417 struct timeout {
418     Shell_t *shp;
419     Sfio_t *iop;
420 };
421 
422 //
423 // Here for read timeout.
424 //
timedout(void * handle)425 static_fn void timedout(void *handle) {
426     struct timeout *tp = (struct timeout *)handle;
427     sfclrlock(tp->iop);
428     sh_exit(tp->shp, 1);
429 }
430 
431 //
432 // This is the code to read a line and to split it into tokens.
433 // <names> is an array of variable names.
434 // <fd> is the file descriptor.
435 // <flags> is union of -A, -r, -s, and contains delimiter if not '\n'.
436 // <timeout> is number of milli-seconds until timeout.
437 //
sh_readline(Shell_t * shp,char ** names,void * readfn,volatile int fd,int flags,ssize_t size,long timeout)438 int sh_readline(Shell_t *shp, char **names, void *readfn, volatile int fd, int flags, ssize_t size,
439                 long timeout) {
440     ssize_t c;
441     unsigned char *cp;
442     char *name, *val;
443     Sfio_t *iop;
444     Namfun_t *nfp;
445     unsigned char *cpmax;
446     unsigned char *del;
447     char *ifs = NULL;
448     Namval_t *np = NULL;
449     Namval_t *nq = NULL;
450     char was_escape = 0;
451     char use_stak = 0;
452     volatile char was_write = 0;
453     volatile char was_share = 1;
454     volatile int keytrap;
455     int rel, wrd;
456     long array_index = 0;
457     Timer_t *timeslot = NULL;
458     int delim = '\n';
459     int jmpval = 0;
460     nvflag_t oflags = NV_ASSIGN | NV_VARNAME;
461     bool inquote = false;
462     checkpt_t buff;
463     Edit_t *ep = (struct edit *)shp->gd->ed_context;
464 
465     if (!(iop = shp->sftable[fd]) && !(iop = sh_iostream(shp, fd, fd))) return 1;
466 
467     memset(&buff, 0, sizeof(buff));
468     sh_stats(STAT_READS);
469     if (names && (name = *names)) {
470         Namval_t *mp = NULL;
471         val = strchr(name, '?');
472         if (val) *val = 0;
473         if (flags & C_FLAG) oflags |= NV_ARRAY;
474         np = nv_open(name, shp->var_tree, oflags);
475         if (np && nv_isarray(np) && (mp = nv_opensub(np))) np = mp;
476         if ((flags & V_FLAG) && shp->gd->ed_context) {
477             ((struct edit *)shp->gd->ed_context)->e_default = np;
478         }
479         if (flags & A_FLAG) {
480             Namarr_t *ap;
481             flags &= ~A_FLAG;
482             array_index = 1;
483             if ((ap = nv_arrayptr(np)) && !ap->fun) ap->nelem++;
484             nv_unset(np);
485             if ((ap = nv_arrayptr(np)) && !ap->fun) ap->nelem--;
486             nv_putsub(np, NULL, 0L, 0);
487         } else if (flags & C_FLAG) {
488             Namval_t *nvenv = np->nvenv;
489             if (strchr(name, '[')) nq = np;
490             delim = -1;
491             nv_unset(np);
492             if (!nv_isattr(np, NV_MINIMAL)) np->nvenv = nvenv;
493             nv_setvtree(np);
494             *(void **)(np->nvfun + 1) = readfn;
495         } else {
496             name = *++names;
497         }
498         if (val) *val = '?';
499     } else {
500         name = 0;
501         if (dtvnext(shp->var_tree) || shp->namespace) {
502             np = nv_open(nv_name(VAR_REPLY), shp->var_tree, 0);
503         } else {
504             np = VAR_REPLY;
505         }
506     }
507     keytrap = ep ? ep->e_keytrap : 0;
508     if (size || (flags >> D_FLAG)) {  // delimiter not new-line or fixed size read
509         if ((shp->fdstatus[fd] & IOTTY) && !keytrap) tty_raw(sffileno(iop), 1);
510         if (!(flags & (N_FLAG | NN_FLAG))) {
511             delim = ((unsigned)flags) >> (D_FLAG + 1);
512             ep->e_nttyparm.c_cc[VEOL] = delim;
513             ep->e_nttyparm.c_lflag |= ISIG;
514             tty_set(sffileno(iop), TCSADRAIN, &ep->e_nttyparm);
515         }
516     }
517     bool binary = nv_isattr(np, NV_BINARY) == NV_BINARY;
518     if (!binary && !(flags & (N_FLAG | NN_FLAG))) {
519         Namval_t *mp;
520         // Set up state table based on IFS.
521         ifs = nv_getval(mp = sh_scoped(shp, VAR_IFS));
522         if ((flags & R_FLAG) && shp->ifstable['\\'] == S_ESC) {
523             shp->ifstable['\\'] = 0;
524         } else if (!(flags & R_FLAG) && shp->ifstable['\\'] == 0) {
525             shp->ifstable['\\'] = S_ESC;
526         }
527         if (delim > 0) shp->ifstable[delim] = S_NL;
528         if (delim != '\n') {
529             if (ifs && strchr(ifs, '\n')) {
530                 shp->ifstable['\n'] = S_DELIM;
531             } else {
532                 shp->ifstable['\n'] = 0;
533             }
534             nv_putval(mp, ifs, NV_RDONLY);
535         }
536         shp->ifstable[0] = S_EOF;
537         if ((flags & SS_FLAG)) {
538             shp->ifstable['"'] = S_QUOTE;
539             shp->ifstable['\r'] = S_ERR;
540         }
541     }
542     sfclrerr(iop);
543     for (nfp = np->nvfun; nfp; nfp = nfp->next) {
544         if (nfp->disc && nfp->disc->readf) {
545             Namval_t *mp;
546             if (nq) {
547                 mp = nq;
548             } else {
549                 mp = nv_open(name, shp->var_tree, oflags | NV_NOREF);
550             }
551             if ((c = (*nfp->disc->readf)(mp, iop, delim, nfp)) >= 0) return c;
552         }
553     }
554     if (binary && !(flags & (N_FLAG | NN_FLAG))) {
555         flags |= NN_FLAG;
556         size = nv_size(np);
557     }
558     was_write = (sfset(iop, SF_WRITE, 0) & SF_WRITE) != 0;
559     if (sffileno(iop) == 0) was_share = (sfset(iop, SF_SHARE, shp->redir0 != 2) & SF_SHARE) != 0;
560     if (timeout || (shp->fdstatus[fd] & (IOTTY | IONOSEEK))) {
561         sh_pushcontext(shp, &buff, 1);
562         jmpval = sigsetjmp(buff.buff, 0);
563         if (jmpval) goto done;
564         if (timeout) {
565             // WARNING: This assumes that ksh is single-threaded and this block will not be run
566             // concurrently by multiple threads. It can't be a stack var because the address of
567             // the var is passed to `timedout()` after this scope has exited.
568             static struct timeout tmout;
569             tmout.shp = shp;
570             tmout.iop = iop;
571             timeslot = sh_timeradd(timeout, 0, timedout, &tmout);
572         }
573     }
574     if (flags & (N_FLAG | NN_FLAG)) {
575         char buf[256], *cur, *end, *up, *v;
576         char *var = buf;
577 
578         // Reserved buffer.
579         if ((c = size) >= sizeof(buf)) {
580             var = malloc(c + 1);
581             end = var + c;
582         } else {
583             end = var + sizeof(buf) - 1;
584         }
585         up = cur = var;
586         if ((sfset(iop, SF_SHARE, 1) & SF_SHARE) && sffileno(iop) != 0) was_share = 1;
587         if (size == 0) {
588             (void)sfreserve(iop, 0, 0);
589             c = 0;
590         } else {
591             ssize_t m;
592             int f;
593             for (;;) {
594                 c = size;
595                 if (keytrap) {
596                     cp = NULL;
597                     f = 0;
598                     m = 0;
599                     while (c-- > 0 && (buf[m] = ed_getchar(ep, 0))) m++;
600                     if (m > 0) cp = (unsigned char *)buf;
601                 } else {
602                     f = 1;
603                     cp = sfreserve(iop, c, SF_LOCKR);
604                     if (cp) {
605                         m = sfvalue(iop);
606                     } else if (flags & NN_FLAG) {
607                         c = size;
608                         m = (cp = sfreserve(iop, c, 0)) ? sfvalue(iop) : 0;
609                         f = 0;
610                     } else {
611                         c = sfvalue(iop);
612                         m = (cp = sfreserve(iop, c, SF_LOCKR)) ? sfvalue(iop) : 0;
613                     }
614                 }
615                 if (m > 0 && (flags & N_FLAG) && !binary && (v = memchr(cp, '\n', m))) {
616                     *v++ = 0;
617                     m = v - (char *)cp;
618                 }
619                 if ((c = m) > size) c = size;
620                 if (c > 0) {
621                     if (c > (end - cur)) {
622                         ssize_t cx = cur - var, ux = up - var;
623                         m = (end - var) + (c - (end - cur));
624                         if (var == buf) {
625                             v = malloc(m + 1);
626                             var = memcpy(v, var, cur - var);
627                         } else {
628                             var = realloc(var, m + 1);
629                         }
630                         end = var + m;
631                         cur = var + cx;
632                         up = var + ux;
633                     }
634                     // There is a theoretical path to here where `cur` could be NULL. That should
635                     // never happen so assert it can't happen. Coverity CID#340037.
636                     assert(cur);
637                     if (cur != (char *)cp) memcpy(cur, cp, c);
638                     if (f) sfread(iop, cp, c);
639                     cur += c;
640                     if (!binary && mbwide()) {
641                         int x;
642                         int z;
643 
644                         *cur = 0;
645                         x = z = 0;
646                         while (up < cur && (z = mblen(up, MB_CUR_MAX)) > 0) {
647                             up += z;
648                             x++;
649                         }
650                         if ((size -= x) > 0 && (up >= cur || z < 0) &&
651                             ((flags & NN_FLAG) || z < 0 || m > c)) {
652                             continue;
653                         }
654                     }
655                 }
656                 if (!binary && mbwide() && (up == var || ((flags & NN_FLAG) && size))) cur = var;
657                 *cur = 0;
658                 if (c >= size || (flags & N_FLAG) || m == 0) {
659                     if (m) sfclrerr(iop);
660                     break;
661                 }
662                 size -= c;
663             }
664         }
665         if (timeslot) timerdel(timeslot);
666         if (binary && !((size = nv_size(np)) && nv_isarray(np) && c != size)) {
667             int optimize = !np->nvfun || !nv_hasdisc(np, &OPTIMIZE_disc);
668             char *cp = FETCH_VT(np->nvalue, cp);
669             if (optimize && (c == size) && cp && !nv_isarray(np)) {
670                 memcpy(cp, var, c);
671             } else {
672                 Namval_t *mp;
673                 if (var == buf) var = memdup(var, c + 1);
674                 nv_putval(np, var, NV_RAW);
675                 nv_setsize(np, c);
676                 if (!nv_isattr(np, NV_IMPORT | NV_EXPORT) && (mp = np->nvenv)) {
677                     nv_setsize(mp, c);
678                 }
679             }
680         } else {
681             nv_putval(np, var, 0);
682             if (var != buf) free(var);
683         }
684         goto done;
685     } else if ((cp = (unsigned char *)sfgetr(iop, delim, 0))) {
686         c = sfvalue(iop);
687     } else if ((cp = (unsigned char *)sfgetr(iop, delim, -1))) {
688         c = sfvalue(iop) + 1;
689         if (!sferror(iop) && sfgetc(iop) >= 0) {
690             errormsg(SH_DICT, ERROR_exit(1), e_overlimit, "line length");
691             __builtin_unreachable();
692         }
693     }
694     if (timeslot) timerdel(timeslot);
695     if ((flags & S_FLAG) && !shp->gd->hist_ptr) {
696         sh_histinit(shp);
697         if (!shp->gd->hist_ptr) flags &= ~S_FLAG;
698     }
699     if (cp) {
700         cpmax = cp + c;
701         if (*(cpmax - 1) != delim) *(cpmax - 1) = delim;
702         if (flags & S_FLAG) sfwrite(shp->gd->hist_ptr->histfp, (char *)cp, c);
703         c = shp->ifstable[*cp++];
704     } else {
705         c = S_NL;
706     }
707     shp->nextprompt = 2;
708     rel = stktell(shp->stk);
709     // val==0 at the start of a field.
710     val = 0;
711     del = 0;
712     while (1) {
713         switch (c) {
714             case S_MBYTE: {
715                 if (val == 0) val = (char *)(cp - 1);
716                 if (sh_strchr(ifs, (char *)cp - 1, cpmax - cp + 1) >= 0) {
717                     c = mblen((char *)cp - 1, MB_CUR_MAX);
718                     if (name) cp[-1] = 0;
719                     if (c > 1) cp += (c - 1);
720                     c = S_DELIM;
721                 } else {
722                     c = 0;
723                 }
724                 continue;
725             }
726             case S_QUOTE: {
727                 c = shp->ifstable[*cp++];
728                 if (inquote && c == S_QUOTE) {
729                     c = -1;
730                 } else {
731                     inquote = !inquote;
732                 }
733                 if (val) {
734                     sfputr(shp->stk, val, -1);
735                     use_stak = 1;
736                     *val = 0;
737                 }
738                 if (c == -1) {
739                     sfputc(shp->stk, '"');
740                     c = shp->ifstable[*cp++];
741                 }
742                 continue;
743             }
744             case S_ESC: {
745                 // Process escape character.
746                 if ((c = shp->ifstable[*cp++]) == S_NL) {
747                     was_escape = 1;
748                 } else {
749                     c = 0;
750                 }
751                 if (val) {
752                     sfputr(shp->stk, val, -1);
753                     use_stak = 1;
754                     was_escape = 1;
755                     *val = 0;
756                 }
757                 continue;
758             }
759             case S_ERR: {
760                 cp++;
761             }
762             // FALLTHRU
763             case S_EOF: {
764                 // Check for end of buffer.
765                 if (val && *val) {
766                     sfputr(shp->stk, val, -1);
767                     use_stak = 1;
768                 }
769                 val = 0;
770                 if (cp >= cpmax) {
771                     c = S_NL;
772                     break;
773                 }
774                 // Eliminate null bytes.
775                 c = shp->ifstable[*cp++];
776                 if (!name && val && (c == S_SPACE || c == S_DELIM || c == S_MBYTE)) c = 0;
777                 continue;
778             }
779             case S_NL: {
780                 if (was_escape) {
781                     was_escape = 0;
782                     cp = (unsigned char *)sfgetr(iop, delim, 0);
783                     if (cp) {
784                         c = sfvalue(iop);
785                     } else if ((cp = (unsigned char *)sfgetr(iop, delim, -1))) {
786                         c = sfvalue(iop) + 1;
787                     }
788                     if (cp) {
789                         if (flags & S_FLAG) sfwrite(shp->gd->hist_ptr->histfp, (char *)cp, c);
790                         cpmax = cp + c;
791                         c = shp->ifstable[*cp++];
792                         val = 0;
793                         if (!name && (c == S_SPACE || c == S_DELIM || c == S_MBYTE)) c = 0;
794                         continue;
795                     }
796                 }
797                 c = S_NL;
798                 break;
799             }
800             case S_SPACE: {
801                 // Skip over blanks.
802                 while ((c = shp->ifstable[*cp++]) == S_SPACE) {
803                     ;  // empty loop
804                 }
805                 if (!val) continue;
806                 if (c == S_MBYTE) {
807                     if (sh_strchr(ifs, (char *)cp - 1, cpmax - cp + 1) >= 0) {
808                         if ((c = mblen((char *)cp - 1, MB_CUR_MAX)) > 1) cp += (c - 1);
809                         c = S_DELIM;
810                     } else {
811                         c = 0;
812                     }
813                 }
814                 if (c != S_DELIM) break;
815             }
816             // FALLTHRU
817             case S_DELIM: {
818                 if (!del) del = cp - 1;
819                 if (name) {
820                     // Skip over trailing blanks.
821                     while ((c = shp->ifstable[*cp++]) == S_SPACE) {
822                         ;  // empty loop
823                     }
824                     break;
825                 }
826             }
827             // FALLTHRU
828             case 0: {
829                 if (val == 0 || was_escape) {
830                     val = (char *)(cp - 1);
831                     was_escape = 0;
832                 }
833                 // Skip over word characters.
834                 wrd = -1;
835                 while (1) {
836                     while ((c = shp->ifstable[*cp++]) == 0) {
837                         if (!wrd) wrd = 1;
838                     }
839                     if (inquote) {
840                         if (c == S_QUOTE) {
841                             if (shp->ifstable[*cp] == S_QUOTE) {
842                                 if (val) {
843                                     sfwrite(shp->stk, val, cp - (unsigned char *)val);
844                                     use_stak = 1;
845                                 }
846                                 val = (char *)++cp;
847                             } else {
848                                 break;
849                             }
850                         }
851                         if (c && c != S_EOF) {
852                             if (c == S_NL) {
853                                 if (val) {
854                                     sfwrite(shp->stk, val, cp - (unsigned char *)val);
855                                     use_stak = 1;
856                                 }
857                                 cp = (unsigned char *)sfgetr(iop, delim, 0);
858                                 if (!cp) cp = (unsigned char *)sfgetr(iop, delim, -1);
859                                 val = (char *)cp;
860                             }
861                             continue;
862                         }
863                     }
864                     if (!del && c == S_DELIM) del = cp - 1;
865                     if (name || c == S_NL || c == S_ESC || c == S_EOF || c == S_MBYTE) break;
866                     if (wrd < 0) wrd = 0;
867                 }
868                 if (wrd > 0) del = (unsigned char *)"";
869                 if (c != S_MBYTE) cp[-1] = 0;
870                 continue;
871             }
872             default: { break; }
873         }
874         // Assign value and advance to next variable.
875         if (!val) val = "";
876         if (use_stak) {
877             sfputr(shp->stk, val, 0);
878             val = stkptr(shp->stk, rel);
879         }
880         if (!name && *val) {
881             // Strip off trailing space delimiters.
882             unsigned char *vp = (unsigned char *)val + strlen(val);
883             while (shp->ifstable[*--vp] == S_SPACE) {
884                 ;  // empty loop
885             }
886             if (vp == del) {
887                 if (vp == (unsigned char *)val) {
888                     vp--;
889                 } else {
890                     while (shp->ifstable[*--vp] == S_SPACE) {
891                         ;  // empty loop
892                     }
893                 }
894             }
895             vp[1] = 0;
896         }
897         assert(np);
898         if (nv_isattr(np, NV_RDONLY)) {
899             errormsg(SH_DICT, ERROR_warn(0), e_readonly, nv_name(np));
900             jmpval = 1;
901         } else {
902             nv_putval(np, val, 0);
903         }
904         val = 0;
905         del = 0;
906         if (use_stak) {
907             stkseek(shp->stk, rel);
908             use_stak = 0;
909         }
910         if (array_index) {
911             nv_putsub(np, NULL, array_index++, 0);
912             if (c != S_NL) continue;
913             name = *++names;
914         }
915         while (1) {
916             if (sh_isoption(shp, SH_ALLEXPORT) && !strchr(nv_name(np), '.') &&
917                 !nv_isattr(np, NV_EXPORT)) {
918                 nv_onattr(np, NV_EXPORT);
919                 sh_envput(shp, np);
920             }
921             if (name) {
922                 nv_close(np);
923                 np = nv_open(name, shp->var_tree, NV_VARNAME);
924                 name = *++names;
925             } else {
926                 np = NULL;
927             }
928             if (c != S_NL) break;
929             if (!np) goto done;
930             if (nv_isattr(np, NV_RDONLY)) {
931                 errormsg(SH_DICT, ERROR_warn(0), e_readonly, nv_name(np));
932                 jmpval = 1;
933             } else {
934                 nv_putval(np, "", 0);
935             }
936         }
937     }
938 
939 done:
940 
941     if (timeout || (shp->fdstatus[fd] & (IOTTY | IONOSEEK))) sh_popcontext(shp, &buff);
942     if (was_write) sfset(iop, SF_WRITE, 1);
943     if (!was_share) sfset(iop, SF_SHARE, 0);
944     nv_close(np);
945     if ((shp->fdstatus[fd] & IOTTY) && !keytrap) tty_cooked(sffileno(iop));
946     if (flags & S_FLAG) hist_flush(shp->gd->hist_ptr);
947     if (jmpval > 1) siglongjmp(shp->jmplist->buff, jmpval);
948     return jmpval;
949 }
950