src.hhvn.uk > rc > file > glom.c

Byron Rakitzis' rc(1) port, with a few modifications
Log | Files | Refs | README | LICENSE

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 }