BSD 4_4_Lite1 development
[unix-history] / usr / src / contrib / rc-1.4 / glom.c
CommitLineData
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
8static List *backq(Node *, Node *);
9static List *bqinput(List *, int);
10static List *count(List *);
11static List *mkcmdarg(Node *);
12
13Rq *redirq = NULL;
14
15extern 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
31extern 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
47extern 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
88extern 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
112extern 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
135static 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
143extern 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
176static 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
242static 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
268extern 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
282static 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
311extern 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}