xref: /original-bsd/local/toolchest/ksh/sh/word.c (revision e59fb703)
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