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
--
⑨