Module Name: src Committed By: kre Date: Fri Jun 30 23:02:56 UTC 2017
Modified Files: src/bin/sh: expand.c input.c input.h option.list parser.c parser.h Log Message: Implement PS1, PS2 and PS4 expansions (variable expansions, arithmetic expansions, and if enabled by the promptcmds option, command substitutions.) To generate a diff of this commit: cvs rdiff -u -r1.118 -r1.119 src/bin/sh/expand.c cvs rdiff -u -r1.58 -r1.59 src/bin/sh/input.c cvs rdiff -u -r1.19 -r1.20 src/bin/sh/input.h cvs rdiff -u -r1.4 -r1.5 src/bin/sh/option.list cvs rdiff -u -r1.139 -r1.140 src/bin/sh/parser.c cvs rdiff -u -r1.22 -r1.23 src/bin/sh/parser.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/bin/sh/expand.c diff -u src/bin/sh/expand.c:1.118 src/bin/sh/expand.c:1.119 --- src/bin/sh/expand.c:1.118 Mon Jun 19 02:46:50 2017 +++ src/bin/sh/expand.c Fri Jun 30 23:02:56 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: expand.c,v 1.118 2017/06/19 02:46:50 kre Exp $ */ +/* $NetBSD: expand.c,v 1.119 2017/06/30 23:02:56 kre Exp $ */ /*- * Copyright (c) 1991, 1993 @@ -37,7 +37,7 @@ #if 0 static char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95"; #else -__RCSID("$NetBSD: expand.c,v 1.118 2017/06/19 02:46:50 kre Exp $"); +__RCSID("$NetBSD: expand.c,v 1.119 2017/06/30 23:02:56 kre Exp $"); #endif #endif /* not lint */ @@ -177,7 +177,7 @@ expandarg(union node *arg, struct arglis line_number = arg->narg.lineno; argstr(arg->narg.text, flag); if (arglist == NULL) { - NULLTERM_4_TRACE(expdest); + STACKSTRNUL(expdest); CTRACE(DBG_EXPAND, ("expandarg: no arglist, done (%d) \"%s\"\n", expdest - stackblock(), stackblock())); return; /* here document expanded */ @@ -597,8 +597,10 @@ expbackq(union node *cmd, int quoted, in if (--in.nleft < 0) { if (in.fd < 0) break; + INTON; while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR) continue; + INTOFF; VTRACE(DBG_EXPAND, ("expbackq: read returns %d\n", i)); if (i <= 0) break; Index: src/bin/sh/input.c diff -u src/bin/sh/input.c:1.58 src/bin/sh/input.c:1.59 --- src/bin/sh/input.c:1.58 Wed Jun 7 05:08:32 2017 +++ src/bin/sh/input.c Fri Jun 30 23:02:56 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: input.c,v 1.58 2017/06/07 05:08:32 kre Exp $ */ +/* $NetBSD: input.c,v 1.59 2017/06/30 23:02:56 kre Exp $ */ /*- * Copyright (c) 1991, 1993 @@ -37,7 +37,7 @@ #if 0 static char sccsid[] = "@(#)input.c 8.3 (Berkeley) 6/9/95"; #else -__RCSID("$NetBSD: input.c,v 1.58 2017/06/07 05:08:32 kre Exp $"); +__RCSID("$NetBSD: input.c,v 1.59 2017/06/30 23:02:56 kre Exp $"); #endif #endif /* not lint */ @@ -471,6 +471,7 @@ setinputfd(int fd, int push) parsefile->buf = ckmalloc(BUFSIZ); parselleft = parsenleft = 0; plinno = 1; + CTRACE(DBG_INPUT, ("setinputfd(%d, %d); plinno=1\n", fd, push)); } @@ -489,7 +490,7 @@ setinputstring(char *string, int push, i parselleft = parsenleft = strlen(string); parsefile->buf = NULL; plinno = line1; - TRACE(("setinputstring(\"%.20s%s\" (%d), %d, %d)\n", string, + CTRACE(DBG_INPUT, ("setinputstring(\"%.20s%s\" (%d), %d, %d)\n", string, (parsenleft > 20 ? "..." : ""), parsenleft, push, line1)); INTON; } @@ -506,6 +507,10 @@ pushfile(void) { struct parsefile *pf; + VTRACE(DBG_INPUT, ("pushfile(): fd=%d nl=%d ll=%d \"%.*s\" plinno=%d\n", + parsefile->fd, parsenleft, parselleft, + parsenleft, parsenextc, plinno)); + parsefile->nleft = parsenleft; parsefile->lleft = parselleft; parsefile->nextc = parsenextc; @@ -536,10 +541,40 @@ popfile(void) parsenleft = parsefile->nleft; parselleft = parsefile->lleft; parsenextc = parsefile->nextc; + VTRACE(DBG_INPUT, + ("popfile(): fd=%d nl=%d ll=%d \"%.*s\" plinno:%d->%d\n", + parsefile->fd, parsenleft, parselleft, + parsenleft, parsenextc, plinno, parsefile->linno)); plinno = parsefile->linno; INTON; } +/* + * Return current file (to go back to it later using popfilesupto()). + */ + +struct parsefile * +getcurrentfile(void) +{ + return parsefile; +} + + +/* + * Pop files until the given file is on top again. Useful for regular + * builtins that read shell commands from files or strings. + * If the given file is not an active file, an error is raised. + */ + +void +popfilesupto(struct parsefile *file) +{ + while (parsefile != file && parsefile != &basepf) + popfile(); + if (parsefile != file) + error("popfilesupto() misused"); +} + /* * Return to top level. Index: src/bin/sh/input.h diff -u src/bin/sh/input.h:1.19 src/bin/sh/input.h:1.20 --- src/bin/sh/input.h:1.19 Wed Jun 7 04:44:17 2017 +++ src/bin/sh/input.h Fri Jun 30 23:02:56 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: input.h,v 1.19 2017/06/07 04:44:17 kre Exp $ */ +/* $NetBSD: input.h,v 1.20 2017/06/30 23:02:56 kre Exp $ */ /*- * Copyright (c) 1991, 1993 @@ -45,8 +45,10 @@ extern int plinno; extern int parsenleft; /* number of characters left in input buffer */ extern const char *parsenextc; /* next character in input buffer */ extern int init_editline; /* 0 == not setup, 1 == OK, -1 == failed */ +extern int whichprompt; /* 1 ==> PS1, 2 ==> PS2 */ struct alias; +struct parsefile; char *pfgets(char *, int); int pgetc(void); @@ -58,6 +60,8 @@ void setinputfile(const char *, int); void setinputfd(int, int); void setinputstring(char *, int, int); void popfile(void); +struct parsefile *getcurrentfile(void); +void popfilesupto(struct parsefile *); void popallfiles(void); void closescript(int); Index: src/bin/sh/option.list diff -u src/bin/sh/option.list:1.4 src/bin/sh/option.list:1.5 --- src/bin/sh/option.list:1.4 Sat Jun 17 07:50:35 2017 +++ src/bin/sh/option.list Fri Jun 30 23:02:56 2017 @@ -1,10 +1,10 @@ -/* $NetBSD: option.list,v 1.4 2017/06/17 07:50:35 kre Exp $ */ +/* $NetBSD: option.list,v 1.5 2017/06/30 23:02:56 kre Exp $ */ /* * define the shell's settable options * * new options can be defined by adding them here, - * but they do nothing untilcode to implement them + * but they do nothing until code to implement them * is added (using the "var name" field) */ @@ -64,6 +64,7 @@ pflag nopriv p # preserve privs if set posix posix # be closer to POSIX compat qflag quietprofile q # disable -v/-x in startup files fnline1 local_lineno L on # number lines in funcs starting at 1 +promptcmds promptcmds # allow $( ) in PS1 (et al). // editline/history related options ("vi" is standard, 'V' and others are not) // only one of vi/emacs can be set, hence the "set" definition, value Index: src/bin/sh/parser.c diff -u src/bin/sh/parser.c:1.139 src/bin/sh/parser.c:1.140 --- src/bin/sh/parser.c:1.139 Sat Jun 24 11:23:35 2017 +++ src/bin/sh/parser.c Fri Jun 30 23:02:56 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: parser.c,v 1.139 2017/06/24 11:23:35 kre Exp $ */ +/* $NetBSD: parser.c,v 1.140 2017/06/30 23:02:56 kre Exp $ */ /*- * Copyright (c) 1991, 1993 @@ -37,7 +37,7 @@ #if 0 static char sccsid[] = "@(#)parser.c 8.7 (Berkeley) 5/16/95"; #else -__RCSID("$NetBSD: parser.c,v 1.139 2017/06/24 11:23:35 kre Exp $"); +__RCSID("$NetBSD: parser.c,v 1.140 2017/06/30 23:02:56 kre Exp $"); #endif #endif /* not lint */ @@ -74,34 +74,34 @@ __RCSID("$NetBSD: parser.c,v 1.139 2017/ #define OPENBRACE '{' #define CLOSEBRACE '}' - -struct heredoc { - struct heredoc *next; /* next here document in list */ +struct HereDoc { + struct HereDoc *next; /* next here document in list */ union node *here; /* redirection node */ char *eofmark; /* string indicating end of input */ int striptabs; /* if set, strip leading tabs */ int startline; /* line number where << seen */ }; +MKINIT struct parse_state parse_state; +union parse_state_p psp = { .c_current_parser = &parse_state }; - -static int noalias = 0; /* when set, don't handle aliases */ -struct heredoc *heredoclist; /* list of here documents to read */ -int parsebackquote; /* nonzero if we are inside backquotes */ -int doprompt; /* if set, prompt the user */ -int needprompt; /* true if interactive and at start of line */ -int lasttoken; /* last token read */ -MKINIT int tokpushback; /* last token pushed back */ -char *wordtext; /* text of last word returned by readtoken */ -MKINIT int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */ -struct nodelist *backquotelist; -union node *redirnode; -struct heredoc *heredoc; -int quoteflag; /* set if (part of) last token was quoted */ -int startlinno; /* line # where last token started */ -int funclinno; /* line # where the current function started */ -int elided_nl; /* count of \ \n (deleted \n's) we have seen */ - +static const struct parse_state init_parse_state = { /* all 0's ... */ + .ps_noalias = 0, + .ps_heredoclist = NULL, + .ps_parsebackquote = 0, + .ps_doprompt = 0, + .ps_needprompt = 0, + .ps_lasttoken = 0, + .ps_tokpushback = 0, + .ps_wordtext = NULL, + .ps_checkkwd = 0, + .ps_redirnode = NULL, + .ps_heredoc = NULL, + .ps_quoteflag = 0, + .ps_startlinno = 0, + .ps_funclinno = 0, + .ps_elided_nl = 0, +}; STATIC union node *list(int, int); STATIC union node *andor(void); @@ -122,7 +122,6 @@ STATIC void synerror(const char *) __dea STATIC void setprompt(int); STATIC int pgetc_linecont(void); - static const char EOFhere[] = "EOF reading here (<<) document"; #ifdef DEBUG @@ -758,8 +757,8 @@ parsefname(void) if (readtoken() != TWORD) synexpect(-1, 0); if (n->type == NHERE) { - struct heredoc *here = heredoc; - struct heredoc *p; + struct HereDoc *here = heredoc; + struct HereDoc *p; if (quoteflag == 0) n->type = NXHERE; @@ -948,7 +947,7 @@ insert_elided_nl(char *str) STATIC void readheredocs(void) { - struct heredoc *here; + struct HereDoc *here; union node *n; int line, l; @@ -1012,7 +1011,7 @@ readtoken(void) #endif struct alias *ap; - top: + top: t = xxreadtoken(); if (checkkwd) { @@ -1045,6 +1044,9 @@ readtoken(void) } if (!noalias && (ap = lookupalias(wordtext, 1)) != NULL) { + VTRACE(DBG_PARSE, + ("alias '%s' recognized -> <:%s:>\n", + wordtext, ap->val)); pushstring(ap->val, strlen(ap->val), ap); checkkwd = savecheckkwd; goto top; @@ -1547,7 +1549,7 @@ parseredir(const char *out, int c) np->nfile.fd = 0; } np->type = NHERE; - heredoc = stalloc(sizeof(struct heredoc)); + heredoc = stalloc(sizeof(struct HereDoc)); heredoc->here = np; heredoc->startline = plinno; if ((c = pgetc_linecont()) == '-') { @@ -2034,13 +2036,14 @@ parsearith: { #ifdef mkinit +INCLUDE "parser.h" + RESET { - struct heredoc; - extern struct heredoc *heredoclist; + psp.v_current_parser = &parse_state; - tokpushback = 0; - checkkwd = 0; - heredoclist = NULL; + parse_state.ps_tokpushback = 0; + parse_state.ps_checkkwd = 0; + parse_state.ps_heredoclist = NULL; } #endif @@ -2177,14 +2180,102 @@ pgetc_linecont(void) const char * getprompt(void *unused) { + char *p; + const char *cp; + + if (!doprompt) + return ""; + + VTRACE(DBG_PARSE|DBG_EXPAND, ("getprompt %d\n", whichprompt)); + switch (whichprompt) { case 0: return ""; case 1: - return ps1val(); + p = ps1val(); + break; case 2: - return ps2val(); + p = ps2val(); + break; default: return "<internal prompt error>"; } + if (p == NULL) + return ""; + + VTRACE(DBG_PARSE|DBG_EXPAND, ("prompt <<%s>>\n", p)); + + cp = expandstr(p, plinno); + + VTRACE(DBG_PARSE|DBG_EXPAND, ("prompt -> <<%s>>\n", cp)); + + return cp; +} + +/* + * Expand a string ... used for expanding prompts (PS1...) + * + * Never return NULL, always some string (return input string if invalid) + */ +const char * +expandstr(char *ps, int lineno) +{ + union node n; + struct jmploc jmploc; + struct jmploc *const savehandler = handler; + struct parsefile *const savetopfile = getcurrentfile(); + const int save_x = xflag; + struct parse_state new_state = init_parse_state; + struct parse_state *const saveparser = psp.v_current_parser; + struct stackmark smark; + const char *result = NULL; + + setstackmark(&smark); + /* + * At this point we anticipate that there may be a string + * growing on the stack, but we have no idea how big it is. + * However we know that it cannot be bigger than the current + * allocated stack block, so simply reserve the whole thing, + * then we can use the stack without barfing all over what + * is there already... (the stack mark undoes this later.) + */ + (void) stalloc(stackblocksize()); + + if (!setjmp(jmploc.loc)) { + handler = &jmploc; + + psp.v_current_parser = &new_state; + setinputstring(ps, 1, lineno); + + readtoken1(pgetc(), DQSYNTAX, 1); + if (backquotelist != NULL && !promptcmds) + result = "-o promptcmds not set: "; + else { + n.narg.type = NARG; + n.narg.next = NULL; + n.narg.text = wordtext; + n.narg.lineno = lineno; + n.narg.backquote = backquotelist; + + xflag = 0; /* we might be expanding PS4 ... */ + expandarg(&n, NULL, 0); + result = stackblock(); + } + INTOFF; + } + psp.v_current_parser = saveparser; + xflag = save_x; + popfilesupto(savetopfile); + handler = savehandler; + popstackmark(&smark); + + if (result != NULL) { + INTON; + } else { + if (exception == EXINT) + exraise(SIGINT); + result = ps; + } + + return result; } Index: src/bin/sh/parser.h diff -u src/bin/sh/parser.h:1.22 src/bin/sh/parser.h:1.23 --- src/bin/sh/parser.h:1.22 Wed Jun 7 05:08:32 2017 +++ src/bin/sh/parser.h Fri Jun 30 23:02:56 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: parser.h,v 1.22 2017/06/07 05:08:32 kre Exp $ */ +/* $NetBSD: parser.h,v 1.23 2017/06/30 23:02:56 kre Exp $ */ /*- * Copyright (c) 1991, 1993 @@ -70,21 +70,90 @@ #define VSTRIMRIGHTMAX 0x9 /* ${var%%pattern} */ #define VSLENGTH 0xa /* ${#var} */ +union node *parsecmd(int); +void fixredir(union node *, const char *, int); +int goodname(char *); +const char *getprompt(void *); +const char *expandstr(char *, int); + +struct HereDoc; +union node; +struct nodelist; + +struct parse_state { + int ps_noalias; /* when set, don't handle aliases */ + struct HereDoc *ps_heredoclist; /* list of here documents to read */ + int ps_parsebackquote; /* nonzero inside backquotes */ + int ps_doprompt; /* if set, prompt the user */ + int ps_needprompt; /* true if interactive at line start */ + int ps_lasttoken; /* last token read */ + int ps_tokpushback; /* last token pushed back */ + char *ps_wordtext; /* text of last word returned by readtoken */ + int ps_checkkwd; /* 1 == check for kwds, 2 += eat newlines */ + struct nodelist *ps_backquotelist; /* list of cmdsubs to process */ + union node *ps_redirnode; /* node for current redirect */ + struct HereDoc *ps_heredoc; /* current heredoc << beign parsed */ + int ps_quoteflag; /* set if (part) of token was quoted */ + int ps_startlinno; /* line # where last token started */ + int ps_funclinno; /* line # of the current function */ + int ps_elided_nl; /* count of \ \n pairs we have seen */ +}; + +/* + * The parser references the elements of struct parse_state quite + * frequently - they used to be simple globals, so one memory ref + * per access, adding an indirect through global ptr would not be + * nice. The following gross hack allows most of that cost to be + * avoided, by allowing the compiler to understand that the global + * pointer is in fact constant in any function, and so its value can + * be cached, rather than needing to be fetched every time in case + * some other called function has changed it. + * + * The rule to make this work is that any function that wants + * to alter the global must restore it before it returns (and thus + * must have an error trap handler). That means that the struct + * used for the new parser state can be a local in that function's + * stack frame, it never needs to be malloc'd. + */ + +union parse_state_p { + struct parse_state *const c_current_parser; + struct parse_state * v_current_parser; +}; + +extern union parse_state_p psp; + +#define current_parser (psp.c_current_parser) + +/* + * Perhaps one day emulate "static" by moving most of these definitions into + * parser.c ... (only checkkwd & tokpushback are used outside parser.c, + * and only in init.c as a RESET activity) + */ +#define tokpushback (current_parser->ps_tokpushback) +#define checkkwd (current_parser->ps_checkkwd) + +#define noalias (current_parser->ps_noalias) +#define heredoclist (current_parser->ps_heredoclist) +#define parsebackquote (current_parser->ps_parsebackquote) +#define doprompt (current_parser->ps_doprompt) +#define needprompt (current_parser->ps_needprompt) +#define lasttoken (current_parser->ps_lasttoken) +#define wordtext (current_parser->ps_wordtext) +#define backquotelist (current_parser->ps_backquotelist) +#define redirnode (current_parser->ps_redirnode) +#define heredoc (current_parser->ps_heredoc) +#define quoteflag (current_parser->ps_quoteflag) +#define startlinno (current_parser->ps_startlinno) +#define funclinno (current_parser->ps_funclinno) +#define elided_nl (current_parser->ps_elided_nl) /* * NEOF is returned by parsecmd when it encounters an end of file. It * must be distinct from NULL, so we use the address of a variable that * happens to be handy. */ -extern int tokpushback; -#define NEOF ((union node *)&tokpushback) -extern int whichprompt; /* 1 == PS1, 2 == PS2 */ - - -union node *parsecmd(int); -void fixredir(union node *, const char *, int); -int goodname(char *); -const char *getprompt(void *); +#define NEOF ((union node *)&psp) #ifdef DEBUG extern int parsing;