From peter@rulingia.com  Tue Jul 10 21:25:04 2012
Return-Path: <peter@rulingia.com>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id EAD4E1065672
	for <FreeBSD-gnats-submit@freebsd.org>; Tue, 10 Jul 2012 21:25:03 +0000 (UTC)
	(envelope-from peter@rulingia.com)
Received: from vps.rulingia.com (host-122-100-2-194.octopus.com.au [122.100.2.194])
	by mx1.freebsd.org (Postfix) with ESMTP id 7BD2A8FC12
	for <FreeBSD-gnats-submit@freebsd.org>; Tue, 10 Jul 2012 21:25:02 +0000 (UTC)
Received: from server.rulingia.com (c220-239-248-69.belrs5.nsw.optusnet.com.au [220.239.248.69])
	by vps.rulingia.com (8.14.5/8.14.5) with ESMTP id q6ALOtlI063340
	(version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK)
	for <FreeBSD-gnats-submit@freebsd.org>; Wed, 11 Jul 2012 07:24:55 +1000 (EST)
	(envelope-from peter@rulingia.com)
Received: from server.rulingia.com (localhost.rulingia.com [127.0.0.1])
	by server.rulingia.com (8.14.5/8.14.5) with ESMTP id q6ALOm5H087587
	(version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO);
	Wed, 11 Jul 2012 07:24:48 +1000 (EST)
	(envelope-from peter@server.rulingia.com)
Received: (from peter@localhost)
	by server.rulingia.com (8.14.5/8.14.5/Submit) id q6ALOmnF087586;
	Wed, 11 Jul 2012 07:24:48 +1000 (EST)
	(envelope-from peter)
Message-Id: <201207102124.q6ALOmnF087586@server.rulingia.com>
Date: Wed, 11 Jul 2012 07:24:48 +1000 (EST)
From: Peter Jeremy <peter@rulingia.com>
Reply-To: Peter Jeremy <peter@rulingia.com>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: Resizing causes /bin/sh to repeat edit operations
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         169773
>Category:       bin
>Synopsis:       [libedit] Resizing causes /bin/sh to repeat edit operations
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Jul 10 21:30:14 UTC 2012
>Closed-Date:    
>Last-Modified:  Wed Jan 15 22:40:00 UTC 2014
>Originator:     Peter Jeremy
>Release:        FreeBSD 10.0-CURRENT sparc64
>Organization:
n/a
>Environment:
System: FreeBSD sb1500.rulingia.com 10.0-CURRENT FreeBSD 10.0-CURRENT #6 r238247M: Tue Jul 10 18:28:55 EST 2012 root@sb1500.rulingia.com:/usr/obj/usr/src/sys/sb1500 sparc64
System: FreeBSD server.rulingia.com 8.3-STABLE FreeBSD 8.3-STABLE #17 r237444M: Sat Jun 23 18:22:46 EST 2012 root@server.rulingia.com:/var/obj/usr/src/sys/server amd64

>Description:
	Given an account with /bin/sh as the login shell, if you type
	some input, ending with an editing operation (eg backspace) and
	then resize the window multiple times without further input,
	the edit operation will be repeated on every second resize.

	This bug was identified during testing of r238173 but it
	predates the libedit changes in r237448.

>How-To-Repeat:
	Login to an account with /bin/sh as the login shell on a FreeBSD host.
	Type some text, ending with a backspace (deleting the last character
	entered).
	Resize the window (or send SIGWINCH to the shell) multiple times
	without entering additional input.
	The last character will be deleted (stortening the line by one
	character) on every second resize.

>Fix:
	Unknown


>Release-Note:
>Audit-Trail:

From: Mark Johnston <markjdb@gmail.com>
To: bug-followup@FreeBSD.org, peter@rulingia.com
Cc:  
Subject: Re: bin/169773: sh(1): Resizing causes /bin/sh to repeat edit
 operations
Date: Thu, 6 Sep 2012 15:03:36 -0400

 --HlL+5n6rz5pIUxbD
 Content-Type: text/plain; charset=us-ascii
 Content-Disposition: inline
 
 The root of the problem is some questionable error-handling in libedit.
 
 First, read_char() doesn't do the right thing if its call to
 read(2) is interrupted by a signal (SIGWINCH in this case). Basically,
 it retries the read once, and returns an error if the second read failed
 (which is why it takes two SIGWINCHs to trigger the delete). IMHO,
 read_char() should always retry the read(2) if its interrupted by a
 signal. The attached patched fixes this.
 
 The second problem has to do with how read(2) errors are handled higher
 up in libedit's stack. We have read_getcmd(), which calls el_getc(),
 which calls the read_char() function mentioned above. read_char()
 returns -1 on error, and then -1 gets returned to read_getcmd() by
 el_getc(). el_getc() returns OKCMD on success, and OKCMD is defined to
 be... -1. Thus read_getcmd() has no idea that an error occured and ends
 up reusing a local variable (cmdnum) which causes the second backspace.
 
 I think the fix here is to define OKCMD to be 1 (the value returned by
 read_char() on success). For the purpose of fixing the bug reported
 here, the check for EINTR is enough, but this second bug should probably
 be fixed too. I'm happy to test alternative fixes. =)
 
 Thanks,
 -Mark
 
 --HlL+5n6rz5pIUxbD
 Content-Type: text/x-diff; charset=us-ascii
 Content-Disposition: attachment; filename="libedit_error_handling.patch"
 
 diff --git a/lib/libedit/read.c b/lib/libedit/read.c
 index 7d7f54b..013f93e 100644
 --- a/lib/libedit/read.c
 +++ b/lib/libedit/read.c
 @@ -49,7 +49,7 @@ __FBSDID("$FreeBSD$");
  #include <stdlib.h>
  #include "el.h"
  
 -#define	OKCMD	-1
 +#define	OKCMD	1
  
  private int	read__fixio(int, int);
  private int	read_preread(EditLine *);
 @@ -169,9 +169,6 @@ read__fixio(int fd __unused, int e)
  #endif /* TRY_AGAIN */
  		return (e ? 0 : -1);
  
 -	case EINTR:
 -		return (0);
 -
  	default:
  		return (-1);
  	}
 @@ -295,9 +292,11 @@ read_char(EditLine *el, char *cp)
   again:
  	el->el_signal->sig_no = 0;
  	while ((num_read = read(el->el_infd, cp, 1)) == -1) {
 -		if (el->el_signal->sig_no == SIGCONT) {
 -			sig_set(el);
 -			el_set(el, EL_REFRESH);
 +		if (errno == EINTR) {
 +			if (el->el_signal->sig_no == SIGCONT) {
 +				sig_set(el);
 +				el_set(el, EL_REFRESH);
 +			}
  			goto again;
  		}
  		if (!tried && read__fixio(el->el_infd, errno) == 0)
 
 --HlL+5n6rz5pIUxbD--
Responsible-Changed-From-To: freebsd-bugs->jilles 
Responsible-Changed-By: eadler 
Responsible-Changed-When: Thu Sep 6 19:26:57 UTC 2012 
Responsible-Changed-Why:  
over to maintainer 

http://www.freebsd.org/cgi/query-pr.cgi?pr=169773 

From: Mark Linimon <linimon@lonesome.com>
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/169773: sh(1): Resizing causes /bin/sh to repeat edit
 operations
Date: Sat, 8 Sep 2012 12:23:22 -0500

 ----- Forwarded message from Steffen Daode Nurpmeso <sdaoden@gmail.com> -----
 
 Date: Sat, 08 Sep 2012 17:44:56 +0200
 From: Steffen Daode Nurpmeso <sdaoden@gmail.com>
 To: freebsd-bugs@FreeBSD.org
 Subject: Re: bin/169773: sh(1): Resizing causes /bin/sh to repeat edit
 	operations
 
  |Synopsis: sh(1): Resizing causes /bin/sh to repeat edit operations
  |
  |http://www.freebsd.org/cgi/query-pr.cgi?pr=169773
 
 Oh, what a mess :)
 I agree with Mark that the handling of OKCMD is simply wrong, but
 it turned out to be wrong to simply use a different value for it.
 Also the passing through of errno is incomplete in there.  (The
 documented interface doesn't state anything about errno or useful
 error handling at all, and however!)
 
 It's a rather quick first diff for editline(3), i have no more
 time but since it took almost three hours to come that far someone
 else may build on top of it (or simply try it or ... wait for a
 second).
 
 I *think* it effectively results in editline(3) behaving the way
 it is supposed to work (retrying once after a whatever signal,
 then failing for a second one).  Since el_gets() now fails (as it
 is supposed to), sh(1) will behave wrong in that the current line
 gets "thrown away".  (I guess the supposed way is to temporarily
 adjust PROMPT if one wants to continue what is on the line yet?
 But i still have no idea of editline(3) :->)
 
 It would be better if editline(3) could be configured to simply
 restart upon EINTR, or to fixate that behaviour (for FreeBSD)?
 I don't think it is acceptable to loose a line of user content due
 to a simple resize?
 So long and ciao,
 
 --steffen
 
 diff --git a/lib/libedit/read.c b/lib/libedit/read.c
 index 7d7f54b..5b51577 100644
 --- a/lib/libedit/read.c
 +++ b/lib/libedit/read.c
 @@ -238,8 +238,7 @@ read_getcmd(EditLine *el, el_action_t *cmdnum, char *ch)
  	el->el_errno = 0;
  	do {
  		if ((num = el_getc(el, ch)) != 1) {	/* if EOF or error */
 -			el->el_errno = num == 0 ? 0 : errno;
 -			return (num);
 +			return (num < 0 ? 1 : 0);
  		}
  
  #ifdef	KANJI
 @@ -294,16 +293,18 @@ read_char(EditLine *el, char *cp)
  
   again:
  	el->el_signal->sig_no = 0;
 -	while ((num_read = read(el->el_infd, cp, 1)) == -1) {
 +	while ((num_read = read(el->el_infd, cp, 1)) < 0) {
 +		int e = errno;
  		if (el->el_signal->sig_no == SIGCONT) {
  			sig_set(el);
  			el_set(el, EL_REFRESH);
  			goto again;
  		}
 -		if (!tried && read__fixio(el->el_infd, errno) == 0)
 +		if (! tried && read__fixio(el->el_infd, e) == 0)
  			tried = 1;
  		else {
  			*cp = '\0';
 +			errno = e;
  			return (-1);
  		}
  	}
 @@ -369,8 +370,10 @@ el_getc(EditLine *el, char *cp)
  	(void) fprintf(el->el_errfile, "Reading a character\n");
  #endif /* DEBUG_READ */
  	num_read = (*el->el_read.read_char)(el, cp);
 +	if (num_read < 0)
 +		el->el_errno = errno;
  #ifdef DEBUG_READ
 -	(void) fprintf(el->el_errfile, "Got it %c\n", *cp);
 +	(void) fprintf(el->el_errfile, "Got <%c> (return %d)\n", *cp, num_read);
  #endif /* DEBUG_READ */
  	return (num_read);
  }
 @@ -511,6 +514,7 @@ el_gets(EditLine *el, int *nread)
  #endif /* DEBUG_EDIT */
  		/* if EOF or error */
  		if ((num = read_getcmd(el, &cmdnum, &ch)) != OKCMD) {
 +			num = -1;
  #ifdef DEBUG_READ
  			(void) fprintf(el->el_errfile,
  			    "Returning from el_gets %d\n", num);
 
 _______________________________________________
 freebsd-bugs@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/freebsd-bugs
 To unsubscribe, send any mail to "freebsd-bugs-unsubscribe@freebsd.org"
 
 
 ----- End forwarded message -----

From: Mark Johnston <markjdb@gmail.com>
To: Steffen Daode Nurpmeso <sdaoden@gmail.com>, bug-followup@FreeBSD.org
Cc: freebsd-bugs@FreeBSD.org, jilles@FreeBSD.org, eadler@FreeBSD.org
Subject: Re: bin/169773: sh(1): Resizing causes /bin/sh to repeat edit
 operations
Date: Sat, 8 Sep 2012 17:29:44 -0400

 --AqsLC8rIMeq19msA
 Content-Type: text/plain; charset=us-ascii
 Content-Disposition: inline
 
 On Sat, Sep 08, 2012 at 05:44:56PM +0200, Steffen Daode Nurpmeso wrote:
 >  |Synopsis: sh(1): Resizing causes /bin/sh to repeat edit operations
 >  |
 >  |http://www.freebsd.org/cgi/query-pr.cgi?pr=169773
 > 
 > Oh, what a mess :)
 > I agree with Mark that the handling of OKCMD is simply wrong, but
 > it turned out to be wrong to simply use a different value for it.
 > Also the passing through of errno is incomplete in there.  (The
 > documented interface doesn't state anything about errno or useful
 > error handling at all, and however!)
 > 
 > It's a rather quick first diff for editline(3), i have no more
 > time but since it took almost three hours to come that far someone
 > else may build on top of it (or simply try it or ... wait for a
 > second).
 
 I took a closer look at the patch... I think the errno handling is
 mostly ok, except for a couple of places in el_gets() where the return
 value of read_char() isn't stored, and the code ends up looking at an
 uninitialized variable. The attached patch is your patch + the fix for
 this.
 
 > 
 > I *think* it effectively results in editline(3) behaving the way
 > it is supposed to work (retrying once after a whatever signal,
 > then failing for a second one).  Since el_gets() now fails (as it
 > is supposed to), sh(1) will behave wrong in that the current line
 > gets "thrown away".  (I guess the supposed way is to temporarily
 > adjust PROMPT if one wants to continue what is on the line yet?
 > But i still have no idea of editline(3) :->)
 > 
 > It would be better if editline(3) could be configured to simply
 > restart upon EINTR, or to fixate that behaviour (for FreeBSD)?
 > I don't think it is acceptable to loose a line of user content due
 > to a simple resize?
 > So long and ciao,
 
 Maybe we need a new option for el_set() which sets a flag in
 el->el_signal that determines whether various functions return on EINTR.
 libfetch for example has fetchRestartCalls for this purpose. It's not
 really clear to me why anyone would want EINTR as an error and return
 though.
 
 -Mark
 
 --AqsLC8rIMeq19msA
 Content-Type: text/x-diff; charset=us-ascii
 Content-Disposition: attachment; filename="libedit_error_handling.patch"
 
 diff --git a/lib/libedit/read.c b/lib/libedit/read.c
 index 7d7f54b..ecd1ee2 100644
 --- a/lib/libedit/read.c
 +++ b/lib/libedit/read.c
 @@ -238,8 +238,7 @@ read_getcmd(EditLine *el, el_action_t *cmdnum, char *ch)
  	el->el_errno = 0;
  	do {
  		if ((num = el_getc(el, ch)) != 1) {	/* if EOF or error */
 -			el->el_errno = num == 0 ? 0 : errno;
 -			return (num);
 +			return (num < 0 ? 1 : 0);
  		}
  
  #ifdef	KANJI
 @@ -294,16 +293,18 @@ read_char(EditLine *el, char *cp)
  
   again:
  	el->el_signal->sig_no = 0;
 -	while ((num_read = read(el->el_infd, cp, 1)) == -1) {
 +	while ((num_read = read(el->el_infd, cp, 1)) < 0) {
 +		int e = errno;
  		if (el->el_signal->sig_no == SIGCONT) {
  			sig_set(el);
  			el_set(el, EL_REFRESH);
  			goto again;
  		}
 -		if (!tried && read__fixio(el->el_infd, errno) == 0)
 +		if (!tried && read__fixio(el->el_infd, e) == 0)
  			tried = 1;
  		else {
  			*cp = '\0';
 +			errno = e;
  			return (-1);
  		}
  	}
 @@ -369,8 +370,10 @@ el_getc(EditLine *el, char *cp)
  	(void) fprintf(el->el_errfile, "Reading a character\n");
  #endif /* DEBUG_READ */
  	num_read = (*el->el_read.read_char)(el, cp);
 +	if (num_read < 0)
 +		el->el_errno = errno;
  #ifdef DEBUG_READ
 -	(void) fprintf(el->el_errfile, "Got it %c\n", *cp);
 +	(void) fprintf(el->el_errfile, "Got <%c> (return %d)\n", *cp, num_read);
  #endif /* DEBUG_READ */
  	return (num_read);
  }
 @@ -426,7 +429,7 @@ el_gets(EditLine *el, int *nread)
  		char *cp = el->el_line.buffer;
  		size_t idx;
  
 -		while ((*el->el_read.read_char)(el, cp) == 1) {
 +		while ((num = (*el->el_read.read_char)(el, cp)) == 1) {
  			/* make sure there is space for next character */
  			if (cp + 1 >= el->el_line.limit) {
  				idx = (cp - el->el_line.buffer);
 @@ -479,7 +482,7 @@ el_gets(EditLine *el, int *nread)
  
  		term__flush(el);
  
 -		while ((*el->el_read.read_char)(el, cp) == 1) {
 +		while ((num = (*el->el_read.read_char)(el, cp)) == 1) {
  			/* make sure there is space next character */
  			if (cp + 1 >= el->el_line.limit) {
  				idx = (cp - el->el_line.buffer);
 @@ -511,6 +514,7 @@ el_gets(EditLine *el, int *nread)
  #endif /* DEBUG_EDIT */
  		/* if EOF or error */
  		if ((num = read_getcmd(el, &cmdnum, &ch)) != OKCMD) {
 +			num = -1;
  #ifdef DEBUG_READ
  			(void) fprintf(el->el_errfile,
  			    "Returning from el_gets %d\n", num);
 
 --AqsLC8rIMeq19msA--

From: Steffen "Daode" Nurpmeso <sdaoden@gmail.com>
To: Mark Johnston <markjdb@gmail.com>
Cc: jilles@FreeBSD.org, freebsd-bugs@FreeBSD.org, eadler@FreeBSD.org,
 bug-followup@FreeBSD.org
Subject: Re: bin/169773: sh(1): Resizing causes /bin/sh to repeat edit
 operations
Date: Mon, 10 Sep 2012 16:56:35 +0200

 This is a multi-part message in MIME format.
 
 --=_504dffa3.AbuPoUzJ/AJ2vkLneAUuvGpW7/sJ2QsUoQEyKvlxmXsEtNrk
 Content-Type: text/plain; charset=us-ascii
 Content-Transfer-Encoding: 7bit
 Content-Disposition: inline
 
 Mark Johnston <markjdb@gmail.com> wrote:
 
  |On Sat, Sep 08, 2012 at 05:44:56PM +0200, Steffen Daode Nurpmeso wrote:
  |>  |Synopsis: sh(1): Resizing causes /bin/sh to repeat edit operations
  |>  |
  |>  |http://www.freebsd.org/cgi/query-pr.cgi?pr=169773
  |> 
  [.]
  |> It's a rather quick first diff for editline(3), i have no more
  [.]
  |
  |I took a closer look at the patch... I think the errno handling is
  |mostly ok, except for a couple of places in el_gets() where the return
  |value of read_char() isn't stored, and the code ends up looking at an
  |uninitialized variable. The attached patch is your patch + the fix for
  |this.
  |
  |> I *think* it effectively results in editline(3) behaving the way
  |> it is supposed to work (retrying once after a whatever signal,
  |> then failing for a second one).  Since el_gets() now fails (as it
  |> is supposed to), sh(1) will behave wrong in that the current line
  [.]
  |> 
  |> It would be better if editline(3) could be configured to simply
  |> restart upon EINTR, or to fixate that behaviour (for FreeBSD)?
  |> I don't think it is acceptable to loose a line of user content due
  |> to a simple resize?
  |> So long and ciao,
  |
  |Maybe we need a new option for el_set() which sets a flag in
  |el->el_signal that determines whether various functions return on EINTR.
  |libfetch for example has fetchRestartCalls for this purpose. It's not
  |really clear to me why anyone would want EINTR as an error and return
  |though.
 
 I have implemented a EL_READRESTART option for editline(3), which
 seems to be the easiest approach to get around this.
 Other options would have been to implement an el_gets_continue(),
 which would have restarted editing with the input of the last
 state (but what if that ended in a newline?), or to commit suicide
 while trying to deal with signals from within sh(1).
 
 I have also tried to extend editline.3 in respect to
 EL_UNBUFFERED, which is only partially documented sofar, and the
 yet completely undocumented errno handling.
 
 It is a whole series of local commits indeed, but i don't dare to
 attach a MBOX or even (horror) send a patch mail-series, and so
 i'll simply attach them in order, including the PR bin/170651
 patch (laziness).  It seems to work.  In reversed order:
 
   - 6.diff: Set EL_READRESTART in interactive sh(1) sessions
   - 5.diff: Add a new EL_READRESTART option for editline(3)
   - 4.diff: Document errno behaviour of el_getc()/el_gets()
   - 3.diff: Document EL_UNBUFFERED for el_set()
   - 2.diff: Fix editline(3) char read and errno code flow
 : This simply reuses your patch.
   - 1.diff: Fix PR bin/170651
 : (The plain patch.  Maybe possible to leave that off.)
 
  |-Mark
 
 Bye and ciao,
 
 --steffen
 
 --=_504dffa3.AbuPoUzJ/AJ2vkLneAUuvGpW7/sJ2QsUoQEyKvlxmXsEtNrk
 Content-Type: text/plain;
  charset=us-ascii
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
  filename="1.diff"
 
 commit b68195b7d21912bd13b74412db43e2dbecdd4b92
 Author: Steffen Daode Nurpmeso <sdaoden@gmail.com>
 Date:   2012-09-01 17:21:14 +0200
 
     Fix PR bin/170651
 
 diff --git a/bin/sh/histedit.c b/bin/sh/histedit.c
 index 6371599..bd47c0d 100644
 --- a/bin/sh/histedit.c
 +++ b/bin/sh/histedit.c
 @@ -67,7 +67,9 @@ __FBSDID("$FreeBSD$");
  History *hist;	/* history cookie */
  EditLine *el;	/* editline cookie */
  int displayhist;
 +int histedit_init;
  static FILE *el_in, *el_out, *el_err;
 +static int e1v2;
  
  static char *fc_replace(const char *, char *, char *);
  static int not_fcnumber(const char *);
 @@ -76,12 +78,21 @@ static int str_to_event(const char *, int);
  /*
   * Set history and editing status.  Called whenever the status may
   * have changed (figures out what to do).
 + * If force is set then an editline reinit is issued even if the actual edit
 + * mode hasn't changed - necessary after the locale has changed because
 + * editline bases it's decision what is reported or not upon isprint(3)
   */
  void
 -histedit(void)
 +histedit(int force)
  {
 +	int nedstate;
  
 -#define editing (Eflag || Vflag)
 +	if (! histedit_init)
 +		return;
 +
 +	histedit_init = 2;
 +	/* options.c ensures these are mutual exclusive */
 +	nedstate = (Eflag ? 1 : 0) | (Vflag ? 2 : 0);
  
  	if (iflag) {
  		if (!hist) {
 @@ -97,7 +108,7 @@ histedit(void)
  			else
  				out2fmt_flush("sh: can't initialize history\n");
  		}
 -		if (editing && !el && isatty(0)) { /* && isatty(2) ??? */
 +		if (nedstate && ! el && isatty(0)) { /* && isatty(2) ??? */
  			/*
  			 * turn editing on
  			 */
 @@ -130,17 +141,14 @@ bad:
  				out2fmt_flush("sh: can't initialize editing\n");
  			}
  			INTON;
 -		} else if (!editing && el) {
 +		} else if (! nedstate && el) {
  			INTOFF;
  			el_end(el);
  			el = NULL;
  			INTON;
  		}
 -		if (el) {
 -			if (Vflag)
 -				el_set(el, EL_EDITOR, "vi");
 -			else if (Eflag)
 -				el_set(el, EL_EDITOR, "emacs");
 +		if (el && (nedstate != e1v2 || force)) {
 +			el_set(el, EL_EDITOR, (nedstate & 1) ? "emacs" : "vi");
  			el_set(el, EL_BIND, "^I", "sh-complete", NULL);
  			el_source(el, NULL);
  		}
 @@ -155,7 +163,10 @@ bad:
  			hist = NULL;
  		}
  		INTON;
 +		nedstate = 0;
  	}
 +
 +	e1v2 = nedstate;
  }
  
  
 diff --git a/bin/sh/input.c b/bin/sh/input.c
 index 12f285f..1da03c6 100644
 --- a/bin/sh/input.c
 +++ b/bin/sh/input.c
 @@ -59,8 +59,10 @@ __FBSDID("$FreeBSD$");
  #include "error.h"
  #include "alias.h"
  #include "parser.h"
 -#include "myhistedit.h"
  #include "trap.h"
 +#ifndef NO_HISTORY
 +# include "myhistedit.h"
 +#endif
  
  #define EOF_NLEFT -99		/* value of parsenleft when EOF pushed back */
  
 @@ -102,8 +104,6 @@ static struct parsefile *parsefile = &basepf;	/* current input file */
  int init_editline = 0;		/* editline library initialized? */
  int whichprompt;		/* 1 == PS1, 2 == PS2 */
  
 -EditLine *el;			/* cookie for editline package */
 -
  static void pushfile(void);
  static int preadfd(void);
  static void popstring(void);
 diff --git a/bin/sh/main.c b/bin/sh/main.c
 index 5eb12e0..684ce8c 100644
 --- a/bin/sh/main.c
 +++ b/bin/sh/main.c
 @@ -73,6 +73,9 @@ __FBSDID("$FreeBSD$");
  #include "exec.h"
  #include "cd.h"
  #include "builtins.h"
 +#ifndef NO_HISTORY
 +# include "myhistedit.h"
 +#endif
  
  int rootpid;
  int rootshell;
 @@ -144,8 +147,12 @@ main(int argc, char *argv[])
  	setstackmark(&smark2);
  	procargs(argc, argv);
  	pwd_init(iflag);
 -	if (iflag)
 +	if (iflag) {
  		chkmail(1);
 +#ifndef NO_HISTORY
 +		histedit_init = 1;
 +#endif
 +	}
  	if (argv[0] && argv[0][0] == '-') {
  		state = 1;
  		read_profile("/etc/profile");
 @@ -157,6 +164,10 @@ state1:
  			read_profile("/etc/suid_profile");
  	}
  state2:
 +#ifndef NO_HISTORY
 +	if (iflag && histedit_init != 2)
 +		histedit(1);
 +#endif
  	state = 3;
  	if (!privileged && iflag) {
  		if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
 diff --git a/bin/sh/myhistedit.h b/bin/sh/myhistedit.h
 index e31276d..24123e1 100644
 --- a/bin/sh/myhistedit.h
 +++ b/bin/sh/myhistedit.h
 @@ -35,8 +35,8 @@
  extern History *hist;
  extern EditLine *el;
  extern int displayhist;
 +extern int histedit_init;
  
 -void histedit(void);
 +void histedit(int);
  void sethistsize(const char *);
  void setterm(const char *);
 -
 diff --git a/bin/sh/options.c b/bin/sh/options.c
 index ad0291e..f84b4d8 100644
 --- a/bin/sh/options.c
 +++ b/bin/sh/options.c
 @@ -58,7 +58,7 @@ __FBSDID("$FreeBSD$");
  #include "mystring.h"
  #include "builtins.h"
  #ifndef NO_HISTORY
 -#include "myhistedit.h"
 +# include "myhistedit.h"
  #endif
  
  char *arg0;			/* value of $0 */
 @@ -131,7 +131,7 @@ optschanged(void)
  {
  	setinteractive(iflag);
  #ifndef NO_HISTORY
 -	histedit();
 +	histedit(0);
  #endif
  	setjobctl(mflag);
  }
 diff --git a/bin/sh/trap.c b/bin/sh/trap.c
 index 521c511..b0d8b11 100644
 --- a/bin/sh/trap.c
 +++ b/bin/sh/trap.c
 @@ -56,8 +56,9 @@ __FBSDID("$FreeBSD$");
  #include "trap.h"
  #include "mystring.h"
  #include "builtins.h"
 -#include "myhistedit.h"
 -
 +#ifndef NO_HISTORY
 +# include "myhistedit.h"
 +#endif
  
  /*
   * Sigmode records the current value of the signal handlers for the various
 diff --git a/bin/sh/var.c b/bin/sh/var.c
 index 6041459..d1af678 100644
 --- a/bin/sh/var.c
 +++ b/bin/sh/var.c
 @@ -65,7 +65,7 @@ __FBSDID("$FreeBSD$");
  #include "parser.h"
  #include "builtins.h"
  #ifndef NO_HISTORY
 -#include "myhistedit.h"
 +# include "myhistedit.h"
  #endif
  
  
 @@ -523,6 +523,9 @@ updatecharset(void)
  
  	charset = nl_langinfo(CODESET);
  	localeisutf8 = !strcmp(charset, "UTF-8");
 +#ifndef NO_HISTORY
 +	histedit(1);
 +#endif
  }
  
  void
 
 --=_504dffa3.AbuPoUzJ/AJ2vkLneAUuvGpW7/sJ2QsUoQEyKvlxmXsEtNrk
 Content-Type: text/plain;
  charset=us-ascii
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
  filename="2.diff"
 
 commit 8582cb9cab300fe486cfeeb46142e3cfd9cfe962
 Author: Steffen Daode Nurpmeso <sdaoden@gmail.com>
 Date:   2012-09-10 15:50:58 +0200
 
     Fix editline(3) char read and errno code flow
 
     The reading call chain failed to initialize local variables
     and also did not map return values that it got back from
     deeper in the call chain to its own meaning of those states,
     thus failing later due to misunderstanding.
 
     In addition the tracking of errno in EditLine::el_errno, and
     vice versa was also broken and is now fixed.
 
 diff --git a/lib/libedit/read.c b/lib/libedit/read.c
 index 7d7f54b..ecd1ee2 100644
 --- a/lib/libedit/read.c
 +++ b/lib/libedit/read.c
 @@ -238,8 +238,7 @@ read_getcmd(EditLine *el, el_action_t *cmdnum, char *ch)
  	el->el_errno = 0;
  	do {
  		if ((num = el_getc(el, ch)) != 1) {	/* if EOF or error */
 -			el->el_errno = num == 0 ? 0 : errno;
 -			return (num);
 +			return (num < 0 ? 1 : 0);
  		}
  
  #ifdef	KANJI
 @@ -294,16 +293,18 @@ read_char(EditLine *el, char *cp)
  
   again:
  	el->el_signal->sig_no = 0;
 -	while ((num_read = read(el->el_infd, cp, 1)) == -1) {
 +	while ((num_read = read(el->el_infd, cp, 1)) < 0) {
 +		int e = errno;
  		if (el->el_signal->sig_no == SIGCONT) {
  			sig_set(el);
  			el_set(el, EL_REFRESH);
  			goto again;
  		}
 -		if (!tried && read__fixio(el->el_infd, errno) == 0)
 +		if (!tried && read__fixio(el->el_infd, e) == 0)
  			tried = 1;
  		else {
  			*cp = '\0';
 +			errno = e;
  			return (-1);
  		}
  	}
 @@ -369,8 +370,10 @@ el_getc(EditLine *el, char *cp)
  	(void) fprintf(el->el_errfile, "Reading a character\n");
  #endif /* DEBUG_READ */
  	num_read = (*el->el_read.read_char)(el, cp);
 +	if (num_read < 0)
 +		el->el_errno = errno;
  #ifdef DEBUG_READ
 -	(void) fprintf(el->el_errfile, "Got it %c\n", *cp);
 +	(void) fprintf(el->el_errfile, "Got <%c> (return %d)\n", *cp, num_read);
  #endif /* DEBUG_READ */
  	return (num_read);
  }
 @@ -426,7 +429,7 @@ el_gets(EditLine *el, int *nread)
  		char *cp = el->el_line.buffer;
  		size_t idx;
  
 -		while ((*el->el_read.read_char)(el, cp) == 1) {
 +		while ((num = (*el->el_read.read_char)(el, cp)) == 1) {
  			/* make sure there is space for next character */
  			if (cp + 1 >= el->el_line.limit) {
  				idx = (cp - el->el_line.buffer);
 @@ -479,7 +482,7 @@ el_gets(EditLine *el, int *nread)
  
  		term__flush(el);
  
 -		while ((*el->el_read.read_char)(el, cp) == 1) {
 +		while ((num = (*el->el_read.read_char)(el, cp)) == 1) {
  			/* make sure there is space next character */
  			if (cp + 1 >= el->el_line.limit) {
  				idx = (cp - el->el_line.buffer);
 @@ -511,6 +514,7 @@ el_gets(EditLine *el, int *nread)
  #endif /* DEBUG_EDIT */
  		/* if EOF or error */
  		if ((num = read_getcmd(el, &cmdnum, &ch)) != OKCMD) {
 +			num = -1;
  #ifdef DEBUG_READ
  			(void) fprintf(el->el_errfile,
  			    "Returning from el_gets %d\n", num);
 
 --=_504dffa3.AbuPoUzJ/AJ2vkLneAUuvGpW7/sJ2QsUoQEyKvlxmXsEtNrk
 Content-Type: text/plain;
  charset=us-ascii
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
  filename="3.diff"
 
 commit 8bc3e8e2d6b42b422ef87e5da916b162cf2fd7d5
 Author: Steffen Daode Nurpmeso <sdaoden@gmail.com>
 Date:   2012-09-10 15:23:37 +0200
 
     Document EL_UNBUFFERED for el_set()
 
 diff --git a/lib/libedit/editline.3 b/lib/libedit/editline.3
 index fe58321..a08a0f0 100644
 --- a/lib/libedit/editline.3
 +++ b/lib/libedit/editline.3
 @@ -385,6 +385,14 @@ check this
  (using
  .Fn el_get )
  to determine if editing should be enabled or not.
 +.It Dv EL_UNBUFFERED , Fa "int flag"
 +If
 +.Fa flag
 +is zero,
 +unbuffered mode is disabled (the default).
 +In unbuffered mode,
 +.Fn el_gets
 +will return immediately after processing a single character.
  .It Dv EL_GETCFN , Fa "int (*f)(EditLine *, char *c)"
  Define the character reading function as
  .Fa f ,
 @@ -487,10 +495,7 @@ previously registered with the corresponding
  .Fn el_set
  call.
  .It Dv EL_UNBUFFERED , Fa "int"
 -Sets or clears unbuffered mode.
 -In this mode,
 -.Fn el_gets
 -will return immediately after processing a single character.
 +Return non-zero if unbuffered mode is enabled.
  .It Dv EL_PREP_TERM , Fa "int"
  Sets or clears terminal editing mode.
  .It Dv EL_GETFP , Fa "int fd", Fa "FILE **fp"
 
 --=_504dffa3.AbuPoUzJ/AJ2vkLneAUuvGpW7/sJ2QsUoQEyKvlxmXsEtNrk
 Content-Type: text/plain;
  charset=us-ascii
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
  filename="4.diff"
 
 commit 2b9d0ef804cbb44d9caf580ee53b1241b6904aa0
 Author: Steffen Daode Nurpmeso <sdaoden@gmail.com>
 Date:   2012-09-10 15:24:29 +0200
 
     Document errno behaviour of el_getc()/el_gets()
 
 diff --git a/lib/libedit/editline.3 b/lib/libedit/editline.3
 index a08a0f0..1f26b39 100644
 --- a/lib/libedit/editline.3
 +++ b/lib/libedit/editline.3
 @@ -174,7 +174,10 @@ and must be copied if the data is to be retained.
  Read a character from the tty.
  .Fa ch
  is modified to contain the character read.
 -Returns the number of characters read if successful, \-1 otherwise.
 +Returns the number of characters read if successful, \-1 otherwise,
 +in which case
 +.Dv errno
 +is set.
  .It Fn el_push
  Pushes
  .Fa str
 @@ -397,7 +400,8 @@ will return immediately after processing a single character.
  Define the character reading function as
  .Fa f ,
  which is to return the number of characters read and store them in
 -.Fa c .
 +.Fa c ,
 +and -1 on failure with errno set to the failure.
  This function is called internally by
  .Fn el_gets
  and
 @@ -788,6 +792,7 @@ is a NUL terminated string to tokenize.
  .\"XXX: provide some examples
  .Sh SEE ALSO
  .Xr sh 1 ,
 +.Xr intro 2 ,
  .Xr signal 3 ,
  .Xr termcap 3 ,
  .Xr editrc 5 ,
 
 --=_504dffa3.AbuPoUzJ/AJ2vkLneAUuvGpW7/sJ2QsUoQEyKvlxmXsEtNrk
 Content-Type: text/plain;
  charset=us-ascii
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
  filename="5.diff"
 
 commit a548d383b230b02d1ca0fc44acc384a32d82abac
 Author: Steffen Daode Nurpmeso <sdaoden@gmail.com>
 Date:   2012-09-10 15:39:00 +0200
 
     Add a new EL_READRESTART option for editline(3)
     
     Which makes it possible to realize read(2) restarts after EINTR
     errors without actually going the expensive (and sometimes
     impossible) way through signal handling.
     
     Unfortunately editline(3) doesn't offer anything like
     "el_gets_continue()", which could be used to simply restart the
     last editing session at the point where it ended, and it seems
     much harder to add that functionality (also in respect to merging
     in between *BSD versions) than to add this flag.
 
 diff --git a/lib/libedit/editline.3 b/lib/libedit/editline.3
 index 1f26b39..7bb783e 100644
 --- a/lib/libedit/editline.3
 +++ b/lib/libedit/editline.3
 @@ -388,6 +388,22 @@ check this
  (using
  .Fn el_get )
  to determine if editing should be enabled or not.
 +.It Dv EL_READRESTART , Fa "int flag"
 +If
 +.Fa flag
 +is zero (the default),
 +then
 +.Fn el_getc
 +and
 +.Fn el_gets
 +will not treat
 +.Dv EINTR
 +errors any special and automatically restart reading characters.
 +Note this may be restricted to the builtin character read function
 +.Dv EL_BUILTIN_GETCFN
 +(see
 +.Dv EL_GETCFN
 +below).
  .It Dv EL_UNBUFFERED , Fa "int flag"
  If
  .Fa flag
 @@ -498,6 +514,9 @@ Retrieve
  previously registered with the corresponding
  .Fn el_set
  call.
 +.It Dv EL_READRESTART , Fa "int"
 +Return non-zero if reading of characters is restarted after signal
 +interruption.
  .It Dv EL_UNBUFFERED , Fa "int"
  Return non-zero if unbuffered mode is enabled.
  .It Dv EL_PREP_TERM , Fa "int"
 diff --git a/lib/libedit/el.c b/lib/libedit/el.c
 index d6cfb2d..4be765f 100644
 --- a/lib/libedit/el.c
 +++ b/lib/libedit/el.c
 @@ -274,6 +274,13 @@ el_set(EditLine *el, int op, ...)
  		el->el_data = va_arg(ap, void *);
  		break;
  
 +	case EL_READRESTART:
 +		if (va_arg(ap, int))
 +			el->el_flags |= READRESTART;
 +		else
 +			el->el_flags &= ~READRESTART;
 +		break;
 +
  	case EL_UNBUFFERED:
  		rv = va_arg(ap, int);
  		if (rv && !(el->el_flags & UNBUFFERED)) {
 @@ -435,6 +442,11 @@ el_get(EditLine *el, int op, ...)
  		rv = 0;
  		break;
  
 +	case EL_READRESTART:
 +		*va_arg(ap, int *) = (el->el_flags & READRESTART) != 0;
 +		rv = 0;
 +		break;
 +
  	case EL_UNBUFFERED:
  		*va_arg(ap, int *) = (!(el->el_flags & UNBUFFERED));
  		rv = 0;
 diff --git a/lib/libedit/el.h b/lib/libedit/el.h
 index 67d01ff..d1321cc 100644
 --- a/lib/libedit/el.h
 +++ b/lib/libedit/el.h
 @@ -54,7 +54,8 @@
  #define	HANDLE_SIGNALS	0x01
  #define	NO_TTY		0x02
  #define	EDIT_DISABLED	0x04
 -#define	UNBUFFERED	0x08
 +#define	READRESTART	0x08
 +#define	UNBUFFERED	0x10
  
  typedef int bool_t;			/* True or not			*/
  
 diff --git a/lib/libedit/histedit.h b/lib/libedit/histedit.h
 index 8a6caf9..13d0cbf 100644
 --- a/lib/libedit/histedit.h
 +++ b/lib/libedit/histedit.h
 @@ -130,15 +130,16 @@ unsigned char	_el_fn_sh_complete(EditLine *, int);
  #define	EL_RPROMPT	12	/* , el_pfunc_t);		*/
  #define	EL_GETCFN	13	/* , el_rfunc_t);		*/
  #define	EL_CLIENTDATA	14	/* , void *);			*/
 -#define	EL_UNBUFFERED	15	/* , int);			*/
 -#define	EL_PREP_TERM    16	/* , int);                      */
 -#define	EL_GETTC	17	/* , const char *, ..., NULL);	*/
 -#define	EL_GETFP	18	/* , int, FILE **);		*/
 -#define	EL_SETFP	19	/* , int, FILE *);		*/
 -#define	EL_REFRESH	20	/* , void);			      set     */
 -#define	EL_PROMPT_ESC	21	/* , prompt_func, Char);	      set/get */
 -#define	EL_RPROMPT_ESC	22	/* , prompt_func, Char);	      set/get */
 -#define	EL_RESIZE	23	/* , el_zfunc_t, void *);	      set     */
 +#define	EL_READRESTART	15	/* , int);			*/
 +#define	EL_UNBUFFERED	16	/* , int);			*/
 +#define	EL_PREP_TERM    17	/* , int);                      */
 +#define	EL_GETTC	18	/* , const char *, ..., NULL);	*/
 +#define	EL_GETFP	19	/* , int, FILE **);		*/
 +#define	EL_SETFP	20	/* , int, FILE *);		*/
 +#define	EL_REFRESH	21	/* , void);			      set     */
 +#define	EL_PROMPT_ESC	22	/* , prompt_func, Char);	      set/get */
 +#define	EL_RPROMPT_ESC	23	/* , prompt_func, Char);	      set/get */
 +#define	EL_RESIZE	24	/* , el_zfunc_t, void *);	      set     */
  
  #define	EL_BUILTIN_GETCFN	(NULL)
  
 diff --git a/lib/libedit/read.c b/lib/libedit/read.c
 index ecd1ee2..3634d7e 100644
 --- a/lib/libedit/read.c
 +++ b/lib/libedit/read.c
 @@ -300,6 +300,8 @@ read_char(EditLine *el, char *cp)
  			el_set(el, EL_REFRESH);
  			goto again;
  		}
 +		if (e == EINTR && (el->el_flags & READRESTART))
 +			goto again;
  		if (!tried && read__fixio(el->el_infd, e) == 0)
  			tried = 1;
  		else {
 
 --=_504dffa3.AbuPoUzJ/AJ2vkLneAUuvGpW7/sJ2QsUoQEyKvlxmXsEtNrk
 Content-Type: text/plain;
  charset=us-ascii
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
  filename="6.diff"
 
 commit 55f0420fc73153132fabadd84edc46e569fc0a50 (HEAD, refs/heads/i)
 Author: Steffen Daode Nurpmeso <sdaoden@gmail.com>
 Date:   2012-09-10 15:14:30 +0200
 
     Set EL_READRESTART in interactive sh(1) sessions
     
     This closes PR bin/169773.
 
 diff --git a/bin/sh/histedit.c b/bin/sh/histedit.c
 index bd47c0d..e4ef7e0 100644
 --- a/bin/sh/histedit.c
 +++ b/bin/sh/histedit.c
 @@ -136,6 +136,7 @@ histedit(int force)
  				el_set(el, EL_ADDFN, "sh-complete",
  				    "Filename completion",
  				    _el_fn_sh_complete);
 +				el_set(el, EL_READRESTART, 1);
  			} else {
  bad:
  				out2fmt_flush("sh: can't initialize editing\n");
 
 --=_504dffa3.AbuPoUzJ/AJ2vkLneAUuvGpW7/sJ2QsUoQEyKvlxmXsEtNrk--

From: Mark Johnston <markjdb@gmail.com>
To: Steffen Daode Nurpmeso <sdaoden@gmail.com>
Cc: jilles@FreeBSD.org, freebsd-bugs@FreeBSD.org, eadler@FreeBSD.org,
	bug-followup@FreeBSD.org
Subject: Re: bin/169773: sh(1): Resizing causes /bin/sh to repeat edit
 operations
Date: Mon, 10 Sep 2012 11:58:54 -0400

 On Mon, Sep 10, 2012 at 04:56:35PM +0200, Steffen Daode Nurpmeso wrote:
 > 
 > I have implemented a EL_READRESTART option for editline(3), which
 > seems to be the easiest approach to get around this.
 > Other options would have been to implement an el_gets_continue(),
 > which would have restarted editing with the input of the last
 > state (but what if that ended in a newline?), or to commit suicide
 > while trying to deal with signals from within sh(1).
 > 
 > I have also tried to extend editline.3 in respect to
 > EL_UNBUFFERED, which is only partially documented sofar, and the
 > yet completely undocumented errno handling.
 > 
 > It is a whole series of local commits indeed, but i don't dare to
 > attach a MBOX or even (horror) send a patch mail-series, and so
 > i'll simply attach them in order, including the PR bin/170651
 > patch (laziness).  It seems to work.  In reversed order:
 > 
 >   - 6.diff: Set EL_READRESTART in interactive sh(1) sessions
 >   - 5.diff: Add a new EL_READRESTART option for editline(3)
 >   - 4.diff: Document errno behaviour of el_getc()/el_gets()
 >   - 3.diff: Document EL_UNBUFFERED for el_set()
 >   - 2.diff: Fix editline(3) char read and errno code flow
 > : This simply reuses your patch.
 >   - 1.diff: Fix PR bin/170651
 > : (The plain patch.  Maybe possible to leave that off.)
 
 I didn't test 1.diff, but the rest of the patches apply and work for me.
 
 Thanks,
 -Mark

From: Steffen "Daode" Nurpmeso <sdaoden@gmail.com>
To: Mark Johnston <markjdb@gmail.com>
Cc: jilles@FreeBSD.org, freebsd-bugs@FreeBSD.org, eadler@FreeBSD.org,
 bug-followup@FreeBSD.org
Subject: Re: bin/169773: sh(1): Resizing causes /bin/sh to repeat edit
 operations
Date: Mon, 10 Sep 2012 21:15:04 +0200

 Mark Johnston <markjdb@gmail.com> wrote:
 
  |On Mon, Sep 10, 2012 at 04:56:35PM +0200, Steffen Daode Nurpmeso wrote:
  |> 
  |> I have implemented a EL_READRESTART option for editline(3), which
  [.]
  |>   - 6.diff: Set EL_READRESTART in interactive sh(1) sessions
  |>   - 5.diff: Add a new EL_READRESTART option for editline(3)
  |>   - 4.diff: Document errno behaviour of el_getc()/el_gets()
  |>   - 3.diff: Document EL_UNBUFFERED for el_set()
  |>   - 2.diff: Fix editline(3) char read and errno code flow
  |> : This simply reuses your patch.
  |>   - 1.diff: Fix PR bin/170651
  |> : (The plain patch.  Maybe possible to leave that off.)
  |
  |I didn't test 1.diff, but the rest of the patches apply and work for me.
 
 Good.
 
  |Thanks,
  |-Mark
 
 My pleasure.
 
 --steffen

From: Steffen "Daode" Nurpmeso <sdaoden@gmail.com>
To: Mark Johnston <markjdb@gmail.com>
Cc: pfg@FreeBSD.org, jilles@FreeBSD.org, freebsd-bugs@FreeBSD.org,
 eadler@FreeBSD.org, bug-followup@FreeBSD.org
Subject: Re: bin/169773: sh(1): Resizing causes /bin/sh to repeat edit
 operations
Date: Tue, 11 Sep 2012 18:10:15 +0200

 This is a multi-part message in MIME format.
 
 --=_504f6267.iUCe6UiHu0SzYFl1a67qpbTPwc33pf/H4idnoT7GkXfIKRja
 Content-Type: text/plain; charset=us-ascii
 Content-Transfer-Encoding: 7bit
 Content-Disposition: inline
 
  |Mark Johnston <markjdb@gmail.com> wrote:
  |
  ||On Sat, Sep 08, 2012 at 05:44:56PM +0200, Steffen Daode Nurpmeso wrote:
  ||>  |Synopsis: sh(1): Resizing causes /bin/sh to repeat edit operations
  ||>  |
  ||>  |http://www.freebsd.org/cgi/query-pr.cgi?pr=169773
  ||> 
  | [.]
  ||> It's a rather quick first diff for editline(3), i have no more
  | [.]
  ||
  ||I took a closer look at the patch... I think the errno handling is
  ||mostly ok, except for a couple of places in el_gets() where the return
  ||value of read_char() isn't stored, and the code ends up looking at an
  ||uninitialized variable. The attached patch is your patch + the fix for
  ||this.
  ||
  ||> I *think* it effectively results in editline(3) behaving the way
  ||> it is supposed to work (retrying once after a whatever signal,
  ||> then failing for a second one).  Since el_gets() now fails (as it
  ||> is supposed to), sh(1) will behave wrong in that the current line
  | [.]
  ||> 
  ||> It would be better if editline(3) could be configured to simply
  ||> restart upon EINTR, or to fixate that behaviour (for FreeBSD)?
  ||> I don't think it is acceptable to loose a line of user content due
  ||> to a simple resize?
  ||> So long and ciao,
  ||
  ||Maybe we need a new option for el_set() which sets a flag in
  ||el->el_signal that determines whether various functions return on EINTR.
  ||libfetch for example has fetchRestartCalls for this purpose. It's not
  ||really clear to me why anyone would want EINTR as an error and return
  ||though.
  |
  |I have implemented a EL_READRESTART option for editline(3), which
  [.]
  |
  ||-Mark
 
 I have posted some NetBSD problem reports and some of the patches
 have been accepted upstream and are already committed. [1][2][3]
 And upstream editline.3 already documented errno behaviour for
 el_gets() (not for el_getc(), yet posted a PR for that [4]).
 
 It however turned out that fixing the behaviour is even more
 difficult than i thought yesterday, and the issue is also not
 completed upstream.  (But at least someone who really knows is
 thinking about it from now on.)
 
 So i do attach the current state (less anything which will come in
 from upstream anyway), including the new restart patch, for which
 i've also posted a PR upstream [5], since i think it is a useful
 flag to have.  (Though upstream editline(3) does do some EINTR.
 I also saw pfg@'s commit on that itm.)
 
 Anyway - this new restart patch rather applies 1:1 on upstream,
 too, and it really seems to fix the problem now.  It is anyway
 better than the patch from yesterday, just in case you use that.
 Ciao,
 
 --steffen
 
 [1] http://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=46935
 [2] http://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=46941
 [3] http://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=46942
 [4] http://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=46945
 [5] http://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=46943
 
 --=_504f6267.iUCe6UiHu0SzYFl1a67qpbTPwc33pf/H4idnoT7GkXfIKRja
 Content-Type: text/plain;
  charset=us-ascii
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
  filename="0001-Fix-editline-3-char-read-and-errno-code-flow.patch"
 
 From e889bca8dc1fe7ddb95651ecbe98ad05bd59e22e Mon Sep 17 00:00:00 2001
 Message-Id: <e889bca8dc1fe7ddb95651ecbe98ad05bd59e22e.1347378617.git.sdaoden@gmail.com>
 From: "Steffen \"Daode\" Nurpmeso" <sdaoden@gmail.com>
 Date: Mon, 10 Sep 2012 15:50:58 +0200
 Subject: [PATCH 1/3] Fix editline(3) char read and errno code flow
 
 The reading call chain failed to initialize local variables.
 From Mark Johnston (markjdb AT gmail DOT com).
 
 A return value from deeper in the chain was reused without
 localizing the meaning, which resulted in misinterpretation
 later on.
 
 And the tracking of errno in EditLine::el_errno, and vice versa,
 was also fixed.
 
 All this resulted in the requirement for a different way to
 control the edit loop, fixed by introduction of the new enum
 rcmd.
 With help from Christos Zoulas (christos AT zoulas DOT com).
 ---
  lib/libedit/read.c |   53 +++++++++++++++++++++++++++++++--------------------
  1 files changed, 32 insertions(+), 21 deletions(-)
 
 diff --git a/lib/libedit/read.c b/lib/libedit/read.c
 index 7d7f54b..0880b5c 100644
 --- a/lib/libedit/read.c
 +++ b/lib/libedit/read.c
 @@ -49,13 +49,17 @@ __FBSDID("$FreeBSD$");
  #include <stdlib.h>
  #include "el.h"
  
 -#define	OKCMD	-1
 -
 -private int	read__fixio(int, int);
 -private int	read_preread(EditLine *);
 -private int	read_char(EditLine *, char *);
 -private int	read_getcmd(EditLine *, el_action_t *, char *);
 -private void	read_pop(c_macro_t *);
 +enum rcmd {
 +	OKCMD	= -1,
 +	EOFCMD	= 0,
 +	ERRCMD	= 1
 +};
 +
 +private int		read__fixio(int, int);
 +private int		read_preread(EditLine *);
 +private int		read_char(EditLine *, char *);
 +private enum rcmd	read_getcmd(EditLine *, el_action_t *, char *);
 +private void		read_pop(c_macro_t *);
  
  /* read_init():
   *	Initialize the read stuff
 @@ -227,9 +231,9 @@ el_push(EditLine *el, const char *str)
  
  
  /* read_getcmd():
 - *	Return next command from the input stream.
 + *	Get next command from the input stream.
   */
 -private int
 +private enum rcmd
  read_getcmd(EditLine *el, el_action_t *cmdnum, char *ch)
  {
  	el_action_t cmd;
 @@ -238,8 +242,7 @@ read_getcmd(EditLine *el, el_action_t *cmdnum, char *ch)
  	el->el_errno = 0;
  	do {
  		if ((num = el_getc(el, ch)) != 1) {	/* if EOF or error */
 -			el->el_errno = num == 0 ? 0 : errno;
 -			return (num);
 +			return (num < 0 ? ERRCMD : EOFCMD);
  		}
  
  #ifdef	KANJI
 @@ -294,16 +297,18 @@ read_char(EditLine *el, char *cp)
  
   again:
  	el->el_signal->sig_no = 0;
 -	while ((num_read = read(el->el_infd, cp, 1)) == -1) {
 +	while ((num_read = read(el->el_infd, cp, 1)) < 0) {
 +		int e = errno;
  		if (el->el_signal->sig_no == SIGCONT) {
  			sig_set(el);
  			el_set(el, EL_REFRESH);
  			goto again;
  		}
 -		if (!tried && read__fixio(el->el_infd, errno) == 0)
 +		if (!tried && read__fixio(el->el_infd, e) == 0)
  			tried = 1;
  		else {
  			*cp = '\0';
 +			errno = e;
  			return (-1);
  		}
  	}
 @@ -369,8 +374,9 @@ el_getc(EditLine *el, char *cp)
  	(void) fprintf(el->el_errfile, "Reading a character\n");
  #endif /* DEBUG_READ */
  	num_read = (*el->el_read.read_char)(el, cp);
 +	el->el_errno = (num_read < 0) ? errno : 0;
  #ifdef DEBUG_READ
 -	(void) fprintf(el->el_errfile, "Got it %c\n", *cp);
 +	(void) fprintf(el->el_errfile, "Got <%c> (return %d)\n", *cp, num_read);
  #endif /* DEBUG_READ */
  	return (num_read);
  }
 @@ -426,7 +432,7 @@ el_gets(EditLine *el, int *nread)
  		char *cp = el->el_line.buffer;
  		size_t idx;
  
 -		while ((*el->el_read.read_char)(el, cp) == 1) {
 +		while ((num = (*el->el_read.read_char)(el, cp)) == 1) {
  			/* make sure there is space for next character */
  			if (cp + 1 >= el->el_line.limit) {
  				idx = (cp - el->el_line.buffer);
 @@ -479,7 +485,7 @@ el_gets(EditLine *el, int *nread)
  
  		term__flush(el);
  
 -		while ((*el->el_read.read_char)(el, cp) == 1) {
 +		while ((num = (*el->el_read.read_char)(el, cp)) == 1) {
  			/* make sure there is space next character */
  			if (cp + 1 >= el->el_line.limit) {
  				idx = (cp - el->el_line.buffer);
 @@ -504,13 +510,14 @@ el_gets(EditLine *el, int *nread)
  		goto done;
  	}
  
 -	for (num = OKCMD; num == OKCMD;) {	/* while still editing this
 -						 * line */
 +	/* While still editing this line */
 +	for (num = 0;; num = 0) {
 +		enum rcmd rcmd;
  #ifdef DEBUG_EDIT
  		read_debug(el);
  #endif /* DEBUG_EDIT */
 -		/* if EOF or error */
 -		if ((num = read_getcmd(el, &cmdnum, &ch)) != OKCMD) {
 +		if ((rcmd = read_getcmd(el, &cmdnum, &ch)) != OKCMD) {
 +			num = (rcmd == ERRCMD) ? -1 : 0;
  #ifdef DEBUG_READ
  			(void) fprintf(el->el_errfile,
  			    "Returning from el_gets %d\n", num);
 @@ -589,9 +596,10 @@ el_gets(EditLine *el, int *nread)
  			continue;	/* keep going... */
  
  		case CC_EOF:	/* end of file typed */
 +			rcmd = EOFCMD;
  			if ((el->el_flags & UNBUFFERED) == 0)
  				num = 0;
 -			else if (num == -1) {
 +			else {
  				*el->el_line.lastchar++ = CONTROL('d');
  				el->el_line.cursor = el->el_line.lastchar;
  				num = 1;
 @@ -599,6 +607,7 @@ el_gets(EditLine *el, int *nread)
  			break;
  
  		case CC_NEWLINE:	/* normal end of line */
 +			rcmd = EOFCMD;
  			num = (int)(el->el_line.lastchar - el->el_line.buffer);
  			break;
  
 @@ -628,6 +637,8 @@ el_gets(EditLine *el, int *nread)
  		el->el_chared.c_vcmd.action = NOP;
  		if (el->el_flags & UNBUFFERED)
  			break;
 +		if (rcmd != OKCMD)
 +			break;
  	}
  
  	term__flush(el);		/* flush any buffered output */
 -- 
 1.7.9.rc2.1.g69204
 
 
 --=_504f6267.iUCe6UiHu0SzYFl1a67qpbTPwc33pf/H4idnoT7GkXfIKRja
 Content-Type: text/plain;
  charset=us-ascii
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
  filename="0002-Add-an-EL_RESTART_READ-option-to-editline-3.patch"
 
 From eb154cc49285ead7852e4e50358d48bb90cda9df Mon Sep 17 00:00:00 2001
 Message-Id: <eb154cc49285ead7852e4e50358d48bb90cda9df.1347378617.git.sdaoden@gmail.com>
 In-Reply-To: <e889bca8dc1fe7ddb95651ecbe98ad05bd59e22e.1347378617.git.sdaoden@gmail.com>
 References: <e889bca8dc1fe7ddb95651ecbe98ad05bd59e22e.1347378617.git.sdaoden@gmail.com>
 From: "Steffen \"Daode\" Nurpmeso" <sdaoden@gmail.com>
 Date: Tue, 11 Sep 2012 15:39:47 +0200
 Subject: [PATCH 2/3] Add an EL_RESTART_READ option to editline(3)
 
 Make it possible to realize read(2) restarts after EINTR errors
 without actually going the expensive (and sometimes impossible
 or at least undesirable) way through signal handling.
 ---
  lib/libedit/editline.3 |   20 ++++++++++++++++++++
  lib/libedit/el.c       |   12 ++++++++++++
  lib/libedit/el.h       |    1 +
  lib/libedit/histedit.h |    1 +
  lib/libedit/read.c     |    2 ++
  5 files changed, 36 insertions(+), 0 deletions(-)
 
 diff --git a/lib/libedit/editline.3 b/lib/libedit/editline.3
 index fe58321..6ecbac8 100644
 --- a/lib/libedit/editline.3
 +++ b/lib/libedit/editline.3
 @@ -385,6 +385,22 @@ check this
  (using
  .Fn el_get )
  to determine if editing should be enabled or not.
 +.It Dv EL_RESTART_READ , Fa "int flag"
 +If
 +.Fa flag
 +is not zero (as per default),
 +then
 +.Fn el_getc
 +and
 +.Fn el_gets
 +will restart character reads that failed with
 +.Dv EINTR
 +errors.
 +Note this may be restricted to the builtin character read function
 +.Dv EL_BUILTIN_GETCFN
 +(see
 +.Dv EL_GETCFN
 +below).
  .It Dv EL_GETCFN , Fa "int (*f)(EditLine *, char *c)"
  Define the character reading function as
  .Fa f ,
 @@ -486,6 +502,10 @@ Retrieve
  previously registered with the corresponding
  .Fn el_set
  call.
 +.It Dv EL_RESTART_READ , Fa "int"
 +Return non-zero if reading of characters is automatically restarted for
 +.Dv EINTR
 +errors.
  .It Dv EL_UNBUFFERED , Fa "int"
  Sets or clears unbuffered mode.
  In this mode,
 diff --git a/lib/libedit/el.c b/lib/libedit/el.c
 index d6cfb2d..8418f46 100644
 --- a/lib/libedit/el.c
 +++ b/lib/libedit/el.c
 @@ -274,6 +274,13 @@ el_set(EditLine *el, int op, ...)
  		el->el_data = va_arg(ap, void *);
  		break;
  
 +	case EL_RESTART_READ:
 +		if (va_arg(ap, int))
 +			el->el_flags |= RESTART_READ;
 +		else
 +			el->el_flags &= ~RESTART_READ;
 +		break;
 +
  	case EL_UNBUFFERED:
  		rv = va_arg(ap, int);
  		if (rv && !(el->el_flags & UNBUFFERED)) {
 @@ -435,6 +442,11 @@ el_get(EditLine *el, int op, ...)
  		rv = 0;
  		break;
  
 +	case EL_RESTART_READ:
 +		*va_arg(ap, int *) = ((el->el_flags & RESTART_READ) != 0);
 +		rv = 0;
 +		break;
 +
  	case EL_UNBUFFERED:
  		*va_arg(ap, int *) = (!(el->el_flags & UNBUFFERED));
  		rv = 0;
 diff --git a/lib/libedit/el.h b/lib/libedit/el.h
 index 67d01ff..e296ff6 100644
 --- a/lib/libedit/el.h
 +++ b/lib/libedit/el.h
 @@ -55,6 +55,7 @@
  #define	NO_TTY		0x02
  #define	EDIT_DISABLED	0x04
  #define	UNBUFFERED	0x08
 +#define RESTART_READ	0x100
  
  typedef int bool_t;			/* True or not			*/
  
 diff --git a/lib/libedit/histedit.h b/lib/libedit/histedit.h
 index 8a6caf9..5f457f8 100644
 --- a/lib/libedit/histedit.h
 +++ b/lib/libedit/histedit.h
 @@ -139,6 +139,7 @@ unsigned char	_el_fn_sh_complete(EditLine *, int);
  #define	EL_PROMPT_ESC	21	/* , prompt_func, Char);	      set/get */
  #define	EL_RPROMPT_ESC	22	/* , prompt_func, Char);	      set/get */
  #define	EL_RESIZE	23	/* , el_zfunc_t, void *);	      set     */
 +#define	EL_RESTART_READ	24	/* , int);			      set/get */
  
  #define	EL_BUILTIN_GETCFN	(NULL)
  
 diff --git a/lib/libedit/read.c b/lib/libedit/read.c
 index 0880b5c..4c5996c 100644
 --- a/lib/libedit/read.c
 +++ b/lib/libedit/read.c
 @@ -304,6 +304,8 @@ read_char(EditLine *el, char *cp)
  			el_set(el, EL_REFRESH);
  			goto again;
  		}
 +		if (e == EINTR && (el->el_flags & RESTART_READ))
 +			goto again;
  		if (!tried && read__fixio(el->el_infd, e) == 0)
  			tried = 1;
  		else {
 -- 
 1.7.9.rc2.1.g69204
 
 
 --=_504f6267.iUCe6UiHu0SzYFl1a67qpbTPwc33pf/H4idnoT7GkXfIKRja
 Content-Type: text/plain;
  charset=us-ascii
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
  filename="0003-Set-EL_RESTART_READ-in-interactive-sh-1-sessions.patch"
 
 From d9ba7f771ff084df56ee8ebacd0d7cb455189ecc Mon Sep 17 00:00:00 2001
 Message-Id: <d9ba7f771ff084df56ee8ebacd0d7cb455189ecc.1347378617.git.sdaoden@gmail.com>
 In-Reply-To: <e889bca8dc1fe7ddb95651ecbe98ad05bd59e22e.1347378617.git.sdaoden@gmail.com>
 References: <e889bca8dc1fe7ddb95651ecbe98ad05bd59e22e.1347378617.git.sdaoden@gmail.com>
 From: "Steffen \"Daode\" Nurpmeso" <sdaoden@gmail.com>
 Date: Tue, 11 Sep 2012 17:50:11 +0200
 Subject: [PATCH 3/3] Set EL_RESTART_READ in interactive sh(1) sessions
 
 ---
  bin/sh/histedit.c |    1 +
  1 files changed, 1 insertions(+), 0 deletions(-)
 
 diff --git a/bin/sh/histedit.c b/bin/sh/histedit.c
 index 6371599..67056d0 100644
 --- a/bin/sh/histedit.c
 +++ b/bin/sh/histedit.c
 @@ -125,6 +125,7 @@ histedit(void)
  				el_set(el, EL_ADDFN, "sh-complete",
  				    "Filename completion",
  				    _el_fn_sh_complete);
 +				el_set(el, EL_RESTART_READ, 1);
  			} else {
  bad:
  				out2fmt_flush("sh: can't initialize editing\n");
 -- 
 1.7.9.rc2.1.g69204
 
 
 --=_504f6267.iUCe6UiHu0SzYFl1a67qpbTPwc33pf/H4idnoT7GkXfIKRja--
Responsible-Changed-From-To: jilles->freebsd-bugs 
Responsible-Changed-By: jilles 
Responsible-Changed-When: Sun Sep 1 16:11:38 UTC 2013 
Responsible-Changed-Why:  
The underlying problem is in libedit, not in sh. 

A dirty workaround is possible in sh by setting SA_RESTART for SIGWINCH. 
This prevents the repeated operations but does not apply the size change 
until the next line. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=169773 

From: Jilles Tjoelker <jilles@stack.nl>
To: bug-followup@FreeBSD.org, peter@rulingia.com
Cc:  
Subject: Re: bin/169773: [libedit] Resizing causes /bin/sh to repeat edit
 operations
Date: Wed, 15 Jan 2014 23:31:57 +0100

 In PR bin/169773, you wrote:
 > Given an account with /bin/sh as the login shell, if you type
 > some input, ending with an editing operation (eg backspace) and
 > then resize the window multiple times without further input,
 > the edit operation will be repeated on every second resize.
 
 I have removed the SIGWINCH handler from sh in r260654, as I don't think
 the libedit problem will be fixed properly any time soon. This is not
 entirely ideal, but stops most of the erratic behaviour.
 
 -- 
 Jilles Tjoelker
>Unformatted:
