less: fix very obscure memory corruption bug

This commit is contained in:
Denis Vlasenko 2007-05-31 15:56:10 +00:00
parent a1d24a0b6e
commit 22a9a3c6f8

View File

@ -204,7 +204,27 @@ static void fill_match_lines(unsigned pos);
#define fill_match_lines(pos) ((void)0) #define fill_match_lines(pos) ((void)0)
#endif #endif
/* Devilishly complex routine.
*
* Has to deal with EOF and EPIPE on input,
* with line wrapping, with last line not ending in '\n'
* (possibly not ending YET!), with backspace and tabs.
*
* Variables used:
* flines[] - array of lines already read. Linewrap may cause
* one source file line to occupy several flines[n].
* flines[max_fline] - last line, possibly incomplete.
* terminated - 1 if flines[max_fline] is 'terminated'
* (if there was '\n' [which isn't stored itself, we just remember
* that it was seen])
* max_lineno - last line's number, this one doesn't increment
* on line wrap, only on "real" new lines.
* readbuf[0..readeof-1] - small preliminary buffer.
* readbuf[readpos] - next character to add to current line.
* linepos - screen line position of next char to be read
* (takes into account tabs and backspaces)
* eof_error - < 0 error, == 0 EOF, > 0 not EOF/error
*/
static void read_lines(void) static void read_lines(void)
{ {
#define readbuf bb_common_bufsiz1 #define readbuf bb_common_bufsiz1
@ -225,6 +245,7 @@ static void read_lines(void)
cp += 8; cp += 8;
strcpy(current_line, cp); strcpy(current_line, cp);
p += strlen(current_line); p += strlen(current_line);
/* linepos is still valid from previous read_lines() */
} else { } else {
linepos = 0; linepos = 0;
} }
@ -275,17 +296,27 @@ static void read_lines(void)
if (c == '\x8' && linepos && p[-1] != '\t') { if (c == '\x8' && linepos && p[-1] != '\t') {
readpos++; /* eat it */ readpos++; /* eat it */
linepos--; linepos--;
/* was buggy (p could end up <= current_line)... */
*--p = '\0'; *--p = '\0';
continue; continue;
} }
if (c == '\t') {
linepos += (linepos^7) & 7; size_t new_linepos = linepos + 1;
linepos++; if (c == '\t') {
if (linepos >= w) new_linepos += 7;
break; new_linepos &= (~7);
}
if (new_linepos >= w)
break;
linepos = new_linepos;
}
/* ok, we will eat this char */ /* ok, we will eat this char */
readpos++; readpos++;
if (c == '\n') { terminated = 1; break; } if (c == '\n') {
terminated = 1;
linepos = 0;
break;
}
/* NUL is substituted by '\n'! */ /* NUL is substituted by '\n'! */
if (c == '\0') c = '\n'; if (c == '\0') c = '\n';
*p++ = c; *p++ = c;