Subject: | bug in redisplay() when cursor is (length($prompt) from right margin |
This bug is in Term-ReadLine-Perl-1.0203. I'm using perl version 5.8.3 on Solaris 8.
The error occurs when the cursor is (length($prompt) characters from the right margin. If you insert a character, the readline library incorrectly assumes that the cursor is at the end of the line and so it leaves it there.
Here is an example: run the "make test" in the Term-ReadLine-Perl distribution:
PERL_DL_NONLAZY=1 /apps/perl/5.8.3/bin/perl "-Iblib/lib" "-Iblib/arch" test.pl
Features present: preput 1 getHistory 1 addHistory 1 attribs 1 ornaments 1 appname 1 minline 1 autohistory 1 newTTY 1 tkRunning 1 setHistory 1
Enter arithmetic or Perl expression: exit
To make it easier to see the bug, make the prompt shorter: delete the "exit" string and put in
$prompt = "test:";
and then press return to get:
test:
test:exit
Now, add 5 characters to the end of the line:
test:
test:exit12345
Assuming you are in vi mode, press Escape to exit "edit" mode, and then back up until the cursor is over the "1" (by pressing "h" four times).
Press "i" to enter insert mode, and type some input, say, "abc":
test:exitabc12345
It inserts correctly, but the cursor is now at the end of the line. Press Escape and it will stay over the "5". Now press "h" repeatedly to try to get to the beginning of the line. It won't work. The cursor only goes back as far as the "b".
I hope that you were able to reproduce the problem using this explanation. Again, basically the way to see the problem is to make the cursor be N characters from the end of the string, where N is the length of the prompt. Then try inserting something and the readline module is now confused about where the cursor is displayed.
The problem seems to be in the redisplay() subroutine, in particular in these lines (lines 1795-1796 in my copy of the code):
print $term_OUT "\r",substr_with_props($prompt, $line, 0, $delta, $have_ket)
if $delta != length ("$line") || $lastlen > $thislen;
Basically what's going on here is it's going to reprint a portion of the line in order to position the cursor in the correct place. The first part of the if statement checks to see if the cursor is already at the end of the line ($delta is the desired cursor position, counted from the left margin, and $line is the line of input). If it's already at the end of the line, it doesn't bother reprinting the line.
The problem is that while at the beginning of the function $line is the entire printed line, including the prompt *and* the input, at this part of the function $line is now just the input string - not the prompt. So comparing $delta to length($line) doesn't compare the right things (again, $delta is measured from the left column, before the prompt, but length($line) is the length of the input - *after* the prompt).
The fix appears to be to compare $delta to the input line and prompt together. Here's one way to fix the problem. Change the lines to read:
print $term_OUT "\r",substr_with_props($prompt, $line, 0, $delta, $have_ket)
if $delta != length ("$prompt$line") || $lastlen > $thislen;
So it now checks length of "$prompt$line", not just $line.
I made this change in my copy of the code and now the redisplay function seems to work correctly.