1 /* @(#)word.c 1.1 */ 2 /* 3 * UNIX shell 4 * 5 * S. R. Bourne 6 * Rewritten by David Korn 7 * AT&T Bell Laboratories 8 * 9 */ 10 11 #include "flags.h" 12 #include "defs.h" 13 #include "io.h" 14 #include "history.h" 15 #include "stak.h" 16 #include "sym.h" 17 #include "shtype.h" 18 #include "brkincr.h" 19 #include "name.h" 20 #include "builtins.h" 21 #ifdef JOBS 22 #ifdef BSD 23 #include <signal.h> 24 static jmp_buf readerr; 25 static int nreadc; 26 #endif /* BSD */ 27 #endif /* JOBS */ 28 29 30 31 static int letflg = 0; 32 static FILEBLK a_fb; 33 static FILE a_fd; 34 35 /* This module defines the following routines */ 36 char *match_paren(); 37 int nextc(); 38 int readc(); 39 int word(); 40 41 /* This module references these external routines */ 42 extern void arg_clear(); 43 extern void chktrap(); 44 extern void chkpr(); 45 extern void exitsh(); 46 extern NAMPTR findnod(); 47 extern long lseek(); 48 extern char *movstr(); 49 extern void synbad(); 50 extern char *tilde(); 51 extern char *valup(); 52 53 /* ======== character handling for command lines ========*/ 54 55 /* 56 * Get the next word and put it on the top of the stak 57 * Determine the type of word and set wdnum and wdset accordingly 58 * Returns the token type 59 */ 60 61 word() 62 { 63 register int c; 64 register int d; 65 register char *argp; 66 register char *tildp; 67 char not_alias; 68 char chk_keywd; 69 int alpha = 0; 70 wdnum=0; 71 /* condition needed to check for keywords, name=value */ 72 chk_keywd = reserv!=0 || (wdset&KEYFLG); 73 wdset &= ~KEYFLG; 74 wdarg = (ARGPTR)locstak(); 75 argp = wdarg->argval; 76 if(letflg) 77 { 78 letflg = 0; 79 *argp++ =(DQUOTE); 80 argp = match_paren(argp, LPAREN, RPAREN, 1); 81 *(argp-1)=(DQUOTE); 82 c = nextc(); 83 wdval = 0; 84 if(c != ')') 85 { 86 stakbot = wdarg->argval; 87 cpystak(let_syntax); 88 synbad(); 89 } 90 endstak(--argp); 91 return(0); 92 } 93 tildp = NULL; 94 while(1) 95 { 96 while((c=nextc(), isspace(c))); 97 if(c==COMCHAR) 98 { 99 while((c=readc()) != NL && c != ENDOF); 100 peekc=c; 101 } 102 else /* out of comment - white isspace loop */ 103 break; 104 } 105 if(c=='~') 106 tildp = argp; 107 not_alias = (aliflg==0); 108 if(!ismeta(c)) 109 { 110 do 111 { 112 if(c==LITERAL) 113 argp = match_paren(argp,c,c,0); 114 else 115 { 116 if(argp==wdarg->argval&&chk_keywd&&isalpha(c)) 117 { 118 alpha++; 119 } 120 *argp++=(c); 121 if(c == ESCAPE) 122 *argp++ = readc(); 123 if(alpha) 124 { 125 if(c == '[') 126 { 127 argp = match_paren(argp,'[',']',-1); 128 } 129 else if(c=='=') 130 { 131 wdset |= KEYFLG; 132 tildp = argp; 133 alpha = 0; 134 } 135 else if(!isalnum(c)) 136 alpha = 0; 137 } 138 if(qotchar(c)) 139 { 140 argp = match_paren(argp,c,c,0); 141 } 142 } 143 d = c; 144 c = nextc(); 145 if(d==DOLLAR && c ==LPAREN) 146 { 147 subflag++; 148 *argp++ = c; 149 argp = match_paren(argp, LPAREN, RPAREN, 0); 150 c = nextc(); 151 } 152 else if(tildp!=NULL && (c == '/' || c==':' || ismeta(c))) 153 { 154 /* check for tilde expansion */ 155 register char *dir; 156 *argp = 0; 157 staktop = argp; 158 dir=tilde(tildp); 159 /* This check needed if tilde() uses malloc() */ 160 #ifndef INT16 161 if(stakbot != (STKPTR)wdarg) 162 { 163 tildp += ((char*)staktop-argp); 164 argp = staktop; 165 wdarg = (ARGPTR)stakbot; 166 } 167 #endif /*INT16 */ 168 if(dir) 169 { 170 argp=tildp; 171 argp = movstr(dir,argp); 172 } 173 else 174 tildp = NULL; 175 } 176 if(c==':' && (wdset&KEYFLG)) 177 tildp = argp+1; 178 } 179 while(!ismeta(c)); 180 argp=endstak(argp); 181 peekn=c|MARK; 182 if(((ARGPTR) argp)->argval[1]==0 && 183 (d=((ARGPTR) argp)->argval[0], isdigit(d)) && (c=='>' || c=='<')) 184 { 185 word(); 186 wdnum=d-'0'; 187 } 188 else 189 { 190 /*check for reserved words and aliases */ 191 wdval = (reserv!=0?syslook(((ARGPTR)argp)->argval,reserved):0); 192 /* for unity database software, allow select to be aliased */ 193 if((reserv!=0 && (wdval==0||wdval==SELSYM)) || (wdset&S_FLAG)) 194 { 195 /* check for aliases */ 196 NAMPTR np; 197 char *alp = ((ARGPTR)argp)->argval; 198 if(not_alias && (wdset&(E_FLAG|KEYFLG))==0 && 199 (np=findnod(alp,alias,CHK_FOR))) 200 { 201 if(attest(np,T_FLAG)==0 && (alp=valup(np))) 202 { 203 wdval = 0; 204 c = standin->flin; 205 push(&a_fb); 206 estabf(alp,&a_fd); 207 a_fb.flin = c; 208 aliflg = peekn; 209 peekn = 0; 210 wdset |= KEYFLG; 211 c = word(); 212 return(c); 213 } 214 } 215 } 216 } 217 } 218 else if(dipchar(c)) 219 { 220 if((d=nextc())==c) 221 { 222 wdval = c|SYMREP; 223 if(c=='<') 224 { 225 if((d=nextc())=='-') 226 wdnum |= IOSTRIP; 227 else 228 peekn = d|MARK; 229 } 230 /* arithmetic evaluation ((expr)) */ 231 else if(c == LPAREN && reserv != 0) 232 { 233 wdval = 0; 234 letflg = 1; 235 argp = endstak(movstr(blet,argp)); 236 } 237 } 238 else if(c=='|' && d=='&') 239 wdval = COOPSYM; 240 #ifdef DEVFD 241 else if(d==LPAREN && (c=='<'||c == '>')) 242 wdval = (c=='>'?OPROC:IPROC); 243 #endif /* DEVFD */ 244 else 245 { 246 peekn = d|MARK; 247 wdval = c; 248 } 249 } 250 else 251 { 252 if((wdval=c)==ENDOF) 253 { 254 wdval=EOFSYM; 255 } 256 if(iopend && eolchar(c)) 257 { 258 copy(iopend); 259 iopend=0; 260 } 261 } 262 reserv=0; 263 return(wdval); 264 } 265 266 /* 267 * skip until matching <closed> 268 * if flag > 0, then newlines and spaces are removed 269 * if flag < 0, then each newline cause syntax errors 270 */ 271 272 char *match_paren(argp,open,close,flag) 273 register char *argp; 274 register int open; 275 { 276 register int c; 277 register int count = 1; 278 register int quoted = 0; 279 int was_dollar=0; 280 char *oldargp = argp; 281 int line = standin->flin; 282 while(count) 283 { 284 /* check for unmatched <open> */ 285 if((c=(open==LITERAL?readc():nextc()))==0) 286 { 287 /* eof before matching quote */ 288 /* This keeps old shell scripts running */ 289 if(fileno(input) == F_STRING) 290 break; 291 standin->flin = line; 292 wdval = open|EOFSYM; 293 synbad(); 294 } 295 if(c == NL) 296 { 297 if(flag<0) 298 break; 299 chkpr(0); 300 if(flag) 301 continue; 302 } 303 else if(c == close) 304 { 305 if(!quoted) 306 count--; 307 } 308 else if(c == open && !quoted) 309 count++; 310 if(flag<=0 || c != SP ) 311 { 312 if(open==LITERAL) 313 *argp++ = ESCAPE; 314 if(argp >= (char*)brkend) 315 setbrk(BRKINCR); 316 *argp++ = c; 317 if(open==LITERAL) 318 continue; 319 } 320 if(!quoted && flag==0) 321 { 322 /* check for nested '', "", and `` within $() */ 323 if(open!=close) 324 { 325 if(c==LITERAL) 326 argp--; 327 else if(!qotchar(c)) 328 goto skip; 329 argp = match_paren(argp,c,c,0); 330 } 331 /* check for $() within '', "", and `` */ 332 else if(was_dollar && c==LPAREN) 333 { 334 argp = match_paren(argp,LPAREN,RPAREN,0); 335 } 336 skip: 337 was_dollar = (c==DOLLAR); 338 } 339 if(c == ESCAPE) 340 quoted = 1 - quoted; 341 else 342 quoted = 0; 343 } 344 if(open==LITERAL) 345 { 346 argp -= 2; 347 if(argp==oldargp) 348 { 349 /* handle null argument specially */ 350 *argp++ = '"'; 351 *argp++ = '"'; 352 } 353 } 354 return(argp); 355 } 356 357 /* 358 * If quote is equal to zero then 359 * this routine returns the next input character but strips shell 360 * line continuations and issues prompts at end of line 361 * Otherwise this routine is the same as readc() 362 */ 363 364 nextc() 365 { 366 register int c, d; 367 static int oldd; 368 retry: 369 d = readc(); 370 if(d==ESCAPE && oldd!=ESCAPE) 371 { 372 if((c=readc())==NL) 373 { 374 chkpr(0); 375 goto retry; 376 } 377 peekc = c|MARK; 378 } 379 oldd = d; 380 return(d); 381 } 382 383 readc() 384 { 385 register int c; 386 register SHFILE f = standin; 387 register FILE *fd = input; 388 int maxtry = 20; 389 if(staktop >= brkend) 390 setbrk(BRKINCR); 391 if(peekn) 392 { 393 c = peekn&~MARK; 394 peekn = 0; 395 return(c); 396 } 397 if(peekc) 398 { 399 c = peekc&~MARK; 400 peekc = 0; 401 return(c); 402 } 403 retry: 404 #ifdef JOBS 405 #ifdef BSD 406 if(states&READC) 407 nreadc++; 408 else 409 { 410 nreadc = 1; 411 states |= READC; 412 } 413 /* this is needed to implement Bourne shell semantics of traps */ 414 /* reads automatically restart with jobs library */ 415 if(fd->_cnt==0 && setjmp(readerr)) 416 goto trapfound; 417 #endif /* BSD */ 418 #endif /* JOBS */ 419 if((c=getc(fd)) != EOF) 420 { 421 if(c==0) 422 { 423 if(f->feval && estabf(*f->feval++,fd)==0) 424 c = SP; 425 /* treat the NULL byte as eof for TMPIO */ 426 else if(fileno(fd) == TMPIO) 427 { 428 setbuf(fd,NIL); 429 lseek(TMPIO,0L,0); 430 } 431 /* skip over null bytes in files */ 432 else if(fileno(fd) != F_STRING) 433 goto retry; 434 else if(aliflg) 435 { 436 c = (aliflg&~MARK); 437 aliflg = 0; 438 wdset |= S_FLAG; 439 pop(1); 440 } 441 else 442 /* end-of-string is end-of-file */ 443 { 444 f->feval = 0; 445 estabf(nullstr,fd); 446 fd->_flag |= _IOEOF; 447 } 448 } 449 if((f->fstak==0 || (states&FIXFLG)) && c != 0) 450 { 451 if((states&READPR) && aliflg==0) 452 putc(c,output); 453 if((states&(FIXFLG)) && fileno(fd)!=F_STRING) 454 putc(c,fc_fix->fixfd); 455 } 456 if(c==NL) 457 f->flin++; 458 } 459 else if(feof(fd)) 460 { 461 fd->_flag |= _IOEOF; 462 c = ENDOF; 463 } 464 else 465 { 466 clearerr(fd); 467 if(trapnote&SIGSET) 468 { 469 newline(); 470 exitsh(SIGFAIL); 471 } 472 else if((trapnote&TRAPSET) && (states&RWAIT)) 473 { 474 trapfound: 475 newline(); 476 chktrap(); 477 arg_clear(); 478 } 479 else if(--maxtry > 0) 480 goto retry; 481 else 482 fd->_flag |= _IOERR; 483 c = ENDOF; 484 } 485 #ifdef JOBS 486 #ifdef BSD 487 if(--nreadc <=0) 488 states &= ~READC; 489 #endif /* BSD */ 490 #endif /* JOBS */ 491 return(c); 492 } 493 494 #ifdef JOBS 495 #ifdef BSD 496 /* 497 * This routine is here because signals behave differently with sigset 498 */ 499 500 interrupt() 501 { 502 register FILE *fd = input; 503 clearerr(fd); 504 if(trapnote&SIGSET) 505 { 506 newline(); 507 trapnote = 0; 508 exitsh(SIGFAIL); 509 } 510 else if((trapnote&TRAPSET) && (states&RWAIT)) 511 longjmp(readerr,1); 512 } 513 514 #endif /* BSD */ 515 #endif /* JOBS */ 516 517