386BSD 0.1 development
[unix-history] / usr / othersrc / public / zsh-2.2 / src / subst.c
CommitLineData
dbf02a84
WJ
1/*
2 *
3 * subst.c - various substitutions
4 *
5 * This file is part of zsh, the Z shell.
6 *
7 * This software is Copyright 1992 by Paul Falstad
8 *
9 * Permission is hereby granted to copy, reproduce, redistribute or otherwise
10 * use this software as long as: there is no monetary profit gained
11 * specifically from the use or reproduction of this software, it is not
12 * sold, rented, traded or otherwise marketed, and this copyright notice is
13 * included prominently in any copy made.
14 *
15 * The author make no claims as to the fitness or correctness of this software
16 * for any use whatsoever, and it is provided as is. Any use of this software
17 * is at the user's own risk.
18 *
19 */
20
21#include "zsh.h"
22#include <pwd.h>
23
24/* do substitutions before fork */
25
26void prefork(list) /**/
27Lklist list;
28{
29Lknode node = firstnode(list);
30int qt;
31
32 while (node)
33 {
34 char *str,*str3;
35
36 str = str3 = getdata(node);
37 if ((*str == Inang || *str == Outang || *str == Equals) &&
38 str[1] == Inpar)
39 {
40 if (*str == Inang)
41 setdata(node,getoutproc(str+2)); /* <(...) */
42 else if (*str == Equals)
43 setdata(node,getoutputfile(str+2)); /* =(...) */
44 else
45 setdata(node,getinproc(str+2)); /* >(...) */
46 if (!getdata(node))
47 {
48 zerr("parse error in process substitution",NULL,0);
49 return;
50 }
51 }
52 else while (*str)
53 {
54 if ((qt = *str == Qstring) || *str == String)
55 if (str[1] != Inpar)
56 if (str[1] == Inbrack)
57 {
58 arithsubst((vptr*) &str,&str3); /* $[...] */
59 setdata(node,str3);
60 str = str3;
61 continue;
62 }
63 else
64 {
65 paramsubst(list,node,str,str3,qt);
66 if (errflag)
67 return;
68 str3 = str = getdata(node);
69 continue;
70 }
71 str++;
72 if (errflag)
73 return;
74 }
75 if (*(char *) getdata(node))
76 remnulargs(getdata(node));
77 if (unset(IGNOREBRACES))
78 while (hasbraces(getdata(node)))
79 xpandbraces(list,&node);
80 filesub((char **) getaddrdata(node));
81 if (errflag)
82 return;
83 incnode(node);
84 }
85}
86
87void postfork(list,doglob) /**/
88Lklist list;int doglob;
89{
90Lknode node = firstnode(list);
91int glb = 1;
92
93 badcshglob = 0;
94 if (isset(NOGLOBOPT) || !doglob)
95 glb = 0;
96 while (node)
97 {
98 char *str3,*str;
99
100 str = str3 = getdata(node);
101 while (*str)
102 {
103 if (((*str == String || *str == Qstring) && str[1] == Inpar) ||
104 *str == Tick || *str == Qtick)
105 {
106 Lknode n = prevnode(node);
107
108 commsubst(list,node,str,str3,
109 (*str == Qstring || *str == Qtick)); /* `...`,$(...) */
110 if (errflag)
111 return;
112 str = str3 = getdata(node = nextnode(n));
113 }
114 str++;
115 }
116 if (glb)
117 {
118 if (haswilds(getdata(node)))
119 glob(list,&node);
120 if (errflag)
121 return;
122 }
123 incnode(node);
124 }
125 if (badcshglob == 1) zerr("no match",NULL,0);
126}
127
128/* perform substitution on a single word */
129
130void singsub(s) /**/
131char **s;
132{
133Lklist foo;
134char *t;
135
136 for (t = *s; *t; t++)
137 if (*t == String)
138 *t = Qstring;
139 else if (*t == Tick)
140 *t = Qtick;
141 foo = newlist();
142 addnode(foo,*s);
143 prefork(foo);
144 if (errflag)
145 return;
146 postfork(foo,0);
147 if (errflag)
148 return;
149 *s = ugetnode(foo);
150 if (firstnode(foo))
151 zerr("ambiguous: %s",*s,0);
152}
153
154/* strdup, but returns "Nularg" if this is a null string */
155
156vptr nstrdup(s) /**/
157vptr s;
158{
159char *t = s;
160char u[2];
161
162 u[0] = Nularg; u[1] = '\0';
163 if (!*t)
164 return strdup(u);
165 return strdup(t);
166}
167
168char *dynread(stop) /**/
169int stop;
170{
171int bsiz = 256,ct = 0,c;
172char *buf = zalloc(bsiz),*ptr;
173
174 ptr = buf;
175 while ((c = hgetc()) != stop)
176 {
177 *ptr++ = c;
178 if (++ct == bsiz)
179 {
180 buf = realloc(buf,bsiz *= 2);
181 ptr = buf+ct;
182 }
183 }
184 *ptr = 0;
185 return buf;
186}
187
188int filesub(namptr) /**/
189char **namptr;
190{
191char *str = *namptr,*cnam;
192
193 if (*str == Tilde && str[1] != '=')
194 {
195 if (str[1] == '+' && (str[2] == '/' || str[2] == '\0'))
196 {
197 char *foo = strdup(pwd); /* ~+ */
198
199 str+=2;
200 modify(&foo,&str);
201 *namptr = dyncat(pwd,str);
202 return 1;
203 }
204 else if (str[1] == '-' && (str[2] == '/' || str[2] == '\0'))
205 {
206 char *foo; /* ~- */
207
208 if (cnam = oldpwd)
209 foo = cnam;
210 else
211 foo = pwd;
212 str += 2;
213 foo = strdup(foo);
214 modify(&foo,&str);
215 *namptr = dyncat(foo,str);
216 return 1;
217 }
218 if (ialpha(str[1])) /* ~foo */
219 {
220 char *ptr,*hom;
221
222 for (ptr = ++str; *ptr && iuser(*ptr); ptr++)
223 if (*ptr == '-')
224 *ptr = '-';
225 if (*ptr && *ptr != '/') return 0;
226 if (!(hom = gethome(str,ptr-str)))
227 {
228 zerr("user not found: %l",str,ptr-str);
229 errflag = 1;
230 return 0;
231 }
232 modify(&hom,&ptr);
233 *namptr = dyncat(hom,ptr);
234 return 1;
235 }
236 else if (str[1] == '/') /* ~/foo */
237 {
238 *namptr = dyncat(home,str+1);
239 return 1;
240 }
241 else if (!str[1]) /* ~ by itself */
242 {
243 *namptr = strdup(home);
244 return 1;
245 }
246 }
247 if (*str == Equals && iuser(str[1]) && unset(NOEQUALS))
248 {
249 char *ptr,*s,*ds;
250 int val;
251
252 if (ialpha(str[1])) /* =foo */
253 {
254 char sav,*pp;
255
256 for (pp = str+1; *pp && *pp != ':'; pp++);
257 sav = *pp;
258 *pp = '\0';
259 if (!(cnam = findcmd(str+1)))
260 {
261 zerr("%s not found",str+1,0);
262 errflag = 1;
263 return 0;
264 }
265 *namptr = cnam;
266 if ((*pp = sav) == ':')
267 {
268 modify(namptr,&pp);
269 s = *namptr;
270 *namptr = dyncat(*namptr,pp);
271 }
272 return 1;
273 }
274 if (str[1] == '-') /* =- */
275 {
276 val = -1;
277 ptr = str+2;
278 }
279 else
280 val = zstrtol(str+1,&ptr,10); /* =# */
281 ds = dstackent(val);
282 if (!ds)
283 return 1;
284 s = strdup(ds);
285 modify(&s,&ptr);
286 *namptr = dyncat(s,ptr);
287 return 1;
288 }
289 return 0;
290}
291
292/* get a named directory */
293
294char *gethome(user,len) /**/
295char *user;int len;
296{
297char sav,*str;
298struct passwd *pw;
299
300 if (len == 0)
301 return strdup(home);
302 sav = user[len];
303 user[len] = '\0';
304 if ((str = getsparamval(user,len)) && *str == '/')
305 {
306 str = strdup(str);
307 adduserdir(user,str);
308 user[len] = sav;
309 return str;
310 }
311 permalloc(); /* fixes iris bug--getpwnam calls strdup! */
312 pw = getpwnam(user);
313 lastalloc();
314 if (!pw) {
315 user[len] = sav;
316 return NULL;
317 }
318 str = xsymlink(pw->pw_dir);
319 adduserdir(user,str);
320 user[len] = sav;
321 return str;
322}
323
324/* `...`, $(...) */
325
326void commsubst(l,n,str3,str,qt) /**/
327Lklist l;Lknode n;char *str3;char *str;int qt;
328{
329char *str2;
330Lknode where = prevnode(n);
331Lklist pl;
332
333 if (*str3 == Tick || *str3 == Qtick)
334 {
335 *str3 = '\0';
336 for (str2 = ++str3; *str3 != Tick && *str3 != Qtick; str3++);
337 *str3++ = '\0';
338 }
339 else
340 {
341 *str3++ = '\0';
342 for (str2 = ++str3; *str3 != Outpar; str3++);
343 *str3++ = '\0';
344 }
345 uremnode(l,n);
346 if (!(pl = getoutput(str2,qt)))
347 {
348 zerr("parse error in command substitution",NULL,0);
349 errflag = 1;
350 return;
351 }
352 if (full(pl))
353 {
354 setdata(firstnode(pl),dyncat(str,peekfirst(pl)));
355 setdata(lastnode(pl),dyncat(getdata(lastnode(pl)),str3));
356 inslist(pl,where,l);
357 }
358 else
359 insnode(l,where,dyncat(str,str3));
360}
361
362/* parameter substitution */
363
364void paramsubst(l,n,aptr,bptr,qt) /**/
365Lklist l;Lknode n;char *aptr;char *bptr;int qt;
366{
367char *s = aptr,*u,*idbeg,*idend,*ostr = bptr;
368int brs; /* != 0 means ${...}, otherwise $... */
369int colf; /* != 0 means we found a colon after the name */
370int doub = 0; /* != 0 means we have %%, not %, or ##, not # */
371int isarr = 0;
372int wasnularr = 0;
373int plan9 = isset(RCEXPANDPARAM);
374int getlen = 0;
375int vunset = 0;
376int spbreak = isset(SHWORDSPLIT) && !qt;
377char *val = NULL,**aval = NULL;
378int fwidth = 0;
379Value v;
380
381 *s++ = '\0';
382 if (!ialnum(*s) && *s != '#' && *s != Pound && *s != '-' &&
383 *s != '!' && *s != '$' && *s != String && *s != Qstring &&
384 *s != '?' && *s != Quest && *s != '_' &&
385 *s != '*' && *s != Star && *s != '@' && *s != '{' &&
386 *s != Inbrace && *s != '=' && *s != Hat && *s != '^') {
387 s[-1] = '$';
388 return;
389 }
390 if (brs = (*s == '{' || *s == Inbrace)) s++;
391 for (;;)
392 if (*s == '^' || *s == Hat)
393 plan9 ^= 1,s++;
394 else if (*s == '=')
395 spbreak ^= 1,s++;
396 else if ((*s == '#' || *s == Pound) && iident(s[1]))
397 getlen = 1,s++;
398 else
399 break;
400
401 idbeg = s;
402 if (!(v = getvalue(&s,1))) {
403 vunset = 1;
404 idend = s;
405 } else
406 if (isarr = v->isarr)
407 aval = getarrvalue(v);
408 else {
409 val = getstrvalue(v);
410 fwidth = v->pm->ct;
411 switch (v->pm->flags & (PMFLAG_L | PMFLAG_R | PMFLAG_Z)) {
412 char *t;
413 int t0;
414
415 case PMFLAG_L:
416 case PMFLAG_L|PMFLAG_Z:
417 t = val;
418 if (v->pm->flags & PMFLAG_Z)
419 while (*t == '0') t++;
420 else
421 while (isep(*t)) t++;
422 val = ncalloc(fwidth+1);
423 val[fwidth] = '\0';
424 if ((t0 = strlen(t)) > fwidth)
425 t0 = fwidth;
426 memset(val,' ',fwidth);
427 strncpy(val,t,t0);
428 break;
429 case PMFLAG_R:
430 case PMFLAG_Z:
431 case PMFLAG_Z|PMFLAG_R:
432 if (strlen(val) < fwidth) {
433 t = ncalloc(fwidth+1);
434 memset(t,(v->pm->flags & PMFLAG_R) ? ' ' : '0',fwidth);
435 if ((t0 = strlen(val)) > fwidth)
436 t0 = fwidth;
437 strcpy(t+(fwidth-t0),val);
438 val = t;
439 } else {
440 t = ncalloc(fwidth+1);
441 t[fwidth] = '\0';
442 strncpy(t,val+strlen(val)-fwidth,fwidth);
443 val = t;
444 }
445 break;
446 }
447 switch (v->pm->flags & (PMFLAG_l | PMFLAG_u)) {
448 char *t;
449
450 case PMFLAG_l:
451 t = val;
452 for (;*t;t++)
453 *t = tulower(*t);
454 break;
455 case PMFLAG_u:
456 t = val;
457 for (;*t;t++)
458 *t = tuupper(*t);
459 break;
460 }
461 }
462 if (colf = *s == ':') s++;
463
464 /* check for ${..?...} or ${..=..} or one of those. Only works
465 if the name is in braces. */
466
467 if (brs && (*s == '-' || *s == '=' || *s == '?' || *s == '+' || *s == '#' ||
468 *s == '%' || *s == Quest || *s == Pound)) {
469 if (v && v->isarr && (*s == '%' || *s == '#' || *s == Pound)) {
470 zerr("operator requires a scalar",NULL,0);
471 return;
472 }
473 if (*s == s[1]) {
474 s++;
475 doub = 1;
476 }
477 u = ++s;
478 if (brs) {
479 int bct = 1;
480
481 for (;;) {
482 if (*s == '{' || *s == Inbrace)
483 bct++;
484 else if (*s == '}' || *s == Outbrace)
485 bct--;
486 if (!bct || !*s)
487 break;
488 s++;
489 }
490 } else {
491 while (*s++);
492 s--;
493 }
494 if (*s) *s++ = '\0';
495 if (colf && !vunset)
496 vunset = (isarr) ? !*aval : !*val;
497 switch ((int)(unsigned char)u[-1]) {
498 case '-':
499 if (vunset)
500 val = strdup(u), isarr = 0;
501 break;
502 case '=':
503 if (vunset) {
504 char sav = *idend;
505
506 *idend = '\0';
507 setsparam(idbeg,ztrdup(val = strdup(u)));
508 *idend = sav;
509 isarr = 0;
510 }
511 break;
512 case '?':
513 case (int)(unsigned char)Quest:
514 if (vunset) {
515 zerr("%s",(*u) ? u : "parameter not set",0);
516 if (!interact)
517 exit(1);
518 return;
519 }
520 break;
521 case '+':
522 if (vunset)
523 val = strdup("");
524 else
525 val = strdup(u);
526 isarr = 0;
527 break;
528 case '#':
529 case (int)(unsigned char)Pound:
530 if (vunset)
531 val = strdup("");
532 singsub(&u);
533 getmatch(&val,u,doub);
534 break;
535 case '%':
536 if (vunset)
537 val = strdup("");
538 singsub(&u);
539 getmatch(&val,u,doub+2);
540 break;
541 }
542 } else { /* no ${...=...} or anything, but possible modifiers. */
543 if (vunset) {
544 if (isset(NOUNSET)) {
545 zerr("parameter not set",NULL,0);
546 return;
547 }
548 val = strdup("");
549 }
550 if (colf) {
551 s--;
552 if (!isarr) modify(&val,&s);
553 else {
554 char *ss = s;
555 char **ap = aval;
556 while (*ap) {
557 ss = s;
558 modify(ap,&ss);
559 }
560 }
561 }
562 if (brs) {
563 if (*s != '}' && *s != Outbrace) {
564 zerr("closing brace expected",NULL,0);
565 errflag = 1;
566 return;
567 }
568 s++;
569 }
570 }
571 if (errflag)
572 return;
573 if (getlen) {
574 long len = 0;
575 char buf[14];
576
577 if (isarr) {
578 char **ctr;
579 for (ctr = aval; *ctr; ctr++,len++);
580 } else
581 len = strlen(val);
582 sprintf(buf,"%ld",len);
583 val = strdup(buf);
584 isarr = 0;
585 }
586 if (isarr)
587 if (!aval || !aval[0]) {
588 if (isarr < 0)
589 wasnularr = 1;
590 val = strdup("");
591 isarr = 0;
592 } else if (!aval[1]) {
593 val = aval[0];
594 isarr = 0;
595 }
596 if (qt) {
597 if (isarr > 0) {
598 val = spacejoin(aval);
599 isarr = 0;
600 }
601 } else if (spbreak) {
602 if (isarr)
603 val = spacejoin(aval);
604 isarr = 1;
605 aval = spacesplit(val);
606 if (!aval || !aval[0]) {
607 val = strdup("");
608 isarr = 0;
609 } else if (!aval[1]) {
610 val = aval[0];
611 isarr = 0;
612 }
613 /* if only one member, not really an array */
614 if (!aval[1])
615 isarr = 0;
616 }
617 if (isarr)
618 if (plan9) {
619 int dlen;
620 char *y;
621
622 y = ncalloc((dlen = (char *) aptr-bptr+strlen(s)+1)+strlen(aval[0]));
623 setdata(n,y);
624 strcpy(y,ostr);
625 strcat(y,aval[0]);
626 strcat(y,s);
627 while (*++aval) {
628 char *x = ncalloc(dlen+strlen(*aval));
629
630 strcpy(x,ostr);
631 strcat(x,*aval);
632 strcat(x,s);
633 insnode(l,n,x), incnode(n);
634 }
635 } else {
636 char *zz;
637
638 zz = ncalloc((char *) aptr-(bptr)+strlen(aval[0])+1);
639 setdata(n,zz);
640 strcpy(zz,ostr);
641 strcat(zz,*aval++);
642 while (aval[1])
643 insnode(l,n,*aval++), incnode(n);
644 zz = ncalloc(strlen(*aval)+strlen(s)+1);
645 strcpy(zz,*aval);
646 strcat(zz,s);
647 insnode(l,n,zz);
648 }
649 else {
650 bptr = ncalloc((char *) aptr-bptr+strlen(val)+strlen(s)+1);
651 setdata(n,bptr);
652 strcpy(bptr,ostr);
653 strcat(bptr,val);
654 strcat(bptr,s);
655 }
656}
657
658/* arithmetic substitution */
659
660void arithsubst(aptr,bptr) /**/
661vptr *aptr;char **bptr;
662{
663char *s = *aptr,*t,buf[16];
664long v;
665
666 *s = '\0';
667 for (; *s != Outbrack; s++);
668 *s++ = '\0';
669 v = matheval((char *) *aptr+2);
670 sprintf(buf,"%ld",v);
671 t = ncalloc(strlen(*bptr)+strlen(buf)+strlen(s)+1);
672 strcpy(t,*bptr);
673 strcat(t,buf);
674 strcat(t,s);
675 *bptr = t;
676}
677
678void modify(str,ptr) /**/
679char **str;char **ptr;
680{
681char *ptr1,*ptr2,*ptr3,del,*lptr;
682int gbal;
683
684 if (**ptr == ':')
685 *str = strdup(*str);
686 while (**ptr == ':')
687 {
688 lptr = *ptr;
689 (*ptr)++;
690 gbal = 0;
691here:
692 switch(*(*ptr)++)
693 {
694 case 'h': remtpath(str); break;
695 case 'r': remtext(str); break;
696 case 'e': rembutext(str); break;
697 case 't': remlpaths(str); break;
698 case 'l': downcase(str); break;
699 case 'u': upcase(str); break;
700 case 's':
701 if (hsubl)
702 free(hsubl);
703 if (hsubr)
704 free(hsubr);
705 ptr1 = *ptr;
706 del = *ptr1++;
707 for (ptr2 = ptr1; *ptr2 != del && *ptr2; ptr2++);
708 if (!*ptr2)
709 {
710 zerr("bad subtitution",NULL,0);
711 errflag = 1;
712 return;
713 }
714 *ptr2++ = '\0';
715 for (ptr3 = ptr2; *ptr3 != del && *ptr3; ptr3++);
716 if (*ptr3)
717 *ptr3++ = '\0';
718 hsubl = ztrdup(ptr1);
719 hsubr = ztrdup(ptr2);
720 *ptr = ptr3;
721 case '&':
722 if (hsubl && hsubr)
723 subst(str,hsubl,hsubr,gbal);
724 break;
725 case 'g': gbal = 1; goto here;
726 default: *ptr = lptr; return;
727 }
728 }
729}
730
731/* get a directory stack entry */
732
733char *dstackent(val) /**/
734int val;
735{
736Lknode node;
737
738 if ((val < 0 && !firstnode(dirstack)) || !val--)
739 return pwd;
740 if (val < 0)
741 node = lastnode(dirstack);
742 else
743 for (node = firstnode(dirstack); node && val; val--,incnode(node));
744 if (!node)
745 {
746 zerr("not enough dir stack entries.",NULL,0);
747 errflag = 1;
748 return NULL;
749 }
750 return getdata(node);
751}
752
753/* make an alias hash table node */
754
755struct alias *mkanode(txt,cmflag) /**/
756char *txt;int cmflag;
757{
758struct alias *ptr = (Alias) zcalloc(sizeof *ptr);
759
760 ptr->text = txt;
761 ptr->cmd = cmflag;
762 ptr->inuse = 0;
763 return ptr;
764}