lexeme *st_in_quotes(lexer_info *linfo, buffer *buf) { #ifdef LEXER_DEBUG print_state("ST_IN_QUOTES", linfo->c); #endif switch (linfo->c) { case EOF: linfo->state = ST_ERROR; return NULL; case '\\': deferred_get_char(linfo); linfo->state = ST_BACKSLASH_IN_QUOTES; break; case '\"': deferred_get_char(linfo); linfo->state = ST_WORD; break; case '\n': print_prompt2(); /* fallthrough */ default: add_to_buffer(buf, linfo->c); deferred_get_char(linfo); break; } return NULL; }
lexeme *st_backslash(lexer_info *linfo, buffer *buf) { #ifdef LEXER_DEBUG print_state("ST_BACKSLASH", linfo->c); #endif switch (linfo->c) { case EOF: linfo->state = ST_ERROR; return NULL; case '\n': /* Ignore newline symbol */ print_prompt2(); break; /* Non bash-like behaviour. In bash substitution * makes in $'string' costruction. */ case 'a': add_to_buffer(buf, '\a'); break; case 'b': add_to_buffer(buf, '\b'); break; case 'f': add_to_buffer(buf, '\f'); break; case 'n': add_to_buffer(buf, '\n'); break; case 'r': add_to_buffer(buf, '\r'); break; case 't': add_to_buffer(buf, '\t'); break; case 'v': add_to_buffer(buf, '\v'); break; default: add_to_buffer(buf, linfo->c); break; } deferred_get_char(linfo); linfo->state = ST_WORD; return NULL; }
/* snarfheredoc -- read a heredoc until the eof marker */ extern Tree *snarfheredoc(const char *eof, Boolean quoted) { Tree *tree, **tailp; Buffer *buf; unsigned char *s; assert(quoted || strchr(eof, '$') == NULL); /* can never be typed (whew!) */ if (strchr(eof, '\n') != NULL) { yyerror("here document eof-marker contains a newline"); return NULL; } disablehistory = TRUE; for (tree = NULL, tailp = &tree, buf = openbuffer(0);;) { int c; print_prompt2(); for (s = (unsigned char *) eof; (c = GETC()) == *s; s++) ; if (*s == '\0' && (c == '\n' || c == EOF)) { if (buf->current == 0 && tree != NULL) freebuffer(buf); else *tailp = treecons(mk(nQword, sealcountedbuffer(buf)), NULL); break; } if (s != (unsigned char *) eof) buf = bufncat(buf, eof, s - (unsigned char *) eof); for (;; c = GETC()) { if (c == EOF) { yyerror("incomplete here document"); freebuffer(buf); disablehistory = FALSE; return NULL; } if (c == '$' && !quoted && (c = GETC()) != '$') { Tree *var; UNGETC(c); if (buf->current == 0) freebuffer(buf); else { *tailp = treecons(mk(nQword, sealcountedbuffer(buf)), NULL); tailp = &(*tailp)->CDR; } var = getherevar(); if (var == NULL) { freebuffer(buf); disablehistory = FALSE; return NULL; } *tailp = treecons(var, NULL); tailp = &(*tailp)->CDR; buf = openbuffer(0); continue; } buf = bufputc(buf, c); if (c == '\n') break; } } disablehistory = FALSE; return tree->CDR == NULL ? tree->CAR : tree; }
extern int yylex(void) { static Boolean dollar = FALSE; int c; size_t i; /* The purpose of all these local assignments is to */ const char *meta; /* allow optimizing compilers like gcc to load these */ char *buf = tokenbuf; /* values into registers. On a sparc this is a */ YYSTYPE *y = &yylval; /* win, in code size *and* execution time */ if (goterror) { goterror = FALSE; return NL; } /* rc variable-names may contain only alnum, '*' and '_', so use dnw if we are scanning one. */ meta = (dollar ? dnw : nw); dollar = FALSE; if (newline) { --input->lineno; /* slight space optimization; print_prompt2() always increments lineno */ print_prompt2(); newline = FALSE; } top: while ((c = GETC()) == ' ' || c == '\t') w = NW; if (c == EOF) return ENDFILE; if (!meta[(unsigned char) c]) { /* it's a word or keyword. */ InsertFreeCaret(); w = RW; i = 0; do { buf[i++] = c; if (i >= bufsize) buf = tokenbuf = erealloc(buf, bufsize *= 2); } while ((c = GETC()) != EOF && !meta[(unsigned char) c]); UNGETC(c); buf[i] = '\0'; w = KW; if (buf[1] == '\0') { int k = *buf; if (k == '@' || k == '~') return k; } else if (*buf == 'f') { if (streq(buf + 1, "n")) return FN; if (streq(buf + 1, "or")) return FOR; } else if (*buf == 'l') { if (streq(buf + 1, "ocal")) return LOCAL; if (streq(buf + 1, "et")) return LET; } else if (streq(buf, "~~")) return EXTRACT; else if (streq(buf, "%closure")) return CLOSURE; w = RW; y->str = gcdup(buf); return WORD; } if (c == '`' || c == '!' || c == '$' || c == '\'') { InsertFreeCaret(); if (c == '!') w = KW; } switch (c) { case '!': return '!'; case '`': c = GETC(); if (c == '`') return BACKBACK; UNGETC(c); return '`'; case '$': dollar = TRUE; switch (c = GETC()) { case '#': return COUNT; case '^': return FLAT; case '&': return PRIM; default: UNGETC(c); return '$'; } case '\'': w = RW; i = 0; while ((c = GETC()) != '\'' || (c = GETC()) == '\'') { buf[i++] = c; if (c == '\n') print_prompt2(); if (c == EOF) { w = NW; scanerror("eof in quoted string"); return ERROR; } if (i >= bufsize) buf = tokenbuf = erealloc(buf, bufsize *= 2); } UNGETC(c); buf[i] = '\0'; y->str = gcdup(buf); return QWORD; case '\\': if ((c = GETC()) == '\n') { print_prompt2(); UNGETC(' '); goto top; /* Pretend it was just another space. */ } if (c == EOF) { UNGETC(EOF); goto badescape; } UNGETC(c); c = '\\'; InsertFreeCaret(); w = RW; c = GETC(); switch (c) { case 'a': *buf = '\a'; break; case 'b': *buf = '\b'; break; case 'e': *buf = '\033'; break; case 'f': *buf = '\f'; break; case 'n': *buf = '\n'; break; case 'r': *buf = '\r'; break; case 't': *buf = '\t'; break; case 'x': case 'X': { int n = 0; for (;;) { c = GETC(); if (!isxdigit(c)) break; n = (n << 4) | (c - (isdigit(c) ? '0' : ((islower(c) ? 'a' : 'A') - 0xA))); } if (n == 0) goto badescape; UNGETC(c); *buf = n; break; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': { int n = 0; do { n = (n << 3) | (c - '0'); c = GETC(); } while (isodigit(c)); if (n == 0) goto badescape; UNGETC(c); *buf = n; break; } default: if (isalnum(c)) { badescape: scanerror("bad backslash escape"); return ERROR; } *buf = c; break; } buf[1] = 0; y->str = gcdup(buf); return QWORD; case '#': while ((c = GETC()) != '\n') /* skip comment until newline */ if (c == EOF) return ENDFILE; /* FALLTHROUGH */ case '\n': input->lineno++; newline = TRUE; w = NW; return NL; case '(': if (w == RW) /* not keywords, so let & friends work */ c = SUB; /* FALLTHROUGH */ case ';': case '^': case ')': case '=': case '{': case '}': w = NW; return c; case '&': w = NW; c = GETC(); if (c == '&') return ANDAND; UNGETC(c); return '&'; case '|': { int p[2]; w = NW; c = GETC(); if (c == '|') return OROR; if (!getfds(p, c, 1, 0)) return ERROR; if (p[1] == CLOSED) { scanerror("expected digit after '='"); /* can't close a pipe */ return ERROR; } y->tree = mk(nPipe, p[0], p[1]); return PIPE; } { char *cmd; int fd[2]; case '<': fd[0] = 0; if ((c = GETC()) == '>') if ((c = GETC()) == '>') { c = GETC(); cmd = "%open-append"; } else cmd = "%open-write"; else if (c == '<') if ((c = GETC()) == '<') { c = GETC(); cmd = "%here"; } else cmd = "%heredoc"; else if (c == '=') return CALL; else cmd = "%open"; goto redirection; case '>': fd[0] = 1; if ((c = GETC()) == '>') if ((c = GETC()) == '<') { c = GETC(); cmd = "%open-append"; } else cmd = "%append"; else if (c == '<') { c = GETC(); cmd = "%open-create"; } else cmd = "%create"; goto redirection; redirection: w = NW; if (!getfds(fd, c, fd[0], DEFAULT)) return ERROR; if (fd[1] != DEFAULT) { y->tree = (fd[1] == CLOSED) ? mkclose(fd[0]) : mkdup(fd[0], fd[1]); return DUP; } y->tree = mkredircmd(cmd, fd[0]); return REDIR; } default: assert(c != '\0'); w = NW; return c; /* don't know what it is, let yacc barf on it */ } }
extern int yylex() { static bool dollar = FALSE; bool saw_meta = FALSE; int c; SIZE_T i; /* The purpose of all these local assignments is to */ char *meta; /* allow optimizing compilers like gcc to load these */ char *buf = realbuf; /* values into registers. On a sparc this is a */ YYSTYPE *y = &yylval; /* win, in code size *and* execution time */ if (errset) { errset = FALSE; return '\n'; } /* rc variable-names may contain only alnum, '*' and '_', so use dnw if we are scanning one. */ meta = (dollar ? dnw : nw); dollar = FALSE; if (newline) { --lineno; /* slight space optimization; print_prompt2() always increments lineno */ print_prompt2(); newline = FALSE; } top: while ((c = gchar()) == ' ' || c == '\t') w = NW; if (c == EOF) return END; if (!meta[(unsigned char) c]) { /* it's a word or keyword. */ checkfreecaret; w = RW; i = 0; read: do { buf[i++] = c; if (c == '?' || c == '[' || c == '*') saw_meta = TRUE; if (i >= bufsize) buf = realbuf = erealloc(buf, bufsize *= 2); } while ((c = gchar()) != EOF && !meta[(unsigned char) c]); while (c == '\\') { if ((c = gchar()) == '\n') { print_prompt2(); c = ' '; /* Pretend a space was read */ break; } else { bs: if (meta != dnw) { /* all words but varnames may have a bslash */ buf[i++] = '\\'; if (i >= bufsize) buf = realbuf = erealloc(buf, bufsize *= 2); if (!meta[(unsigned char) c]) goto read; } else { ugchar(c); c = '\\'; break; } } } ugchar(c); buf[i] = '\0'; w = KW; if (i == 2) { if (*buf == 'i' && buf[1] == 'f') return IF; if (*buf == 'f' && buf[1] == 'n') return FN; if (*buf == 'i' && buf[1] == 'n') return IN; } if (streq(buf, "for")) return FOR; if (streq(buf, "else")) return ELSE; if (streq(buf, "switch")) return SWITCH; if (streq(buf, "while")) return WHILE; if (streq(buf, "case")) return CASE; w = RW; y->word.w = ncpy(buf); if (saw_meta) { char *r, *s; y->word.m = nalloc(strlen(buf) + 1); for (r = buf, s = y->word.m; *r != '\0'; r++, s++) *s = (*r == '?' || *r == '[' || *r == '*'); } else { y->word.m = NULL; } return WORD; } if (c == '`' || c == '!' || c == '@' || c == '~' || c == '$' || c == '\'') { checkfreecaret; if (c == '!' || c == '@' || c == '~') w = KW; } switch (c) { case '!': return BANG; case '@': return SUBSHELL; case '~': return TWIDDLE; case '`': c = gchar(); if (c == '`') return BACKBACK; ugchar(c); return '`'; case '$': dollar = TRUE; c = gchar(); if (c == '#') return COUNT; if (c == '^') return FLAT; ugchar(c); return '$'; case '\'': w = RW; i = 0; do { buf[i++] = c; if (c == '\n') print_prompt2(); if (c == EOF) { w = NW; scanerror("eof in quoted string"); return HUH; } if (i >= bufsize) buf = realbuf = erealloc(buf, bufsize *= 2); } while ((c = gchar()) != '\'' || (c = gchar()) == '\''); /* quote "'" thus: 'how''s it going?' */ ugchar(c); buf[i] = '\0'; y->word.w = ncpy(buf); y->word.m = NULL; return WORD; case '\\': if ((c = gchar()) == '\n') { print_prompt2(); goto top; /* Pretend it was just another space. */ } ugchar(c); c = '\\'; checkfreecaret; c = gchar(); i = 0; goto bs; case '(': if (w == RW) /* SUB's happen only after real words, not keyowrds, so if () and while () work */ c = SUB; w = NW; return c; case '#': while ((c = gchar()) != '\n') /* skip comment until newline */ if (c == EOF) return END; /* FALLTHROUGH */ case '\n': lineno++; newline = TRUE; /* FALLTHROUGH */ case ';': case '^': case ')': case '=': case '{': case '}': w = NW; return c; case '&': w = NW; c = gchar(); if (c == '&') return ANDAND; ugchar(c); return '&'; case '|': w = NW; c = gchar(); if (c == '|') return OROR; getpair(c); if (errset) return HUH; if ((y->pipe.left = fd_left) == UNSET) y->pipe.left = 1; /* default to fd 1 */ if ((y->pipe.right = fd_right) == UNSET) y->pipe.right = 0; /* default to fd 0 */ if (y->pipe.right == CLOSED) { scanerror("expected digit after '='"); /* can't close a pipe */ return HUH; } return PIPE; case '>': c = gchar(); if (c == '>') { c = gchar(); y->redir.type = rAppend; } else y->redir.type = rCreate; y->redir.fd = 1; goto common; case '<': c = gchar(); if (c == '<') { c = gchar(); if (c == '<') { c = gchar(); y->redir.type = rHerestring; } else { y->redir.type = rHeredoc; } } else y->redir.type = rFrom; y->redir.fd = 0; common: w = NW; getpair(c); if (errset) return HUH; if (fd_right == UNSET) { /* redirection, not dup */ if (fd_left != UNSET) { y->redir.fd = fd_left; return SREDIR; } return (y->redir.type == rFrom || y->redir.type == rCreate) ? REDIR : SREDIR; } else { /* dup; recast yylval */ y->dup.type = y->redir.type; y->dup.left = fd_left; y->dup.right = fd_right; return DUP; } default: w = NW; return c; /* don't know what it is, let yacc barf on it */ } }