rredpatch.c 19.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*  This file is part of "reprepro"
 *  Copyright (C) 2009 Bernhard R. Link
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02111-1301  USA
 */
#include <config.h>

#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <assert.h>
#include "error.h"
29
#include "rredpatch.h"
30 31 32

struct modification {
	/* next item in the list (sorted by oldlinestart) */
33
	struct modification *next, *previous;
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
	/* each modification removes an (possible empty) range from
	 * the file and replaces it with an (possible empty) range
	 * of new lines */
	int oldlinestart, oldlinecount, newlinecount;
	size_t len;
	const char *content;
	/* a entry might be followed by one other with the same
	 * oldlinestart (due to merging or inefficient patches),
	 * but always: next->oldlinestart >= oldlinestart + oldlinecount
	 */
};

struct rred_patch {
	int fd;
	/* content of the file mapped with mmap */
	char *data;
	off_t len;
	struct modification *modifications;
52
	bool alreadyinuse;
53 54
};

55
void modification_freelist(struct modification *p) {
56
	while (p != NULL) {
57 58 59 60 61 62
		struct modification *m = p;
		p = m->next;
		free(m);
	}
}

63 64 65
struct modification *modification_dup(const struct modification *p) {
	struct modification *first = NULL, *last = NULL;

66 67
	for (; p != NULL ; p = p->next) {
		struct modification *m = NEW(struct modification);
68

69
		if (FAILEDTOALLOC(m)) {
70 71 72 73 74 75
			modification_freelist(first);
			return NULL;
		}
		*m = *p;
		m->next = NULL;
		m->previous = last;
76
		if (last == NULL)
77 78 79 80 81 82 83 84
			first = m;
		else
			m->previous->next = m;
		last = m;
	}
	return first;
}

85 86 87
struct modification *patch_getmodifications(struct rred_patch *p) {
	struct modification *m;

88
	assert (!p->alreadyinuse);
89 90 91 92 93 94
	m = p->modifications;
	p->modifications = NULL;
	p->alreadyinuse = true;
	return m;
}

95
const struct modification *patch_getconstmodifications(struct rred_patch *p) {
96
	assert (!p->alreadyinuse);
97 98 99
	return p->modifications;
}

100 101 102 103 104 105
static struct modification *modification_freehead(/*@only@*/struct modification *p) {
	struct modification *m = p->next;
	free(p);
	return m;
}

106
void patch_free(/*@only@*/struct rred_patch *p) {
107
	if (p->data != NULL)
108
		(void)munmap(p->data, p->len);
109
	if (p->fd >= 0)
110 111 112 113 114
		(void)close(p->fd);
	modification_freelist(p->modifications);
	free(p);
}

115
retvalue patch_load(const char *filename, off_t length, struct rred_patch **patch_p) {
116 117 118
	int fd;

	fd = open(filename, O_NOCTTY|O_RDONLY);
119
	if (fd < 0) {
120 121 122 123 124 125 126 127 128 129
		int err = errno;
		fprintf(stderr,
"Error %d opening '%s' for reading: %s\n", err, filename, strerror(err));
		return RET_ERRNO(err);
	}
	return patch_loadfd(filename, fd, length, patch_p);

}

retvalue patch_loadfd(const char *filename, int fd, off_t length, struct rred_patch **patch_p) {
130 131 132 133 134 135 136 137
	int i;
	struct rred_patch *patch;
	const char *p, *e, *d, *l;
	int number, number2, line;
	char type;
	struct modification *n;
	struct stat statbuf;

138 139
	patch = zNEW(struct rred_patch);
	if (FAILEDTOALLOC(patch)) {
140
		(void)close(fd);
141 142
		return RET_ERROR_OOM;
	}
143
	patch->fd = fd;
144
	i = fstat(patch->fd, &statbuf);
145
	if (i != 0) {
146 147 148 149 150 151
		int err = errno;
		fprintf(stderr,
"Error %d retrieving length of '%s': %s\n", err, filename, strerror(err));
		patch_free(patch);
		return RET_ERRNO(err);
	}
152
	if (length == -1)
153
		length = statbuf.st_size;
154
	if (statbuf.st_size != length) {
155 156 157 158 159 160 161
		int err = errno;
		fprintf(stderr,
"Unexpected size of '%s': expected %lld, got %lld\n", filename,
			(long long)length, (long long)statbuf.st_size);
		patch_free(patch);
		return RET_ERRNO(err);
	}
162
	if (length == 0) {
163 164 165 166 167 168 169 170 171
		/* handle empty patches gracefully */
		close(patch->fd);
		patch->fd = -1;
		patch->data = NULL;
		patch->len = 0;
		patch->modifications = NULL;
		*patch_p = patch;
		return RET_OK;
	}
172 173 174
	patch->len = length;
	patch->data = mmap(NULL, patch->len, PROT_READ, MAP_PRIVATE,
			patch->fd, 0);
175
	if (patch->data == MAP_FAILED) {
176 177 178 179 180 181 182 183 184
		int err = errno;
		fprintf(stderr,
"Error %d mapping '%s' into memory: %s\n", err, filename, strerror(err));
		patch_free(patch);
		return RET_ERRNO(err);
	}
	p = patch->data;
	e = p + patch->len;
	line = 1;
185
	while (p < e) {
186 187 188
		/* <number>,<number>(c|d)\n or <number>(a|i|c|d) */
		d = p;
		number = 0; number2 = -1;
189
		while (d < e && *d >= '0' && *d <= '9') {
190 191 192
			number = (*d - '0') + 10 * number;
			d++;
		}
193
		if (d > p && d < e && *d == ',') {
194 195
			d++;
			number2 = 0;
196
			while (d < e && *d >= '0' && *d <= '9') {
197 198 199
				number2 = (*d - '0') + 10 * number2;
				d++;
			}
200
			if (number2 < number) {
201 202 203 204 205 206 207
				fprintf(stderr,
"Error parsing '%s': malformed range (2nd number smaller than 1s) at line %d\n",
					filename, line);
				patch_free(patch);
				return RET_ERROR;
			}
		}
208
		if (d >= e || (*d != 'c' && *d != 'i' && *d != 'a' && *d != 'd')) {
209 210 211 212 213 214 215 216
			fprintf(stderr,
"Error parsing '%s': expected rule (c,i,a or d) at line %d\n",
				filename, line);
			patch_free(patch);
			return RET_ERROR;
		}
		type = *d;
		d++;
217
		while (d < e && *d == '\r')
218
			d++;
219
		if (d >= e || *d != '\n') {
220 221 222 223 224 225 226 227 228
			fprintf(stderr,
"Error parsing '%s': expected newline after command at line %d\n",
				filename, line);
			patch_free(patch);
			return RET_ERROR;
		}
		d++;
		line++;

229
		if (type != 'a' && number == 0) {
230 231 232 233 234 235
			fprintf(stderr,
"Error parsing '%s': missing number at line %d\n",
				filename, line);
			patch_free(patch);
			return RET_ERROR;
		}
236
		if (type != 'c' && type != 'd'  && number2 >= 0) {
237 238 239 240 241 242
			fprintf(stderr,
"Error parsing '%s': line range not allowed with %c at line %d\n",
				filename, (char)type, line);
			patch_free(patch);
			return RET_ERROR;
		}
243 244
		n = zNEW(struct modification);
		if (FAILEDTOALLOC(n)) {
245 246 247 248
			patch_free(patch);
			return RET_ERROR_OOM;
		}
		n->next = patch->modifications;
249
		if (n->next != NULL)
250
			n->next->previous = n;
251 252 253
		patch->modifications = n;

		p = d;
254
		if (type == 'd') {
255 256 257 258 259 260 261
			n->content = NULL;
			n->len = 0;
			n->newlinecount = 0;
		} else {
			int startline = line;

			l = p;
262
			while (l < e) {
263
				p = l;
264
				while (l < e && *l != '\n')
265
					l++;
266 267
				if (l >= e) {
					if (l == p + 1 && *p == '.') {
268 269 270 271 272 273 274 275 276 277 278
						/* that is also corrupted,
						 * but we can cure it */
						break;
					}
					fprintf(stderr,
"Error parsing '%s': ends in unterminated line. File most likely corrupted\n",
							filename);
					patch_free(patch);
					return RET_ERROR;
				}
				l++;
279
				if (p[0] == '.' && (p[1] == '\n' || p[1] == '\r'))
280 281 282
					break;
				line++;
			}
283
			if (p[0] != '.' || (l > p + 1 && p[1] != '\n' && p[1] != '\r')) {
284 285 286 287 288 289 290 291 292 293 294 295
					fprintf(stderr,
"Error parsing '%s': ends waiting for dot. File most likely corrupted\n",
							filename);
					patch_free(patch);
					return RET_ERROR;
			}
			n->content = d;
			n->len = p - d;
			n->newlinecount = line - startline;
			p = l;
			line++;
		}
296
		if (type == 'a') {
297 298 299
			/* appends appends after instead of before something: */
			n->oldlinestart = number + 1;
			n->oldlinecount = 0;
300
		} else if (type == 'i') {
301 302 303 304
			n->oldlinestart = number;
			n->oldlinecount = 0;
		} else {
			n->oldlinestart = number;
305
			if (number2 < 0)
306 307 308 309 310 311
				n->oldlinecount = 1;
			else
				n->oldlinecount = (number2 - number) + 1;
		}
		/* make sure things are in the order diff usually
		 * generates them, which makes line-calculation much easier: */
312 313 314
		if (n->next != NULL) {
			if (n->oldlinestart + n->oldlinecount
					> n->next->oldlinestart) {
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
				struct modification *first, *second;
				retvalue r;

				// TODO: it might be more efficient to
				// first store the different parts as different
				// patchsets and then combine...

				/* unlink and feed into patch merger */
				first = n->next;
				first->previous = NULL;
				second = n;
				n->next = NULL;
				n = NULL;
				r = combine_patches(&n, first, second);
				patch->modifications = n;
330
				if (RET_WAS_ERROR(r)) {
331 332 333
					patch_free(patch);
					return r;
				}
334 335 336 337 338 339 340 341 342 343 344 345 346 347
			}
		}
	}
	*patch_p = patch;
	return RET_OK;
}

static void modification_stripendlines(struct modification *m, int r) {
	int lines;
	const char *p;

	m->newlinecount -= r;
	lines = m->newlinecount;
	p = m->content;
348 349
	while (lines > 0) {
		while (*p != '\n')
350 351 352 353
			p++;
		p++;
		lines--;
	}
354
	assert ((size_t)(p - m->content) <= m->len);
355 356 357 358 359 360 361 362
	m->len = p - m->content;
}

static void modification_stripstartlines(struct modification *m, int r) {
	const char *p;

	m->newlinecount -= r;
	p = m->content;
363 364
	while (r > 0) {
		while (*p != '\n')
365 366 367 368
			p++;
		p++;
		r--;
	}
369
	assert ((size_t)(p - m->content) <= m->len);
370 371 372 373
	m->len -= p - m->content;
	m->content = p;
}

374 375 376 377 378 379
static inline void move_queue(struct modification **last_p, struct modification **result_p, struct modification **from_p) {
	struct modification *toadd, *last;

	/* remove from queue: */
	toadd = *from_p;
	*from_p = toadd->next;
380
	if (toadd->next != NULL) {
381 382 383 384 385
		toadd->next->previous = NULL;
		toadd->next = NULL;
	}

	/* if nothing yet, make it the first */
386
	if (*last_p == NULL) {
387 388 389 390 391 392 393
		*result_p = toadd;
		toadd->previous = NULL;
		*last_p = toadd;
		return;
	}

	last = *last_p;
394
	if (toadd->oldlinestart == last->oldlinestart + last->oldlinecount) {
395
		/* check if something can be combined: */
396
		if (toadd->newlinecount == 0) {
397 398 399 400
			last->oldlinecount += toadd->oldlinecount;
			free(toadd);
			return;
		}
401
		if (last->newlinecount == 0) {
402 403 404
			toadd->oldlinestart = last->oldlinestart;
			toadd->oldlinecount += last->oldlinecount;
			toadd->previous = last->previous;
405
			if (toadd->previous == NULL)
406 407 408 409 410 411 412
				*result_p = toadd;
			else
				toadd->previous->next = toadd;
			*last_p = toadd;
			free(last);
			return;
		}
413
		if (last->content + last->len == toadd->content) {
414 415 416 417 418 419 420 421 422
			last->oldlinecount += toadd->oldlinecount;
			last->newlinecount += toadd->newlinecount;
			last->len += toadd->len;
			free(toadd);
			return;
		}
	}
	toadd->previous = last;
	last->next = toadd;
423
	assert (last->oldlinestart + last->oldlinecount <= toadd->oldlinestart);
424 425 426 427
	*last_p = toadd;
	return;
}

428 429 430
/* this merges a set of modifications into an already existing stack,
 * modifying line numbers or even cutting away deleted/newly overwritten
 * stuff as necessary */
431 432
retvalue combine_patches(struct modification **result_p, /*@only@*/struct modification *first, /*@only@*/struct modification *second) {
	struct modification *p, *a, *result, *last;
433 434 435 436
	long lineofs;

	p = first;
	result = NULL;
437
	last = NULL;
438 439 440 441
	a = second;

	lineofs = 0;

442
	while (a != NULL) {
443 444
		/* modification totally before current one,
		 * so just add it before it */
445 446
		if (p == NULL || lineofs + a->oldlinestart + a->oldlinecount
				<= p->oldlinestart) {
447
			a->oldlinestart += lineofs;
448
			move_queue(&last, &result, &a);
449 450
			assert (p == NULL || p->oldlinestart >=
					last->oldlinestart + last->oldlinecount);
451 452 453 454
			continue;
		}
		/* modification to add after current head modification,
		 * so finalize head modification and update lineofs */
455 456
		if (lineofs + a->oldlinestart
				>= p->oldlinestart + p->newlinecount) {
457
			lineofs += p->oldlinecount - p->newlinecount;
458
			move_queue(&last, &result, &p);
459 460
			assert (lineofs + a->oldlinestart >=
					last->oldlinestart + last->oldlinecount);
461 462 463
			continue;
		}
		/* new modification removes everything the old one added: */
464
		if (lineofs + a->oldlinestart <= p->oldlinestart
465
			  && lineofs + a->oldlinestart + a->oldlinecount
466
				>= p->oldlinestart + p->newlinecount) {
467
			a->oldlinestart -= p->oldlinecount - p->newlinecount;
468
			a->oldlinecount += p->oldlinecount - p->newlinecount;
469
			lineofs += p->oldlinecount - p->newlinecount;
470
			p = modification_freehead(p);
471
			if (a->oldlinecount == 0 && a->newlinecount == 0) {
472 473 474
				/* a exactly cancels p */
				a = modification_freehead(a);
			}
475 476
			/* otherwise a is not yet finished,
			 * it might modify more */
477 478 479 480 481
			continue;
		}
		/* otherwise something overlaps, things get complicated here: */

		/* start of *a removes end of *p, so reduce *p: */
482
		if (lineofs + a->oldlinestart > p->oldlinestart &&
483 484 485
				lineofs + a->oldlinestart
				< p->oldlinestart + p->newlinecount &&
				lineofs + a->oldlinestart + a->oldlinecount
486
				>= p->oldlinestart + p->newlinecount) {
487 488 489 490 491 492 493 494 495 496
			int removedlines = p->oldlinestart + p->newlinecount
				- (lineofs + a->oldlinestart);

			/* finalize p as before */
			lineofs += p->oldlinecount - p->newlinecount;
			/* just telling a to delete less */
			a->oldlinestart += removedlines;
			a->oldlinecount -= removedlines;
			/* and p to add less */
			modification_stripendlines(p, removedlines);
497
			move_queue(&last, &result, &p);
498 499
			assert (lineofs + a->oldlinestart >=
					last->oldlinestart + last->oldlinecount);
500 501 502
			continue;
		}
		/* end of *a remove start of *p, so finalize *a and reduce *p */
503
		if (lineofs + a->oldlinestart <= p->oldlinestart &&
504 505 506
				lineofs + a->oldlinestart + a->oldlinecount
				> p->oldlinestart  &&
				lineofs + a->oldlinestart + a->oldlinecount
507
				< p->oldlinestart + p->newlinecount) {
508 509 510 511 512 513
			int removedlines =
				lineofs + a->oldlinestart + a->oldlinecount
				- p->oldlinestart;
			/* finalize *a with less lines deleted:*/
			a->oldlinestart += lineofs;
			a->oldlinecount -= removedlines;
514
			if (a->oldlinecount == 0 && a->newlinecount == 0) {
515 516 517 518 519
				/* a only removed something and this was hereby
				 * removed from p */
				a = modification_freehead(a);
			} else
				move_queue(&last, &result, &a);
520
			/* and reduce the number of lines of *p */
521
			assert (removedlines < p->newlinecount);
522
			modification_stripstartlines(p, removedlines);
523 524
			/* p->newlinecount got smaller,
			 * so less will be deleted later */
525
			lineofs -= removedlines;
526 527 528 529 530 531
			if (last != NULL) {
			assert (p->oldlinestart >=
				last->oldlinestart + last->oldlinecount);
			if (a != NULL)
				assert (lineofs + a->oldlinestart >=
				       last->oldlinestart + last->oldlinecount);
532
			}
533 534 535 536 537 538
			/* note that a->oldlinestart+a->oldlinecount+1
			 *        == p->oldlinestart */
			continue;
		}
		/* the most complex case left, a inside p, this
		 * needs p split in two */
539
		if (lineofs + a->oldlinestart > p->oldlinestart &&
540
				lineofs + a->oldlinestart + a->oldlinecount
541
				< p->oldlinestart + p->newlinecount) {
542 543 544 545
			struct modification *n;
			int removedlines = p->oldlinestart + p->newlinecount
				- (lineofs + a->oldlinestart);

546 547
			n = zNEW(struct modification);
			if (FAILEDTOALLOC(n)) {
548 549 550 551 552 553
				modification_freelist(result);
				modification_freelist(p);
				modification_freelist(a);
				return RET_ERROR_OOM;
			}
			*n = *p;
Bernhard Link's avatar
Bernhard Link committed
554
			/* all removing into the later p, so
555
			 * that later numbers fit */
556
			n->next = NULL;
557
			n->oldlinecount = 0;
558
			assert (removedlines < n->newlinecount);
Bernhard Link's avatar
Bernhard Link committed
559
			modification_stripendlines(n, removedlines);
560
			lineofs += n->oldlinecount - n->newlinecount;
561
			assert (lineofs+a->oldlinestart <= p->oldlinestart);
562
			move_queue(&last, &result, &n);
563
			assert (n == NULL);
564 565
			/* only remove this and let the rest of the
			 * code handle the other changes */
566 567
			modification_stripstartlines(p,
					p->newlinecount - removedlines);
Bernhard Link's avatar
Bernhard Link committed
568
			assert(p->newlinecount == removedlines);
569 570
			assert (lineofs + a->oldlinestart >=
					last->oldlinestart + last->oldlinecount);
Bernhard Link's avatar
Bernhard Link committed
571
			continue;
572 573 574 575 576 577 578
		}
		modification_freelist(result);
		modification_freelist(p);
		modification_freelist(a);
		fputs("Internal error in rred merging!\n", stderr);
		return RET_ERROR;
	}
579
	while (p != NULL) {
580 581 582
		move_queue(&last, &result, &p);
	}
	*result_p = result;
583 584 585
	return RET_OK;
}

586 587
retvalue patch_file(FILE *o, const char *source, const struct modification *patch) {
	FILE *i;
588 589 590
	int currentline, ignore, c;

	i = fopen(source, "r");
591
	if (i == NULL) {
592 593 594 595
		int e = errno;
		fprintf(stderr, "Error %d opening %s: %s\n",
				e, source, strerror(e));
		return RET_ERRNO(e);
596
	}
597
	assert (patch == NULL || patch->oldlinestart > 0);
598 599
	currentline = 1;
	do {
600
		while (patch != NULL && patch->oldlinestart == currentline) {
601 602 603
			fwrite(patch->content, patch->len, 1, o);
			ignore = patch->oldlinecount;
			patch = patch->next;
604
			while (ignore > 0) {
605 606
				do {
					c = getc(i);
607
				} while (c != '\n' && c != EOF);
608 609 610 611
				ignore--;
				currentline++;
			}
		}
612 613 614 615
		assert (patch == NULL || patch->oldlinestart >= currentline);
		while ((c = getc(i)) != '\n') {
			if (c == EOF) {
				if (patch != NULL) {
616 617 618
					fprintf(stderr,
"Error patching '%s', file shorter than expected by patches!\n",
						source);
619
					(void)fclose(i);
620 621
					return RET_ERROR;
				}
622
				break;
623 624 625
			}
			putc(c, o);
		}
626
		if (c == EOF)
Bernhard Link's avatar
Bernhard Link committed
627
			break;
628 629 630
		putc(c, o);
		currentline++;
	} while (1);
631
	if (ferror(i) != 0) {
632 633 634 635 636
		int e = errno;
		fprintf(stderr, "Error %d reading %s: %s\n",
				e, source, strerror(e));
		(void)fclose(i);
		return RET_ERRNO(e);
637
	}
638
	if (fclose(i) != 0) {
639 640 641 642 643 644
		int e = errno;
		fprintf(stderr, "Error %d reading %s: %s\n",
				e, source, strerror(e));
		return RET_ERRNO(e);
	}
	return RET_OK;
645
}
646

647
void modification_printaspatch(void *f, const struct modification *m, void write_func(const void *, size_t, void *)) {
648
	const struct modification *p, *q, *r;
649 650
	char line[30];
	int len;
651

652
	if (m == NULL)
653
		return;
654
	assert (m->previous == NULL);
655 656
	/* go to the end, as we have to print it backwards */
	p = m;
657 658
	while (p->next != NULL) {
		assert (p->next->previous == p);
659 660 661
		p = p->next;
	}
	/* then print, possibly merging things */
662
	while (p != NULL) {
663 664 665 666 667
		int start, oldcount, newcount;
		start = p->oldlinestart;
		oldcount = p->oldlinecount;
		newcount = p->newlinecount;

668 669
		if (p->next != NULL)
			assert (start + oldcount <= p->next->oldlinestart);
670

671
		r = p;
672
		for (q = p->previous ;
673
		     q != NULL && q->oldlinestart + q->oldlinecount == start ;
674
		     q = q->previous) {
675 676 677 678 679
			oldcount += q->oldlinecount;
			start = q->oldlinestart;
			newcount += q->newlinecount;
			r = q;
		}
680 681 682
		if (newcount == 0) {
			assert (oldcount > 0);
			if (oldcount == 1)
683 684
				len = snprintf(line, sizeof(line), "%dd\n",
						start);
685
			else
686 687
				len = snprintf(line, sizeof(line), "%d,%dd\n",
						start, start + oldcount - 1);
688
		} else {
689
			if (oldcount == 0)
690 691
				len = snprintf(line, sizeof(line), "%da\n",
						start - 1);
692
			else if (oldcount == 1)
693 694
				len = snprintf(line, sizeof(line), "%dc\n",
						start);
695
			else
696 697 698
				len = snprintf(line, sizeof(line), "%d,%dc\n",
						start, start + oldcount - 1);
		}
699
		assert (len < (int)sizeof(line));
700
		write_func(line, len, f);
701 702 703
		if (newcount != 0) {
			while (r != p->next) {
				if (r->len > 0)
704
					write_func(r->content, r->len, f);
705 706 707
				newcount -= r->newlinecount;
				r = r->next;
			}
708
			assert (newcount == 0);
709
			write_func(".\n", 2, f);
710 711 712
		}
		p = q;
	}
713
}
714 715 716 717 718 719 720 721 722 723 724 725 726

/* make sure a patch is not empty and does not only add lines at the start,
 * to work around some problems in apt */

retvalue modification_addstuff(const char *source, struct modification **patch_p, char **line_p) {
	struct modification **pp, *n, *m = NULL;
	char *line = NULL; size_t bufsize = 0;
	ssize_t got;
	FILE *i;
	long lineno = 0;

	pp = patch_p;
	/* check if this only adds things at the start and count how many */
727
	while (*pp != NULL) {
728
		m = *pp;
729
		if (m->oldlinecount > 0 || m->oldlinestart > 1) {
730 731 732 733 734 735 736 737
			*line_p = NULL;
			return RET_OK;
		}
		lineno += m->newlinecount;
		pp = &(*pp)->next;
	}
	/* not get the next line and claim it was changed */
	i = fopen(source, "r");
738
	if (i == NULL) {
739 740 741 742 743 744 745
		int e = errno;
		fprintf(stderr, "Error %d opening '%s': %s\n",
				e, source, strerror(e));
		return RET_ERRNO(e);
	}
	do {
		got = getline(&line, &bufsize, i);
746 747
	} while (got >= 0 && lineno-- > 0);
	if (got < 0) {
748 749 750 751 752 753 754 755 756 757
		int e = errno;

		/* You should have made sure the old file is not empty */
		fprintf(stderr, "Error %d reading '%s': %s\n",
				e, source, strerror(e));
		(void)fclose(i);
		return RET_ERRNO(e);
	}
	(void)fclose(i);

758 759
	n = NEW(struct modification);
	if (FAILEDTOALLOC(n))
760 761 762 763 764 765 766 767 768 769 770 771 772
		return RET_ERROR_OOM;
	*pp = n;
	n->next = NULL;
	n->previous = m;
	n->oldlinestart = 1;
	n->oldlinecount = 1;
	n->newlinecount = 1;
	n->len = got;
	n->content = line;
	*line_p = line;
	return RET_OK;
}