xref: /original-bsd/local/toolchest/ksh/sh/macro.c (revision f052b07a)
1 /*
2 
3  *      Copyright (c) 1984, 1985, 1986 AT&T
4  *      All Rights Reserved
5 
6  *      THIS IS UNPUBLISHED PROPRIETARY SOURCE
7  *      CODE OF AT&T.
8  *      The copyright notice above does not
9  *      evidence any actual or intended
10  *      publication of such source code.
11 
12  */
13 /* @(#)macro.c	1.1 */
14 /*
15  * UNIX shell
16  *
17  * S. R. Bourne
18  * AT&T Bell Laboratories
19  * Rewritten by David Korn
20  *
21  */
22 
23 #include	<sys/types.h>
24 #include	<sys/stat.h>
25 #include	"flags.h"
26 #include	"defs.h"
27 #include	"io.h"
28 #include	"sym.h"
29 #include	"stak.h"
30 #include	"name.h"
31 #include	"shtype.h"
32 #include	"mode.h"
33 #include	"jobs.h"
34 #include	"builtins.h"
35 #include	"brkincr.h"
36 #ifdef MULTIBYTE
37 #include	"national.h"
38 #endif /* MULTIBYTE */
39 
40 #define unreadc(c)	(peekc = (c)|MARK)
41 #define blt_no(t)	((t)>>(COMBITS+1))
42 #define RBRACE		'}'
43 
44 /* These routines are defined by this module */
45 char	*macro();
46 char	*mactry();
47 char	*mactrim();
48 void	mac_subst();
49 
50 /* These external routines are referenced by this module */
51 extern char	*arg_dolminus();
52 extern void	assign();
53 extern void	await();
54 extern FILE	*chkopen();
55 extern void	chkpipe();
56 extern TREPTR	cmd();
57 extern void	exfunct();
58 extern void	failed();
59 extern void	initf();
60 extern char	*itos();
61 extern NAMPTR	lookup();
62 extern long	lseek();
63 extern TREPTR	makefork();
64 extern char	*match_paren();
65 extern char	*movstr();
66 extern void	p_setout();
67 extern int	readc();
68 extern char	*strcpy();
69 extern void	tdystak();
70 extern FILE	*tmp_open();
71 extern void	trim();
72 extern char	*valup();
73 
74 #ifdef MULTIBYTE
75 static int	charlen();
76 #endif /* MULTIBYTE */
77 static char	*copyto();
78 static char	*substring();
79 static void	skipto();
80 static int	getch();
81 static void	comsubst();
82 static void	mac_error();
83 
84 static char	quote;	/* used locally */
85 static char	quoted;	/* used locally */
86 static char	mflag;	/* set for macro expansion, unset for here docs */
87 static FILE	*w_fd;
88 static int mac_try;
89 static jmp_buf mac_buf;
90 static char	idb[2];
91 
92 
93 static char *copyto(endch)
94 register char	endch;
95 {
96 	register int	c;
97 
98 	while((c=getch(endch))!=endch && c)
99 	{
100 		if(quote || c==ESCAPE)
101 		{
102 			pushstak(ESCAPE);
103 			if(c==ESCAPE)
104 			{
105 				c = readc();
106 				if(quote && !escchar(c) && c!= '"')
107 				{
108 					pushstak(ESCAPE);
109 					pushstak(ESCAPE);
110 				}
111 			}
112 		}
113 		pushstak(c);
114 	}
115 	zerostak();
116 	if(c!=endch)
117 		mac_error();
118 }
119 
120 	/* skip chars up to } */
121 static void skipto(endch)
122 register char endch;
123 {
124 	register char	c;
125 	while((c=readc()) && c!=endch)
126 	{
127 		switch(c)
128 		{
129 			case SQUOTE:	case DQUOTE:
130 				skipto(c);
131 				break;
132 
133 			case DOLLAR:
134 				if(readc()==BRACE)
135 					skipto(RBRACE);
136 		}
137 	}
138 	if(c!=endch)
139 		mac_error();
140 }
141 
142 static int getch(endch)
143 int	endch;
144 {
145 	register int	c;
146 	int atflag;  /* set if $@ or ${array[@]} within double quotes */
147 retry:
148 	c = readc();
149 	if(!subchar(c))
150 		return(c);
151 	if(c==DOLLAR)
152 	{
153 		register int	 bra = 0; /* {...} bra =1, {#...} bra=2 */
154 		register char *v;
155 		register char *argp;
156 		register NAMPTR	 n=(NAMPTR)NULL;
157 		int 	dolg=0;
158 		int dolmax = dolc+1;
159 		BOOL 	nulflg;
160 		char *id=idb;
161 		*id = 0;
162 	retry1:
163 		c = readc();
164 		switch(c)
165 		{
166 			case DOLLAR:
167 				v=pidadr;
168 				break;
169 
170 			case '!':
171 				v=pcsadr;
172 				break;
173 
174 			case BRACE:
175 				if(bra++ ==0)
176 					goto retry1;
177 
178 			case LPAREN:
179 				if(bra==0 && mac_try==0)
180 				{
181 					comsubst(1);
182 					goto retry;
183 				}
184 				goto nosub;
185 
186 			case RBRACE:
187 				if(bra!=2)
188 					goto nosub;
189 				bra = 0;
190 			case '#':
191 				if(bra ==1)
192 				{
193 					bra++;
194 					goto retry1;
195 				}
196 				v=itos(dolc);
197 				break;
198 
199 			case '?':
200 				v=itos(savexit);
201 				break;
202 
203 			case '-':
204 				v=arg_dolminus();
205 				break;
206 
207 			default:
208 				if(isalpha(c))
209 				{
210 					argp=(char *) relstak();
211 					while(isalnum(c))
212 					{
213 						pushstak(c);
214 						c= readc();
215 					}
216 					if(c=='[' && bra)
217 					{
218 						if((c=readc(),astchar(c)))
219 						{
220 							*id = c;
221 							if(c=readc()!=']')
222 								mac_error();
223 							dolmax = 0;
224 						}
225 						else
226 						{
227 							int savq = quote;
228 							pushstak('[');
229 							unreadc(c);
230 							quote = 0;
231 							copyto(']');
232 							quote = savq;
233 							pushstak(']');
234 						}
235 					}
236 					else
237 						unreadc(c);
238 					zerostak();
239 					n=lookup(absstak(argp));
240 					setstak(argp);
241 					v = valup(n);
242 					id = n->namid;
243 					if(dolmax == 0 && attest(n, ARRAY))
244 					{
245 						dolg = -((int)(arayp(n)->maxi) + 1);
246 						while(v==0)
247 						{
248 							arayp(n)->adot++;
249 							if(++dolg == 0)
250 								break;
251 							v = valup(n);
252 						}
253 					}
254 					goto cont1;
255 				}
256 				if(digchar(c))
257 				{
258 					*id = c;
259 					if(astchar(c))
260 					{
261 						dolg=1;
262 						c=1;
263 					}
264 					else
265 					{
266 						c -= '0';
267 						if(bra)
268 						{
269 							int d;
270 							while((d=readc(),isdigit(d)))
271 								c = 10*c + (d-'0');
272 							unreadc(d);
273 						}
274 					}
275 					v=((c==0)?cmdadr:(c<=dolc)?dolv[c] : (char *)(dolg=0));
276 					goto cont1;
277 	 			}
278 			nosub:
279 				if(bra)
280 					mac_error();
281 				else
282 				{
283 					unreadc(c);
284 					return(DOLLAR);
285 				}
286 			}
287 	cont1:
288 		c = readc();
289 		if(bra==2)
290 		{
291 			if(c!=RBRACE)
292 				mac_error();
293 			if(dolg==0 && dolmax)
294 #ifdef MULTIBYTE
295 				c = (v?charlen(v):0);
296 #else
297 				c = (v?strlen(v):0);
298 #endif /* MULTIBYTE */
299 			else if(dolg>0)
300 				c = dolc;
301 			else if(dolg<0)
302 				c = arayp(n)->maxi+1;
303 			else
304 				c = (v!=0);
305 			v = itos(c);
306 			dolg = 0;
307 			c = RBRACE;
308 		}
309 		/* check for quotes @ */
310 		if(idb[0]=='@' && quote && !atflag)
311 		{
312 			quoted--;
313 			atflag = 1;
314 		}
315 		if(c==':' && bra)	/* null and unset fix */
316 		{
317 			nulflg=1;
318 			c=readc();
319 		}
320 		else
321 			nulflg=0;
322 		if(!defchar(c) && bra)
323 			mac_error();
324 		argp = 0;
325 		if(bra)
326 		{
327 			if(c!=RBRACE)
328 			{
329 				argp=(char *)relstak();
330 				if((v==0 || (nulflg && *v==0)) ^ (setchar(c)!=0))
331 					copyto(RBRACE);
332 				else
333 					skipto(RBRACE);
334 				argp=absstak(argp);
335 			}
336 		}
337 		else
338 		{
339 			unreadc(c);
340 			c=0;
341 		}
342 		/* check for substring operations */
343 		if(c == '#' || c == '%')
344 		{
345 			if(dolg != 0)
346 				mac_error();
347 			if(v && *v)
348 			{
349 				/* allow room for escapes */
350 				staktop += strlen(v);
351 				strcpy(staktop,v);
352 				trim(argp);
353 				if(*argp==c)
354 				{
355 					c |= MARK;
356 					argp++;
357 				}
358 				v = substring(staktop,argp,c);
359 				if(c&MARK)
360 					argp--;
361 			}
362 			staktop = argp;
363 		}
364 		if(v && (!nulflg || *v ) && c!='+')
365 		{
366 			while(1)
367 			{
368 				BOOL no_ifs = 0;
369 				int sep = SP;
370 				argp = valup(IFSNOD);
371 				if(argp==0 || *argp==0)
372 					no_ifs++;
373 				else
374 					sep = *argp;
375 				/* quoted null strings have to be marked */
376 				if(*v==0 && quote)
377 				{
378 					pushstak(ESCAPE);
379 					pushstak(0);
380 				}
381 				while(c = *v++)
382 				{
383 					if(staktop >= brkend)
384 						setbrk(BRKINCR);
385 					if(quote || (c==ESCAPE&&mflag)
386 						 || (no_ifs&&isspace(c)))
387 				 		pushstak(ESCAPE);
388 			 		pushstak(c);
389 				}
390 				if(dolg==0 || (++dolg>=dolmax))
391 					 break;
392 				if(dolg>0)
393 					v = dolv[dolg];
394 				else
395 				{
396 					arayp(n)->adot++;
397 					while((v=valup(n))==0)
398 					{
399 						arayp(n)->adot++;
400 						if(dolg++==0)
401 						break;
402 					}
403 						if(v==0)
404 					break;
405 				}
406 				if(quote && *id=='*')
407 				{
408 					if(no_ifs)
409 						continue;
410 					pushstak(ESCAPE);
411 				}
412 				pushstak(sep);
413 			}
414 		}
415 		else if(argp)
416 		{
417 			if(c=='?')
418 			{
419 				if(mac_try)
420 					mac_error();
421 				else
422 				{
423 					trim(argp);
424 					failed(id,*argp?argp:badparam);
425 				}
426 			}
427 			else if(c=='=')
428 			{
429 				if(n)
430 				{
431 					trim(argp);
432 					assign(n,argp);
433 					staktop = movstr(valup(n),argp);
434 				}
435 				else
436 					mac_error();
437 			}
438 		}
439 		else if(is_option(NOSET))
440 		{
441 			if(mac_try)
442 				mac_error();
443 			else
444 				failed(id,unset);
445 		}
446 		goto retry;
447 	}
448 	else if(c==endch)
449 		return(c);
450 	else if(c==SQUOTE && mac_try==0)
451 	{
452 		comsubst(0);
453 		goto retry;
454 	}
455 	else if(c==DQUOTE)
456 	{
457 		if(quote ==0)
458 		{
459 			atflag = 0;
460 			quoted++;
461 		}
462 		quote ^= 1;
463 		goto retry;
464 	}
465 	return(c);
466 }
467 
468 	/* Strip "" and do $ substitution
469 	 * Leaves result on top of stack
470 	 */
471 char *macro(as)
472 char *as;
473 {
474 	register BOOL	savqu =quoted;
475 	register char	savq = quote;
476 	FILE	fblk;
477 	FILEBLK	cb;
478 	mflag = 1;
479 	push(&cb);
480 	estabf(as,&fblk);
481 	usestak();
482 	quote=0;
483 	quoted=0;
484 	copyto(0);
485 	pop(1);
486 	if(quoted && (stakbot == staktop))
487 	{
488 		pushstak(ESCAPE);
489 		pushstak(0);
490 	}
491 	/* above is the fix for *'.c' bug	*/
492 	quote=savq;
493 	quoted=savqu;
494 	return(fixstak());
495 }
496 
497 /*
498  * command substitution
499  * type==0 for ``
500  * type==1 for $()
501 */
502 
503 static void comsubst(type)
504 int type;
505 {
506 	FILEBLK	cb;
507 	register FILE	*fd;
508 	FILE 	*pv[2];
509 	FILE	fblk;
510 	char tmp_fname[TMPSIZ];
511 	register unsigned int	d;
512 	register TREPTR t;
513 	register char *argc;
514 	IOPTR saviotemp = iotemp;
515 	int forkflag = FPOU|FCOMSUB;
516 	STKPTR savtop = staktop;
517 	STKPTR savptr = fixstak();
518 	char inbuff[BUFSIZ];
519 	int saveflag = states&FIXFLG;
520 	register int waitflag = 0;
521 	if(w_fd)
522 		fflush(w_fd);	/* flush before executing command */
523 	usestak();
524 	if(type)
525 	{
526 		staktop = (STKPTR)(match_paren((char*)stakbot,LPAREN,RPAREN,0)-1);
527 	}
528 	else
529 	{
530 		while((d=readc())!=SQUOTE && d)
531 		{
532 			if(d==ESCAPE)
533 			{
534 				d = readc();
535 				/*
536 				 * This is wrong but it preserves compatibility with
537 				 * the SVR2 shell
538 				 */
539 				if(!(escchar(d) || (d=='"' && quote)))
540 					pushstak(ESCAPE);
541 			}
542 			pushstak(d);
543 		}
544 	}
545 	argc=fixstak();
546 	states &= ~FIXFLG;		/* do not save command subs in fc file */
547 	push(&cb);
548 	estabf(argc,&fblk);
549 	subflag = 0;
550 	exec_flag++;
551 	t = cmd(EOFSYM,MTFLG|NLFLG);
552 	exec_flag--;
553 	d = t->tretyp;
554 	if(!subflag && !t->treio && (d&COMMSK)==TCOM && blt_no(d)>SYSSPECIAL)
555 	{
556 		/* nested command subs not handled specially */
557 		/* handle command substitution of most builtins separately */
558 		/* exec, login, cd, ., eval and shift not handled this way */
559 		/* put output into tmpfile */
560 		FILE *save1_out = standout;
561 		if((states&IS_TMP)==0)
562 		{
563 			/* create and keep open a /tmp file for command subs */
564 			fd = tmp_open(tmp_fname);
565 			fd = frenumber(fd,TMPIO);
566 			states |= IS_TMP;
567 			/* root cannot unlink because fsck could give bad ref count */
568 			if(userid)
569 				unlink(tmp_fname);
570 			else
571 				states |= RM_TMP;
572 		}
573 		else
574 			fd = file_fd(TMPIO);
575 		standout = fd;
576 		/* this will only flush the buffer if output is fd already */
577 		p_setout(fd);
578 #ifdef JOBS
579 		states |= NONSTOP;
580 #endif	/* JOBS */
581 		putc(0,fd);
582 		exfunct(t,(char**)0,states&ERRFLG);
583 		putc(0,fd);
584 #ifdef JOBS
585 		states &= ~NONSTOP;
586 #endif	/* JOBS */
587 		if(*_sobuf != 0)
588 		{
589 			/* file is larger than buffer, read from it */
590 			fflush(fd);
591 			fseek(fd,1L,0);
592 			initf(fd);
593 			waitflag = -1;
594 		}
595 		else
596 		{
597 			/* The file is all in the buffer */
598 			setbuf(fd,NIL);
599 			strcpy(inbuff,(char*)_sobuf+1);
600 			setbuf(fd,(char*)_sobuf);
601 			estabf(inbuff,(fd= &fblk));
602 		}
603 		standout = save1_out;
604 		goto readit;
605 	}
606 	else if(d==0 && ((COMPTR)t)->comarg==0)
607 	{
608 		if(((t->treio)->iofile) == 0)
609 			argc = mactrim((t->treio)->ioname,1);
610 		else
611 			argc = devnull;
612 		fd = chkopen(argc);
613 	}
614 	else
615 	{
616 		waitflag++;
617 		if(iotemp!=saviotemp)
618 			forkflag |= FTMP;
619 		t = makefork(forkflag,t);
620 		  /* this is done like this so that the pipe
621 		   * is open only when needed
622 		   */
623 		chkpipe(pv);
624 #ifdef JOBS
625 		jobstat.cur_pgrp = jobstat.mypid;
626 		jobstat.j_flag++;
627 #endif	/* JOBS */
628 		execute(t, states&ERRFLG, (FILE**)0, pv);
629 #ifdef JOBS
630 		jobstat.j_flag = 0;
631 #endif	/* JOBS */
632 		fd = pv[INPIPE];
633 		fclose(pv[OTPIPE]);
634 	}
635 	setbuf(fd,inbuff);
636 	initf(fd);
637 
638 readit:
639 	tdystak(savptr);
640 	d = savtop - savptr;
641 	while(d--)
642 		*staktop++ = *savptr++;
643 	while(d=readc())
644 	{
645 		if(quote || (d==ESCAPE&&mflag))
646 			pushstak(ESCAPE);
647 		pushstak(d);
648 	}
649 	if(waitflag>0)
650 		await(parent,0);
651 	while(stakbot!=staktop)
652 	{
653 		if(*--staktop != NL)
654 		{
655 			*++staktop;
656 			break;
657 		}
658 		else if(quote)
659 			staktop--;
660 	}
661 	pop(waitflag>=0?0:1);
662 	states |= saveflag;
663 }
664 
665 
666 void mac_subst(in,ot)
667 FILE 	*in;
668 register FILE *ot;
669 {
670 	register char	c;
671 	register flag = is_option(EXECPR);
672 	FILEBLK 	fb;
673 	char inbuff[BUFSIZ];
674 	char otbuff[BUFSIZ];
675 	mflag = 0;
676 	w_fd = ot;
677 	push(&fb);
678 	initf(in);
679 	/* DQUOTE used to stop it from quoting */
680 	setbuf(in,inbuff);
681 	setbuf(ot,otbuff);
682 	if(flag)
683 		p_setout(stderr);
684 	usestak();
685 	while(1)
686 	{
687 		c=getch(DQUOTE);
688 		if(c==ESCAPE)
689 		{
690 			c = readc();
691 			if(!escchar(c))
692 				pushstak(ESCAPE);
693 		}
694 		if(staktop!=stakbot)
695 		{
696 			*staktop = 0;
697 			fputs(stakbot,ot);
698 			if(flag)
699 				fputs(stakbot,output);
700 			staktop = stakbot;
701 		}
702 		if(c==0)
703 			break;
704 		putc(c,ot);
705 		if(flag)
706 			putc(c,output);
707 	}
708 	pop(0);
709 	w_fd = NULL;
710 	fflush(ot);
711 	fseek(ot,0L,0);
712 	setbuf(ot,NIL);
713 }
714 
715 
716 
717 /*
718  * Computes the substring of STRING using the expression PAT
719  * depending on which FLAG is set.
720  */
721 
722 static char *substring(string,pat,flag)
723 char *string;
724 char *pat;
725 int flag;
726 {
727 	register char *sp = string;
728 	register char *cp;
729 	switch(flag)
730 	{
731 		case '#':
732 		case MARK|'#':
733 		{
734 			register int c;
735 			cp = sp;
736 			do
737 			{
738 #ifdef MULTIBYTE
739 				c = *sp;
740 				c = echarset(c);
741 				sp += (in_csize(c)+(c>=2));
742 				c = *sp;
743 #else
744 				c= *++sp;
745 #endif /* MULTIBYTE */
746 				*sp=0;
747 				if(gmatch(string,pat))
748 				{
749 					cp = sp;
750 					if(flag=='#')
751 						break;
752 				}
753 				*sp = c;
754 			}
755 			while(c);
756 			*sp = c;
757 			return(cp);
758 		}
759 
760 		case '%':
761 		case MARK|'%':
762 		{
763 			sp += strlen(sp);
764 			cp = sp;
765 			while(sp>=string)
766 			{
767 				if(gmatch(sp,pat))
768 				{
769 					cp = sp;
770 					if(flag=='%')
771 						break;
772 				}
773 				sp--;
774 #ifdef MULTIBYTE
775 				if(*sp&HIGHBIT)
776 				{
777 					if(*(sp-in_csize(3))==ESS3)
778 						sp -= in_csize(3);
779 					else if(*(sp-in_csize(2))==ESS2)
780 						sp -= in_csize(2);
781 					else
782 						sp -= (in_csize(1)-1);
783 				}
784 #endif /* MULTIBYTE */
785 			}
786 			*cp = 0;
787 			return(string);
788 		}
789 	}
790 	return(sp);
791 }
792 
793 
794 /*
795  * do parameter and command substitution and strip of quotes
796  * attempt file name expansion if <type> not zero
797  */
798 
799 char *mactrim(s,type)
800 char *	s;
801 {
802 	register char *t=macro(s);
803 	ARGPTR schain = gchain;
804 	if(type && f_complete(t,nullstr)==1)
805 		t = gchain->argval;
806 	gchain = schain;
807 	trim(t);
808 	return(t);
809 }
810 
811 /*
812  * perform only parameter substitution and catch failures
813  */
814 
815 char *mactry(s)
816 register char *s;
817 {
818 	mac_try++;
819 	if(setjmp(mac_buf)==0)
820 		s = mactrim(s,0);
821 	mac_try = 0;
822 	return(s);
823 }
824 
825 static void mac_error()
826 {
827 	if(mac_try)
828 		longjmp(mac_buf,1);
829 	error(badsub);
830 }
831 
832 
833 
834 #ifdef MULTIBYTE
835 static int	charlen(str)
836 register char *str;
837 {
838 	register int n = 0;
839 	register int c;
840 	while(*str)
841 	{
842 		c = echarset(*str);		/* find character set */
843 		str += (in_csize(c)+(c>=2));	/* move to next char */
844 		n += out_csize(c);		/* add character size */
845 	}
846 	return(n);
847 }
848 #endif /* MULTIBYTE */
849