static int zzlex(void) { int cct = 0; char *ie; yyval.type = MN_INTEGER; for (;; cct = 0) switch (*ptr++) { case '+': if (*ptr == '+') { ptr++; return (unary) ? PREPLUS : POSTPLUS; } if (*ptr == '=') { ptr++; return PLUSEQ; } return (unary) ? UPLUS : PLUS; case '-': if (*ptr == '-') { ptr++; return (unary) ? PREMINUS : POSTMINUS; } if (*ptr == '=') { ptr++; return MINUSEQ; } if (unary) { if (idigit(*ptr) || *ptr == '.') { ptr--; return lexconstant(); } else return UMINUS; } else return MINUS; case '(': return M_INPAR; case ')': return M_OUTPAR; case '!': if (*ptr == '=') { ptr++; return NEQ; } return NOT; case '~': return COMP; case '&': if (*ptr == '&') { if (*++ptr == '=') { ptr++; return DANDEQ; } return DAND; } else if (*ptr == '=') { ptr++; return ANDEQ; } return AND; case '|': if (*ptr == '|') { if (*++ptr == '=') { ptr++; return DOREQ; } return DOR; } else if (*ptr == '=') { ptr++; return OREQ; } return OR; case '^': if (*ptr == '^') { if (*++ptr == '=') { ptr++; return DXOREQ; } return DXOR; } else if (*ptr == '=') { ptr++; return XOREQ; } return XOR; case '*': if (*ptr == '*') { if (*++ptr == '=') { ptr++; return POWEREQ; } return POWER; } if (*ptr == '=') { ptr++; return MULEQ; } return MUL; case '/': if (*ptr == '=') { ptr++; return DIVEQ; } return DIV; case '%': if (*ptr == '=') { ptr++; return MODEQ; } return MOD; case '<': if (*ptr == '<') { if (*++ptr == '=') { ptr++; return SHLEFTEQ; } return SHLEFT; } else if (*ptr == '=') { ptr++; return LEQ; } return LES; case '>': if (*ptr == '>') { if (*++ptr == '=') { ptr++; return SHRIGHTEQ; } return SHRIGHT; } else if (*ptr == '=') { ptr++; return GEQ; } return GRE; case '=': if (*ptr == '=') { ptr++; return DEQ; } return EQ; case '$': yyval.u.l = mypid; return NUM; case '?': if (unary) { yyval.u.l = lastval; return NUM; } return QUEST; case ':': return COLON; case ',': return COMMA; case '\0': ptr--; return EOI; case '[': { int n, checkradix = 0; if (idigit(*ptr)) { n = zstrtol(ptr, &ptr, 10); if (*ptr != ']' || !idigit(*++ptr)) { zerr("bad base syntax"); return EOI; } yyval.u.l = zstrtol(ptr, &ptr, lastbase = n); return NUM; } if (*ptr == '#') { n = 1; if (*++ptr == '#') { n = -1; ptr++; } if (!idigit(*ptr) && *ptr != '_') goto bofs; if (idigit(*ptr)) { outputradix = n * zstrtol(ptr, &ptr, 10); checkradix = 1; } if (*ptr == '_') { ptr++; if (idigit(*ptr)) outputunderscore = zstrtol(ptr, &ptr, 10); else outputunderscore = 3; } } else { bofs: zerr("bad output format specification"); return EOI; } if(*ptr != ']') goto bofs; if (checkradix) { n = (outputradix < 0) ? -outputradix : outputradix; if (n < 2 || n > 36) { zerr("invalid base (must be 2 to 36 inclusive): %d", outputradix); return EOI; } } ptr++; break; } case ' ': case '\t': case '\n': break; /* Fall through! */ default: if (idigit(*--ptr) || *ptr == '.') return lexconstant(); if (*ptr == '#') { if (*++ptr == '\\' || *ptr == '#') { int v; ptr++; if (!*ptr) { zerr("character missing after ##"); return EOI; } ptr = getkeystring(ptr, NULL, GETKEYS_MATH, &v); yyval.u.l = v; return NUM; } cct = 1; } if ((ie = itype_end(ptr, IIDENT, 0)) != ptr) { int func = 0; char *p; p = ptr; ptr = ie; if (*ptr == '[' || (!cct && *ptr == '(')) { char op = *ptr, cp = ((*ptr == '[') ? ']' : ')'); int l; func = (op == '('); for (ptr++, l = 1; *ptr && l; ptr++) { if (*ptr == op) l++; if (*ptr == cp) l--; if (*ptr == '\\' && ptr[1]) ptr++; } } yylval = dupstrpfx(p, ptr - p); return (func ? FUNC : (cct ? CID : ID)); } else if (cct) { yyval.u.l = poundgetfn(NULL); return NUM; } return EOI; } }
static enum lextok gettokstr(int c, int sub) { int bct = 0, pct = 0, brct = 0, seen_brct = 0, fdpar = 0; int intpos = 1, in_brace_param = 0; int inquote, unmatched = 0; enum lextok peek; #ifdef DEBUG int ocmdsp = cmdsp; #endif peek = STRING; if (!sub) { lexbuf.len = 0; lexbuf.ptr = tokstr = (char *) hcalloc(lexbuf.siz = LEX_HEAP_SIZE); } for (;;) { int act; int e; int inbl = inblank(c); if (fdpar && !inbl && c != ')') fdpar = 0; if (inbl && !in_brace_param && !pct) act = LX2_BREAK; else { act = lexact2[STOUC(c)]; c = lextok2[STOUC(c)]; } switch (act) { case LX2_BREAK: if (!in_brace_param && !sub) goto brk; break; case LX2_META: c = hgetc(); #ifdef DEBUG if (lexstop) { fputs("BUG: input terminated by Meta\n", stderr); fflush(stderr); goto brk; } #endif add(Meta); break; case LX2_OUTPAR: if (fdpar) { /* this is a single word `( )', treat as INOUTPAR */ add(c); *lexbuf.ptr = '\0'; return INOUTPAR; } if ((sub || in_brace_param) && isset(SHGLOB)) break; if (!in_brace_param && !pct--) { if (sub) { pct = 0; break; } else goto brk; } c = Outpar; break; case LX2_BAR: if (!pct && !in_brace_param) { if (sub) break; else goto brk; } if (unset(SHGLOB) || (!sub && !in_brace_param)) c = Bar; break; case LX2_STRING: e = hgetc(); if (e == '[') { cmdpush(CS_MATHSUBST); add(String); add(Inbrack); c = dquote_parse(']', sub); cmdpop(); if (c) { peek = LEXERR; goto brk; } c = Outbrack; } else if (e == '(') { add(String); switch (cmd_or_math_sub()) { case CMD_OR_MATH_CMD: c = Outpar; break; case CMD_OR_MATH_MATH: c = Outparmath; break; default: peek = LEXERR; goto brk; } } else { if (e == '{') { add(c); c = Inbrace; ++bct; cmdpush(CS_BRACEPAR); if (!in_brace_param) { if ((in_brace_param = bct)) seen_brct = 0; } } else { hungetc(e); lexstop = 0; } } break; case LX2_INBRACK: if (!in_brace_param) { brct++; seen_brct = 1; } c = Inbrack; break; case LX2_OUTBRACK: if (!in_brace_param) brct--; if (brct < 0) brct = 0; c = Outbrack; break; case LX2_INPAR: if (isset(SHGLOB)) { if (sub || in_brace_param) break; if (incasepat && !lexbuf.len) return INPAR; if (!isset(KSHGLOB) && lexbuf.len) goto brk; } if (!in_brace_param) { if (!sub) { e = hgetc(); hungetc(e); lexstop = 0; /* For command words, parentheses are only * special at the start. But now we're tokenising * the remaining string. So I don't see what * the old incmdpos test here is for. * pws 1999/6/8 * * Oh, no. * func1( ) * is a valid function definition in [k]sh. The best * thing we can do, without really nasty lookahead tricks, * is break if we find a blank after a parenthesis. At * least this can't happen inside braces or brackets. We * only allow this with SHGLOB (set for both sh and ksh). * * Things like `print @( |foo)' should still * work, because [k]sh don't allow multiple words * in a function definition, so we only do this * in command position. * pws 1999/6/14 */ if (e == ')' || (isset(SHGLOB) && inblank(e) && !bct && !brct && !intpos && incmdpos)) { /* * Either a () token, or a command word with * something suspiciously like a ksh function * definition. * The current word isn't spellcheckable. */ nocorrect |= 2; goto brk; } } /* * This also handles the [k]sh `foo( )' function definition. * Maintain a variable fdpar, set as long as a single set of * parentheses contains only space. Then if we get to the * closing parenthesis and it is still set, we can assume we * have a function definition. Only do this at the start of * the word, since the (...) must be a separate token. */ if (!pct++ && isset(SHGLOB) && intpos && !bct && !brct) fdpar = 1; } c = Inpar; break; case LX2_INBRACE: if (isset(IGNOREBRACES) || sub) c = '{'; else { if (!lexbuf.len && incmdpos) { add('{'); *lexbuf.ptr = '\0'; return STRING; } if (in_brace_param) { cmdpush(CS_BRACE); } bct++; } break; case LX2_OUTBRACE: if ((isset(IGNOREBRACES) || sub) && !in_brace_param) break; if (!bct) break; if (in_brace_param) { cmdpop(); } if (bct-- == in_brace_param) in_brace_param = 0; c = Outbrace; break; case LX2_COMMA: if (unset(IGNOREBRACES) && !sub && bct > in_brace_param) c = Comma; break; case LX2_OUTANG: if (in_brace_param || sub) break; e = hgetc(); if (e != '(') { hungetc(e); lexstop = 0; goto brk; } add(OutangProc); if (skipcomm()) { peek = LEXERR; goto brk; } c = Outpar; break; case LX2_INANG: if (isset(SHGLOB) && sub) break; e = hgetc(); if (!(in_brace_param || sub) && e == '(') { add(Inang); if (skipcomm()) { peek = LEXERR; goto brk; } c = Outpar; break; } hungetc(e); if(isnumglob()) { add(Inang); while ((c = hgetc()) != '>') add(c); c = Outang; break; } lexstop = 0; if (in_brace_param || sub) break; goto brk; case LX2_EQUALS: if (!sub) { if (intpos) { e = hgetc(); if (e != '(') { hungetc(e); lexstop = 0; c = Equals; } else { add(Equals); if (skipcomm()) { peek = LEXERR; goto brk; } c = Outpar; } } else if (peek != ENVSTRING && (incmdpos || intypeset) && !bct && !brct) { char *t = tokstr; if (idigit(*t)) while (++t < lexbuf.ptr && idigit(*t)); else { int sav = *lexbuf.ptr; *lexbuf.ptr = '\0'; t = itype_end(t, IIDENT, 0); if (t < lexbuf.ptr) { skipparens(Inbrack, Outbrack, &t); } else { *lexbuf.ptr = sav; } } if (*t == '+') t++; if (t == lexbuf.ptr) { e = hgetc(); if (e == '(') { *lexbuf.ptr = '\0'; return ENVARRAY; } hungetc(e); lexstop = 0; peek = ENVSTRING; intpos = 2; } else c = Equals; } else c = Equals; } break; case LX2_BKSLASH: c = hgetc(); if (c == '\n') { c = hgetc(); if (!lexstop) continue; } else { add(Bnull); if (c == STOUC(Meta)) { c = hgetc(); #ifdef DEBUG if (lexstop) { fputs("BUG: input terminated by Meta\n", stderr); fflush(stderr); goto brk; } #endif add(Meta); } } if (lexstop) goto brk; break; case LX2_QUOTE: { int strquote = (lexbuf.len && lexbuf.ptr[-1] == String); add(Snull); cmdpush(CS_QUOTE); for (;;) { STOPHIST while ((c = hgetc()) != '\'' && !lexstop) { if (strquote && c == '\\') { c = hgetc(); if (lexstop) break; /* * Mostly we don't need to do anything special * with escape backslashes or closing quotes * inside $'...'; however in completion we * need to be able to strip multiple backslashes * neatly. */ if (c == '\\' || c == '\'') add(Bnull); else add('\\'); } else if (!sub && isset(CSHJUNKIEQUOTES) && c == '\n') { if (lexbuf.ptr[-1] == '\\') lexbuf.ptr--, lexbuf.len--; else break; } add(c); } ALLOWHIST if (c != '\'') { unmatched = '\''; peek = LEXERR; cmdpop(); goto brk; } e = hgetc(); if (e != '\'' || unset(RCQUOTES) || strquote) break; add(c); } cmdpop(); hungetc(e); lexstop = 0; c = Snull; break; } case LX2_DQUOTE: add(Dnull); cmdpush(CS_DQUOTE); c = dquote_parse('"', sub); cmdpop(); if (c) { unmatched = '"'; peek = LEXERR; goto brk; } c = Dnull; break; case LX2_BQUOTE: add(Tick); cmdpush(CS_BQUOTE); SETPARBEGIN inquote = 0; while ((c = hgetc()) != '`' && !lexstop) { if (c == '\\') { c = hgetc(); if (c != '\n') { add(c == '`' || c == '\\' || c == '$' ? Bnull : '\\'); add(c); } else if (!sub && isset(CSHJUNKIEQUOTES)) add(c); } else { if (!sub && isset(CSHJUNKIEQUOTES) && c == '\n') { break; } add(c); if (c == '\'') { if ((inquote = !inquote)) STOPHIST else ALLOWHIST } } } if (inquote) ALLOWHIST cmdpop(); if (c != '`') { unmatched = '`'; peek = LEXERR; goto brk; } c = Tick; SETPAREND break; case LX2_DASH: /* * - shouldn't be treated as a special character unless * we're in a pattern. Howeve,simply counting "[" doesn't * work as []a-z] is a valid expression and we don't know * down here what this "[" is for as $foo[stuff] is valid * in zsh. So just detect an opening [, which is enough * to turn this into a pattern; the Dash will be harmlessly * untokenised if not wanted. */ if (seen_brct) c = Dash; else c = '-'; break; case LX2_BANG: /* * Same logic as Dash, for ! to perform negation in range. */ if (seen_brct) c = Bang; else c = '!'; } add(c); c = hgetc(); if (intpos) intpos--; if (lexstop) break; } brk: if (errflag) { if (in_brace_param) { while(bct-- >= in_brace_param) cmdpop(); } return LEXERR; } hungetc(c); if (unmatched) zerr("unmatched %c", unmatched); if (in_brace_param) { while(bct-- >= in_brace_param) cmdpop(); zerr("closing brace expected"); } else if (unset(IGNOREBRACES) && !sub && lexbuf.len > 1 && peek == STRING && lexbuf.ptr[-1] == '}' && lexbuf.ptr[-2] != Bnull) { /* hack to get {foo} command syntax work */ lexbuf.ptr--; lexbuf.len--; lexstop = 0; hungetc('}'); } *lexbuf.ptr = '\0'; DPUTS(cmdsp != ocmdsp, "BUG: gettok: cmdstack changed."); return peek; }