rc

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

footobar.c (9328B)


      1 /*
      2    footobar.c: a collection of functions to convert internal representations of
      3    variables and functions to external representations, and vice versa
      4 */
      5 
      6 #include "rc.h"
      7 
      8 #include "input.h"
      9 
     10 /* protect an exported name from brain-dead shells */
     11 
     12 #if PROTECT_ENV
     13 static bool Fconv(Format *f, int ignore) {
     14 	unsigned const char *s = va_arg(f->args, unsigned const char *);
     15 	int c;
     16 
     17 	while ((c = *s++) != '\0')
     18 		if (dnw[c] || c == '*' || (c == '_' && *s == '_'))
     19 			fmtprint(f, "__%02x", c);
     20 		else
     21 			fmtputc(f, c);
     22 	return FALSE;
     23 }
     24 #endif
     25 
     26 /* convert a redirection to a printable form */
     27 
     28 static bool Dconv(Format *f, int ignore) {
     29 	const char *name = "?";
     30 	int n = va_arg(f->args, int);
     31 	switch (n) {
     32 	case rCreate:		name = ">";	break;
     33 	case rAppend:		name = ">>";	break;
     34 	case rFrom:		name = "<";	break;
     35 	case rHeredoc:		name = "<<";	break;
     36 	case rHerestring:	name = "<<<";	break;
     37 	}
     38 	fmtcat(f, name);
     39 	return FALSE;
     40 }
     41 
     42 /* defaultfd -- return the default fd for a given redirection operation */
     43 
     44 static int defaultfd(int op) {
     45 	return (op == rCreate || op == rAppend) ? 1 : 0;
     46 }
     47 
     48 /* convert a function in Node * form into something rc can parse (and humans can read?) */
     49 
     50 static bool Tconv(Format *f, int ignore) {
     51 	bool dollar = f->flags & FMT_altform;
     52 	Node *n = va_arg(f->args, Node *);
     53 
     54 	if (n == NULL) {
     55 		fmtprint(f, "()");
     56 		return FALSE;
     57 	}
     58 	switch (n->type) {
     59 	case nBang:	fmtprint(f, "!%T", n->u[0].p);				break;
     60 	case nCase:	fmtprint(f, "case %T", n->u[0].p);			break;
     61 	case nNowait:	fmtprint(f, "%T&", n->u[0].p);				break;
     62 	case nRmfn:	fmtprint(f, "fn %T", n->u[0].p);			break;
     63 	case nSubshell:	fmtprint(f, "@ %T", n->u[0].p);				break;
     64 	case nAndalso:	fmtprint(f, "%T&&%T", n->u[0].p, n->u[1].p);		break;
     65 	case nAssign:	fmtprint(f, "%T=%T", n->u[0].p, n->u[1].p);		break;
     66 	case nConcat:	fmtprint(f, "%T^%T", n->u[0].p, n->u[1].p);		break;
     67 	case nElse:	fmtprint(f, "{%T}else %T", n->u[0].p, n->u[1].p);	break;
     68 	case nNewfn:	fmtprint(f, "fn %T {%T}", n->u[0].p, n->u[1].p);	break;
     69 	case nIf:	fmtprint(f, "if(%T)%T", n->u[0].p, n->u[1].p);		break;
     70 	case nIfnot:	fmtprint(f, "if not %T", n->u[0].p);			break;
     71 	case nOrelse:	fmtprint(f, "%T||%T", n->u[0].p, n->u[1].p);		break;
     72 	case nArgs:	fmtprint(f, "%T %T", n->u[0].p, n->u[1].p);		break;
     73 	case nSwitch:	fmtprint(f, "switch(%T){%T}", n->u[0].p, n->u[1].p);	break;
     74 	case nMatch:	fmtprint(f, "~ %T %T", n->u[0].p, n->u[1].p);		break;
     75 	case nWhile:	fmtprint(f, "while(%T)%T", n->u[0].p, n->u[1].p);	break;
     76 	case nForin:	fmtprint(f, "for(%T in %T)%T", n->u[0].p, n->u[1].p, n->u[2].p); break;
     77 	case nVarsub:	fmtprint(f, "$%T(%T)", n->u[0].p, n->u[1].p);		break;
     78 	case nWord:
     79 		fmtprint(f, n->u[2].i && quotep(n->u[0].s, dollar) ?
     80 				"%#S" : "%S", n->u[0].s);
     81 		break;
     82 	case nLappend: {
     83 		static bool inlist;
     84 		if (!inlist) {
     85 			inlist = TRUE;
     86 			fmtprint(f, "(%T %T)", n->u[0].p, n->u[1].p);
     87 			inlist = FALSE;
     88 		} else {
     89 			fmtprint(f, "%T %T", n->u[0].p, n->u[1].p);
     90 		}
     91 		break;
     92 	}
     93 	case nCount: case nFlat: case nVar: {
     94 		char *lp = "", *rp = "";
     95 		Node *n0 = n->u[0].p;
     96 
     97 		if (n0->type != nWord)
     98 			lp = "(", rp = ")";
     99 
    100 		switch (n->type) {
    101 		default:	panic("this can't happen");		break;
    102 		case nCount:	fmtprint(f, "$#%s%#T%s", lp, n0, rp);	break;
    103 		case nFlat:	fmtprint(f, "$^%s%#T%s", lp, n0, rp);	break;
    104 		case nVar:	fmtprint(f, "$%s%#T%s", lp, n0, rp);	break;
    105 		}
    106 		break;
    107 	}
    108 	case nDup:
    109 		if (n->u[2].i != -1)
    110 			fmtprint(f, "%D[%d=%d]", n->u[0].i, n->u[1].i, n->u[2].i);
    111 		else
    112 			fmtprint(f, "%D[%d=]", n->u[0].i, n->u[1].i);
    113 		break;
    114 	case nBackq: {
    115 		Node *n0 = n->u[0].p, *n00;
    116 		if (n0 != NULL && n0->type == nVar
    117 		    && (n00 = n0->u[0].p) != NULL && n00->type == nWord && streq(n00->u[0].s, "ifs"))
    118 			fmtprint(f, "`");
    119 		else
    120 			fmtprint(f, "``%T", n0);
    121 		fmtprint(f, "{%T}", n->u[1].p);
    122 		break;
    123 	}
    124 	case nCbody:
    125 	case nBody: {
    126 		Node *n0 = n->u[0].p;
    127 		if (n0 != NULL)
    128 			fmtprint(f, "%T", n->u[0].p);
    129 		if (n->u[1].p != NULL) {
    130 			if (n0 != NULL && n0->type != nNowait)
    131 				fmtprint(f, ";");
    132 			fmtprint(f, "%T", n->u[1].p);
    133 		}
    134 		break;
    135 	}
    136 	case nBrace:
    137 		fmtprint(f, "{%T}", n->u[0].p);
    138 		if (n->u[1].p != NULL)
    139 			fmtprint(f, "%T", n->u[1].p);
    140 		break;
    141 	case nEpilog:
    142 	case nPre:
    143 		fmtprint(f, "%T", n->u[0].p);
    144 		if (n->u[1].p != NULL)
    145 			fmtprint(f, " %T", n->u[1].p);
    146 		break;
    147 	case nPipe: {
    148 		int ofd = n->u[0].i, ifd = n->u[1].i;
    149 		fmtprint(f, "%T|", n->u[2].p);
    150 		if (ifd != 0)
    151 			fmtprint(f, "[%d=%d]", ofd, ifd);
    152 		else if (ofd != 1)
    153 			fmtprint(f, "[%d]", ofd);
    154 		fmtprint(f, "%T", n->u[3].p);
    155 		break;
    156 	}
    157 	case nRedir: {
    158 		int op = n->u[0].i;
    159 		fmtprint(f, "%D", op);
    160 		if (n->u[1].i != defaultfd(op))
    161 			fmtprint(f, "[%d]", n->u[1].i);
    162 		fmtprint(f, "%T", n->u[2].p);
    163 		break;
    164 	}
    165 	case nNmpipe: {
    166 		int op = n->u[0].i;
    167 		fmtprint(f, "%D", op);
    168 		if (n->u[1].i != defaultfd(op))
    169 			fmtprint(f, "[%d]", n->u[1].i);
    170 		fmtprint(f, "{%T}", n->u[2].p);
    171 		break;
    172 	}
    173 	}
    174 	return FALSE;
    175 }
    176 
    177 /* convert a List to an array, for execve() */
    178 
    179 extern char **list2array(List *s, bool print) {
    180 	char **argv, **av;
    181 
    182 	if (print)
    183 		fprint(2, "%L\n", s, " ");
    184 	/*
    185 	   Allocate 3 extra spots (2 for the fake execve & 1 for defaulting to
    186 	   sh) and hide these from exec().
    187 	*/
    188 	argv = av = (char **) nalloc((listnel(s) + 4) * sizeof *av) + 3;
    189 	while (s != NULL) {
    190 		*av++ = s->w;
    191 		s = s->n;
    192 	}
    193 	*av = NULL;
    194 	return argv;
    195 }
    196 
    197 /* figure out the name of a variable given an environment string. */
    198 
    199 extern char *get_name(char *s) {
    200 	char *eq = strchr(s, '=');
    201 	char *r, *result;
    202 	int c;
    203 	
    204 	if (eq == NULL)
    205 		return NULL;
    206 	r = result = nalloc(eq - s + 1);
    207 	while (1)
    208 		switch (c = *s++) {
    209 		case '=':
    210 			*r++ = '\0';
    211 			return result;
    212 #if PROTECT_ENV
    213 		case '_':
    214 			if (*s == '_') {
    215 				static const char hexchar[] = "0123456789abcdef";
    216 				char *h1 = strchr(hexchar, s[1]);
    217 				char *h2 = strchr(hexchar, s[2]);
    218 				if (h1 != NULL && h2 != NULL) {
    219 					*r++ = ((h1 - hexchar) << 4) | (h2 - hexchar);
    220 					s += 3;
    221 					break;
    222 				}
    223 			}
    224 			/* FALLTHROUGH */
    225 #endif
    226 		default:
    227 			*r++ = c;
    228 			break;
    229 		}
    230 }
    231 
    232 /* interpret a variable from environment.  ^A separates list elements;
    233    ^B escapes a literal ^A or ^B.  For minimal surprise, ^B followed
    234    by anything other than ^A or ^B is preserved. */
    235 
    236 extern List *parse_var(char *extdef) {
    237 	char *begin, *end, *from, *to;
    238 	int len;
    239 	List *first, *last, *new;
    240 
    241 	first = last = NULL;
    242 	begin = strchr(extdef, '=');
    243 	assert(begin); /* guaranteed by initenv() */
    244 	while (*begin) {
    245 		++begin;
    246 		end = begin;
    247 		len = 0;
    248 		while (*end != ENV_SEP && *end != '\0') {
    249 			if (*end == ENV_ESC) {
    250 				++end;
    251 				if (*end != ENV_SEP && *end != ENV_ESC) --end;
    252 			}
    253 			++end; ++len;
    254 		}
    255 		new = enew(List);
    256 		if (last)
    257 			last->n = new;
    258 		else
    259 			first = new;
    260 		last = new;
    261 		new->w = ealloc(len + 1);
    262 		new->m = NULL;
    263 		new->n = NULL;
    264 		to = new->w;
    265 		for (from = begin; from < end; ++from) {
    266 			if (*from == ENV_ESC) {
    267 				++from;
    268 				if (*from != ENV_SEP && *from != ENV_ESC)
    269 					--from;
    270 			}
    271 			*to = *from;
    272 			++to;
    273 		}
    274 		*to = '\0';
    275 		begin = end;
    276 	}
    277 	return first;
    278 }
    279 
    280 /* get an environment entry for a function and have rc parse it. */
    281 
    282 #define PREFIX "fn x"
    283 #define PRELEN conststrlen(PREFIX)
    284 extern Node *parse_fn(char *extdef) {
    285 	Node *def;
    286 	char *s, old[PRELEN];
    287 	if ((s = strchr(extdef, '=')) == NULL)
    288 		return NULL;
    289 	memcpy(old, s -= (PRELEN-1), PRELEN);
    290 	memcpy(s, PREFIX, PRELEN);
    291 	def = parseline(s);
    292 	memcpy(s, old, PRELEN);
    293 	return (def == NULL || def->type != nNewfn) ? NULL : def->u[1].p;
    294 }
    295 
    296 static bool Aconv(Format *f, int ignore) {
    297 	char **a = va_arg(f->args, char **);
    298 	if (*a != NULL) {
    299 		fmtcat(f, *a);
    300 		while (*++a != NULL)
    301 			fmtprint(f, " %s", *a);
    302 	}
    303 	return FALSE;
    304 }
    305 
    306 /* %L -- print a list */
    307 static bool Lconv(Format *f, int ignore) {
    308 	bool plain;
    309 	char *sep;
    310 	List *l, *n;
    311 
    312 	plain = f->flags & FMT_leftside;
    313 	l = va_arg(f->args, List *);
    314 	sep = va_arg(f->args, char *);
    315 	if (l == NULL && (f->flags & FMT_leftside) == 0)
    316 		fmtprint(f, "()");
    317 	else {
    318 		for (; l != NULL; l = n) {
    319 			n = l->n;
    320 			fmtprint(f, plain ? "%s" : "%-S", l->w);
    321 			if (n != NULL) fmtputc(f, *sep);
    322 		}
    323 	}
    324 	return FALSE;
    325 }
    326 
    327 /* %W -- print a list for exporting */
    328 static bool Wconv(Format *f, int ignore) {
    329 	List *l, *n;
    330 
    331 	l = va_arg(f->args, List *);
    332 	for (; l != NULL; l = n) {
    333 		char c, *s;
    334 
    335 		for (s = l->w; (c = *s) != '\0'; ++s) {
    336 			if (c == ENV_SEP || c == ENV_ESC)
    337 				fmtputc(f, ENV_ESC);
    338 			fmtputc(f, c);
    339 		}
    340 		n = l->n;
    341 		if (n != NULL) fmtputc(f, ENV_SEP);
    342 	}
    343 	return FALSE;
    344 }
    345 
    346 #define	ISMETA(c)	(c == '*' || c == '?' || c == '[')
    347 
    348 static bool Sconv(Format *f, int ignore) {
    349 	int c;
    350 	unsigned char *s = va_arg(f->args, unsigned char *), *t = s;
    351 	bool quoted    = (f->flags & FMT_altform)  != 0;	/* '#' */
    352 	bool metaquote = (f->flags & FMT_leftside) != 0;	/* '-' */
    353 	if (*s == '\0') {
    354 		fmtprint(f, "''");
    355 		return FALSE;
    356 	}
    357 	if (!quoted) {
    358 		while ((c = *t++) != '\0')
    359 			if (nw[c] == 1 || (metaquote && ISMETA(c)))
    360 				goto quoteit;
    361 		fmtprint(f, "%s", s);
    362 		return FALSE;
    363 	}
    364 quoteit:
    365 	fmtputc(f, '\'');
    366 	while ((c = *s++) != '\0') {
    367 		fmtputc(f, c);
    368 		if (c == '\'')
    369 			fmtputc(f, '\'');
    370 
    371 	}
    372 	fmtputc(f, '\'');
    373 	return FALSE;
    374 }
    375 
    376 void initprint(void) {
    377 	fmtinstall('A', Aconv);
    378 	fmtinstall('L', Lconv);
    379 	fmtinstall('S', Sconv);
    380 	fmtinstall('T', Tconv);
    381 	fmtinstall('D', Dconv);
    382 	fmtinstall('W', Wconv);
    383 #if PROTECT_ENV
    384 	fmtinstall('F', Fconv);
    385 #else
    386 	fmtinstall('F', fmtinstall('s', NULL));
    387 #endif
    388 }