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