Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
readline: fix issue with newline-less last line
The logic for reading lines was slightly flawed, in that it assumed
there would be a final new line. It handled the case where there are no
new lines, but this then broke if there were some new lines.

The fix in logic is basically removing the special case where there are
no new lines by changing it to always read the final line with no new
lines. This works because if a file contains no new lines, the final
line is the first line, and all is well.

There is some subtlety in this functioning, however. If the last line
contains no new lines, then `lastIndex` will be the start of the last
line, and `kInsertString` will be called from that point. If it does
contain a new line, `lastIndex` will be equal to `s.length`, so the
slice will be the empty string.

Fixes: #47305
  • Loading branch information
harrisi committed Mar 30, 2023
commit 44c465c08e38f1703ff5a385c97259058b6c5a5c
26 changes: 12 additions & 14 deletions lib/internal/readline/interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -1324,21 +1324,19 @@ class Interface extends InterfaceConstructor {
if (typeof s === 'string' && s) {
// Erase state of previous searches.
lineEnding.lastIndex = 0;
let nextMatch = RegExpPrototypeExec(lineEnding, s);
// If no line endings are found, just insert the string as is.
if (nextMatch === null) {
this[kInsertString](s);
} else {
// Keep track of the end of the last match.
let lastIndex = 0;
do {
this[kInsertString](StringPrototypeSlice(s, lastIndex, nextMatch.index));
({ lastIndex } = lineEnding);
this[kLine]();
// Restore lastIndex as the call to kLine could have mutated it.
lineEnding.lastIndex = lastIndex;
} while ((nextMatch = RegExpPrototypeExec(lineEnding, s)) !== null);
let nextMatch;
// Keep track of the end of the last match.
let lastIndex = 0;
while ((nextMatch = RegExpPrototypeExec(lineEnding, s)) !== null) {
this[kInsertString](StringPrototypeSlice(s, lastIndex, nextMatch.index));
({ lastIndex } = lineEnding);
this[kLine]();
// Restore lastIndex as the call to kLine could have mutated it.
lineEnding.lastIndex = lastIndex;
}
// This ensures that the last line is written if it doesn't end in a newline.
// Note that the last line may be the first line, in which case this still works.
this[kInsertString](StringPrototypeSlice(s, lastIndex));
}
}
}
Expand Down