rc

[fork] interactive rc shell
git clone https://hhvn.uk/rc
git clone git://hhvn.uk/rc
Log | Files | Refs | README | LICENSE

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 }