rc

[fork] interactive rc shell
Log | Files | Refs | README | LICENSE

commit 8ce5bc05bcf9b263a65eb6e8d6bae0e610a88b16
parent 593a3857b992c85038276eb0e4c8aa20d796f3de
Author: tjg <tjg>
Date:   Mon,  1 Oct 2001 12:54:26 +0000

  Bug: it's no longer possible to use parentheses to sneak a word that
  needs quoting past the "quoting detector", so `fn x { echo $(x.y) }'
  now works both in the current rc and its descendants.

Diffstat:
MChangeLog | 7++++++-
Mfootobar.c | 7++++---
Mheredoc.c | 10+++++-----
Mlex.c | 17+++++++++++++++--
Mparse.y | 8++++----
Mrc.1 | 16----------------
Mrc.h | 6++++--
Mtree.c | 9+++++----
Mtrip.rc | 2+-
9 files changed, 44 insertions(+), 38 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -694,10 +694,15 @@ Changes since rc-1.5b2 Bug: you can't pass a short to a stdargs function! - 2001-09-27 Documentation: we don't consider that `.' failing to search path is a bug. Bug: the ^A bug is fixed. + +2001-10-01 + + Bug: it's no longer possible to use parentheses to sneak a word that + needs quoting past the "quoting detector", so `fn x { echo $(x.y) }' + now works both in the current rc and its descendants. diff --git a/footobar.c b/footobar.c @@ -52,8 +52,6 @@ static bool Tconv(Format *f, int ignore) { return FALSE; } switch (n->type) { - case nWord: fmtprint(f, "%S", n->u[0].s); break; - case nQword: fmtprint(f, "%#S", n->u[0].s); break; case nBang: fmtprint(f, "!%T", n->u[0].p); break; case nCase: fmtprint(f, "case %T", n->u[0].p); break; case nNowait: fmtprint(f, "%T&", n->u[0].p); break; @@ -73,11 +71,14 @@ static bool Tconv(Format *f, int ignore) { case nLappend: fmtprint(f, "(%T %T)", n->u[0].p, n->u[1].p); break; case nForin: fmtprint(f, "for(%T in %T)%T", n->u[0].p, n->u[1].p, n->u[2].p); break; case nVarsub: fmtprint(f, "$%T(%T)", n->u[0].p, n->u[1].p); break; + case nWord: + fmtprint(f, quotep(n->u[0].s) ? "%#S" : "%S", n->u[0].s); + break; case nCount: case nFlat: case nVar: { char *lp = "", *rp = ""; Node *n0 = n->u[0].p; - if (n0->type != nWord && n0->type != nQword) + if (n0->type != nWord) lp = "(", rp = ")"; switch (n->type) { diff --git a/heredoc.c b/heredoc.c @@ -82,14 +82,14 @@ static Node *parseheredoc(char *s) { while ((c = *s++) != '\0' && c != '$') ; *--s = '\0'; - node = mk(nQword, begin, NULL); + node = mk(nWord, begin, NULL); break; } case '$': { char *begin = ++s, *var; c = *s++; if (c == '$') { - node = mk(nQword, "$", NULL); + node = mk(nWord, "$", NULL); c = *s; } else { size_t len = 0; @@ -129,7 +129,7 @@ extern int heredoc(int end) { char *s = readheredoc(here->name); if (dead) return FALSE; - n->u[2].p = here->quoted ? mk(nQword, s, NULL) : parseheredoc(s); + n->u[2].p = here->quoted ? mk(nWord, s, NULL, FALSE) : parseheredoc(s); n->u[0].i = rHerestring; } while ((here = here->n) != NULL); } @@ -140,7 +140,7 @@ extern int heredoc(int end) { extern int qdoc(Node *name, Node *n) { Hq *new, **prev; - if (name->type != nWord && name->type != nQword) { + if (name->type != nWord) { yyerror("eof-marker not a single literal word"); flushu(); return FALSE; @@ -149,7 +149,7 @@ extern int qdoc(Node *name, Node *n) { ; *prev = new = nnew(Hq); new->name = name->u[0].s; - new->quoted = (name->type == nQword); + new->quoted = name->u[2].i; new->doc = n; new->n = NULL; return TRUE; diff --git a/lex.c b/lex.c @@ -69,6 +69,16 @@ enum filedescriptors { UNSET = -9, CLOSED = -1 }; +/* does this string require quoting? */ +extern bool quotep(char *s) { + unsigned char c; + + while ((c = *s++)) + if (nw[c]) + return TRUE; + return FALSE; +} + extern int yylex() { static bool dollar = FALSE; bool saw_meta = FALSE; @@ -147,6 +157,7 @@ top: while ((c = gchar()) == ' ' || c == '\t') } else { y->word.m = NULL; } + y->word.q = FALSE; return WORD; } if (c == '`' || c == '!' || c == '@' || c == '~' || c == '$' || c == '\'') { @@ -179,7 +190,8 @@ top: while ((c = gchar()) == ' ' || c == '\t') case '\'': w = RW; i = 0; - do { + /* double ' to quote it, like this: 'how''s it going?' */ + while ((c = gchar()) != '\'' || (c = gchar()) == '\'') { buf[i++] = c; if (c == '\n') print_prompt2(); @@ -190,11 +202,12 @@ top: while ((c = gchar()) == ' ' || c == '\t') } 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; + y->word.q = TRUE; return WORD; case '\\': if ((c = gchar()) == '\n') { diff --git a/parse.y b/parse.y @@ -124,7 +124,7 @@ first : comword | first '^' sword { $$ = mk(nConcat,$1,$3); } sword : comword - | keyword { $$ = mk(nWord,$1, NULL); } + | keyword { $$ = mk(nWord, $1, NULL, FALSE); } word : sword | word '^' sword { $$ = mk(nConcat,$1,$3); } @@ -139,7 +139,7 @@ comword : '$' sword { $$ = mk(nVar,$2); } | BACKBACK word sword { $$ = mk(nBackq,$2,$3); } | '(' nlwords ')' { $$ = $2; } | REDIR brace { $$ = mk(nNmpipe,$1.type,$1.fd,$2); } - | WORD { $$ = ($1.w[0] == '\'') ? mk(nQword, $1.w+1, NULL) : mk(nWord,$1.w, $1.m); } + | WORD { $$ = mk(nWord, $1.w, $1.m, $1.q); } keyword : FOR { $$ = "for"; } | IN { $$ = "in"; } @@ -166,7 +166,7 @@ optnl : /* empty */ %% void initparse() { - star = treecpy(mk(nVar,mk(nWord,"*",NULL)), ealloc); - nolist = treecpy(mk(nVar,mk(nWord,"ifs",NULL)), ealloc); + star = treecpy(mk(nVar, mk(nWord,"*", NULL, FALSE)), ealloc); + nolist = treecpy(mk(nVar, mk(nWord,"ifs", NULL, FALSE)), ealloc); } diff --git a/rc.1 b/rc.1 @@ -1933,17 +1933,6 @@ The notation for flattening should allow for using an arbitrary separating character, not just space. .PP -It is usually possible to use parentheses to defeat -free caret insertion; that is, to use -.Cr $(x.y) -as a synonym for -.Cr $'x.y' . -However, the former syntax is not correctly handled in functions -exported to the environment: a function defined using this syntax will -not work properly in descendant -.I rc -processes. -.PP List definitions in exported functions are "noisier" than they need to be. .PP If both @@ -1960,11 +1949,6 @@ but such elements will be split at the colon in descendant .I rc processes. .PP -The -.Cr . -command should search -.Cr $path . -.PP Extra parentheses around a .Cr ~ expression or a diff --git a/rc.h b/rc.h @@ -29,8 +29,8 @@ typedef struct Format Format; typedef union Edata Edata; typedef enum nodetype { - nAndalso, nAssign, nBackq, nBang, nBody, nCbody, nNowait, nBrace, nConcat, - nCount, nElse, nFlat, nDup, nEpilog, nNewfn, nForin, nIf, nQword, + nAndalso, nAssign, nBackq, nBang, nBody, nCbody, nNowait, nBrace, + nConcat, nCount, nElse, nFlat, nDup, nEpilog, nNewfn, nForin, nIf, nOrelse, nPipe, nPre, nRedir, nRmfn, nArgs, nSubshell, nCase, nSwitch, nMatch, nVar, nVarsub, nWhile, nWord, nLappend, nNmpipe } nodetype; @@ -97,6 +97,7 @@ struct Redir { struct Word { char *w, *m; + bool q; }; struct Rq { @@ -279,6 +280,7 @@ extern bool rcrc; /* lex.c */ +extern bool quotep(char *); extern int yylex(void); extern void inityy(void); extern void yyerror(const char *); diff --git a/tree.c b/tree.c @@ -18,10 +18,11 @@ extern Node *mk(int /*nodetype*/ t,...) { n->u[1].i = va_arg(ap, int); n->u[2].i = va_arg(ap, int); break; - case nWord: case nQword: - n = nalloc(offsetof(Node, u[2])); + case nWord: + n = nalloc(offsetof(Node, u[3])); n->u[0].s = va_arg(ap, char *); n->u[1].s = va_arg(ap, char *); + n->u[2].i = va_arg(ap, int); break; case nBang: case nNowait: case nCount: case nFlat: case nRmfn: case nSubshell: @@ -79,7 +80,7 @@ extern Node *treecpy(Node *s, void *(*alloc)(size_t)) { n->u[1].i = s->u[1].i; n->u[2].i = s->u[2].i; break; - case nWord: case nQword: + case nWord: n = (*alloc)(offsetof(Node, u[2])); n->u[0].s = strcpy((char *) (*alloc)(strlen(s->u[0].s) + 1), s->u[0].s); if (s->u[1].s != NULL) { @@ -138,7 +139,7 @@ extern void treefree(Node *s) { /* NOTREACHED */ case nDup: break; - case nWord: case nQword: + case nWord: efree(s->u[0].s); efree(s->u[1].s); break; diff --git a/trip.rc b/trip.rc @@ -444,7 +444,7 @@ if (whatis printenv >/dev/null>[2=1]) { } else printenv=() -if (~ $#printenv 1 && !~ `` $nl {$printenv | grep fn___2d__2d__2d} 'fn___2d__2d__2d={for(i in $*)a|[2=3]b >>c <<<''e''&f >[2=1]}') +if (~ $#printenv 1 && !~ `` $nl {$printenv | grep fn___2d__2d__2d} 'fn___2d__2d__2d={for(i in $*)a|[2=3]b >>c <<<e&f >[2=1]}') fail protect_env fn --- {replace}