[comp.bugs.sys5] Bug in S5R4 handling of VLNEXT when ICANON not set

guy@auspex.auspex.com (Guy Harris) (04/09/91)

The SunOS 4.x "ldterm" streams module - and, according to a person I
asked at Sun, the S5R4 version, derived from the SunOS version - doesn't
correctly handle the "literal-next" character when ICANON isn't set; it
does cause the character typed after it to be treated as ordinary input
(rather than, say, an interrupt/quit/suspend character), but the VLNEXT
character itself appears as input.

The fix is to the routine "do_noncanon_input()", or the moral equivalent
in the S5R4 "ldterm"; here's a patch to the SunOS 4.1 version - S5R4's
mileage may differ.  The bug has been reported to AT&T, but if you want
to fix it before they do....

*** /home/unix_src/sunos/sunos.4.1/src/sys/os/tty_ldterm.c	Sun Nov 25 13:37:31 1990
--- ./tty_ldterm.c	Wed Mar 27 18:21:14 1991
***************
*** 1860,1913 ****
  {
  	queue_t *wrq = WR(q);
  	int ebsize;
! 	register mblk_t *bp, *prevbp;
! 	mblk_t *savebp;
! 	register unsigned char *rptr;
  
  	for (bp = mp, prevbp = NULL; bp != NULL;
! 	    prevbp = bp, bp = bp->b_cont) {
! 		while (bp->b_rptr == bp->b_wptr) {
  			/*
! 			 * Zero-length block.  Throw it away.
  			 */
! 			if (prevbp == NULL)
! 				mp = bp->b_cont;
! 			else
! 				prevbp->b_cont = bp->b_cont;
! 			savebp = bp;
! 			bp = bp->b_cont;
! 			savebp->b_cont = NULL;
! 			freeb(savebp);
! 			if (bp == NULL)
! 				return;	/* entire message gone */
! 		}
! 		if (tp->t_modes.c_lflag & (ECHO|ECHONL)) {
! 			/*
! 			 * Echo the data in this message.
! 			 */
! 			if (tp->t_modes.c_lflag & ECHO) {
! 				ebsize = bp->b_wptr - bp->b_rptr;
! 				if (ebsize > EBSIZE)
! 					ebsize = EBSIZE;
! 				rptr = bp->b_rptr;
! 				while (rptr < bp->b_wptr)
! 					(void) echo_char(*rptr++, wrq,
! 					    ebsize, tp);
! 			} else {
  				/*
! 				 * Echo NL, even though ECHO is not
! 				 * set.
  				 */
! 				rptr = bp->b_rptr;
! 				while (rptr < bp->b_wptr) {
! 					if (*rptr++ == '\n')
  						output_echo_char('\n',
  						    wrq, 1, tp);
  				}
  			}
  		}
  	}
! 	putnext(q, mp);
  
  	/*
  	 * Send whatever we echoed downstream.
--- 1860,1956 ----
  {
  	queue_t *wrq = WR(q);
  	int ebsize;
! 	register mblk_t *bp, *prevbp, *nextbp;
! 	register unsigned char *rptr, *wptr;
! 	register unsigned char c;
  
  	for (bp = mp, prevbp = NULL; bp != NULL;
! 	    prevbp = bp, bp = nextbp) {
! 		if (tp->t_modes.c_lflag & (ECHO|ECHONL|IEXTEN)) {
  			/*
! 			 * Either we must echo the characters, or we must
! 			 * echo NL, or we must check for VLNEXT.
! 			 * Process characters one at a time.
  			 */
! 			ebsize = bp->b_wptr - bp->b_rptr;
! 			if (ebsize > EBSIZE)
! 				ebsize = EBSIZE;
! 			rptr = bp->b_rptr;
! 			wptr = bp->b_rptr;
! 			while (rptr < bp->b_wptr) {
! 				c = *rptr++;
  				/*
! 				 * If this character is the literal next
! 				 * character, echo it as '^' and backspace
! 				 * over it if echoing is enabled, indicate
! 				 * that the next character is to be treated
! 				 * literally, and remove the LNEXT from the
! 				 * input stream.
! 				 *
! 				 * If the *previous* character was the
! 				 * literal next character, don't check
! 				 * whether this is a literal next or not.
  				 */
! 				if ((tp->t_modes.c_lflag & IEXTEN)
! 				    && !(tp->t_state & TS_SLNCH)
! 				    && c != VDISABLE
! 				    && c == tp->t_modes.c_cc[VLNEXT]) {
! 					if (tp->t_modes.c_lflag & ECHO)
! 						output_echo_string(
! 						    (unsigned char *)"^\b",
! 						    2, wrq, ebsize, tp);
! 					tp->t_state |= TS_SLNCH;
! 					continue;	/* and ignore it */
! 				}
! 
! 				/*
! 				 * Not a "literal next" character, so
! 				 * it should show up as input.
! 				 * If it was literal-nexted, turn off the
! 				 * literal-next flag.
! 				 */
! 				tp->t_state &= ~TS_SLNCH;
! 				*wptr++ = c;
! 				if (tp->t_modes.c_lflag & ECHO) {
! 					/*
! 					 * Echo the character.
! 					 */
! 					(void) echo_char(c, wrq, ebsize, tp);
! 				} else if (tp->t_modes.c_lflag & ECHONL) {
! 					/*
! 					 * Echo NL, even though ECHO is not
! 					 * set.
! 					 */
! 					if (c == '\n')
  						output_echo_char('\n',
  						    wrq, 1, tp);
  				}
  			}
+ 			bp->b_wptr = wptr;
+ 		} else {
+ 			/*
+ 			 * If there are any characters in this buffer,
+ 			 * and the first of them was literal-nexted,
+ 			 * turn off the literal-next flag.
+ 			 */
+ 			if (bp->b_rptr != bp->b_wptr)
+ 				tp->t_state &= ~TS_SLNCH;
  		}
+ 		nextbp = bp->b_cont;	/* next block */
+ 		if (bp->b_rptr == bp->b_wptr) {
+ 			/*
+ 			 * Zero-length block.  Throw it away.
+ 			 */
+ 			if (prevbp == NULL)
+ 				mp = nextbp;
+ 			else
+ 				prevbp->b_cont = nextbp;
+ 			bp->b_cont = NULL;
+ 			freeb(bp);
+ 		}
  	}
! 	if (mp != NULL)
! 		putnext(q, mp);
  
  	/*
  	 * Send whatever we echoed downstream.