From 6bb38f6a01dbf4bbd584e038ceace0a00faa27bb Mon Sep 17 00:00:00 2001 From: Byron Rakitzis Date: Wed, 27 May 1992 00:00:01 +0100 Subject: release: rc-1.4 --- heredoc.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 heredoc.c (limited to 'heredoc.c') diff --git a/heredoc.c b/heredoc.c new file mode 100644 index 0000000..3930d9e --- /dev/null +++ b/heredoc.c @@ -0,0 +1,156 @@ +/* heredoc.c: heredoc slurping is done here */ + +#include "rc.h" + +struct Hq { + Node *doc; + char *name; + Hq *n; + bool quoted; +} *hq; + +static bool dead = FALSE; + +/* + * read in a heredocument. A clever trick: skip over any partially matched end-of-file + * marker storing only the number of characters matched. If the whole marker is matched, + * return from readheredoc(). If only part of the marker is matched, copy that part into + * the heredocument. + * + * BUG: if the eof string contains a newline, the state can get confused, and the + * heredoc may continue past where it should. on the other hand, /bin/sh seems to + * never get out of its readheredoc() when the heredoc string contains a newline + */ + +static char *readheredoc(char *eof) { + int c; + char *t, *buf, *bufend; + unsigned char *s; + SIZE_T bufsize; + t = buf = nalloc(bufsize = 512); + bufend = &buf[bufsize]; + dead = FALSE; +#define RESIZE(extra) { \ + char *nbuf; \ + bufsize = bufsize * 2 + extra; \ + nbuf = nalloc(bufsize); \ + memcpy(nbuf, buf, (SIZE_T) (t - buf)); \ + t = nbuf + (t - buf); \ + buf = nbuf; \ + bufend = &buf[bufsize]; \ + } + for (;;) { + print_prompt2(); + for (s = (unsigned char *) eof; (c = gchar()) == *s; s++) + ; + if (*s == '\0' && (c == '\n' || c == EOF)) { + *t++ = '\0'; + return buf; + } + if (s != (unsigned char *) eof) { + SIZE_T len = s - (unsigned char *) eof; + if (t + len >= bufend) + RESIZE(len); + memcpy(t, eof, len); + t += len; + } + for (;; c = gchar()) { + if (c == EOF) { + yyerror("heredoc incomplete"); + dead = TRUE; + return NULL; + } + if (t + 1 >= bufend) + RESIZE(0); + *t++ = c; + if (c == '\n') + break; + } + } +} + +/* parseheredoc -- turn a heredoc with variable references into a node chain */ + +static Node *parseheredoc(char *s) { + int c = *s; + Node *result = NULL; + while (TRUE) { + Node *node; + switch (c) { + default: { + char *begin = s; + while ((c = *s++) != '\0' && c != '$') + ; + *--s = '\0'; + node = mk(nQword, begin, NULL); + break; + } + case '$': { + char *begin = ++s, *var; + c = *s++; + if (c == '$') { + node = mk(nQword, "$", NULL); + c = *s; + } else { + SIZE_T len = 0; + do + len++; + while (!dnw[c = *(unsigned char *) s++]); + if (c == '^') + c = *s; + else + s--; + var = nalloc(len + 1); + var[len] = '\0'; + memcpy(var, begin, len); + node = mk(nFlat, mk(nWord, var, NULL)); + } + break; + } + case '\0': + return result; + } + result = (result == NULL) ? node : mk(nConcat, result, node); + } +} + +/* read in heredocs when yyparse hits a newline. called from yyparse */ + +extern int heredoc(int end) { + Hq *here; + if ((here = hq) != NULL) { + hq = NULL; + if (end) { + yyerror("heredoc incomplete"); + return FALSE; + } + do { + Node *n = here->doc; + char *s = readheredoc(here->name); + if (dead) + return FALSE; + n->u[2].p = here->quoted ? mk(nQword, s, NULL) : parseheredoc(s); + n->u[0].i = rHerestring; + } while ((here = here->n) != NULL); + } + return TRUE; +} + +/* queue pending heredocs into a queue. called from yyparse */ + +extern int qdoc(Node *name, Node *n) { + Hq *new, **prev; + if (name->type != nWord && name->type != nQword) { + yyerror("eof-marker not a single literal word"); + flushu(); + return FALSE; + } + for (prev = &hq; (new = *prev) != NULL; prev = &new->n) + ; + *prev = new = nnew(Hq); + new->name = name->u[0].s; + new->quoted = (name->type == nQword); + new->doc = n; + new->n = NULL; + return TRUE; +} -- cgit v1.2.3