Hi,
this diff doesn't just fix the "division by zero" for input files with
lines longer than 1023 chars in Plan B mode, it actually removes this
line limit!
Before:
$ dd if=/dev/zero bs=1 count=1024 | tr '\0' a > a
$ dd if=/dev/zero bs=1 count=1024 | tr '\0' b > b
$ diff -u a b > a.diff
$ patch -x 8 a a.diff
Hmm... Looks like a unified diff to me...
The text leading up to this was:
--------------------------
|--- a Sat Nov 29 17:34:15 2014
|+++ b Sat Nov 29 17:34:19 2014
--------------------------
Floating point exception (core dumped)
$_
After:
$ patch -x 8 a a.diff
Hmm... Looks like a unified diff to me...
The text leading up to this was:
--------------------------
|--- a Sat Nov 29 17:34:15 2014
|+++ b Sat Nov 29 17:34:19 2014
--------------------------
Patching file a using Plan B...
Hunk #1 succeeded at 1.
done
With this diff, patch uses fgetln to read very long lines. One could argue
that this limits patch to the amount of RAM it has available -- but
that will happen anyway because at least twice as much memory as the
longest line has to be allocated. fgetln is therefore a good choice to
easily parse the file.
If you want to regress-test, you can supply "-x 8" in PATCHOPTIONS to
enforce Plan B. Keep in mind that test5 will fail because this debug
option skips a check if the input file is available.
Tobias
Index: inp.c
===================================================================
RCS file: /cvs/src/usr.bin/patch/inp.c,v
retrieving revision 1.41
diff -u -p -u -p -r1.41 inp.c
--- inp.c 25 Nov 2014 10:22:08 -0000 1.41
+++ inp.c 29 Nov 2014 16:14:45 -0000
@@ -55,8 +55,9 @@ static char **i_ptr; /* pointers to line
static int tifd = -1; /* plan b virtual string array */
static char *tibuf[2]; /* plan b buffers */
static LINENUM tiline[2] = {-1, -1}; /* 1st line in each buffer */
-static LINENUM lines_per_buf; /* how many lines per buffer */
-static int tireclen; /* length of records in tmp file */
+static size_t lines_per_buf; /* how many lines per buffer */
+static size_t tibuflen; /* plan b buffer length */
+static size_t tireclen; /* length of records in tmp file */
static bool rev_in_string(const char *);
static bool reallocate_lines(size_t *);
@@ -333,8 +334,8 @@ static void
plan_b(const char *filename)
{
FILE *ifp;
- size_t i = 0, j, maxlen = 1;
- char *p;
+ size_t i = 0, j, len, maxlen = 1;
+ char *lbuf = NULL, *p;
bool found_revision = (revision == NULL);
using_plan_a = false;
@@ -343,15 +344,28 @@ plan_b(const char *filename)
(void) unlink(TMPINNAME);
if ((tifd = open(TMPINNAME, O_EXCL | O_CREAT | O_WRONLY, 0666)) < 0)
pfatal("can't open file %s", TMPINNAME);
- while (fgets(buf, sizeof buf, ifp) != NULL) {
- if (revision != NULL && !found_revision && rev_in_string(buf))
+ while ((p = fgetln(ifp, &len)) != NULL) {
+ if (p[len - 1] == '\n')
+ p[len - 1] = '\0';
+ else {
+ /* EOF without EOL, copy and add the NUL */
+ if ((lbuf = malloc(len + 1)) == NULL)
+ fatal("out of memory\n");
+ memcpy(lbuf, p, len);
+ lbuf[len] = '\0';
+ p = lbuf;
+
+ last_line_missing_eol = true;
+ len++;
+ }
+ if (revision != NULL && !found_revision && rev_in_string(p))
found_revision = true;
- if ((i = strlen(buf)) > maxlen)
- maxlen = i; /* find longest line */
+ if (len > maxlen)
+ maxlen = len; /* find longest line */
}
- last_line_missing_eol = i > 0 && buf[i - 1] != '\n';
- if (last_line_missing_eol && maxlen == i)
- maxlen++;
+ free(lbuf);
+ if (ferror(ifp))
+ pfatal("can't read file %s", filename);
if (revision != NULL) {
if (!found_revision) {
@@ -376,23 +390,26 @@ plan_b(const char *filename)
revision);
}
fseek(ifp, 0L, SEEK_SET); /* rewind file */
- lines_per_buf = BUFFERSIZE / maxlen;
tireclen = maxlen;
- tibuf[0] = malloc(BUFFERSIZE + 1);
+ tibuflen = maxlen > BUFFERSIZE ? maxlen : BUFFERSIZE;
+ lines_per_buf = tibuflen / maxlen;
+ tibuf[0] = malloc(tibuflen + 1);
if (tibuf[0] == NULL)
fatal("out of memory\n");
- tibuf[1] = malloc(BUFFERSIZE + 1);
+ tibuf[1] = malloc(tibuflen + 1);
if (tibuf[1] == NULL)
fatal("out of memory\n");
for (i = 1;; i++) {
p = tibuf[0] + maxlen * (i % lines_per_buf);
if (i % lines_per_buf == 0) /* new block */
- if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
+ if (write(tifd, tibuf[0], tibuflen) !=
+ (ssize_t) tibuflen)
pfatal("can't write temp file");
if (fgets(p, maxlen + 1, ifp) == NULL) {
input_lines = i - 1;
if (i % lines_per_buf != 0)
- if (write(tifd, tibuf[0], BUFFERSIZE) <
BUFFERSIZE)
+ if (write(tifd, tibuf[0], tibuflen) !=
+ (ssize_t) tibuflen)
pfatal("can't write temp file");
break;
}
@@ -434,10 +451,11 @@ ifetch(LINENUM line, int whichbuf)
tiline[whichbuf] = baseline;
if (lseek(tifd, (off_t) (baseline / lines_per_buf *
- BUFFERSIZE), SEEK_SET) < 0)
+ tibuflen), SEEK_SET) < 0)
pfatal("cannot seek in the temporary input
file");
- if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0)
+ if (read(tifd, tibuf[whichbuf], tibuflen)
+ != (ssize_t) tibuflen)
pfatal("error reading tmp file %s", TMPINNAME);
}
return tibuf[whichbuf] + (tireclen * offline);