1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1982-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * David Korn <dgk@research.att.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22 * read [-ACprs] [-d delim] [-u filenum] [-t timeout] [-n n] [-N n] [name...]
23 *
24 * David Korn
25 * AT&T Labs
26 *
27 */
28
29 #include <ast.h>
30 #include <error.h>
31 #include "defs.h"
32 #include "variables.h"
33 #include "lexstates.h"
34 #include "io.h"
35 #include "name.h"
36 #include "builtins.h"
37 #include "history.h"
38 #include "terminal.h"
39 #include "edit.h"
40
41 #define R_FLAG 1 /* raw mode */
42 #define S_FLAG 2 /* save in history file */
43 #define A_FLAG 4 /* read into array */
44 #define N_FLAG 8 /* fixed size read at most */
45 #define NN_FLAG 0x10 /* fixed size read exact */
46 #define V_FLAG 0x20 /* use default value */
47 #define C_FLAG 0x40 /* read into compound variable */
48 #define D_FLAG 8 /* must be number of bits for all flags */
49
50 struct read_save
51 {
52 char **argv;
53 char *prompt;
54 short fd;
55 short plen;
56 int flags;
57 long timeout;
58 };
59
b_read(int argc,char * argv[],void * extra)60 int b_read(int argc,char *argv[], void *extra)
61 {
62 Sfdouble_t sec;
63 register char *name;
64 register int r, flags=0, fd=0;
65 register Shell_t *shp = ((Shbltin_t*)extra)->shp;
66 long timeout = 1000*shp->st.tmout;
67 int save_prompt, fixargs=((Shbltin_t*)extra)->invariant;
68 struct read_save *rp;
69 static char default_prompt[3] = {ESC,ESC};
70 rp = (struct read_save*)(((Shbltin_t*)extra)->data);
71 if(argc==0)
72 {
73 if(rp)
74 free((void*)rp);
75 return(0);
76 }
77 if(rp)
78 {
79 flags = rp->flags;
80 timeout = rp->timeout;
81 fd = rp->fd;
82 argv = rp->argv;
83 name = rp->prompt;
84 r = rp->plen;
85 goto bypass;
86 }
87 while((r = optget(argv,sh_optread))) switch(r)
88 {
89 case 'A':
90 flags |= A_FLAG;
91 break;
92 case 'C':
93 flags |= C_FLAG;
94 break;
95 case 't':
96 sec = sh_strnum(opt_info.arg, (char**)0,1);
97 timeout = sec ? 1000*sec : 1;
98 break;
99 case 'd':
100 if(opt_info.arg && *opt_info.arg!='\n')
101 {
102 char *cp = opt_info.arg;
103 flags &= ~((1<<D_FLAG)-1);
104 flags |= (mbchar(cp)<< D_FLAG);
105 }
106 break;
107 case 'p':
108 if((fd = shp->cpipe[0])<=0)
109 errormsg(SH_DICT,ERROR_exit(1),e_query);
110 break;
111 case 'n': case 'N':
112 flags &= ((1<<D_FLAG)-1);
113 flags |= (r=='n'?N_FLAG:NN_FLAG);
114 r = (int)opt_info.num;
115 if((unsigned)r > (1<<((8*sizeof(int))-D_FLAG))-1)
116 errormsg(SH_DICT,ERROR_exit(1),e_overlimit,opt_info.name);
117 flags |= (r<< D_FLAG);
118 break;
119 case 'r':
120 flags |= R_FLAG;
121 break;
122 case 's':
123 /* save in history file */
124 flags |= S_FLAG;
125 break;
126 case 'u':
127 fd = (int)opt_info.num;
128 if(sh_inuse(fd))
129 fd = -1;
130 break;
131 case 'v':
132 flags |= V_FLAG;
133 break;
134 case ':':
135 errormsg(SH_DICT,2, "%s", opt_info.arg);
136 break;
137 case '?':
138 errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg);
139 break;
140 }
141 argv += opt_info.index;
142 if(error_info.errors)
143 errormsg(SH_DICT,ERROR_usage(2), "%s", optusage((char*)0));
144 if(!((r=shp->fdstatus[fd])&IOREAD) || !(r&(IOSEEK|IONOSEEK)))
145 r = sh_iocheckfd(shp,fd);
146 if(fd<0 || !(r&IOREAD))
147 errormsg(SH_DICT,ERROR_system(1),e_file+4);
148 /* look for prompt */
149 if((name = *argv) && (name=strchr(name,'?')) && (r&IOTTY))
150 r = strlen(name++);
151 else
152 r = 0;
153 if(argc==fixargs && (rp=newof(NIL(struct read_save*),struct read_save,1,0)))
154 {
155 ((Shbltin_t*)extra)->data = (void*)rp;
156 rp->fd = fd;
157 rp->flags = flags;
158 rp->timeout = timeout;
159 rp->argv = argv;
160 rp->prompt = name;
161 rp->plen = r;
162 }
163 bypass:
164 shp->prompt = default_prompt;
165 if(r && (shp->prompt=(char*)sfreserve(sfstderr,r,SF_LOCKR)))
166 {
167 memcpy(shp->prompt,name,r);
168 sfwrite(sfstderr,shp->prompt,r-1);
169 }
170 shp->timeout = 0;
171 save_prompt = shp->nextprompt;
172 shp->nextprompt = 0;
173 r=sh_readline(shp,argv,fd,flags,timeout);
174 shp->nextprompt = save_prompt;
175 if(r==0 && (r=(sfeof(shp->sftable[fd])||sferror(shp->sftable[fd]))))
176 {
177 if(fd == shp->cpipe[0])
178 {
179 sh_pclose(shp->cpipe);
180 return(1);
181 }
182 }
183 sfclrerr(shp->sftable[fd]);
184 return(r);
185 }
186
187 /*
188 * here for read timeout
189 */
timedout(void * handle)190 static void timedout(void *handle)
191 {
192 sfclrlock((Sfio_t*)handle);
193 sh_exit(1);
194 }
195
196 /*
197 * This is the code to read a line and to split it into tokens
198 * <names> is an array of variable names
199 * <fd> is the file descriptor
200 * <flags> is union of -A, -r, -s, and contains delimiter if not '\n'
201 * <timeout> is number of milli-seconds until timeout
202 */
203
sh_readline(register Shell_t * shp,char ** names,int fd,int flags,long timeout)204 int sh_readline(register Shell_t *shp,char **names, int fd, int flags,long timeout)
205 {
206 register ssize_t c;
207 register unsigned char *cp;
208 register Namval_t *np;
209 register char *name, *val;
210 register Sfio_t *iop;
211 Namfun_t *nfp;
212 char *ifs;
213 unsigned char *cpmax;
214 unsigned char *del;
215 char was_escape = 0;
216 char use_stak = 0;
217 volatile char was_write = 0;
218 volatile char was_share = 1;
219 int rel, wrd;
220 long array_index = 0;
221 void *timeslot=0;
222 int delim = '\n';
223 int jmpval=0;
224 ssize_t size = 0;
225 int binary;
226 struct checkpt buff;
227 if(!(iop=shp->sftable[fd]) && !(iop=sh_iostream(shp,fd)))
228 return(1);
229 sh_stats(STAT_READS);
230 if(names && (name = *names))
231 {
232 Namval_t *mp;
233 if(val= strchr(name,'?'))
234 *val = 0;
235 np = nv_open(name,shp->var_tree,NV_NOASSIGN|NV_VARNAME);
236 if(np && nv_isarray(np) && (mp=nv_opensub(np)))
237 np = mp;
238 if((flags&V_FLAG) && shp->ed_context)
239 ((struct edit*)shp->ed_context)->e_default = np;
240 if(flags&A_FLAG)
241 {
242 flags &= ~A_FLAG;
243 array_index = 1;
244 nv_unset(np);
245 nv_putsub(np,NIL(char*),0L);
246 }
247 else if(flags&C_FLAG)
248 {
249 delim = -1;
250 nv_unset(np);
251 nv_setvtree(np);
252 }
253 else
254 name = *++names;
255 if(val)
256 *val = '?';
257 }
258 else
259 {
260 name = 0;
261 if(dtvnext(shp->var_tree) || shp->namespace)
262 np = nv_open(nv_name(REPLYNOD),shp->var_tree,0);
263 else
264 np = REPLYNOD;
265 }
266 if(flags>>D_FLAG) /* delimiter not new-line or fixed size read */
267 {
268 if(flags&(N_FLAG|NN_FLAG))
269 size = ((unsigned)flags)>>D_FLAG;
270 else
271 delim = ((unsigned)flags)>>D_FLAG;
272 if(shp->fdstatus[fd]&IOTTY)
273 tty_raw(fd,1);
274 }
275 binary = nv_isattr(np,NV_BINARY);
276 if(!binary && !(flags&(N_FLAG|NN_FLAG)))
277 {
278 Namval_t *mp;
279 /* set up state table based on IFS */
280 ifs = nv_getval(mp=sh_scoped(shp,IFSNOD));
281 if((flags&R_FLAG) && shp->ifstable['\\']==S_ESC)
282 shp->ifstable['\\'] = 0;
283 else if(!(flags&R_FLAG) && shp->ifstable['\\']==0)
284 shp->ifstable['\\'] = S_ESC;
285 shp->ifstable[delim] = S_NL;
286 if(delim!='\n')
287 {
288 shp->ifstable['\n'] = 0;
289 nv_putval(mp, ifs, NV_RDONLY);
290 }
291 shp->ifstable[0] = S_EOF;
292 }
293 sfclrerr(iop);
294 for(nfp=np->nvfun; nfp; nfp = nfp->next)
295 {
296 if(nfp->disc && nfp->disc->readf)
297 {
298 if((c=(*nfp->disc->readf)(np,iop,delim,nfp))>=0)
299 return(c);
300 }
301 }
302 if(binary && !(flags&(N_FLAG|NN_FLAG)))
303 {
304 flags |= NN_FLAG;
305 size = nv_size(np);
306 }
307 was_write = (sfset(iop,SF_WRITE,0)&SF_WRITE)!=0;
308 if(fd==0)
309 was_share = (sfset(iop,SF_SHARE,1)&SF_SHARE)!=0;
310 if(timeout || (shp->fdstatus[fd]&(IOTTY|IONOSEEK)))
311 {
312 sh_pushcontext(&buff,1);
313 jmpval = sigsetjmp(buff.buff,0);
314 if(jmpval)
315 goto done;
316 if(timeout)
317 timeslot = (void*)sh_timeradd(timeout,0,timedout,(void*)iop);
318 }
319 if(flags&(N_FLAG|NN_FLAG))
320 {
321 char buf[256],*var=buf,*cur,*end,*up,*v;
322 /* reserved buffer */
323 if((c=size)>=sizeof(buf))
324 {
325 if(!(var = (char*)malloc(c+1)))
326 sh_exit(1);
327 end = var + c;
328 }
329 else
330 end = var + sizeof(buf) - 1;
331 up = cur = var;
332 if((sfset(iop,SF_SHARE,1)&SF_SHARE) && fd!=0)
333 was_share = 1;
334 if(size==0)
335 {
336 cp = sfreserve(iop,0,0);
337 c = 0;
338 }
339 else
340 {
341 ssize_t m;
342 int f;
343 for (;;)
344 {
345 c = size;
346 cp = sfreserve(iop,c,SF_LOCKR);
347 f = 1;
348 if(cp)
349 m = sfvalue(iop);
350 else if(flags&NN_FLAG)
351 {
352 c = size;
353 m = (cp = sfreserve(iop,c,0)) ? sfvalue(iop) : 0;
354 f = 0;
355 }
356 else
357 {
358 c = sfvalue(iop);
359 m = (cp = sfreserve(iop,c,SF_LOCKR)) ? sfvalue(iop) : 0;
360 }
361 if(m>0 && (flags&N_FLAG) && !binary && (v=memchr(cp,'\n',m)))
362 {
363 *v++ = 0;
364 m = v-(char*)cp;
365 }
366 if((c=m)>size)
367 c = size;
368 if(c>0)
369 {
370 if(c > (end-cur))
371 {
372 ssize_t cx = cur - var, ux = up - var;
373 m = (end - var) + (c - (end - cur));
374 if (var == buf)
375 {
376 v = (char*)malloc(m+1);
377 var = memcpy(v, var, cur - var);
378 }
379 else
380 var = newof(var, char, m, 1);
381 end = var + m;
382 cur = var + cx;
383 up = var + ux;
384 }
385 memcpy((void*)cur,cp,c);
386 if(f)
387 sfread(iop,cp,c);
388 cur += c;
389 #if SHOPT_MULTIBYTE
390 if(!binary && mbwide())
391 {
392 int x;
393 int z;
394
395 mbinit();
396 *cur = 0;
397 x = z = 0;
398 while (up < cur && (z = mbsize(up)) > 0)
399 {
400 up += z;
401 x++;
402 }
403 if((size -= x) > 0 && (up >= cur || z < 0) && ((flags & NN_FLAG) || z < 0 || m > c))
404 continue;
405 }
406 #endif
407 }
408 #if SHOPT_MULTIBYTE
409 if(!binary && mbwide() && (up == var || (flags & NN_FLAG) && size))
410 cur = var;
411 #endif
412 *cur = 0;
413 if(c>=size || (flags&N_FLAG) || m==0)
414 {
415 if(m)
416 sfclrerr(iop);
417 break;
418 }
419 size -= c;
420 }
421 }
422 if(timeslot)
423 timerdel(timeslot);
424 if(binary && !((size=nv_size(np)) && nv_isarray(np) && c!=size))
425 {
426 if((c==size) && np->nvalue.cp && !nv_isarray(np))
427 memcpy((char*)np->nvalue.cp,var,c);
428 else
429 {
430 Namval_t *mp;
431 if(var==buf)
432 var = memdup(var,c+1);
433 nv_putval(np,var,NV_RAW);
434 nv_setsize(np,c);
435 if(!nv_isattr(np,NV_IMPORT|NV_EXPORT) && (mp=(Namval_t*)np->nvenv))
436 nv_setsize(mp,c);
437 }
438 }
439 else
440 {
441 nv_putval(np,var,0);
442 if(var!=buf)
443 free((void*)var);
444 }
445 goto done;
446 }
447 else if(cp = (unsigned char*)sfgetr(iop,delim,0))
448 c = sfvalue(iop);
449 else if(cp = (unsigned char*)sfgetr(iop,delim,-1))
450 c = sfvalue(iop)+1;
451 if(timeslot)
452 timerdel(timeslot);
453 if((flags&S_FLAG) && !shp->hist_ptr)
454 {
455 sh_histinit((void*)shp);
456 if(!shp->hist_ptr)
457 flags &= ~S_FLAG;
458 }
459 if(cp)
460 {
461 cpmax = cp + c;
462 #if SHOPT_CRNL
463 if(delim=='\n' && c>=2 && cpmax[-2]=='\r')
464 cpmax--;
465 #endif /* SHOPT_CRNL */
466 if(*(cpmax-1) != delim)
467 *(cpmax-1) = delim;
468 if(flags&S_FLAG)
469 sfwrite(shp->hist_ptr->histfp,(char*)cp,c);
470 c = shp->ifstable[*cp++];
471 #if !SHOPT_MULTIBYTE
472 if(!name && (flags&R_FLAG)) /* special case single argument */
473 {
474 /* skip over leading blanks */
475 while(c==S_SPACE)
476 c = shp->ifstable[*cp++];
477 /* strip trailing delimiters */
478 if(cpmax[-1] == '\n')
479 cpmax--;
480 if(cpmax>cp)
481 {
482 while((c=shp->ifstable[*--cpmax])==S_DELIM || c==S_SPACE);
483 cpmax[1] = 0;
484 }
485 else
486 *cpmax =0;
487 if(nv_isattr(np, NV_RDONLY))
488 {
489 errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
490 jmpval = 1;
491 }
492 else
493 nv_putval(np,(char*)cp-1,0);
494 goto done;
495 }
496 #endif /* !SHOPT_MULTIBYTE */
497 }
498 else
499 c = S_NL;
500 shp->nextprompt = 2;
501 rel= staktell();
502 /* val==0 at the start of a field */
503 val = 0;
504 del = 0;
505 while(1)
506 {
507 switch(c)
508 {
509 #if SHOPT_MULTIBYTE
510 case S_MBYTE:
511 if(val==0)
512 val = (char*)(cp-1);
513 if(sh_strchr(ifs,(char*)cp-1)>=0)
514 {
515 c = mbsize((char*)cp-1);
516 if(name)
517 cp[-1] = 0;
518 if(c>1)
519 cp += (c-1);
520 c = S_DELIM;
521 }
522 else
523 c = 0;
524 continue;
525 #endif /*SHOPT_MULTIBYTE */
526 case S_ESC:
527 /* process escape character */
528 if((c = shp->ifstable[*cp++]) == S_NL)
529 was_escape = 1;
530 else
531 c = 0;
532 if(val)
533 {
534 stakputs(val);
535 use_stak = 1;
536 was_escape = 1;
537 *val = 0;
538 }
539 continue;
540
541 case S_EOF:
542 /* check for end of buffer */
543 if(val && *val)
544 {
545 stakputs(val);
546 use_stak = 1;
547 }
548 val = 0;
549 if(cp>=cpmax)
550 {
551 c = S_NL;
552 break;
553 }
554 /* eliminate null bytes */
555 c = shp->ifstable[*cp++];
556 if(!name && val && (c==S_SPACE||c==S_DELIM||c==S_MBYTE))
557 c = 0;
558 continue;
559 case S_NL:
560 if(was_escape)
561 {
562 was_escape = 0;
563 if(cp = (unsigned char*)sfgetr(iop,delim,0))
564 c = sfvalue(iop);
565 else if(cp=(unsigned char*)sfgetr(iop,delim,-1))
566 c = sfvalue(iop)+1;
567 if(cp)
568 {
569 if(flags&S_FLAG)
570 sfwrite(shp->hist_ptr->histfp,(char*)cp,c);
571 cpmax = cp + c;
572 c = shp->ifstable[*cp++];
573 val=0;
574 if(!name && (c==S_SPACE || c==S_DELIM || c==S_MBYTE))
575 c = 0;
576 continue;
577 }
578 }
579 c = S_NL;
580 break;
581
582 case S_SPACE:
583 /* skip over blanks */
584 while((c=shp->ifstable[*cp++])==S_SPACE);
585 if(!val)
586 continue;
587 #if SHOPT_MULTIBYTE
588 if(c==S_MBYTE)
589 {
590 if(sh_strchr(ifs,(char*)cp-1)>=0)
591 {
592 if((c = mbsize((char*)cp-1))>1)
593 cp += (c-1);
594 c = S_DELIM;
595 }
596 else
597 c = 0;
598 }
599 #endif /* SHOPT_MULTIBYTE */
600 if(c!=S_DELIM)
601 break;
602 /* FALL THRU */
603
604 case S_DELIM:
605 if(!del)
606 del = cp - 1;
607 if(name)
608 {
609 /* skip over trailing blanks */
610 while((c=shp->ifstable[*cp++])==S_SPACE);
611 break;
612 }
613 /* FALL THRU */
614
615 case 0:
616 if(val==0 || was_escape)
617 {
618 val = (char*)(cp-1);
619 was_escape = 0;
620 }
621 /* skip over word characters */
622 wrd = -1;
623 while(1)
624 {
625 while((c=shp->ifstable[*cp++])==0)
626 if(!wrd)
627 wrd = 1;
628 if(!del&&c==S_DELIM)
629 del = cp - 1;
630 if(name || c==S_NL || c==S_ESC || c==S_EOF || c==S_MBYTE)
631 break;
632 if(wrd<0)
633 wrd = 0;
634 }
635 if(wrd>0)
636 del = (unsigned char*)"";
637 if(c!=S_MBYTE)
638 cp[-1] = 0;
639 continue;
640 }
641 /* assign value and advance to next variable */
642 if(!val)
643 val = "";
644 if(use_stak)
645 {
646 stakputs(val);
647 stakputc(0);
648 val = stakptr(rel);
649 }
650 if(!name && *val)
651 {
652 /* strip off trailing space delimiters */
653 register unsigned char *vp = (unsigned char*)val + strlen(val);
654 while(shp->ifstable[*--vp]==S_SPACE);
655 if(vp==del)
656 {
657 if(vp==(unsigned char*)val)
658 vp--;
659 else
660 while(shp->ifstable[*--vp]==S_SPACE);
661 }
662 vp[1] = 0;
663 }
664 if(nv_isattr(np, NV_RDONLY))
665 {
666 errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
667 jmpval = 1;
668 }
669 else
670 nv_putval(np,val,0);
671 val = 0;
672 del = 0;
673 if(use_stak)
674 {
675 stakseek(rel);
676 use_stak = 0;
677 }
678 if(array_index)
679 {
680 nv_putsub(np, NIL(char*), array_index++);
681 if(c!=S_NL)
682 continue;
683 name = *++names;
684 }
685 while(1)
686 {
687 if(sh_isoption(SH_ALLEXPORT)&&!strchr(nv_name(np),'.') && !nv_isattr(np,NV_EXPORT))
688 {
689 nv_onattr(np,NV_EXPORT);
690 sh_envput(sh.env,np);
691 }
692 if(name)
693 {
694 nv_close(np);
695 np = nv_open(name,shp->var_tree,NV_NOASSIGN|NV_VARNAME);
696 name = *++names;
697 }
698 else
699 np = 0;
700 if(c!=S_NL)
701 break;
702 if(!np)
703 goto done;
704 if(nv_isattr(np, NV_RDONLY))
705 {
706 errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
707 jmpval = 1;
708 }
709 else
710 nv_putval(np, "", 0);
711 }
712 }
713 done:
714 if(timeout || (shp->fdstatus[fd]&(IOTTY|IONOSEEK)))
715 sh_popcontext(&buff);
716 if(was_write)
717 sfset(iop,SF_WRITE,1);
718 if(!was_share)
719 sfset(iop,SF_SHARE,0);
720 nv_close(np);
721 if((flags>>D_FLAG) && (shp->fdstatus[fd]&IOTTY))
722 tty_cooked(fd);
723 if(flags&S_FLAG)
724 hist_flush(shp->hist_ptr);
725 if(jmpval > 1)
726 siglongjmp(*shp->jmplist,jmpval);
727 return(jmpval);
728 }
729
730