rc

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

heredoc.c (3429B)


      1 /* heredoc.c: heredoc slurping is done here */
      2 
      3 #include "rc.h"
      4 
      5 #include "input.h"
      6 
      7 struct Hq {
      8 	Node *doc;
      9 	char *name;
     10 	Hq *n;
     11 	bool quoted;
     12 } *hq;
     13 
     14 static bool dead = FALSE;
     15 
     16 /*
     17  * read in a heredocument. A clever trick: skip over any partially matched end-of-file
     18  * marker storing only the number of characters matched. If the whole marker is matched,
     19  * return from readheredoc(). If only part of the marker is matched, copy that part into
     20  * the heredocument.
     21  *
     22  * BUG: if the eof string contains a newline, the state can get confused, and the
     23  * heredoc may continue past where it should.  on the other hand, /bin/sh seems to
     24  * never get out of its readheredoc() when the heredoc string contains a newline
     25  */
     26 
     27 static char *readheredoc(char *eof) {
     28 	int c;
     29 	char *t, *buf, *bufend;
     30 	unsigned char *s;
     31 	size_t bufsize;
     32 	t = buf = nalloc(bufsize = 512);
     33 	bufend = &buf[bufsize];
     34 	dead = FALSE;
     35 #define	RESIZE(extra) { \
     36 		char *nbuf; \
     37 		bufsize = bufsize * 2 + extra; \
     38 		nbuf = nalloc(bufsize); \
     39 		memcpy(nbuf, buf, (size_t) (t - buf)); \
     40 		t = nbuf + (t - buf); \
     41 		buf = nbuf; \
     42 		bufend = &buf[bufsize]; \
     43 	}
     44 	for (;;) {
     45 		nextline();
     46 		for (s = (unsigned char *) eof; (c = gchar()) == *s; s++)
     47 			;
     48 		if (*s == '\0' && (c == '\n' || c == EOF)) {
     49 			*t++ = '\0';
     50 			return buf;
     51 		}
     52 		if (s != (unsigned char *) eof) {
     53 			size_t len = s - (unsigned char *) eof;
     54 			if (t + len >= bufend)
     55 				RESIZE(len);
     56 			memcpy(t, eof, len);
     57 			t += len;
     58 		}
     59 		for (;; c = gchar()) {
     60 			if (c == EOF) {
     61 				yyerror("heredoc incomplete");
     62 				dead = TRUE;
     63 				return NULL;
     64 			}
     65 			if (t + 1 >= bufend)
     66 				RESIZE(0);
     67 			*t++ = c;
     68 			if (c == '\n')
     69 				break;
     70 		}
     71 	}
     72 }
     73 
     74 /* parseheredoc -- turn a heredoc with variable references into a node chain */
     75 
     76 static Node *parseheredoc(char *s) {
     77 	int c = *s;
     78 	Node *result = NULL;
     79 	while (TRUE) {
     80 		Node *node;
     81 		switch (c) {
     82 		default: {
     83 			char *begin = s;
     84 			while ((c = *s++) != '\0' && c != '$')
     85 				;
     86 			*--s = '\0';
     87 			node = mk(nWord, begin, NULL);
     88 			break;
     89 		}
     90 		case '$': {
     91 			char *begin = ++s, *var;
     92 			c = *s++;
     93 			if (c == '$') {
     94 				node = mk(nWord, "$", NULL);
     95 				c = *s;
     96 			} else {
     97 				size_t len = 0;
     98 				do
     99 					len++;
    100 				while (!dnw[c = *(unsigned char *) s++]);
    101 				if (c == '^')
    102 					c = *s;
    103 				else
    104 					s--;
    105 				var = nalloc(len + 1);
    106 				var[len] = '\0';
    107 				memcpy(var, begin, len);
    108 				node = mk(nFlat, mk(nWord, var, NULL));
    109 			}
    110 			break;
    111 		}
    112 		case '\0':
    113 			return result;
    114 		}
    115 		result = (result == NULL) ? node : mk(nConcat, result, node);
    116 	}
    117 }
    118 
    119 /* read in heredocs when yyparse hits a newline. called from yyparse */
    120 
    121 extern int heredoc(int end) {
    122 	Hq *here;
    123 	if ((here = hq) != NULL) {
    124 		hq = NULL;
    125 		if (end) {
    126 			yyerror("heredoc incomplete");
    127 			return FALSE;
    128 		}
    129 		do {
    130 			Node *n = here->doc;
    131 			char *s = readheredoc(here->name);
    132 			if (dead)
    133 				return FALSE;
    134 			n->u[2].p = here->quoted ? mk(nWord, s, NULL, FALSE) : parseheredoc(s);
    135 			n->u[0].i = rHerestring;
    136 		} while ((here = here->n) != NULL);
    137 	}
    138 	return TRUE;
    139 }
    140 
    141 /* queue pending heredocs into a queue. called from yyparse */
    142 
    143 extern int qdoc(Node *name, Node *n) {
    144 	Hq *new, **prev;
    145 	if (name->type != nWord) {
    146 		yyerror("eof-marker not a single literal word");
    147 		skiptonl();
    148 		return FALSE;
    149 	}
    150 	for (prev = &hq; (new = *prev) != NULL; prev = &new->n)
    151 		;
    152 	*prev = new = nnew(Hq);
    153 	new->name = name->u[0].s;
    154 	new->quoted = name->u[2].i;
    155 	new->doc = n;
    156 	new->n = NULL;
    157 	return TRUE;
    158 }