diff options
author | Peter Stephenson <pws@users.sourceforge.net> | 2005-04-12 15:11:07 +0000 |
---|---|---|
committer | Peter Stephenson <pws@users.sourceforge.net> | 2005-04-12 15:11:07 +0000 |
commit | b3f8e32e5cf5771eb5efb1e11c38dab377b14432 (patch) | |
tree | a0029fa908983c35956961f92cc51bda73487d93 | |
parent | bd718425bb41f08fb9d06968b339455abb37e2dd (diff) | |
download | zsh-b3f8e32e5cf5771eb5efb1e11c38dab377b14432.tar.gz zsh-b3f8e32e5cf5771eb5efb1e11c38dab377b14432.tar.xz zsh-b3f8e32e5cf5771eb5efb1e11c38dab377b14432.zip |
21133: New {myfd} syntax for allocating file descriptors
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | Doc/Zsh/redirect.yo | 25 | ||||
-rw-r--r-- | Src/exec.c | 102 | ||||
-rw-r--r-- | Src/parse.c | 132 | ||||
-rw-r--r-- | Src/text.c | 9 | ||||
-rw-r--r-- | Src/zsh.h | 17 | ||||
-rw-r--r-- | Test/A04redirect.ztst | 25 |
7 files changed, 256 insertions, 60 deletions
diff --git a/ChangeLog b/ChangeLog index ba385b60c..560902b01 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2005-04-12 Peter Stephenson <pws@csr.com> + + * 21133: Doc/Zsh/redirect.yo, Src/exec.c, Src/parse.c, Src/text.c, + Src/zsh.h, Test/A04redirect.ztst: New {myfd}> syntax for + allocating file descriptors. + 2005-04-11 Clint Adams <clint@zsh.org> * 21132: Completion/Unix/Type/_pdf, Completion/X/Command/_xpdf: diff --git a/Doc/Zsh/redirect.yo b/Doc/Zsh/redirect.yo index 167c3ef21..3ce4b4369 100644 --- a/Doc/Zsh/redirect.yo +++ b/Doc/Zsh/redirect.yo @@ -149,6 +149,31 @@ file descriptor 2 would be associated with the terminal (assuming file descriptor 1 had been) and then file descriptor 1 would be associated with file var(fname). +If instead of a digit one of the operators above is preceded by +a valid identifier enclosed in braces, the shell will open a new +file descriptor that is guaranteed to be at least 10 and set the +parameter named by the identifier to the file descriptor opened. +No whitespace is allowed between the closing brace and the redirection +character. The option tt(IGNORE_BRACES) must not be set. +For example: + +indent(... {myfd}>&1) + +This opens a new file descriptor that is a duplicate of file descriptor +1 and sets the parameter tt(myfd) to the number of the file descriptor, +which will be at least 10. The new file descriptor can be written to using +the syntax tt(>&$myfd). + +The syntax tt({)var(varid)tt(}>&-), for example tt({myfd}>&-), may be used +to close a file descriptor opened in this fashion. Note that the +parameter given by var(varid) must previously be set to a file descriptor +in this case. + +It is an error to open or close a file descriptor in this fashion when the +parameter is readonly. However, it is not an error to read or write a file +descriptor using tt(<&$)var(param) or tt(>&$)var(param) if var(param) is +readonly. + The `tt(|&)' command separator described in ifzman(em(Simple Commands & Pipelines) in zmanref(zshmisc))\ ifnzman(noderef(Simple Commands & Pipelines)) diff --git a/Src/exec.c b/Src/exec.c index d19c55ad4..e3555afd2 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1293,7 +1293,8 @@ execpline2(Estate state, wordcode pcode, wordcode code; state->pc++; - for (pc = state->pc; wc_code(code = *pc) == WC_REDIR; pc += 3); + for (pc = state->pc; wc_code(code = *pc) == WC_REDIR; + pc += WC_REDIR_WORDS(code)); mpipe(pipes); @@ -1532,32 +1533,52 @@ closeallelse(struct multio *mn) } } -/* A multio is a list of fds associated with a certain fd. * - * Thus if you do "foo >bar >ble", the multio for fd 1 will have * - * two fds, the result of open("bar",...), and the result of * - * open("ble",....). */ +/* + * A multio is a list of fds associated with a certain fd. + * Thus if you do "foo >bar >ble", the multio for fd 1 will have + * two fds, the result of open("bar",...), and the result of + * open("ble",....). + */ -/* Add a fd to an multio. fd1 must be < 10, and may be in any state. * - * fd2 must be open, and is `consumed' by this function. Note that * - * fd1 == fd2 is possible, and indicates that fd1 was really closed. * - * We effectively do `fd2 = movefd(fd2)' at the beginning of this * - * function, but in most cases we can avoid an extra dup by delaying * - * the movefd: we only >need< to move it if we're actually doing a * - * multiple redirection. */ +/* + * Add a fd to an multio. fd1 must be < 10, and may be in any state. + * fd2 must be open, and is `consumed' by this function. Note that + * fd1 == fd2 is possible, and indicates that fd1 was really closed. + * We effectively do `fd2 = movefd(fd2)' at the beginning of this + * function, but in most cases we can avoid an extra dup by delaying + * the movefd: we only >need< to move it if we're actually doing a + * multiple redirection. + * + * If varid is not NULL, we open an fd above 10 and set the parameter + * named varid to that value. fd1 is not used. + */ /**/ static void -addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag) +addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag, + char *varid) { int pipes[2]; - if (!mfds[fd1] || unset(MULTIOS)) { + if (varid) { + /* fd will be over 10, don't touch mfds */ + fd1 = movefd(fd2); + fdtable[fd1] = FDT_EXTERNAL; + setiparam(varid, (zlong)fd1); + /* + * If setting the parameter failed, close the fd else + * it will leak. + */ + if (errflag) + zclose(fd1); + } else if (!mfds[fd1] || unset(MULTIOS)) { if(!mfds[fd1]) { /* starting a new multio */ mfds[fd1] = (struct multio *) zhalloc(sizeof(struct multio)); if (!forked && save[fd1] == -2) save[fd1] = (fd1 == fd2) ? -1 : movefd(fd1); } - redup(fd2, fd1); + if (!varid) + redup(fd2, fd1); mfds[fd1]->ct = 1; mfds[fd1]->fds[0] = fd1; mfds[fd1]->rflag = rflag; @@ -2207,9 +2228,9 @@ execcmd(Estate state, int input, int output, int how, int last1) /* Add pipeline input/output to mnodes */ if (input) - addfd(forked, save, mfds, 0, input, 0); + addfd(forked, save, mfds, 0, input, 0, NULL); if (output) - addfd(forked, save, mfds, 1, output, 1); + addfd(forked, save, mfds, 1, output, 1, NULL); /* Do process substitutions */ if (redir) @@ -2226,14 +2247,14 @@ execcmd(Estate state, int input, int output, int how, int last1) fixfds(save); execerr(); } - addfd(forked, save, mfds, fn->fd1, fn->fd2, 0); + addfd(forked, save, mfds, fn->fd1, fn->fd2, 0, fn->varid); } else if (fn->type == REDIR_OUTPIPE) { if (fn->fd2 == -1) { closemnodes(mfds); fixfds(save); execerr(); } - addfd(forked, save, mfds, fn->fd1, fn->fd2, 1); + addfd(forked, save, mfds, fn->fd1, fn->fd2, 1, fn->varid); } else { if (fn->type != REDIR_HERESTR && xpandredir(fn, redir)) continue; @@ -2258,7 +2279,7 @@ execcmd(Estate state, int input, int output, int how, int last1) zwarn("%e", NULL, errno); execerr(); } - addfd(forked, save, mfds, fn->fd1, fil, 0); + addfd(forked, save, mfds, fn->fd1, fil, 0, fn->varid); break; case REDIR_READ: case REDIR_READWRITE: @@ -2274,7 +2295,7 @@ execcmd(Estate state, int input, int output, int how, int last1) zwarn("%e: %s", fn->name, errno); execerr(); } - addfd(forked, save, mfds, fn->fd1, fil, 0); + addfd(forked, save, mfds, fn->fd1, fil, 0, fn->varid); /* If this is 'exec < file', read from stdin, * * not terminal, unless `file' is a terminal. */ if (nullexec == 1 && fn->fd1 == 0 && @@ -2282,9 +2303,32 @@ execcmd(Estate state, int input, int output, int how, int last1) init_io(); break; case REDIR_CLOSE: + if (fn->varid) { + char *s = fn->varid; + struct value vbuf; + Value v; + int bad = 0; + + if (!(v = getvalue(&vbuf, &s, 0))) { + bad = 1; + } else if (v->pm->flags & PM_READONLY) { + bad = 2; + } else { + fn->fd1 = (int)getintvalue(v); + bad = errflag; + } + if (bad) { + zwarn(bad == 2 ? + "can't close file descriptor from readonly parameter" : + "parameter %s does not contain a file descriptor", + fn->varid, 0); + execerr(); + } + } if (!forked && fn->fd1 < 10 && save[fn->fd1] == -2) save[fn->fd1] = movefd(fn->fd1); - closemn(mfds, fn->fd1); + if (fn->fd1 < 10) + closemn(mfds, fn->fd1); zclose(fn->fd1); break; case REDIR_MERGEIN: @@ -2292,7 +2336,8 @@ execcmd(Estate state, int input, int output, int how, int last1) if (fn->fd2 < 10) closemn(mfds, fn->fd2); if (fn->fd2 > 9 && - (fdtable[fn->fd2] != FDT_UNUSED || + ((fdtable[fn->fd2] != FDT_UNUSED && + fdtable[fn->fd2] != FDT_EXTERNAL) || fn->fd2 == coprocin || fn->fd2 == coprocout)) { fil = -1; @@ -2313,7 +2358,8 @@ execcmd(Estate state, int input, int output, int how, int last1) zwarn("%s: %e", fn->fd2 == -2 ? "coprocess" : fdstr, errno); execerr(); } - addfd(forked, save, mfds, fn->fd1, fil, fn->type == REDIR_MERGEOUT); + addfd(forked, save, mfds, fn->fd1, fil, + fn->type == REDIR_MERGEOUT, fn->varid); break; default: if (IS_APPEND_REDIR(fn->type)) @@ -2336,11 +2382,14 @@ execcmd(Estate state, int input, int output, int how, int last1) zwarn("%e: %s", fn->name, errno); execerr(); } - addfd(forked, save, mfds, fn->fd1, fil, 1); + addfd(forked, save, mfds, fn->fd1, fil, 1, fn->varid); if(IS_ERROR_REDIR(fn->type)) - addfd(forked, save, mfds, 2, dfil, 1); + addfd(forked, save, mfds, 2, dfil, 1, NULL); break; } + if (errflag) { + execerr(); + } } } @@ -2845,6 +2894,7 @@ getoutput(char *cmd, int qt) WC_SUBLIST_TYPE(pc[1]) == WC_SUBLIST_END && wc_code(pc[2]) == WC_PIPE && WC_PIPE_TYPE(pc[2]) == WC_PIPE_END && wc_code(pc[3]) == WC_REDIR && WC_REDIR_TYPE(pc[3]) == REDIR_READ && + !WC_REDIR_VARID(pc[3]) && !pc[4] && wc_code(pc[6]) == WC_SIMPLE && !WC_SIMPLE_ARGC(pc[6])) { /* $(< word) */ diff --git a/Src/parse.c b/Src/parse.c index 9a4a95bec..20649a6fb 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -111,6 +111,8 @@ struct heredocs *hdocs; * - must precede command-code (or WC_ASSIGN) * - data contains type (<, >, ...) * - followed by fd1 and name from struct redir + * - for the extended form {var}>... where the fd is assigned + * to var, there is an extra item to contain var * * WC_ASSIGN * - data contains type (scalar, array) and number of array-elements @@ -737,7 +739,8 @@ par_pline(int *complex) } else if (tok == BARAMP) { int r; - for (r = p + 1; wc_code(ecbuf[r]) == WC_REDIR; r += 3); + for (r = p + 1; wc_code(ecbuf[r]) == WC_REDIR; + r += WC_REDIR_WORDS(ecbuf[r])); ecispace(r, 3); ecbuf[r] = WCB_REDIR(REDIR_MERGEOUT); @@ -779,8 +782,7 @@ par_cmd(int *complex) if (IS_REDIROP(tok)) { *complex = 1; while (IS_REDIROP(tok)) { - nr++; - par_redir(&r); + nr += par_redir(&r, NULL); } } switch (tok) { @@ -871,10 +873,10 @@ par_cmd(int *complex) if (!nr) return 0; } else { - /* Three codes per redirection. */ + /* Take account of redirections */ if (sr > 1) { *complex = 1; - r += (sr - 1) * 3; + r += sr - 1; } } } @@ -883,7 +885,7 @@ par_cmd(int *complex) if (IS_REDIROP(tok)) { *complex = 1; while (IS_REDIROP(tok)) - par_redir(&r); + (void)par_redir(&r, NULL); } incmdpos = 1; incasepat = 0; @@ -1510,6 +1512,9 @@ par_dinbrack(void) * simple : { COMMAND | EXEC | NOGLOB | NOCORRECT | DASH } { STRING | ENVSTRING | ENVARRAY wordlist OUTPAR | redir } [ INOUTPAR { SEPER } ( list1 | INBRACE list OUTBRACE ) ] + * + * Returns 0 if no code, else 1 plus the number of code words + * used up by redirections. */ /**/ @@ -1517,7 +1522,7 @@ static int par_simple(int *complex, int nr) { int oecused = ecused, isnull = 1, r, argc = 0, p, isfunc = 0, sr = 0; - int c = *complex; + int c = *complex, nrediradd; r = ecused; for (;;) { @@ -1576,16 +1581,55 @@ par_simple(int *complex, int nr) for (;;) { if (tok == STRING) { + int redir_var = 0; + *complex = 1; incmdpos = 0; - ecstr(tokstr); - argc++; - yylex(); + + if (!isset(IGNOREBRACES) && *tokstr == Inbrace) + { + char *eptr = tokstr + strlen(tokstr) - 1; + char *ptr = eptr; + + if (*ptr == Outbrace && ptr > tokstr + 1) + { + while (--ptr > tokstr) + if (!iident(*ptr)) + break; + if (ptr == tokstr) + { + char *toksave = tokstr; + char *idstring = dupstrpfx(tokstr+1, eptr-tokstr-1); + redir_var = 1; + yylex(); + + if (IS_REDIROP(tok) && tokfd == -1) + { + *complex = c = 1; + nrediradd = par_redir(&r, idstring); + p += nrediradd; + sr += nrediradd; + } + else + { + ecstr(toksave); + argc++; + } + } + } + } + + if (!redir_var) + { + ecstr(tokstr); + argc++; + yylex(); + } } else if (IS_REDIROP(tok)) { *complex = c = 1; - par_redir(&r); - p += 3; /* 3 codes per redirection */ - sr++; + nrediradd = par_redir(&r, NULL); + p += nrediradd; + sr += nrediradd; } else if (tok == INOUTPAR) { int oldlineno = lineno, onp, so, oecssub = ecssub; @@ -1670,6 +1714,8 @@ par_simple(int *complex, int nr) /* * redir : ( OUTANG | ... | TRINANG ) STRING + * + * Return number of code words required for redirection */ static int redirtab[TRINANG - OUTANG + 1] = { @@ -1691,10 +1737,10 @@ static int redirtab[TRINANG - OUTANG + 1] = { }; /**/ -static void -par_redir(int *rp) +static int +par_redir(int *rp, char *idstring) { - int r = *rp, type, fd1, oldcmdpos, oldnc; + int r = *rp, type, fd1, oldcmdpos, oldnc, ncodes; char *name; oldcmdpos = incmdpos; @@ -1706,7 +1752,7 @@ par_redir(int *rp) fd1 = tokfd; yylex(); if (tok != STRING && tok != ENVSTRING) - YYERRORV(ecused); + YYERROR(ecused); incmdpos = oldcmdpos; nocorrect = oldnc; @@ -1721,23 +1767,35 @@ par_redir(int *rp) case REDIR_HEREDOCDASH: { /* <<[-] name */ struct heredocs **hd; + int htype = type; - /* If we ever need more than three codes (or less), we have to change - * the factors in par_cmd() and par_simple(), too. */ - ecispace(r, 3); - *rp = r + 3; + if (idstring) + { + type |= REDIR_VARID_MASK; + ncodes = 4; + } + else + ncodes = 3; + + /* If we ever to change the number of codes, we have to change + * the definition of WC_REDIR_WORDS. */ + ecispace(r, ncodes); + *rp = r + ncodes; ecbuf[r] = WCB_REDIR(type); ecbuf[r + 1] = fd1; + if (idstring) + ecbuf[r + 3] = ecstrcode(idstring); + for (hd = &hdocs; *hd; hd = &(*hd)->next); *hd = zalloc(sizeof(struct heredocs)); (*hd)->next = NULL; - (*hd)->type = type; + (*hd)->type = htype; (*hd)->pc = r; (*hd)->str = tokstr; yylex(); - return; + return ncodes; } case REDIR_WRITE: case REDIR_WRITENOW: @@ -1745,14 +1803,14 @@ par_redir(int *rp) /* > >(...) */ type = REDIR_OUTPIPE; else if (tokstr[0] == Inang && tokstr[1] == Inpar) - YYERRORV(ecused); + YYERROR(ecused); break; case REDIR_READ: if (tokstr[0] == Inang && tokstr[1] == Inpar) /* < <(...) */ type = REDIR_INPIPE; else if (tokstr[0] == Outang && tokstr[1] == Inpar) - YYERRORV(ecused); + YYERROR(ecused); break; case REDIR_READWRITE: if ((tokstr[0] == Inang || tokstr[0] == Outang) && tokstr[1] == Inpar) @@ -1761,13 +1819,25 @@ par_redir(int *rp) } yylex(); - /* If we ever need more than three codes (or less), we have to change - * the factors in par_cmd() and par_simple(), too. */ - ecispace(r, 3); - *rp = r + 3; + /* If we ever to change the number of codes, we have to change + * the definition of WC_REDIR_WORDS. */ + if (idstring) + { + type |= REDIR_VARID_MASK; + ncodes = 4; + } + else + ncodes = 3; + + ecispace(r, ncodes); + *rp = r + ncodes; ecbuf[r] = WCB_REDIR(type); ecbuf[r + 1] = fd1; ecbuf[r + 2] = ecstrcode(name); + if (idstring) + ecbuf[r + 3] = ecstrcode(idstring); + + return ncodes; } /**/ @@ -2316,6 +2386,10 @@ ecgetredirs(Estate s) r->type = WC_REDIR_TYPE(code); r->fd1 = *s->pc++; r->name = ecgetstr(s, EC_DUP, NULL); + if (WC_REDIR_VARID(code)) + r->varid = ecgetstr(s, EC_DUP, NULL); + else + r->varid = NULL; addlinknode(ret, r); diff --git a/Src/text.c b/Src/text.c index f7d80ae73..ceb4bfdf4 100644 --- a/Src/text.c +++ b/Src/text.c @@ -789,10 +789,15 @@ getredirs(LinkList redirs) case REDIR_MERGEOUT: case REDIR_INPIPE: case REDIR_OUTPIPE: - if (f->fd1 != (IS_READFD(f->type) ? 0 : 1)) + if (f->varid) { + taddchr('{'); + taddstr(f->varid); + taddchr('}'); + } else if (f->fd1 != (IS_READFD(f->type) ? 0 : 1)) taddchr('0' + f->fd1); taddstr(fstr[f->type]); - taddchr(' '); + if (f->type != REDIR_MERGEIN && f->type != REDIR_MERGEOUT) + taddchr(' '); if (f->type == REDIR_HERESTR) { if (has_token(f->name)) { taddchr('\"'); diff --git a/Src/zsh.h b/Src/zsh.h index b0ca94e09..6e1916690 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -246,6 +246,8 @@ enum { REDIR_INPIPE, /* < <(...) */ REDIR_OUTPIPE /* > >(...) */ }; +#define REDIR_TYPE_MASK (0x1f) +#define REDIR_VARID_MASK (0x20) #define IS_WRITE_FILE(X) ((X)>=REDIR_WRITE && (X)<=REDIR_READWRITE) #define IS_APPEND_REDIR(X) (IS_WRITE_FILE(X) && ((X) & 2)) @@ -267,9 +269,14 @@ enum { */ #define FDT_INTERNAL 1 /* + * Entry visible to other processes, for example created using + * the {varid}> file syntax. + */ +#define FDT_EXTERNAL 2 +/* * Entry used by output from the XTRACE option. */ -#define FDT_XTRACE 2 +#define FDT_XTRACE 3 #ifdef PATH_DEV_FD /* * Entry used by a process substition. @@ -277,7 +284,7 @@ enum { * decremented on exit; we don't close entries greater than * FDT_PROC_SUBST except when closing everything. */ -#define FDT_PROC_SUBST 3 +#define FDT_PROC_SUBST 4 #endif /* Flags for input stack */ @@ -453,6 +460,7 @@ struct redir { int type; int fd1, fd2; char *name; + char *varid; }; /* The number of fds space is allocated for * @@ -629,8 +637,11 @@ struct eccstr { #define WC_PIPE_LINENO(C) (wc_data(C) >> 1) #define WCB_PIPE(T,L) wc_bld(WC_PIPE, ((T) | ((L) << 1))) -#define WC_REDIR_TYPE(C) wc_data(C) +#define WC_REDIR_TYPE(C) (wc_data(C) & REDIR_TYPE_MASK) +#define WC_REDIR_VARID(C) (wc_data(C) & REDIR_VARID_MASK) #define WCB_REDIR(T) wc_bld(WC_REDIR, (T)) +/* Size of redir is 4 words if REDIR_VARID_MASK is set, else 3 */ +#define WC_REDIR_WORDS(C) (WC_REDIR_VARID(C) ? 4 : 3) #define WC_ASSIGN_TYPE(C) (wc_data(C) & ((wordcode) 1)) #define WC_ASSIGN_TYPE2(C) ((wc_data(C) & ((wordcode) 2)) >> 1) diff --git a/Test/A04redirect.ztst b/Test/A04redirect.ztst index e4e8783f9..b85d6ecf5 100644 --- a/Test/A04redirect.ztst +++ b/Test/A04redirect.ztst @@ -235,3 +235,28 @@ 0:null redir with NULLCMD=cat <input >input + + exec {myfd}>logfile + print This is my logfile. >&$myfd + print Examining contents of logfile... + cat logfile +0:Using {fdvar}> syntax to open a new file descriptor +>Examining contents of logfile... +>This is my logfile. + + exec {myfd}>&- + print This message should disappear >&$myfd +1q:Closing file descriptor using brace syntax +?(eval):2: $myfd: bad file descriptor + + typeset -r myfd + echo This should not appear {myfd}>nologfile +1:Error opening file descriptor using readonly variable +?(eval):2: read-only variable: myfd + + typeset +r myfd + exec {myfd}>newlogfile + typeset -r myfd + exec {myfd}>&- +1:Error closing file descriptor using readonly variable +?(eval):4: can't close file descriptor from readonly parameter |