1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1982-2007 AT&T Knowledge Ventures * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Knowledge Ventures * 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 [-Aprs] [-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 <ctype.h> 32 #include "defs.h" 33 #include "variables.h" 34 #include "lexstates.h" 35 #include "io.h" 36 #include "name.h" 37 #include "builtins.h" 38 #include "history.h" 39 #include "terminal.h" 40 #include "edit.h" 41 42 #define R_FLAG 1 /* raw mode */ 43 #define S_FLAG 2 /* save in history file */ 44 #define A_FLAG 4 /* read into array */ 45 #define N_FLAG 8 /* fixed size read at most */ 46 #define NN_FLAG 0x10 /* fixed size read exact */ 47 #define V_FLAG 0x20 /* use default value */ 48 #define D_FLAG 8 /* must be number of bits for all flags */ 49 50 int b_read(int argc,char *argv[], void *extra) 51 { 52 Sfdouble_t sec; 53 register char *name; 54 register int r, flags=0, fd=0; 55 register Shell_t *shp = (Shell_t*)extra; 56 long timeout = 1000*shp->st.tmout; 57 int save_prompt; 58 static char default_prompt[3] = {ESC,ESC}; 59 NOT_USED(argc); 60 while((r = optget(argv,sh_optread))) switch(r) 61 { 62 case 'A': 63 flags |= A_FLAG; 64 break; 65 case 't': 66 sec = sh_strnum(opt_info.arg, (char**)0,1); 67 timeout = sec ? 1000*sec : 1; 68 break; 69 case 'd': 70 if(opt_info.arg && *opt_info.arg!='\n') 71 { 72 char *cp = opt_info.arg; 73 flags &= ~((1<<D_FLAG)-1); 74 flags |= (mbchar(cp)<< D_FLAG); 75 } 76 break; 77 case 'p': 78 if((fd = shp->cpipe[0])<=0) 79 errormsg(SH_DICT,ERROR_exit(1),e_query); 80 break; 81 case 'n': case 'N': 82 flags &= ~((1<<D_FLAG)-1); 83 flags |= (r=='n'?N_FLAG:NN_FLAG); 84 r = (int)opt_info.num; 85 if((unsigned)r > (1<<((8*sizeof(int))-D_FLAG))-1) 86 errormsg(SH_DICT,ERROR_exit(1),e_overlimit,"n"); 87 flags |= (r<< D_FLAG); 88 break; 89 case 'r': 90 flags |= R_FLAG; 91 break; 92 case 's': 93 /* save in history file */ 94 flags |= S_FLAG; 95 break; 96 case 'u': 97 fd = (int)opt_info.num; 98 if(sh_inuse(fd)) 99 fd = -1; 100 break; 101 case 'v': 102 flags |= V_FLAG; 103 break; 104 case ':': 105 errormsg(SH_DICT,2, "%s", opt_info.arg); 106 break; 107 case '?': 108 errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); 109 break; 110 } 111 argv += opt_info.index; 112 if(error_info.errors) 113 errormsg(SH_DICT,ERROR_usage(2), "%s", optusage((char*)0)); 114 if(!((r=shp->fdstatus[fd])&IOREAD) || !(r&(IOSEEK|IONOSEEK))) 115 r = sh_iocheckfd(fd); 116 if(fd<0 || !(r&IOREAD)) 117 errormsg(SH_DICT,ERROR_system(1),e_file+4); 118 /* look for prompt */ 119 shp->prompt = default_prompt; 120 if((name = *argv) && (name=strchr(name,'?')) && (r&IOTTY)) 121 { 122 r = strlen(++name)+1; 123 if(shp->prompt=(char*)sfreserve(sfstderr,r,SF_LOCKR)) 124 { 125 memcpy(shp->prompt,name,r); 126 sfwrite(sfstderr,shp->prompt,r-1); 127 } 128 } 129 shp->timeout = 0; 130 save_prompt = shp->nextprompt; 131 shp->nextprompt = 0; 132 r=sh_readline(shp,argv,fd,flags,timeout); 133 shp->nextprompt = save_prompt; 134 if(r==0 && (r=(sfeof(shp->sftable[fd])||sferror(shp->sftable[fd])))) 135 { 136 if(fd == shp->cpipe[0]) 137 { 138 sh_pclose(shp->cpipe); 139 return(1); 140 } 141 } 142 sfclrerr(shp->sftable[fd]); 143 return(r); 144 } 145 146 /* 147 * here for read timeout 148 */ 149 static void timedout(void *handle) 150 { 151 sfclrlock((Sfio_t*)handle); 152 sh_exit(1); 153 } 154 155 /* 156 * This is the code to read a line and to split it into tokens 157 * <names> is an array of variable names 158 * <fd> is the file descriptor 159 * <flags> is union of -A, -r, -s, and contains delimiter if not '\n' 160 * <timeout> is number of milli-seconds until timeout 161 */ 162 163 int sh_readline(register Shell_t *shp,char **names, int fd, int flags,long timeout) 164 { 165 register int c; 166 register unsigned char *cp; 167 register Namval_t *np; 168 register char *name, *val; 169 register Sfio_t *iop; 170 char *ifs; 171 unsigned char *cpmax; 172 unsigned char *del; 173 char was_escape = 0; 174 char use_stak = 0; 175 char was_write = 0; 176 char was_share = 1; 177 int rel, wrd; 178 long array_index = 0; 179 void *timeslot=0; 180 int delim = '\n'; 181 int jmpval=0; 182 int size = 0; 183 struct checkpt buff; 184 if(!(iop=shp->sftable[fd]) && !(iop=sh_iostream(fd))) 185 return(1); 186 if(names && (name = *names)) 187 { 188 if(val= strchr(name,'?')) 189 *val = 0; 190 np = nv_open(name,shp->var_tree,NV_NOASSIGN|NV_VARNAME|NV_ARRAY); 191 if((flags&V_FLAG) && shp->ed_context) 192 ((struct edit*)shp->ed_context)->e_default = np; 193 if(flags&A_FLAG) 194 { 195 flags &= ~A_FLAG; 196 array_index = 1; 197 nv_unset(np); 198 nv_putsub(np,NIL(char*),0L); 199 } 200 else 201 name = *++names; 202 if(val) 203 *val = '?'; 204 } 205 else 206 { 207 name = 0; 208 if(dtvnext(shp->var_tree) || shp->namespace) 209 np = nv_open(nv_name(REPLYNOD),shp->var_tree,0); 210 else 211 np = REPLYNOD; 212 } 213 if(flags>>D_FLAG) /* delimiter not new-line or fixed size read */ 214 { 215 if(flags&(N_FLAG|NN_FLAG)) 216 size = ((unsigned)flags)>>D_FLAG; 217 else 218 delim = ((unsigned)flags)>>D_FLAG; 219 if(shp->fdstatus[fd]&IOTTY) 220 tty_raw(fd,1); 221 } 222 if(!(flags&(N_FLAG|NN_FLAG))) 223 { 224 Namval_t *mp; 225 /* set up state table based on IFS */ 226 ifs = nv_getval(mp=nv_scoped(IFSNOD)); 227 if((flags&R_FLAG) && shp->ifstable['\\']==S_ESC) 228 shp->ifstable['\\'] = 0; 229 else if(!(flags&R_FLAG) && shp->ifstable['\\']==0) 230 shp->ifstable['\\'] = S_ESC; 231 shp->ifstable[delim] = S_NL; 232 if(delim!='\n') 233 { 234 shp->ifstable['\n'] = 0; 235 nv_putval(mp, ifs, NV_RDONLY); 236 } 237 shp->ifstable[0] = S_EOF; 238 } 239 sfclrerr(iop); 240 if(np->nvfun && np->nvfun->disc->readf) 241 return((* np->nvfun->disc->readf)(np,iop,delim,np->nvfun)); 242 was_write = (sfset(iop,SF_WRITE,0)&SF_WRITE)!=0; 243 if(fd==0) 244 was_share = (sfset(iop,SF_SHARE,1)&SF_SHARE)!=0; 245 if(timeout || (shp->fdstatus[fd]&(IOTTY|IONOSEEK))) 246 { 247 sh_pushcontext(&buff,1); 248 jmpval = sigsetjmp(buff.buff,0); 249 if(jmpval) 250 goto done; 251 if(timeout) 252 timeslot = (void*)sh_timeradd(timeout,0,timedout,(void*)iop); 253 } 254 if(flags&(N_FLAG|NN_FLAG)) 255 { 256 char buf[64],*var=buf; 257 /* reserved buffer */ 258 if((c=size)>=sizeof(buf)) 259 { 260 if(!(var = (char*)malloc(c+1))) 261 sh_exit(1); 262 } 263 if((sfset(iop,SF_SHARE,1)&SF_SHARE) && fd!=0) 264 was_share = 1; 265 if(size==0) 266 { 267 cp = sfreserve(iop,0,0); 268 c = 0; 269 } 270 else 271 { 272 c= (shp->fdstatus[fd]&(IOTTY|IONOSEEK))?1:-1; 273 if(flags&NN_FLAG) 274 c = size; 275 if(cp = sfreserve(iop,c,!(flags&NN_FLAG))) 276 c = sfvalue(iop); 277 else 278 c = 0; 279 if(c>size) 280 c = size; 281 if(c>0) 282 { 283 memcpy((void*)var,cp,c); 284 if(flags&N_FLAG) 285 sfread(iop,cp,c); 286 } 287 var[c] = 0; 288 if(c>=size) 289 sfclrerr(iop); 290 } 291 if(timeslot) 292 timerdel(timeslot); 293 if(nv_isattr(np,NV_BINARY)) 294 { 295 if(c<sizeof(buf)) 296 var = memdup(var,c); 297 nv_putval(np,var, NV_RAW); 298 nv_setsize(np,c); 299 } 300 else 301 { 302 nv_putval(np,var,0); 303 if(c>=sizeof(buf)) 304 free((void*)var); 305 } 306 goto done; 307 } 308 else if(cp = (unsigned char*)sfgetr(iop,delim,0)) 309 c = sfvalue(iop); 310 else if(cp = (unsigned char*)sfgetr(iop,delim,-1)) 311 c = sfvalue(iop)+1; 312 if(timeslot) 313 timerdel(timeslot); 314 if((flags&S_FLAG) && !shp->hist_ptr) 315 { 316 sh_histinit(); 317 if(!shp->hist_ptr) 318 flags &= ~S_FLAG; 319 } 320 if(cp) 321 { 322 cpmax = cp + c; 323 #if SHOPT_CRNL 324 if(delim=='\n' && c>=2 && cpmax[-2]=='\r') 325 cpmax--; 326 #endif /* SHOPT_CRNL */ 327 if(*(cpmax-1) != delim) 328 *(cpmax-1) = delim; 329 if(flags&S_FLAG) 330 sfwrite(shp->hist_ptr->histfp,(char*)cp,c); 331 c = shp->ifstable[*cp++]; 332 #if !SHOPT_MULTIBYTE 333 if(!name && (flags&R_FLAG)) /* special case single argument */ 334 { 335 /* skip over leading blanks */ 336 while(c==S_SPACE) 337 c = shp->ifstable[*cp++]; 338 /* strip trailing delimiters */ 339 if(cpmax[-1] == '\n') 340 cpmax--; 341 if(cpmax>cp) 342 { 343 while((c=shp->ifstable[*--cpmax])==S_DELIM || c==S_SPACE); 344 cpmax[1] = 0; 345 } 346 else 347 *cpmax =0; 348 if(nv_isattr(np, NV_RDONLY)) 349 { 350 errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np)); 351 jmpval = 1; 352 } 353 else 354 nv_putval(np,(char*)cp-1,0); 355 goto done; 356 } 357 #endif /* !SHOPT_MULTIBYTE */ 358 } 359 else 360 c = S_NL; 361 shp->nextprompt = 2; 362 rel= staktell(); 363 /* val==0 at the start of a field */ 364 val = 0; 365 del = 0; 366 while(1) 367 { 368 switch(c) 369 { 370 #if SHOPT_MULTIBYTE 371 case S_MBYTE: 372 if(val==0) 373 val = (char*)(cp-1); 374 if(sh_strchr(ifs,(char*)cp-1)>=0) 375 { 376 c = mbsize((char*)cp-1); 377 if(name) 378 cp[-1] = 0; 379 if(c>1) 380 cp += (c-1); 381 c = S_DELIM; 382 } 383 else 384 c = 0; 385 continue; 386 #endif /*SHOPT_MULTIBYTE */ 387 case S_ESC: 388 /* process escape character */ 389 if((c = shp->ifstable[*cp++]) == S_NL) 390 was_escape = 1; 391 else 392 c = 0; 393 if(val) 394 { 395 stakputs(val); 396 use_stak = 1; 397 was_escape = 1; 398 *val = 0; 399 } 400 continue; 401 402 case S_EOF: 403 /* check for end of buffer */ 404 if(val && *val) 405 { 406 stakputs(val); 407 use_stak = 1; 408 } 409 val = 0; 410 if(cp>=cpmax) 411 { 412 c = S_NL; 413 break; 414 } 415 /* eliminate null bytes */ 416 c = shp->ifstable[*cp++]; 417 if(!name && val && (c==S_SPACE||c==S_DELIM||c==S_MBYTE)) 418 c = 0; 419 continue; 420 case S_NL: 421 if(was_escape) 422 { 423 was_escape = 0; 424 if(cp = (unsigned char*)sfgetr(iop,delim,0)) 425 c = sfvalue(iop); 426 else if(cp=(unsigned char*)sfgetr(iop,delim,-1)) 427 c = sfvalue(iop)+1; 428 if(cp) 429 { 430 if(flags&S_FLAG) 431 sfwrite(shp->hist_ptr->histfp,(char*)cp,c); 432 cpmax = cp + c; 433 c = shp->ifstable[*cp++]; 434 val=0; 435 if(!name && (c==S_SPACE || c==S_DELIM || c==S_MBYTE)) 436 c = 0; 437 continue; 438 } 439 } 440 c = S_NL; 441 break; 442 443 case S_SPACE: 444 /* skip over blanks */ 445 while((c=shp->ifstable[*cp++])==S_SPACE); 446 if(!val) 447 continue; 448 #if SHOPT_MULTIBYTE 449 if(c==S_MBYTE) 450 { 451 if(sh_strchr(ifs,(char*)cp-1)>=0) 452 { 453 if((c = mbsize((char*)cp-1))>1) 454 cp += (c-1); 455 c = S_DELIM; 456 } 457 else 458 c = 0; 459 } 460 #endif /* SHOPT_MULTIBYTE */ 461 if(c!=S_DELIM) 462 break; 463 /* FALL THRU */ 464 465 case S_DELIM: 466 if(!del) 467 del = cp - 1; 468 if(name) 469 { 470 /* skip over trailing blanks */ 471 while((c=shp->ifstable[*cp++])==S_SPACE); 472 break; 473 } 474 /* FALL THRU */ 475 476 case 0: 477 if(val==0 || was_escape) 478 { 479 val = (char*)(cp-1); 480 was_escape = 0; 481 } 482 /* skip over word characters */ 483 wrd = -1; 484 while(1) 485 { 486 while((c=shp->ifstable[*cp++])==0) 487 if(!wrd) 488 wrd = 1; 489 if(!del&&c==S_DELIM) 490 del = cp - 1; 491 if(name || c==S_NL || c==S_ESC || c==S_EOF || c==S_MBYTE) 492 break; 493 if(wrd<0) 494 wrd = 0; 495 } 496 if(wrd>0) 497 del = (unsigned char*)""; 498 if(c!=S_MBYTE) 499 cp[-1] = 0; 500 continue; 501 } 502 /* assign value and advance to next variable */ 503 if(!val) 504 val = ""; 505 if(use_stak) 506 { 507 stakputs(val); 508 stakputc(0); 509 val = stakptr(rel); 510 } 511 if(!name && *val) 512 { 513 /* strip off trailing space delimiters */ 514 register unsigned char *vp = (unsigned char*)val + strlen(val); 515 while(shp->ifstable[*--vp]==S_SPACE); 516 if(vp==del) 517 { 518 if(vp==(unsigned char*)val) 519 vp--; 520 else 521 while(shp->ifstable[*--vp]==S_SPACE); 522 } 523 vp[1] = 0; 524 } 525 if(nv_isattr(np, NV_RDONLY)) 526 { 527 errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np)); 528 jmpval = 1; 529 } 530 else 531 nv_putval(np,val,0); 532 val = 0; 533 del = 0; 534 if(use_stak) 535 { 536 stakseek(rel); 537 use_stak = 0; 538 } 539 if(array_index) 540 { 541 nv_putsub(np, NIL(char*), array_index++); 542 if(c!=S_NL) 543 continue; 544 name = *++names; 545 } 546 while(1) 547 { 548 if(sh_isoption(SH_ALLEXPORT)&&!strchr(nv_name(np),'.') && !nv_isattr(np,NV_EXPORT)) 549 { 550 nv_onattr(np,NV_EXPORT); 551 sh_envput(sh.env,np); 552 } 553 if(name) 554 { 555 nv_close(np); 556 np = nv_open(name,shp->var_tree,NV_NOASSIGN|NV_VARNAME); 557 name = *++names; 558 } 559 else 560 np = 0; 561 if(c!=S_NL) 562 break; 563 if(!np) 564 goto done; 565 if(nv_isattr(np, NV_RDONLY)) 566 { 567 errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np)); 568 jmpval = 1; 569 } 570 else 571 nv_putval(np, "", 0); 572 } 573 } 574 done: 575 if(timeout || (shp->fdstatus[fd]&(IOTTY|IONOSEEK))) 576 sh_popcontext(&buff); 577 if(was_write) 578 sfset(iop,SF_WRITE,1); 579 if(!was_share) 580 sfset(iop,SF_SHARE,0); 581 nv_close(np); 582 if((flags>>D_FLAG) && (shp->fdstatus[fd]&IOTTY)) 583 tty_cooked(fd); 584 if(flags&S_FLAG) 585 hist_flush(shp->hist_ptr); 586 if(jmpval > 1) 587 siglongjmp(*shp->jmplist,jmpval); 588 return(jmpval); 589 } 590 591