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
word()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
match_paren(argp,open,close,flag)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
nextc()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
readc()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
interrupt()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