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 }