Commit | Line | Data |
---|---|---|
ac39d190 C |
1 | /* heredoc.c: heredoc slurping is done here */ |
2 | ||
3 | #include "rc.h" | |
4 | ||
5 | struct Hq { | |
6 | Node *doc; | |
7 | char *name; | |
8 | Hq *n; | |
9 | bool quoted; | |
10 | } *hq; | |
11 | ||
12 | static 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 | ||
25 | static 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 | ||
74 | static 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 | ||
119 | extern 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 | ||
141 | extern 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 | } |