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
copyto(endch)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 } */
skipto(endch)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
getch(endch)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 */
macro(as)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
comsubst(type)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
mac_subst(in,ot)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
substring(string,pat,flag)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
mactrim(s,type)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
mactry(s)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
mac_error()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
charlen(str)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