📄 lex.c
字号:
return c; case '(': /*)*/#ifdef KSH if ((c2 = getsc()) == '(') /*)*/ /* XXX need to handle ((...); (...)) */ c = MDPAREN; else ungetsc(c2);#endif /* KSH */ return c; /*(*/ case ')': return c; } } *wp++ = EOS; /* terminate word */ yylval.cp = Xclose(ws, wp); if (state == SWORD#ifdef KSH || state == SLETPAREN#endif /* KSH */ ) /* ONEWORD? */ return LWORD; ungetsc(c); /* unget terminator */ /* copy word to unprefixed string ident */ for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; ) *dp++ = *sp++; /* Make sure the ident array stays '\0' paded */ memset(dp, 0, (ident+IDENT) - dp + 1); if (c != EOS) *ident = '\0'; /* word is not unquoted */ if (*ident != '\0' && (cf&(KEYWORD|ALIAS))) { struct tbl *p; int h = hash(ident); /* { */ if ((cf & KEYWORD) && (p = tsearch(&keywords, ident, h)) && (!(cf & ESACONLY) || p->val.i == ESAC || p->val.i == '}')) { afree(yylval.cp, ATEMP); return p->val.i; } if ((cf & ALIAS) && (p = tsearch(&aliases, ident, h)) && (p->flag & ISSET)) { register Source *s; for (s = source; s->type == SALIAS; s = s->next) if (s->u.tblp == p) return LWORD; /* push alias expansion */ s = pushs(SALIAS, source->areap); s->start = s->str = p->val.s; s->u.tblp = p; s->next = source; source = s; afree(yylval.cp, ATEMP); goto Again; } } return LWORD;}static voidgethere(){ register struct ioword **p; for (p = heres; p < herep; p++) readhere(*p); herep = heres;}/* * read "<<word" text into temp file */static voidreadhere(iop) struct ioword *iop;{ register int c; char *volatile eof; char *eofp; int skiptabs; XString xs; char *xp; int xpos; eof = evalstr(iop->delim, 0); if (!(iop->flag & IOEVAL)) ignore_backslash_newline++; Xinit(xs, xp, 256, ATEMP); for (;;) { eofp = eof; skiptabs = iop->flag & IOSKIP; xpos = Xsavepos(xs, xp); while ((c = getsc()) != 0) { if (skiptabs) { if (c == '\t') continue; skiptabs = 0; } if (c != *eofp) break; Xcheck(xs, xp); Xput(xs, xp, c); eofp++; } /* Allow EOF here so commands with out trailing newlines * will work (eg, ksh -c '...', $(...), etc). */ if (*eofp == '\0' && (c == 0 || c == '\n')) { xp = Xrestpos(xs, xp, xpos); break; } ungetsc(c); while ((c = getsc()) != '\n') { if (c == 0) yyerror("here document `%s' unclosed\n", eof); Xcheck(xs, xp); Xput(xs, xp, c); } Xcheck(xs, xp); Xput(xs, xp, c); } Xput(xs, xp, '\0'); iop->heredoc = Xclose(xs, xp); if (!(iop->flag & IOEVAL)) ignore_backslash_newline--;}void#ifdef HAVE_PROTOTYPESyyerror(const char *fmt, ...)#elseyyerror(fmt, va_alist) const char *fmt; va_dcl#endif{ va_list va; /* pop aliases and re-reads */ while (source->type == SALIAS || source->type == SREREAD) source = source->next; source->str = null; /* zap pending input */ error_prefix(TRUE); SH_VA_START(va, fmt); shf_vfprintf(shl_out, fmt, va); va_end(va); errorf(null);}/* * input for yylex with alias expansion */Source *pushs(type, areap) int type; Area *areap;{ register Source *s; s = (Source *) alloc(sizeof(Source), areap); s->type = type; s->str = null; s->start = NULL; s->line = 0; s->errline = 0; s->file = NULL; s->flags = 0; s->next = NULL; s->areap = areap; if (type == SFILE || type == SSTDIN) { char *dummy; Xinit(s->xs, dummy, 256, s->areap); } else memset(&s->xs, 0, sizeof(s->xs)); return s;}static intgetsc__(){ register Source *s = source; register int c; while ((c = *s->str++) == 0) { s->str = NULL; /* return 0 for EOF by default */ switch (s->type) { case SEOF: s->str = null; return 0; case SSTDIN: case SFILE: getsc_line(s); break; case SWSTR: break; case SSTRING: break; case SWORDS: s->start = s->str = *s->u.strv++; s->type = SWORDSEP; break; case SWORDSEP: if (*s->u.strv == NULL) { s->start = s->str = newline; s->type = SEOF; } else { s->start = s->str = space; s->type = SWORDS; } break; case SALIAS: if (s->flags & SF_ALIASEND) { /* pass on an unused SF_ALIAS flag */ source = s->next; source->flags |= s->flags & SF_ALIAS; s = source; } else if (*s->u.tblp->val.s && isspace(strchr(s->u.tblp->val.s, 0)[-1])) { source = s = s->next; /* pop source stack */ /* Note that this alias ended with a space, * enabling alias expansion on the following * word. */ s->flags |= SF_ALIAS; } else { /* At this point, we need to keep the current * alias in the source list so recursive * aliases can be detected and we also need * to return the next character. Do this * by temporarily popping the alias to get * the next character and then put it back * in the source list with the SF_ALIASEND * flag set. */ source = s->next; /* pop source stack */ source->flags |= s->flags & SF_ALIAS; c = getsc__(); if (c) { s->flags |= SF_ALIASEND; s->ugbuf[0] = c; s->ugbuf[1] = '\0'; s->start = s->str = s->ugbuf; s->next = source; source = s; } else { s = source; /* avoid reading eof twice */ s->str = NULL; break; } } continue; case SREREAD: if (s->start != s->ugbuf) /* yuck */ afree(s->u.freeme, ATEMP); source = s = s->next; continue; } if (s->str == NULL) { s->type = SEOF; s->start = s->str = null; return '\0'; } if (s->flags & SF_ECHO) { shf_puts(s->str, shl_out); shf_flush(shl_out); } } return c;}static voidgetsc_line(s) Source *s;{ char *xp = Xstring(s->xs, xp); int interactive = Flag(FTALKING) && s->type == SSTDIN; int have_tty = interactive && (s->flags & SF_TTY); /* Done here to ensure nothing odd happens when a timeout occurs */ XcheckN(s->xs, xp, LINE); *xp = '\0'; s->start = s->str = xp;#ifdef KSH if (have_tty && ksh_tmout) { ksh_tmout_state = TMOUT_READING; alarm(ksh_tmout); }#endif /* KSH */#ifdef EDIT if (have_tty && (0# ifdef VI || Flag(FVI)# endif /* VI */# ifdef EMACS || Flag(FEMACS) || Flag(FGMACS)# endif /* EMACS */ )) { int nread; nread = x_read(xp, LINE); if (nread < 0) /* read error */ nread = 0; xp[nread] = '\0'; xp += nread; } else#endif /* EDIT */ { if (interactive) { pprompt(prompt, 0); } else s->line++; while (1) { char *p = shf_getse(xp, Xnleft(s->xs, xp), s->u.shf); if (!p && shf_error(s->u.shf) && shf_errno(s->u.shf) == EINTR) { shf_clearerr(s->u.shf); if (trap) runtraps(0); continue; } if (!p || (xp = p, xp[-1] == '\n')) break; /* double buffer size */ xp++; /* move past null so doubling works... */ XcheckN(s->xs, xp, Xlength(s->xs, xp)); xp--; /* ...and move back again */ } /* flush any unwanted input so other programs/builtins * can read it. Not very optimal, but less error prone * than flushing else where, dealing with redirections, * etc.. * todo: reduce size of shf buffer (~128?) if SSTDIN */ if (s->type == SSTDIN) shf_flush(s->u.shf); } /* XXX: temporary kludge to restore source after a * trap may have been executed. */ source = s;#ifdef KSH if (have_tty && ksh_tmout) { ksh_tmout_state = TMOUT_EXECUTING; alarm(0); }#endif /* KSH */ s->start = s->str = Xstring(s->xs, xp); strip_nuls(Xstring(s->xs, xp), Xlength(s->xs, xp)); /* Note: if input is all nulls, this is not eof */ if (Xlength(s->xs, xp) == 0) { /* EOF */ if (s->type == SFILE) shf_fdclose(s->u.shf); s->str = NULL; } else if (interactive) {#ifdef HISTORY char *p = Xstring(s->xs, xp); if (cur_prompt == PS1) while (*p && ctype(*p, C_IFS) && ctype(*p, C_IFSWS)) p++; if (*p) {# ifdef EASY_HISTORY if (cur_prompt == PS2) histappend(Xstring(s->xs, xp), 1); else# endif /* EASY_HISTORY */ { s->line++; histsave(s->line, s->str, 1); } }#endif /* HISTORY */ } if (interactive) set_prompt(PS2, (Source *) 0);}voidset_prompt(to, s) int to; Source *s;{ cur_prompt = to; switch (to) { case PS1: /* command */#ifdef KSH /* Substitute ! and !! here, before substitutions are done * so ! in expanded variables are not expanded. * NOTE: this is not what at&t ksh does (it does it after * substitutions, POSIX doesn't say which is to be done. */ { struct shf *shf; char *ps1; Area *saved_atemp; ps1 = str_val(global("PS1")); shf = shf_sopen((char *) 0, strlen(ps1) * 2, SHF_WR | SHF_DYNAMIC, (struct shf *) 0); while (*ps1) { if (*ps1 != '!' || *++ps1 == '!') shf_putchar(*ps1++, shf); else shf_fprintf(shf, "%d", s ? s->line + 1 : 0); } ps1 = shf_sclose(shf); saved_atemp = ATEMP; newenv(E_ERRH); if (ksh_sigsetjmp(e->jbuf, 0)) { prompt = safe_prompt; /* Don't print an error - assume it has already * been printed. Reason is we may have forked * to run a command and the child may be * unwinding its stack through this code as it * exits. */ } else prompt = str_save(substitute(ps1, 0), saved_atemp); quitenv(); }#else /* KSH */ prompt = str_val(global("PS1"));#endif /* KSH */ break; case PS2: /* command continuation */ prompt = str_val(global("PS2")); break; }}/* See also related routine, promptlen() in edit.c */voidpprompt(cp, ntruncate) const char *cp; int ntruncate;{#if 0 char nbuf[32]; int c; while (*cp != 0) { if (*cp != '!') c = *cp++; else if (*++cp == '!') c = *cp++; else { int len; char *p; shf_snprintf(p = nbuf, sizeof(nbuf), "%d", source->line + 1); len = strlen(nbuf); if (ntruncate) { if (ntruncate >= len) { ntruncate -= len; continue; } p += ntruncate; len -= ntruncate; ntruncate = 0; } shf_write(p, len, shl_out); continue; } if (ntruncate) --ntruncate; else shf_putc(c, shl_out); }#endif /* 0 */ shf_puts(cp + ntruncate, shl_out); shf_flush(shl_out);}/* Read the variable part of a ${...} expression (ie, up to but not including * the :[-+?=#%] or close-brace. */static char *get_brace_var(wsp, wp) XString *wsp; char *wp;{ enum parse_state { PS_INITIAL, PS_SAW_HASH, PS_IDENT, PS_NUMBER, PS_VAR1, PS_END } state; char c; state = PS_INITIAL; while (1) { c = getsc(); /* State machine to figure out where the variable part ends. */ switch (state) { case PS_INITIAL: if (c == '#') { state = PS_SAW_HASH; break; } /* fall through.. */ case PS_SAW_HASH: if (letter(c)) state = PS_IDENT; else if (digit(c)) state = PS_NUMBER; else if (ctype(c, C_VAR1)) state = PS_VAR1; else state = PS_END; break; case PS_IDENT: if (!letnum(c)) { state = PS_END; if (c == '[') { char *tmp, *p; if (!arraysub(&tmp)) yyerror("missing ]\n"); *wp++ = c; for (p = tmp; *p; ) { Xcheck(*wsp, wp); *wp++ = *p++; } afree(tmp, ATEMP); c = getsc(); /* the ] */ } } break; case PS_NUMBER: if (!digit(c)) state = PS_END; break; case PS_VAR1: state = PS_END; break; case PS_END: /* keep gcc happy */ break; } if (state == PS_END) { *wp++ = '\0'; /* end of variable part */ ungetsc(c); break; } Xcheck(*wsp, wp); *wp++ = c; } return wp;}/* * Save an array subscript - returns true if matching bracket found, false * if eof or newline was found. * (Returned string double null terminated) */static intarraysub(strp) char **strp;{ XString ws; char *wp; char c; int depth = 1; /* we are just past the initial [ */ Xinit(ws, wp, 32, ATEMP); do { c = getsc(); Xcheck(ws, wp); *wp++ = c; if (c == '[') depth++; else if (c == ']') depth--; } while (depth > 0 && c && c != '\n'); *wp++ = '\0'; *strp = Xclose(ws, wp); return depth == 0 ? 1 : 0;}/* Unget a char: handles case when we are already at the start of the buffer */static const char *ungetsc(c) int c;{ if (backslash_skip) backslash_skip--; /* Don't unget eof... */ if (source->str == null && c == '\0') return source->str; if (source->str > source->start) source->str--; else { Source *s; s = pushs(SREREAD, source->areap); s->ugbuf[0] = c; s->ugbuf[1] = '\0'; s->start = s->str = s->ugbuf; s->next = source; source = s; } return source->str;}/* Called to get a char that isn't a \newline sequence. */static intgetsc_bn ARGS((void)){ int c, c2; if (ignore_backslash_newline) return getsc_(); if (backslash_skip == 1) { backslash_skip = 2; return getsc_(); } backslash_skip = 0; while (1) { c = getsc_(); if (c == '\\') { if ((c2 = getsc_()) == '\n') /* ignore the \newline; get the next char... */ continue; ungetsc(c2); backslash_skip = 1; } return c; }}static Lex_state *push_state_(si, old_end) State_info *si; Lex_state *old_end;{ Lex_state *new = alloc(sizeof(Lex_state) * STATE_BSIZE, ATEMP); new[0].ls_info.base = old_end; si->base = &new[0]; si->end = &new[STATE_BSIZE]; return &new[1];}static Lex_state *pop_state_(si, old_end) State_info *si; Lex_state *old_end;{ Lex_state *old_base = si->base; si->base = old_end->ls_info.base - STATE_BSIZE; si->end = old_end->ls_info.base; afree(old_base, ATEMP); return si->base + STATE_BSIZE - 1;;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -