Commit | Line | Data |
---|---|---|
1a9c62e9 C |
1 | /* glom.c: builds an argument list out of words, variables, etc. */ |
2 | ||
3 | #include <sys/types.h> | |
4 | #include <sys/stat.h> | |
5 | #include <signal.h> | |
6 | #include "rc.h" | |
7 | ||
8 | static List *backq(Node *, Node *); | |
9 | static List *bqinput(List *, int); | |
10 | static List *count(List *); | |
11 | static List *mkcmdarg(Node *); | |
12 | ||
13 | Rq *redirq = NULL; | |
14 | ||
15 | extern List *word(char *w, char *m) { | |
16 | List *s = NULL; | |
17 | if (w != NULL) { | |
18 | s = nnew(List); | |
19 | s->w = w; | |
20 | s->m = m; | |
21 | s->n = NULL; | |
22 | } | |
23 | return s; | |
24 | } | |
25 | ||
26 | /* | |
27 | Append list s2 to list s1 by copying s1 and making the new copy | |
28 | point at s2. | |
29 | */ | |
30 | ||
31 | extern List *append(List *s1, List *s2) { | |
32 | List *r, *top; | |
33 | if (s1 == NULL) | |
34 | return s2; | |
35 | if (s2 == NULL) | |
36 | return s1; | |
37 | for (r = top = nnew(List); 1; r = r->n = nnew(List)) { | |
38 | r->w = s1->w; | |
39 | r->m = s1->m; | |
40 | if ((s1 = s1->n) == NULL) | |
41 | break; | |
42 | } | |
43 | r->n = s2; | |
44 | return top; | |
45 | } | |
46 | ||
47 | extern List *concat(List *s1, List *s2) { | |
48 | int n1, n2; | |
49 | List *r, *top; | |
50 | if (s1 == NULL) | |
51 | return s2; | |
52 | if (s2 == NULL) | |
53 | return s1; | |
54 | if ((n1 = listnel(s1)) != (n2 = listnel(s2)) && n1 != 1 && n2 != 1) | |
55 | rc_error("bad concatenation"); | |
56 | for (r = top = nnew(List); 1; r = r->n = nnew(List)) { | |
57 | size_t x = strlen(s1->w); | |
58 | size_t y = strlen(s2->w); | |
59 | size_t z = x + y + 1; | |
60 | r->w = nalloc(z); | |
61 | strcpy(r->w, s1->w); | |
62 | strcat(r->w, s2->w); | |
63 | if (s1->m == NULL && s2->m == NULL) { | |
64 | r->m = NULL; | |
65 | } else { | |
66 | r->m = nalloc(z); | |
67 | if (s1->m == NULL) | |
68 | clear(r->m, x); | |
69 | else | |
70 | memcpy(r->m, s1->m, x); | |
71 | if (s2->m == NULL) | |
72 | clear(&r->m[x], y); | |
73 | else | |
74 | memcpy(&r->m[x], s2->m, y); | |
75 | r->m[z] = 0; | |
76 | } | |
77 | if (n1 > 1) | |
78 | s1 = s1->n; | |
79 | if (n2 > 1) | |
80 | s2 = s2->n; | |
81 | if (s1 == NULL || s2 == NULL || (n1 == 1 && n2 == 1)) | |
82 | break; | |
83 | } | |
84 | r->n = NULL; | |
85 | return top; | |
86 | } | |
87 | ||
88 | extern List *varsub(List *var, List *subs) { | |
89 | List *r, *top; | |
90 | int n = listnel(var); | |
91 | for (top = r = NULL; subs != NULL; subs = subs->n) { | |
92 | int i = a2u(subs->w); | |
93 | if (i < 1) | |
94 | rc_error("bad subscript"); | |
95 | if (i <= n) { | |
96 | List *sub = var; | |
97 | while (--i) | |
98 | sub = sub->n; /* loop until sub == var(i) */ | |
99 | if (top == NULL) | |
100 | top = r = nnew(List); | |
101 | else | |
102 | r = r->n = nnew(List); | |
103 | r->w = sub->w; | |
104 | r->m = sub->m; | |
105 | } | |
106 | } | |
107 | if (top != NULL) | |
108 | r->n = NULL; | |
109 | return top; | |
110 | } | |
111 | ||
112 | extern List *flatten(List *s) { | |
113 | List *r; | |
114 | size_t step; | |
115 | char *f; | |
116 | if (s == NULL || s->n == NULL) | |
117 | return s; | |
118 | r = nnew(List); | |
119 | f = r->w = nalloc(listlen(s) + 1); | |
120 | r->m = NULL; /* flattened lists come from variables, so no meta */ | |
121 | r->n = NULL; | |
122 | strcpy(f, s->w); | |
123 | f += strlen(s->w); | |
124 | do { | |
125 | *f++ = ' '; | |
126 | s = s->n; | |
127 | step = strlen(s->w); | |
128 | memcpy(f, s->w, step); | |
129 | f += step; | |
130 | } while (s->n != NULL); | |
131 | *f = '\0'; | |
132 | return r; | |
133 | } | |
134 | ||
135 | static List *count(List *l) { | |
136 | List *s = nnew(List); | |
137 | s->w = nprint("%d", listnel(l)); | |
138 | s->n = NULL; | |
139 | s->m = NULL; | |
140 | return s; | |
141 | } | |
142 | ||
143 | extern void assign(List *s1, List *s2, bool stack) { | |
144 | List *val = s2; | |
145 | if (s1 == NULL) | |
146 | rc_error("null variable name"); | |
147 | if (s1->n != NULL) | |
148 | rc_error("multi-word variable name"); | |
149 | if (*s1->w == '\0') | |
150 | rc_error("zero-length variable name"); | |
151 | if (a2u(s1->w) != -1) | |
152 | rc_error("numeric variable name"); | |
153 | if (strchr(s1->w, '=') != NULL) | |
154 | rc_error("'=' in variable name"); | |
155 | if (*s1->w == '*' && s1->w[1] == '\0') | |
156 | val = append(varlookup("0"), s2); /* preserve $0 when * is assigned explicitly */ | |
157 | if (s2 != NULL || stack) { | |
158 | if (dashex) | |
159 | prettyprint_var(2, s1->w, val); | |
160 | varassign(s1->w, val, stack); | |
161 | alias(s1->w, varlookup(s1->w), stack); | |
162 | } else { | |
163 | if (dashex) | |
164 | prettyprint_var(2, s1->w, NULL); | |
165 | varrm(s1->w, stack); | |
166 | } | |
167 | } | |
168 | ||
169 | /* | |
170 | The following two functions are by the courtesy of Paul Haahr, | |
171 | who could not stand the incompetence of my own backquote implementation. | |
172 | */ | |
173 | ||
174 | #define BUFSIZE ((size_t) 1000) | |
175 | ||
176 | static List *bqinput(List *ifs, int fd) { | |
177 | char *end, *bufend, *s; | |
178 | List *r, *top, *prev; | |
179 | size_t remain, bufsize; | |
180 | char isifs[256]; | |
181 | int n, state; /* a simple FSA is used to read in data */ | |
182 | ||
183 | clear(isifs, sizeof isifs); | |
184 | for (isifs['\0'] = TRUE; ifs != NULL; ifs = ifs->n) | |
185 | for (s = ifs->w; *s != '\0'; s++) | |
186 | isifs[*(unsigned char *)s] = TRUE; | |
187 | remain = bufsize = BUFSIZE; | |
188 | top = r = nnew(List); | |
189 | r->w = end = nalloc(bufsize + 1); | |
190 | r->m = NULL; | |
191 | state = 0; | |
192 | prev = NULL; | |
193 | ||
194 | while (1) { | |
195 | if (remain == 0) { /* is the string bigger than the buffer? */ | |
196 | size_t m = end - r->w; | |
197 | char *buf; | |
198 | while (bufsize < m + BUFSIZE) | |
199 | bufsize *= 2; | |
200 | buf = nalloc(bufsize + 1); | |
201 | memcpy(buf, r->w, m); | |
202 | r->w = buf; | |
203 | end = &buf[m]; | |
204 | remain = bufsize - m; | |
205 | } | |
206 | if ((n = rc_read(fd, end, remain)) <= 0) { | |
207 | if (n == 0) | |
208 | /* break */ break; | |
209 | uerror("backquote read"); | |
210 | rc_error(NULL); | |
211 | } | |
212 | remain -= n; | |
213 | for (bufend = &end[n]; end < bufend; end++) | |
214 | if (state == 0) { | |
215 | if (!isifs[*(unsigned char *)end]) { | |
216 | state = 1; | |
217 | r->w = end; | |
218 | } | |
219 | } else { | |
220 | if (isifs[*(unsigned char *)end]) { | |
221 | state = 0; | |
222 | *end = '\0'; | |
223 | prev = r; | |
224 | r = r->n = nnew(List); | |
225 | r->w = end+1; | |
226 | r->m = NULL; | |
227 | } | |
228 | } | |
229 | } | |
230 | if (state == 1) { /* terminate last string */ | |
231 | *end = '\0'; | |
232 | r->n = NULL; | |
233 | } else { | |
234 | if (prev == NULL) /* no input at all? */ | |
235 | top = NULL; | |
236 | else | |
237 | prev->n = NULL; /* else terminate list */ | |
238 | } | |
239 | return top; | |
240 | } | |
241 | ||
242 | static List *backq(Node *ifs, Node *n) { | |
243 | int p[2], pid, sp; | |
244 | List *bq; | |
245 | if (n == NULL) | |
246 | return NULL; | |
247 | if (pipe(p) < 0) { | |
248 | uerror("pipe"); | |
249 | rc_error(NULL); | |
250 | } | |
251 | if ((pid = rc_fork()) == 0) { | |
252 | setsigdefaults(FALSE); | |
253 | mvfd(p[1], 1); | |
254 | close(p[0]); | |
255 | redirq = NULL; | |
256 | walk(n, FALSE); | |
257 | exit(getstatus()); | |
258 | } | |
259 | close(p[1]); | |
260 | bq = bqinput(glom(ifs), p[0]); | |
261 | close(p[0]); | |
262 | rc_wait4(pid, &sp, TRUE); | |
263 | statprint(-1, sp); | |
264 | SIGCHK; | |
265 | return bq; | |
266 | } | |
267 | ||
268 | extern void qredir(Node *n) { | |
269 | Rq *next; | |
270 | if (redirq == NULL) { | |
271 | next = redirq = nnew(Rq); | |
272 | } else { | |
273 | for (next = redirq; next->n != NULL; next = next->n) | |
274 | ; | |
275 | next->n = nnew(Rq); | |
276 | next = next->n; | |
277 | } | |
278 | next->r = n; | |
279 | next->n = NULL; | |
280 | } | |
281 | ||
282 | static List *mkcmdarg(Node *n) { | |
283 | char *name; | |
284 | List *ret = nnew(List); | |
285 | Estack *e = nnew(Estack); | |
286 | Edata efd; | |
287 | int p[2]; | |
288 | if (pipe(p) < 0) { | |
289 | uerror("pipe"); | |
290 | return NULL; | |
291 | } | |
292 | if (rc_fork() == 0) { | |
293 | setsigdefaults(FALSE); | |
294 | if (mvfd(p[n->u[0].i == rFrom], n->u[0].i == rFrom) < 0) /* stupid hack */ | |
295 | exit(1); | |
296 | close(p[n->u[0].i != rFrom]); | |
297 | redirq = NULL; | |
298 | walk(n->u[2].p, FALSE); | |
299 | exit(getstatus()); | |
300 | } | |
301 | name = nprint("/dev/fd/%d", p[n->u[0].i != rFrom]); | |
302 | efd.fd = p[n->u[0].i != rFrom]; | |
303 | except(eFd, efd, e); | |
304 | close(p[n->u[0].i == rFrom]); | |
305 | ret->w = name; | |
306 | ret->m = NULL; | |
307 | ret->n = NULL; | |
308 | return ret; | |
309 | } | |
310 | ||
311 | extern List *glom(Node *n) { | |
312 | List *v, *head, *tail; | |
313 | Node *words; | |
314 | if (n == NULL) | |
315 | return NULL; | |
316 | switch (n->type) { | |
317 | case nArgs: | |
318 | case nLappend: | |
319 | words = n->u[0].p; | |
320 | tail = NULL; | |
321 | while (words != NULL && (words->type == nArgs || words->type == nLappend)) { | |
322 | if (words->u[1].p != NULL && words->u[1].p->type != nWord && words->u[1].p->type != nQword) | |
323 | break; | |
324 | head = glom(words->u[1].p); | |
325 | if (head != NULL) { | |
326 | head->n = tail; | |
327 | tail = head; | |
328 | } | |
329 | words = words->u[0].p; | |
330 | } | |
331 | v = append(glom(words), tail); /* force left to right evaluation */ | |
332 | return append(v, glom(n->u[1].p)); | |
333 | case nBackq: | |
334 | return backq(n->u[0].p, n->u[1].p); | |
335 | case nConcat: | |
336 | head = glom(n->u[0].p); /* force left-to-right evaluation */ | |
337 | return concat(head, glom(n->u[1].p)); | |
338 | case nDup: | |
339 | case nRedir: | |
340 | qredir(n); | |
341 | return NULL; | |
342 | case nWord: | |
343 | case nQword: | |
344 | return word(n->u[0].s, n->u[1].s); | |
345 | case nNmpipe: | |
346 | return mkcmdarg(n); | |
347 | default: | |
348 | /* | |
349 | The next four operations depend on the left-child of glom | |
350 | to be a variable name. Therefore the variable is looked up | |
351 | here. | |
352 | */ | |
353 | if ((v = glom(n->u[0].p)) == NULL) | |
354 | rc_error("null variable name"); | |
355 | if (v->n != NULL) | |
356 | rc_error("multi-word variable name"); | |
357 | if (*v->w == '\0') | |
358 | rc_error("zero-length variable name"); | |
359 | v = (*v->w == '*' && v->w[1] == '\0') ? varlookup(v->w)->n : varlookup(v->w); | |
360 | switch (n->type) { | |
361 | default: | |
362 | panic("unexpected node in glom"); | |
363 | exit(1); | |
364 | /* NOTREACHED */ | |
365 | case nCount: | |
366 | return count(v); | |
367 | case nFlat: | |
368 | return flatten(v); | |
369 | case nVar: | |
370 | return v; | |
371 | case nVarsub: | |
372 | return varsub(v, glom(n->u[1].p)); | |
373 | } | |
374 | } | |
375 | } |