Anybody want to fix ash's backspace line wrap?

Rob Landley rob at landley.net
Fri May 15 08:17:11 UTC 2009


On Thursday 14 May 2009 19:31:15 Denys Vlasenko wrote:
> On Thursday 14 May 2009 21:59, Rob Landley wrote:
> > > > However, if your shell prompt didn't start at the left edge of the
> > > > screen (which happens surprisingly often), the resulting line editing
> > > > is completely horked once you go past the right edge of the screen.
> > > > (Command history becomes a lot less fun to use, too.)
> > > >
> > > > It's easy enough to fix once you know what the problem is and decide
> > > > to address it.  We just haven't yet...
> > >
> > > How to address it?
> >
> > Query the current cursor position at the start of an interactive shell
> > prompt, which is very similar to querying the width and height o the
> > screen.
>
> Yes, I know that this is achieved with the same ESC sequence.
> My worry is that if the response is processed asyncronously
> (looks like it's more sane way to do that), then it does not know
> what that response means.

Which is why it's probably actually _not_ the more sane way to do it.

> > The ansi escape mentioned previously consists of "save position", "jump
> > to lower right", "query current cursor position", "restore saved
> > position".  If you just send the query without the save/jump/restore, you
> > should get current cursor position back.
>
> I got that.
>
> > The shell's command line editing and vi already have escape parsing
> > logic, so it's relatively easy to fix this in two parts there.  (The
> > escape reply should always come in as a single unit without interspersed
> > characters, we just don't know _when_ we'll get it, or how long the delay
> > might be.  But there could always be unrelated data before/after it,
> > coming in
> > asynchronously.)
> >
> > A more general fix is to extend get_terminal_width_height() with the ANSI
> > escape and parsing code so that it tries harder when it can't get useful
> > info from the tty querying ioctl.  But this is fiddlier, because it can't
> > always blindly output the sequence and hope for the best.
> >
> > For example, the "ls" command calls get_terminal_width_height() but when
> > running that in a pipe adding spurious escape output isn't a good thing.
> > However, most of that could be cleaned up by grabbing the code from the
> > tty applet to identify our controlling tty (and whether we have one) and
> > write the probe string directly to the tty instead of to stdout, and
> > simply not probe if we haven't got a tty.  (I just checked and the tty
> > command does output /dev/ttyS0 or /dev/console when it's your controlling
> > tty, even though neither knows your screen size.  The only time we
> > _don't_ have a tty is when we're in a pipe.)
> >
> > I suspect ls already does a little of this sort of thing for colors, but
> > I haven't checked...
> >
> > > Try running eval `resize`. Does it (a) work at all? and
> > > (b) does it set LINES and COLUMNS?
> >
> > Setting columns and lines is a good thing, but A) it's a separate issue
> > from detecting cursor position, B) I think it's something the shell
> > should do automatically.  (It already does so in the pty case, just not
> > for serial consoles.)
> >
> > The more I think about it, the more the general fix to
> > get_terminal_width_height() seems like the way to go, because there are
> > actually _three_ race conditions with querying cursor position.
> >
> > The first is of course that the user can press a key at any time, so the
> > escape sequence reply you get may not be the first data in the input.
> > Robustifying get_terminal_width_height() means adding at least an
> > optional way to receive back all the other input that came in before the
> > escape reply, so you can process it.  (Note that this could include other
> > unrelated escapes, like "cursor up" for command history, so we can't just
> > stop at the first escape sequence, it has to be the _right_ one.  Again,
> > there's existing escape parsing logic in busybox...)
> >
> > The second race condition is that the standard vi or shell escape parsing
> > logic will print characters out as it receives them, and this moves the
> > cursor position.  So when we _do_ get the cursor position escape back,
> > presumably that's the cursor position at the point in the data stream
> > _before_ we printed the extra characters that were pending in our
> > input... but we've lost that context.  Seems like it's best not to print
> > any output between sending the probe and receiving the reply (or timing
> > out waiting for the reply), and then we can parse the other input
> > afterwards.
> >
> > The third race condition is that if you're querying both screen size
> > _and_ cursor position, you get back the same reply string and it means
> > two different things depending on context. (This is only a race condition
> > if you alter the vi/shell escape parsing logic and respond to it
> > asynchornously in a different context than the probe was sent from.  Yeah
> > you could have a global variable indicate which one you were waiting for
> > but that's ugly.)
>
> Exactly.
>
> I propose to try a simple solution first:
>
> * defeat "echo -n blabla" by outputting \r before the prompt.

So you never see that output.

>   Do we really care about these cases when there is an
>   unfinished line of output?

Well, I do.

(If you ever forget to stick a newline at the end of "hello world" and then 
run it, you'll get no output and never know why.  If your current command 
prompt starts with \r, and yes I've used environments that do that, welcome 
to a fun half hour debugging session until you figure out what's happening to 
your output.  Been there, done that.  Back on Solaris in college, I think.)

> * make read_key listen to ESC"[NNN;NNNR" sequence
>   and return its values

How do you propose to return the out of band data from the function?  (I 
suppose you could write two shorts to its char *buffer argument, although 
that's kind of ugly.)

By the way, what the heck is smalluint?  Why isn't uint16_t usable?  c99 was a 
full decade ago...

> * if neither $LINES/$COLUMNS are set nor ioctl(TIOCGWINSZ)
>   returns useful data, print
>   ESC"7" ESC"[r" ESC"[9999;9999H" ESC"[6n" ESC"8"
>   in order to retrieve terminal's size, and whenever read_key
>   does detect that, use this info for proper linewrapping
>   in line editing.

A) Some terminals don't handle 9999, they max out at 3 digits and abort 
parsing the sequence at 4.  (Yes, I tested this.)

B) That gives you size but not position.

> First snag I hit is that line editing does not yet use read_key,
> it has another (and buggy) implementation. Fixing that.
>
> Is the above plan good?

It's an improvement, but not a full fix.  Jumping to the left edge and 
overwriting data with a prompt is behavior other shells don't do for a 
_reason_.

> --
> vda

Rob
-- 
Latency is more important than throughput. It's that simple. - Linus Torvalds


More information about the busybox mailing list