9base

revived minimalist port of Plan 9 userland to Unix
git clone git://git.suckless.org/9base
Log | Files | Refs | README | LICENSE

varsub.c (4311B)


      1 #include	"mk.h"
      2 
      3 static	Word		*subsub(Word*, char*, char*);
      4 static	Word		*expandvar(char**);
      5 static	Bufblock	*varname(char**);
      6 static	Word		*extractpat(char*, char**, char*, char*);
      7 static	int		submatch(char*, Word*, Word*, int*, char**);
      8 static	Word		*varmatch(char *);
      9 
     10 Word *
     11 varsub(char **s)
     12 {
     13 	Bufblock *b;
     14 	Word *w;
     15 
     16 	if(**s == '{')		/* either ${name} or ${name: A%B==C%D}*/
     17 		return expandvar(s);
     18 
     19 	b = varname(s);
     20 	if(b == 0)
     21 		return 0;
     22 
     23 	w = varmatch(b->start);
     24 	freebuf(b);
     25 	return w;
     26 }
     27 
     28 /*
     29  *	extract a variable name
     30  */
     31 static Bufblock*
     32 varname(char **s)
     33 {
     34 	Bufblock *b;
     35 	char *cp;
     36 	Rune r;
     37 	int n;
     38 
     39 	b = newbuf();
     40 	cp = *s;
     41 	for(;;){
     42 		n = chartorune(&r, cp);
     43 		if (!WORDCHR(r))
     44 			break;
     45 		rinsert(b, r);
     46 		cp += n;
     47 	}
     48 	if (b->current == b->start){
     49 		SYNERR(-1);
     50 		fprint(2, "missing variable name <%s>\n", *s);
     51 		freebuf(b);
     52 		return 0;
     53 	}
     54 	*s = cp;
     55 	insert(b, 0);
     56 	return b;
     57 }
     58 
     59 static Word*
     60 varmatch(char *name)
     61 {
     62 	Word *w;
     63 	Symtab *sym;
     64 	
     65 	sym = symlook(name, S_VAR, 0);
     66 	if(sym){
     67 			/* check for at least one non-NULL value */
     68 		for (w = sym->u.ptr; w; w = w->next)
     69 			if(w->s && *w->s)
     70 				return wdup(w);
     71 	}
     72 	return 0;
     73 }
     74 
     75 static Word*
     76 expandvar(char **s)
     77 {
     78 	Word *w;
     79 	Bufblock *buf;
     80 	Symtab *sym;
     81 	char *cp, *begin, *end;
     82 
     83 	begin = *s;
     84 	(*s)++;						/* skip the '{' */
     85 	buf = varname(s);
     86 	if (buf == 0)
     87 		return 0;
     88 	cp = *s;
     89 	if (*cp == '}') {				/* ${name} variant*/
     90 		(*s)++;					/* skip the '}' */
     91 		w = varmatch(buf->start);
     92 		freebuf(buf);
     93 		return w;
     94 	}
     95 	if (*cp != ':') {
     96 		SYNERR(-1);
     97 		fprint(2, "bad variable name <%s>\n", buf->start);
     98 		freebuf(buf);
     99 		return 0;
    100 	}
    101 	cp++;
    102 	end = shellt->charin(cp , "}");
    103 	if(end == 0){
    104 		SYNERR(-1);
    105 		fprint(2, "missing '}': %s\n", begin);
    106 		Exit();
    107 	}
    108 	*end = 0;
    109 	*s = end+1;
    110 	
    111 	sym = symlook(buf->start, S_VAR, 0);
    112 	if(sym == 0 || sym->u.ptr == 0)
    113 		w = newword(buf->start);
    114 	else
    115 		w = subsub(sym->u.ptr, cp, end);
    116 	freebuf(buf);
    117 	return w;
    118 }
    119 
    120 static Word*
    121 extractpat(char *s, char **r, char *term, char *end)
    122 {
    123 	int save;
    124 	char *cp;
    125 	Word *w;
    126 
    127 	cp = shellt->charin(s, term);
    128 	if(cp){
    129 		*r = cp;
    130 		if(cp == s)
    131 			return 0;
    132 		save = *cp;
    133 		*cp = 0;
    134 		w = stow(s);
    135 		*cp = save;
    136 	} else {
    137 		*r = end;
    138 		w = stow(s);
    139 	}
    140 	return w;
    141 }
    142 
    143 static Word*
    144 subsub(Word *v, char *s, char *end)
    145 {
    146 	int nmid;
    147 	Word *head, *tail, *w, *h;
    148 	Word *a, *b, *c, *d;
    149 	Bufblock *buf;
    150 	char *cp, *enda;
    151 
    152 	a = extractpat(s, &cp, "=%&", end);
    153 	b = c = d = 0;
    154 	if(PERCENT(*cp))
    155 		b = extractpat(cp+1, &cp, "=", end);
    156 	if(*cp == '=')
    157 		c = extractpat(cp+1, &cp, "&%", end);
    158 	if(PERCENT(*cp))
    159 		d = stow(cp+1);
    160 	else if(*cp)
    161 		d = stow(cp);
    162 
    163 	head = tail = 0;
    164 	buf = newbuf();
    165 	for(; v; v = v->next){
    166 		h = w = 0;
    167 		if(submatch(v->s, a, b, &nmid, &enda)){
    168 			/* enda points to end of A match in source;
    169 			 * nmid = number of chars between end of A and start of B
    170 			 */
    171 			if(c){
    172 				h = w = wdup(c);
    173 				while(w->next)
    174 					w = w->next;
    175 			}
    176 			if(PERCENT(*cp) && nmid > 0){	
    177 				if(w){
    178 					bufcpy(buf, w->s, strlen(w->s));
    179 					bufcpy(buf, enda, nmid);
    180 					insert(buf, 0);
    181 					free(w->s);
    182 					w->s = strdup(buf->start);
    183 				} else {
    184 					bufcpy(buf, enda, nmid);
    185 					insert(buf, 0);
    186 					h = w = newword(buf->start);
    187 				}
    188 				buf->current = buf->start;
    189 			}
    190 			if(d && *d->s){
    191 				if(w){
    192 
    193 					bufcpy(buf, w->s, strlen(w->s));
    194 					bufcpy(buf, d->s, strlen(d->s));
    195 					insert(buf, 0);
    196 					free(w->s);
    197 					w->s = strdup(buf->start);
    198 					w->next = wdup(d->next);
    199 					while(w->next)
    200 						w = w->next;
    201 					buf->current = buf->start;
    202 				} else
    203 					h = w = wdup(d);
    204 			}
    205 		}
    206 		if(w == 0)
    207 			h = w = newword(v->s);
    208 	
    209 		if(head == 0)
    210 			head = h;
    211 		else
    212 			tail->next = h;
    213 		tail = w;
    214 	}
    215 	freebuf(buf);
    216 	delword(a);
    217 	delword(b);
    218 	delword(c);
    219 	delword(d);
    220 	return head;
    221 }
    222 
    223 static int
    224 submatch(char *s, Word *a, Word *b, int *nmid, char **enda)
    225 {
    226 	Word *w;
    227 	int n;
    228 	char *end;
    229 
    230 	n = 0;
    231 	for(w = a; w; w = w->next){
    232 		n = strlen(w->s);
    233 		if(strncmp(s, w->s, n) == 0)
    234 			break;
    235 	}
    236 	if(a && w == 0)		/*  a == NULL matches everything*/
    237 		return 0;
    238 
    239 	*enda = s+n;		/* pointer to end a A part match */
    240 	*nmid = strlen(s)-n;	/* size of remainder of source */
    241 	end = *enda+*nmid;
    242 	for(w = b; w; w = w->next){
    243 		n = strlen(w->s);
    244 		if(strcmp(w->s, end-n) == 0){
    245 			*nmid -= n;
    246 			break;
    247 		}
    248 	}
    249 	if(b && w == 0)		/* b == NULL matches everything */
    250 		return 0;
    251 	return 1;
    252 }