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