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