shithub: front

Download patch

ref: fbf9f5fe25e7febf65e9d235b4db8176139e3a36
parent: 85edcb6dfa7a69b2f000b326501a81442b2ce7a9
author: Ori Bernstein <ori@eigenstate.org>
date: Mon May 26 03:59:23 EDT 2025

patch: add support for best-effort patching with reject files

--- a/sys/src/cmd/patch.c
+++ b/sys/src/cmd/patch.c
@@ -17,6 +17,10 @@
 struct Hunk {
 	int	lnum;
 
+	char	*orig;
+	int	origlen;
+	int	origsz;
+
 	char	*oldpath;
 	int	oldln;
 	int	oldcnt;
@@ -50,6 +54,8 @@
 
 int	strip;
 int	reverse;
+int	rejfd = -1;
+char	*rejfile;
 char	*workdir;
 Fchg	*changed;
 int	nchanged;
@@ -136,6 +142,9 @@
 	h->lnum = lnum;
 	h->oldpath = strdup(oldpath);
 	h->newpath = strdup(newpath);
+	h->origlen = 0;
+	h->origsz = 32;
+	h->orig = emalloc(h->origsz);
 	h->oldlen = 0;
 	h->oldsz = 32;
 	h->old = emalloc(h->oldsz);
@@ -182,6 +191,20 @@
 }
 
 void
+addorig(Hunk *h, char *ln)
+{
+	int n;
+
+	n = strlen(ln);
+	while(h->origlen + n >= h->origsz){
+		h->origsz *= 2;
+		h->orig = erealloc(h->orig, h->origsz);
+	}
+	memcpy(h->orig + h->origlen, ln, n);
+	h->origlen += n;
+}
+
+void
 addnew(Hunk *h, char *ln)
 {
 	int n;
@@ -331,6 +354,7 @@
 			break;
 		}
 		c = ln[0];
+		addorig(&h, ln);
 		switch(ln[0]){
 		default:
 			sysfatal("%s:%d: malformed hunk: leading junk", name, lnum);
@@ -575,7 +599,7 @@
 }
 
 char*
-search(Fbuf *f, Hunk *h, char *fname)
+search(Fbuf *f, Hunk *h)
 {
 	int ln, oldln, fuzz, scanning;
 	char *p;
@@ -599,9 +623,19 @@
 				return p;
 		}
 	}
-	sysfatal("%s:%d: unable to find hunk offset near %s:%d", fname, h->lnum, h->oldpath, h->oldln);
+	return nil;
 }
 
+void
+rejected(Hunk *h, char *fname)
+{
+	fprint(2, "%s:%d: skipping failed hunk %s:%d\n", fname, h->lnum, h->oldpath, h->oldln);
+	fprint(rejfd, "--- %s:%d \n", h->oldpath, h->oldln);
+	fprint(rejfd, "+++ %s:%d \n", h->newpath, h->newln);
+	fprint(rejfd, "@@ -%d,%d +%d,%d @@\n", h->oldln, h->oldcnt, h->newln, h->newcnt);
+	write(rejfd, h->orig, h->origlen);
+}
+
 char*
 append(char *o, int *sz, char *s, char *e)
 {
@@ -617,12 +651,12 @@
 int
 apply(Patch *p, char *fname)
 {
-	char *o, *s, *e, *curfile, *nextfile;
+	char *o, *s, *n, *curfile, *nextfile;
 	int i, osz;
 	Hunk *h, *prevh;
 	Fbuf f;
 
-	e = nil;
+	n = nil;
 	o = nil;
 	osz = 0;
 	curfile = nil;
@@ -639,28 +673,38 @@
 		if(curfile == nil || strcmp(curfile, nextfile) != 0){
 			if(curfile != nil){
 				if(!dryrun)
-					o = append(o, &osz, e, f.buf + f.len);
+					o = append(o, &osz, n, f.buf + f.len);
 				blat(prevh->oldpath, prevh->newpath, o, osz, f.mode);
 				osz = 0;
 			}
 			if(!dryrun){
 				slurp(&f, h->oldpath);
-				e = f.buf;
+				n = f.buf;
 			}
 			curfile = nextfile;
 		}
 		if(!dryrun){
-			s = e;
-			e = search(&f, h, fname);
+			char *e;
+			s = n;
+			e = search(&f, h);
+			if(e == nil){
+				if(rejfd == -1)
+					sysfatal("%s:%d: unable to find hunk offset near %s:%d", fname, h->lnum, h->oldpath, h->oldln);
+				else{
+					rejected(h, fname);
+					goto Next;
+				}
+			}
 			o = append(o, &osz, s, e);
 			o = append(o, &osz, h->new, h->new + h->newlen);
-			e += h->oldlen;
+			n = e + h->oldlen;
 		}
+Next:
 		prevh = h;
 	}
 	if(curfile != nil){
 		if(!dryrun)
-			o = append(o, &osz, e, f.buf + f.len);
+			o = append(o, &osz, n, f.buf + f.len);
 		blat(h->oldpath, h->newpath, o, osz, f.mode);
 	}
 	free(o);
@@ -688,7 +732,7 @@
 void
 usage(void)
 {
-	fprint(2, "usage: %s [-nR] [-p nstrip] [patch...]\n", argv0);
+	fprint(2, "usage: %s [-nR] [-p nstrip] [-r rejfile] [patch...]\n", argv0);
 	exits("usage");
 }
 
@@ -703,6 +747,9 @@
 	case 'd':
 		workdir = EARGF(usage());
 		break;
+	case 'r':
+		rejfile = EARGF(usage());
+		break;
 	case 'p':
 		strip = atoi(EARGF(usage()));
 		break;
@@ -718,6 +765,11 @@
 	}ARGEND;
 
 	ok = 1;
+	if(rejfile != nil){
+		rejfd = create(rejfile, OWRITE, 0644);
+		if(rejfd == -1)
+			sysfatal("open %s: %r", rejfile);
+	}
 	if(argc == 0){
 		if((f = Bfdopen(0, OREAD)) == nil)
 			sysfatal("open stdin: %r");
--- a/sys/src/cmd/test/patch/patch.rc
+++ b/sys/src/cmd/test/patch/patch.rc
@@ -11,13 +11,20 @@
 
 fn checkpatch{
 	rm -f $1.out
-	../../$O.patch $1.patch
+	../../$O.patch $*(2-) $1.patch
 	check $1.out $1.expected
 }
 
+fn checkrej {
+	rm -f $1.rout
+	checkpatch $1 -r $1.rout
+	check $1.rout $1.rexpected
+}
+
 checkpatch basic
 checkpatch header
 checkpatch create
+checkrej rej
 
 seq 12 > delete.out
 ../../$O.patch delete.patch
--- /dev/null
+++ b/sys/src/cmd/test/patch/rej.expected
@@ -1,0 +1,98 @@
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+16
+12
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+96
--- /dev/null
+++ b/sys/src/cmd/test/patch/rej.in
@@ -1,0 +1,93 @@
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+16
+12
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+96
--- /dev/null
+++ b/sys/src/cmd/test/patch/rej.patch
@@ -1,0 +1,42 @@
+--- rej.in
++++ rej.out
+@@ -1,3 +1,5 @@
++1
++2
+ 3
+ 4
+ 5
+@@ -10,8 +12,8 @@
+ 12
+ 13
+ 14
+-13
+-12
++15
++16
+ 17
+ 18
+ 19
+@@ -35,6 +37,8 @@
+ 37
+ 38
+ 39
++40
++41
+ 42
+ 43
+ 44
+@@ -80,6 +84,7 @@
+ 84
+ 85
+ 86
++87
+ 88
+ 89
+ 90
+@@ -91,3 +96,5 @@
+ 96
+ 97
+ 98
++99
++100
--- /dev/null
+++ b/sys/src/cmd/test/patch/rej.rexpected
@@ -1,0 +1,21 @@
+--- rej.in:9 
++++ rej.out:11 
+@@ -9,8 +11,8 @@
+ 12
+ 13
+ 14
+-13
+-12
++15
++16
+ 17
+ 18
+ 19
+--- rej.in:90 
++++ rej.out:95 
+@@ -90,3 +95,5 @@
+ 96
+ 97
+ 98
++99
++100
--