From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from euclid.skiles.gatech.edu (list@euclid.skiles.gatech.edu [130.207.146.50]) by melb.werple.net.au (8.7.5/8.7.3/2) with ESMTP id AAA00702 for ; Thu, 6 Jun 1996 00:34:38 +1000 (EST) Received: (from list@localhost) by euclid.skiles.gatech.edu (8.7.3/8.7.3) id KAA24601; Wed, 5 Jun 1996 10:19:08 -0400 (EDT) Resent-Date: Wed, 5 Jun 1996 10:19:08 -0400 (EDT) From: Zefram Message-Id: <4240.199606051417@downwind.dcs.warwick.ac.uk> Subject: Re: Redirection bug To: hzoli@cs.elte.hu (Zoltan Hidvegi) Date: Wed, 5 Jun 1996 15:17:49 +0100 (BST) Cc: zsh-workers@math.gatech.edu In-Reply-To: <199606032245.AAA00255@hzoli.ppp.cs.elte.hu> from "Zoltan Hidvegi" at Jun 4, 96 00:45:18 am X-Patch: 104 X-Loop: zefram@dcs.warwick.ac.uk X-Stardate: [-31]7612.97 X-US-Congress: Moronic fuckers MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Resent-Message-ID: <"mKN8b.0.J06.STPjn"@euclid> Resent-From: zsh-workers@math.gatech.edu X-Mailing-List: archive/latest/1261 X-Loop: zsh-workers@math.gatech.edu Precedence: list Resent-Sender: zsh-workers-request@math.gatech.edu -----BEGIN PGP SIGNED MESSAGE----- Zoltan wrote: >At this point this behavour seems to be correct. bug is duplicated since >stdout is redirected twice. Here bug was printed only once before >zsh-2.6-beta18. That was probably a bug. But now it is quite >counter-intuitive that >& /dev/tty >& /dev/null duplicates stderr on >/dev/tty. I do not understand redirection staff in exec.c very much but I >do hope that Zefram knows some elegant solution to this problem. Thanks for the vote of confidence. Amazingly enough, I do have a good solution. The problem is not with the core redirection code, that I have recently been working on. As far as I can tell, that code is entirely correct, apart from the occasional arbitrary limit. The bug is in the code that turns the redirection operators into the primitives that the core code interprets. When the parser sees a redirection ">& foo", it translates it to "> foo 2>&1". Essentially, it encodes "redirect stdout and stderr to foo" as "redirect stdout to foo and the redirect stderr wherever stdout is". Without multios, these have the same effect. With multios, the meaning of "redirect" is changed, so that "redirect stdout to foo" does not necessarily mean that stdout is going only to foo. The solution is to add new primitives for error redirection. The behaviour of ">& foo" is then much like "9> foo >&9 2>&9 9>&-", where 9 is some arbitrary available fd. The patch below does this. Probably the most noticeable effect (more noticeable than fixing this bug) is that commands generated by text.c will now show ">& foo" rather than "> foo 2>&1". This happens, for example, in "jobs" output. (The note about this that was recently added to the FAQ can go.) The patch also makes a very minor change. Previously "<& foo" meant the same as "<&0"; now it causes a parse error. The code there seemed to make ">& foo" do the same as ">&1", but it had no effect due to this case being treated as an error redirection earlier. -zefram Index: exec.c *** exec.c 1996/06/05 11:17:56 1.15 --- exec.c 1996/06/05 13:26:05 *************** *** 873,879 **** { struct stat buf; ! if (unset(NOCLOBBER) || f->type & 1) return 0; if (stat(unmeta(f->name), &buf) == -1) return 1; --- 873,879 ---- { struct stat buf; ! if (unset(NOCLOBBER) || IS_CLOBBER_REDIR(f->type)) return 0; if (stat(unmeta(f->name), &buf) == -1) return 1; *************** *** 1086,1092 **** struct multio *mfds[10]; char *text; int save[10]; ! int fil, is_cursh, type, i; int nullexec = 0, assign = 0, forked = 0; int is_shfunc = 0, is_builtin = 0; /* Various flags to the command. */ --- 1086,1092 ---- struct multio *mfds[10]; char *text; int save[10]; ! int fil, dfil, is_cursh, type, i; int nullexec = 0, assign = 0, forked = 0; int is_shfunc = 0, is_builtin = 0; /* Various flags to the command. */ *************** *** 1431,1440 **** } addfd(forked, save, mfds, fn->fd1, fn->fd2, 1); } else { ! if (!(fn->type == HERESTR || fn->type == CLOSE || fn->type == ! MERGE || fn->type == MERGEOUT)) ! if (xpandredir(fn, cmd->redir)) ! continue; if (errflag) { fixfds(save); execerr(); --- 1431,1440 ---- } addfd(forked, save, mfds, fn->fd1, fn->fd2, 1); } else { ! if (!(fn->type == HERESTR || fn->type == CLOSE || ! fn->type == MERGEIN || fn->type == MERGEOUT) && ! xpandredir(fn, cmd->redir)) ! continue; if (errflag) { fixfds(save); execerr(); *************** *** 1470,1476 **** closemn(mfds, fn->fd1); zclose(fn->fd1); break; ! case MERGE: case MERGEOUT: if (fn->fd2 == FD_COPROC) fn->fd2 = (fn->type == MERGEOUT) ? coprocout : coprocin; --- 1470,1476 ---- closemn(mfds, fn->fd1); zclose(fn->fd1); break; ! case MERGEIN: case MERGEOUT: if (fn->fd2 == FD_COPROC) fn->fd2 = (fn->type == MERGEOUT) ? coprocout : coprocin; *************** *** 1488,1507 **** addfd(forked, save, mfds, fn->fd1, fil, fn->type == MERGEOUT); break; default: ! if (fn->type >= APP) fil = open(unmeta(fn->name), ! (isset(NOCLOBBER) && !(fn->type & 1)) ? O_WRONLY | O_APPEND : O_WRONLY | O_APPEND | O_CREAT, 0666); else fil = open(unmeta(fn->name), dontclobber(fn) ? O_WRONLY | O_CREAT | O_EXCL : O_WRONLY | O_CREAT | O_TRUNC, 0666); ! if (fil == -1) { fixfds(save); if (errno != EINTR) zerr("%e: %s", fn->name, errno); execerr(); } addfd(forked, save, mfds, fn->fd1, fil, 1); break; } } --- 1488,1515 ---- addfd(forked, save, mfds, fn->fd1, fil, fn->type == MERGEOUT); break; default: ! if (IS_APPEND_REDIR(fn->type)) fil = open(unmeta(fn->name), ! (isset(NOCLOBBER) && !IS_CLOBBER_REDIR(fn->type)) ? O_WRONLY | O_APPEND : O_WRONLY | O_APPEND | O_CREAT, 0666); else fil = open(unmeta(fn->name), dontclobber(fn) ? O_WRONLY | O_CREAT | O_EXCL : O_WRONLY | O_CREAT | O_TRUNC, 0666); ! if(fil != -1 && IS_ERROR_REDIR(fn->type)) ! dfil = dup(fil); ! else ! dfil = 0; ! if (fil == -1 || dfil == -1) { ! if(fil != -1) ! close(fil); fixfds(save); if (errno != EINTR) zerr("%e: %s", fn->name, errno); execerr(); } addfd(forked, save, mfds, fn->fd1, fil, 1); + if(IS_ERROR_REDIR(fn->type)) + addfd(forked, save, mfds, 2, dfil, 1); break; } } Index: globals.h *** globals.h 1996/05/30 14:18:47 1.6 --- globals.h 1996/06/05 12:44:24 *************** *** 50,58 **** READ, HEREDOC, HEREDOCDASH, ! MERGE, ! MERGEOUT, ! MERGEOUTNOW, ERRAPP, ERRAPPNOW, HERESTR, --- 50,58 ---- READ, HEREDOC, HEREDOCDASH, ! MERGEIN, ! ERRWRITE, ! ERRWRITENOW, ERRAPP, ERRAPPNOW, HERESTR, Index: parse.c *** parse.c 1996/06/05 11:17:58 1.3 --- parse.c 1996/06/05 13:05:14 *************** *** 1114,1120 **** { char *toks; struct redir *fn = (struct redir *)allocnode(N_REDIR); - int mergerror = 0; int oldcmdpos, oldnc; int nohist; --- 1114,1119 ---- *************** *** 1142,1150 **** if (fn->fd1 == -1) fn->fd1 = IS_READFD(fn->type) ? 0 : 1; - /* > >(...) or < <(...) */ - if ((*toks == Inang || *toks == Outang) && toks[1] == Inpar) { if ((fn->type & ~1) == WRITE && *toks == Outang) fn->type = OUTPIPE; else if (fn->type == READ && *toks == Inang) --- 1141,1149 ---- if (fn->fd1 == -1) fn->fd1 = IS_READFD(fn->type) ? 0 : 1; if ((*toks == Inang || *toks == Outang) && toks[1] == Inpar) { + /* > >(...) or < <(...) */ + if ((fn->type & ~1) == WRITE && *toks == Outang) fn->type = OUTPIPE; else if (fn->type == READ && *toks == Inang) *************** *** 1153,1165 **** YYERRORV; fn->name = toks; - /* <<[-] name */ - } else if (fn->type == HEREDOC || fn->type == HEREDOCDASH) { char tbuf[256], *tlin = NULL; int tsiz = 0, redirl; ! /* Save the rest of the current line for later tokenization */ if (!isnewlin) { while (ingets(tbuf, 256) != NULL) { redirl = strlen(tbuf); --- 1152,1163 ---- YYERRORV; fn->name = toks; } else if (fn->type == HEREDOC || fn->type == HEREDOCDASH) { + /* <<[-] name */ char tbuf[256], *tlin = NULL; int tsiz = 0, redirl; ! /* Save the rest of the current line for later tokenization */ if (!isnewlin) { while (ingets(tbuf, 256) != NULL) { redirl = strlen(tbuf); *************** *** 1176,1220 **** } } cmdpush(fn->type == HEREDOC ? CS_HEREDOC : CS_HEREDOCD); ! /* Now grab the document */ fn->name = gethere(toks, fn->type); fn->type = HERESTR; cmdpop(); ! /* Put back the saved line to resume tokenizing */ if (tsiz > 0) { inputsetline(tlin, INP_FREE); } - /* >& name or >>& name */ } else if (IS_ERROR_REDIR(fn->type) && getfdstr(toks) == FD_WORD) { ! mergerror = 1; fn->name = toks; - fn->type = UN_ERROR_REDIR(fn->type); ! /* >>& and >>&! are only valid with a name after them */ ! } else if (fn->type == ERRAPP || fn->type == ERRAPPNOW) { YYERRORV; - /* >& # */ - - } else if (fn->type == MERGE || fn->type == MERGEOUT) { - fn->fd2 = getfdstr(toks); - if (fn->fd2 == FD_CLOSE) - fn->type = CLOSE; - else if (fn->fd2 == FD_WORD) - fn->fd2 = (fn->type == MERGEOUT) ? 1 : 0; } else fn->name = toks; addlinknode(l, fn); - if (mergerror) { - struct redir *fe = (struct redir *)allocnode(N_REDIR); - - fe->fd1 = 2; - fe->fd2 = fn->fd1; - fe->type = MERGEOUT; - addlinknode(l, fe); - } } /* --- 1174,1209 ---- } } cmdpush(fn->type == HEREDOC ? CS_HEREDOC : CS_HEREDOCD); ! /* Now grab the document */ fn->name = gethere(toks, fn->type); fn->type = HERESTR; cmdpop(); ! /* Put back the saved line to resume tokenizing */ if (tsiz > 0) { inputsetline(tlin, INP_FREE); } } else if (IS_ERROR_REDIR(fn->type) && getfdstr(toks) == FD_WORD) { ! /* >& name or >>& name */ fn->name = toks; ! } else if (fn->type == MERGEIN || fn->type == ERRWRITE) { ! /* >& # or <& # */ ! fn->fd2 = getfdstr(toks); ! if (fn->fd2 == FD_WORD) ! YYERRORV ! else if (fn->fd2 == FD_CLOSE) ! fn->type = CLOSE; ! else if(fn->type == ERRWRITE) ! fn->type = MERGEOUT; ! } else if (IS_ERROR_REDIR(fn->type)) { ! /* >>& and >>&! are only valid with a name after them */ YYERRORV; } else fn->name = toks; addlinknode(l, fn); } /* Index: text.c *** text.c 1996/06/05 10:45:47 1.1.1.6 --- text.c 1996/06/05 12:38:26 *************** *** 450,456 **** static char *fstr[] = { ">", ">|", ">>", ">>|", ">&", ">&|", ">>&", ">>&|", "<", "<<", ! "<<-", "<<<", "<&", ">&-", "..", ".." }; taddchr(' '); --- 450,456 ---- static char *fstr[] = { ">", ">|", ">>", ">>|", ">&", ">&|", ">>&", ">>&|", "<", "<<", ! "<<-", "<<<", "<&", ">&", ">&-", "..", ".." }; taddchr(' '); *************** *** 462,467 **** --- 462,471 ---- case WRITENOW: case APP: case APPNOW: + case ERRWRITE: + case ERRWRITENOW: + case ERRAPP: + case ERRAPPNOW: case READ: case HERESTR: if (f->fd1 != ((f->type == READ) ? 0 : 1)) *************** *** 471,477 **** taddstr(f->name); taddchr(' '); break; ! case MERGE: case MERGEOUT: if (f->fd1 != ((f->type == MERGEOUT) ? 1 : 0)) taddchr('0' + f->fd1); --- 475,481 ---- taddstr(f->name); taddchr(' '); break; ! case MERGEIN: case MERGEOUT: if (f->fd1 != ((f->type == MERGEOUT) ? 1 : 0)) taddchr('0' + f->fd1); Index: zsh.h *** zsh.h 1996/06/05 11:18:03 1.8 --- zsh.h 1996/06/05 12:56:32 *************** *** 150,177 **** #define UNTIL 58 /* until */ #define WHILE 59 /* while */ ! #define WRITE 0 ! #define WRITENOW 1 ! #define APP 2 ! #define APPNOW 3 ! #define MERGEOUT 4 ! #define MERGEOUTNOW 5 ! #define ERRAPP 6 ! #define ERRAPPNOW 7 ! #define READ 8 ! #define HEREDOC 9 ! #define HEREDOCDASH 10 ! #define HERESTR 11 ! #define MERGE 12 ! #define CLOSE 13 ! #define INPIPE 14 ! #define OUTPIPE 15 ! #define NONE 16 ! #define IS_READFD(X) ((X)>=READ && (X)<=MERGE) ! #define IS_REDIROP(X) ((X)>=OUTANG && (X)<=TRINANG) ! #define IS_ERROR_REDIR(X) ((X)>=MERGEOUT && (X)<=ERRAPPNOW) ! #define UN_ERROR_REDIR(X) ((X)-MERGEOUT+WRITE) #define FD_WORD -1 #define FD_COPROC -2 --- 150,180 ---- #define UNTIL 58 /* until */ #define WHILE 59 /* while */ ! enum { ! WRITE, ! WRITENOW, ! APP, ! APPNOW, ! ERRWRITE, ! ERRWRITENOW, ! ERRAPP, ! ERRAPPNOW, ! READ, ! HEREDOC, ! HEREDOCDASH, ! HERESTR, ! MERGEIN, ! MERGEOUT, ! CLOSE, ! INPIPE, ! OUTPIPE ! }; ! #define IS_APPEND_REDIR(X) ((X)>=WRITE && (X)<=ERRAPPNOW && ((X) & 2)) ! #define IS_CLOBBER_REDIR(X) ((X)>=WRITE && (X)<=ERRAPPNOW && ((X) & 1)) ! #define IS_ERROR_REDIR(X) ((X)>=ERRWRITE && (X)<=ERRAPPNOW) ! #define IS_READFD(X) ((X)>=READ && (X)<=MERGEIN) ! #define IS_REDIROP(X) ((X)>=OUTANG && (X)<=TRINANG) #define FD_WORD -1 #define FD_COPROC -2 -----BEGIN PGP SIGNATURE----- Version: 2.6.2 iQCVAwUBMbWPBnD/+HJTpU/hAQHI4gP+LOyLwqoHi4MgATAon4wUozVGugmDN4nK k8ESP+gXhKKpL9XkMjEExLOwamfxsTFCmNdf3akVm74Cf+RIeQy7sRSPe2ZYyPub MOkcKjN7alXunfJHaZN10poqg5eGC00y9XcBwz2VLebxOOA3zP6o8tb4ZroGT0EC Z9mrI0Ncsfk= =xESZ -----END PGP SIGNATURE-----