Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: Redirection bug



-----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-----




Messages sorted by: Reverse Date, Date, Thread, Author