BSD 4_4_Lite2 development
[unix-history] / usr / src / contrib / rc-1.4 / heredoc.c
/* 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;
}