Subject: bin/1502: Bourne Shell does not honor -v flag in scripts
To: None <gnats-bugs@gnats.netbsd.org>
From: Christos Zoulas <christos@deshaw.com>
List: netbsd-bugs
Date: 09/24/1995 23:24:05
>Number:         1502
>Category:       bin
>Synopsis:       Bourne Shell does not honor -v flag in scripts
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    bin-bug-people (Utility Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon Sep 25 11:35:01 1995
>Last-Modified:
>Originator:     Christos Zoulas
>Organization:
Chaos
>Release:        Sun Sep 24 19:15:40 EDT 1995
>Environment:
System: NetBSD ramoth 1.0A NetBSD 1.0A (ZEOS_AIC) #53: Wed Jul 19 14:26:39 EDT 1995 christos@ramoth:/tmp_mnt/src/NetBSD/NetBSD-current/src/sys/arch/i386/compile/ZEOS_AIC i386


>Description:
	/bin/sh does not honor the -v [verbose flag] in scripts because
	it reads as much as it can at a time instead of a line at a time.
	There is a comment in the source that indicates that the author
	knows about the bug...
>How-To-Repeat:
	Run the following script
	# cat > foo.sh
	set -v
	echo foo
	set -o
	^D
	sh foo.sh
>Fix:
	Keep an extra pointer to the end of the current line, and process
	only the part of the buffer up to that pointer.

*** input.c.dist	Fri Jun  9 05:53:04 1995
--- input.c	Sun Sep 24 19:22:43 1995
***************
*** 74,79 ****
--- 74,80 ----
  	struct strpush *prev;	/* preceding string on stack */
  	char *prevstring;
  	int prevnleft;
+ 	int prevlleft;
  	struct alias *ap;	/* if push was associated with an alias */
  };
  
***************
*** 87,93 ****
  	struct parsefile *prev;	/* preceding file on stack */
  	int linno;		/* current line */
  	int fd;			/* file descriptor (or -1 if string) */
! 	int nleft;		/* number of chars left in buffer */
  	char *nextc;		/* next char in buffer */
  	char *buf;		/* input buffer */
  	struct strpush *strpush; /* for pushing strings at this level */
--- 88,95 ----
  	struct parsefile *prev;	/* preceding file on stack */
  	int linno;		/* current line */
  	int fd;			/* file descriptor (or -1 if string) */
! 	int nleft;		/* number of chars left in this line */
! 	int lleft;		/* number of chars left in this buffer */
  	char *nextc;		/* next char in buffer */
  	char *buf;		/* input buffer */
  	struct strpush *strpush; /* for pushing strings at this level */
***************
*** 97,102 ****
--- 99,105 ----
  
  int plinno = 1;			/* input line number */
  MKINIT int parsenleft;		/* copy of parsefile->nleft */
+ MKINIT int parselleft;		/* copy of parsefile->nleft */
  char *parsenextc;		/* copy of parsefile->nextc */
  MKINIT struct parsefile basepf;	/* top level input file */
  char basebuf[BUFSIZ];		/* buffer for top level input file */
***************
*** 109,114 ****
--- 112,118 ----
  EditLine *el;			/* cookie for editline package */
  
  STATIC void pushfile __P((void));
+ static int pread __P((void));
  
  #ifdef mkinit
  INCLUDE "input.h"
***************
*** 122,128 ****
  
  RESET {
  	if (exception != EXSHELLPROC)
! 		parsenleft = 0;            /* clear input buffer */
  	popallfiles();
  }
  
--- 126,132 ----
  
  RESET {
  	if (exception != EXSHELLPROC)
! 		parselleft = parsenleft = 0;	/* clear input buffer */
  	popallfiles();
  }
  
***************
*** 168,224 ****
   */
  
  int
! pgetc() {
  	return pgetc_macro();
  }
  
  
! /*
!  * Refill the input buffer and return the next input character:
!  *
!  * 1) If a string was pushed back on the input, pop it;
!  * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
!  *    from a string so we can't refill the buffer, return EOF.
!  * 3) Call read to read in the characters.
!  * 4) Delete all nul characters from the buffer.
!  */
! 
! int
! preadbuffer() {
! 	register char *p, *q;
! 	register int i;
! 	register int something;
! 	extern EditLine *el;
  
- 	if (parsefile->strpush) {
- 		popstring();
- 		if (--parsenleft >= 0)
- 			return (*parsenextc++);
- 	}
- 	if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
- 		return PEOF;
- 	flushout(&output);
- 	flushout(&errout);
  retry:
- 	p = parsenextc = parsefile->buf;
  	if (parsefile->fd == 0 && el) {
  		const char *rl_cp;
  		int len;
  
! 		rl_cp = el_gets(el, &len);
! 		if (rl_cp == NULL) {
! 			i = 0;
! 			goto eof;
  		}
- 		strcpy(p, rl_cp);  /* XXX - BUFSIZE should redesign so not necessary */
- 		i = len;
- 
  	} else {
! 		i = read(parsefile->fd, p, BUFSIZ - 1);
  	}
! eof:
! 	if (i <= 0) {
!                 if (i < 0) {
                          if (errno == EINTR)
                                  goto retry;
                          if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
--- 172,207 ----
   */
  
  int
! pgetc()
! {
  	return pgetc_macro();
  }
  
  
! static int
! pread() 
! {
! 	int nr;
! 	parsenextc = parsefile->buf;
  
  retry:
  	if (parsefile->fd == 0 && el) {
  		const char *rl_cp;
  		int len;
  
! 		rl_cp = el_gets(el, &parsenleft);
! 		if (rl_cp == NULL)
! 			nr = 0;
! 		else {
! 			/* XXX - BUFSIZE should redesign so not necessary */
! 			(void) strcpy(parsenextc, rl_cp);
  		}
  	} else {
! 		nr = read(parsefile->fd, parsenextc, BUFSIZ - 1);
  	}
! 
! 	if (nr <= 0) {
!                 if (nr < 0) {
                          if (errno == EINTR)
                                  goto retry;
                          if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
***************
*** 232,288 ****
                                  }
                          }
                  }
!                 parsenleft = EOF_NLEFT;
!                 return PEOF;
  	}
! 	parsenleft = i - 1;	/* we're returning one char in this call */
  
  	/* delete nul characters */
  	something = 0;
! 	for (;;) {
! 		if (*p == '\0')
  			break;
! 		if (*p != ' ' && *p != '\t' && *p != '\n')
  			something = 1;
! 		p++;
! 		if (--i <= 0) {
! 			*p = '\0';
! 			goto done;		/* no nul characters */
  		}
  	}
! 	/*
! 	 * remove nuls
! 	 */
! 	q = p++;
! 	while (--i > 0) {
! 		if (*p != '\0')
! 			*q++ = *p;
! 		p++;
! 	}
  	*q = '\0';
- 	if (q == parsefile->buf)
- 		goto retry;			/* buffer contained nothing but nuls */
- 	parsenleft = q - parsefile->buf - 1;
  
- done:
  	if (parsefile->fd == 0 && hist && something) {
  		INTOFF;
! 		history(hist, whichprompt == 1 ? H_ENTER : H_ADD, 
! 			   parsefile->buf);
  		INTON;
  	}
  	if (vflag) {
! 		/*
! 		 * This isn't right.  Most shells coordinate it with
! 		 * reading a line at a time.  I honestly don't know if its
! 		 * worth it.
! 		 */
! 		i = parsenleft + 1;
! 		p = parsefile->buf;
! 		for (; i--; p++) 
! 			out2c(*p)
  		flushout(out2);
  	}
  	return *parsenextc++;
  }
  
--- 215,313 ----
                                  }
                          }
                  }
!                 nr = -1;
  	}
! 	return nr;
! }
! 
! /*
!  * Refill the input buffer and return the next input character:
!  *
!  * 1) If a string was pushed back on the input, pop it;
!  * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
!  *    from a string so we can't refill the buffer, return EOF.
!  * 3) If the is more stuff in this buffer, use it else call read to fill it.
!  * 4) Process input up to the next newline, deleting nul characters.
!  */
! 
! int
! preadbuffer()
! {
! 	char *p, *q;
! 	int more;
! 	int something;
! 	extern EditLine *el;
! 	char savec;
! 
! 	if (parsefile->strpush) {
! 		popstring();
! 		if (--parsenleft >= 0)
! 			return (*parsenextc++);
! 	}
! 	if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
! 		return PEOF;
! 	flushout(&output);
! 	flushout(&errout);
! 
! again:
! 	if (parselleft <= 0) {
! 		if ((parselleft = pread()) == -1) {
! 			parselleft = parsenleft = EOF_NLEFT;
! 			return PEOF;
! 		}
! 	}
! 
! 	q = p = parsenextc;
  
  	/* delete nul characters */
  	something = 0;
! 	for (more = 1; more;) {
! 		switch (*p) {
! 		case '\0':
! 			p++;	/* Skip NULL */
! 			goto check;
! 		
! 		case '\t':
! 		case ' ':
  			break;
! 
! 		case '\n':
! 			parsenleft = q - parsenextc;
! 			more = 0; /* Stop processing here */
! 			break;
! 
! 		default:
  			something = 1;
! 			break;
! 		}
! 
! 		*q++ = *p++;
! check:
! 		if (--parselleft <= 0) {
! 			parsenleft = q - parsenextc - 1;
! 			if (parsenleft < 0)
! 				goto again;
! 			*q = '\0';
! 			more = 0;
  		}
  	}
! 
! 	savec = *q;
  	*q = '\0';
  
  	if (parsefile->fd == 0 && hist && something) {
  		INTOFF;
! 		history(hist, whichprompt == 1 ? H_ENTER : H_ADD, parsenextc);
  		INTON;
  	}
+ 
  	if (vflag) {
! 		out2str(parsenextc);
  		flushout(out2);
  	}
+ 
+ 	*q = savec;
+ 
  	return *parsenextc++;
  }
  
***************
*** 319,324 ****
--- 344,350 ----
  		sp = parsefile->strpush = &(parsefile->basestrpush);
  	sp->prevstring = parsenextc;
  	sp->prevnleft = parsenleft;
+ 	sp->prevlleft = parselleft;
  	sp->ap = (struct alias *)ap;
  	if (ap)
  		((struct alias *)ap)->flag |= ALIASINUSE;
***************
*** 335,340 ****
--- 361,367 ----
  	INTOFF;
  	parsenextc = sp->prevstring;
  	parsenleft = sp->prevnleft;
+ 	parselleft = sp->prevlleft;
  /*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
  	if (sp->ap)
  		sp->ap->flag &= ~ALIASINUSE;
***************
*** 408,414 ****
  	if (push)
  		pushfile();
  	parsenextc = string;
! 	parsenleft = strlen(string);
  	parsefile->buf = NULL;
  	plinno = 1;
  	INTON;
--- 435,441 ----
  	if (push)
  		pushfile();
  	parsenextc = string;
! 	parselleft = parsenleft = strlen(string);
  	parsefile->buf = NULL;
  	plinno = 1;
  	INTON;
***************
*** 426,431 ****
--- 453,459 ----
  	struct parsefile *pf;
  
  	parsefile->nleft = parsenleft;
+ 	parsefile->lleft = parselleft;
  	parsefile->nextc = parsenextc;
  	parsefile->linno = plinno;
  	pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
***************
*** 451,456 ****
--- 479,485 ----
  	parsefile = pf->prev;
  	ckfree(pf);
  	parsenleft = parsefile->nleft;
+ 	parselleft = parsefile->lleft;
  	parsenextc = parsefile->nextc;
  	plinno = parsefile->linno;
  	INTON;
>Audit-Trail:
>Unformatted: