shithub: front

Download patch

ref: e7f49afcfb2765c3804fcf74eaede43363c7fee6
parent: abcf07e2150712d670628714b607a0edb6bab99d
author: Ori Bernstein <ori@eigenstate.org>
date: Tue Jul 1 19:28:55 EDT 2025

patch: improve behavior when a patch has rejected hunks

When we had removed files in misapplied hunks, we would
bail out early and leave temp files lying around. Now,
we keep trying to apply rejected hunks, and clean up
the temp files correctly on patch failure.

--- a/sys/src/cmd/patch.c
+++ b/sys/src/cmd/patch.c
@@ -52,6 +52,9 @@
 	char	*new;
 };
 
+int	finish(int);
+void	fail(char*, ...);
+
 int	strip;
 int	reverse;
 int	rejfd = -1;
@@ -79,7 +82,7 @@
 	
 	v = mallocz(n, 1);
 	if(v == nil)
-		sysfatal("malloc: %r");
+		fail("malloc: %r");
 	setmalloctag(v, getcallerpc(&n));
 	return v;
 }
@@ -91,11 +94,25 @@
 		n++;
 	v = realloc(v, n);
 	if(v == nil)
-		sysfatal("malloc: %r");
+		fail("malloc: %r");
 	setmalloctag(v, getcallerpc(&n));
 	return v;
 }
 
+void
+fail(char *fmt, ...)
+{
+	char msg[ERRMAX];
+	va_list ap;
+
+	finish(0);
+	va_start(ap, fmt);
+	vsnprint(msg, sizeof(msg), fmt, ap);
+	va_end(ap);
+	fprint(2, "%s\n", msg);
+	exits(msg);
+}
+
 int
 fileheader(char *s, char *pfx, char **name)
 {
@@ -125,7 +142,7 @@
 		while(*s == '/')
 			s++;
 		if(*s == '\0')
-			sysfatal("too many components stripped");
+			fail("too many components stripped");
 	}
 	len = (e - s) + 1;
 	*name = emalloc(len);
@@ -188,7 +205,7 @@
 	if(h->newln > 0)
 		h->newln--;
 	if(h->oldln < 0 || h->newln < 0 || h->oldcnt < 0 || h->newcnt < 0)
-		sysfatal("malformed hunk %s", s);
+		fail("malformed hunk %s", s);
 	return 0;
 }
 
@@ -323,7 +340,7 @@
 		free(ln);
 	}
 	if(p->nhunk == 0)
-		sysfatal("%s: could not find start of patch", name);
+		fail("%s: could not find start of patch", name);
 	goto out;
 
 patch:
@@ -349,9 +366,9 @@
 	while(1){
 		if((ln = readline(f, &lnum)) == nil){
 			if(oldcnt != h.oldcnt)
-				sysfatal("%s:%d: malformed hunk: mismatched -hunk size %d != %d", name, lnum, oldcnt, h.oldcnt);
+				fail("%s:%d: malformed hunk: mismatched -hunk size %d != %d", name, lnum, oldcnt, h.oldcnt);
 			if(newcnt != h.newcnt)
-				sysfatal("%s:%d: malformed hunk: mismatched +hunk size %d != %d", name, lnum, newcnt, h.newcnt);
+				fail("%s:%d: malformed hunk: mismatched +hunk size %d != %d", name, lnum, newcnt, h.newcnt);
 			addhunk(p, &h);
 			break;
 		}
@@ -359,7 +376,7 @@
 		addorig(&h, ln);
 		switch(ln[0]){
 		default:
-			sysfatal("%s:%d: malformed hunk: leading junk", name, lnum);
+			fail("%s:%d: malformed hunk: leading junk", name, lnum);
 		case '\\':
 			if(strncmp(ln, "\\ No newline", nelem("\\ No newline")-1) == 0)
 				trimhunk(c, &h);
@@ -388,7 +405,7 @@
 		}
 		free(ln);
 		if(oldcnt > h.oldcnt || newcnt > h.newcnt)
-			sysfatal("%s:%d: malformed hunk: oversized hunk", name, lnum);
+			fail("%s:%d: malformed hunk: oversized hunk", name, lnum);
 		if(oldcnt < h.oldcnt || newcnt < h.newcnt)
 			continue;
 
@@ -467,51 +484,57 @@
 	int fd;
 
 	tmp = nil;
-	if(strcmp(new, "/dev/null") == 0){
-		if(len != 0)
-			sysfatal("diff modifies removed file");
-	}else if(!dryrun){
+	if(strcmp(new, "/dev/null") != 0 && !dryrun){
 		if(mkpath(new) == -1)
-			sysfatal("mkpath %s: %r", new);
+			fail("mkpath %s: %r", new);
 		if((tmp = smprint("%s.tmp%d", new, getpid())) == nil)
-			sysfatal("smprint: %r");
+			fail("smprint: %r");
 		if((fd = create(tmp, OWRITE, mode|0200)) == -1)
-			sysfatal("open %s: %r", tmp);
+			fail("open %s: %r", tmp);
 		if(write(fd, o, len) != len)
-			sysfatal("write %s: %r", tmp);
+			fail("write %s: %r", tmp);
 		close(fd);
 	}
 	if((changed = realloc(changed, (nchanged+1)*sizeof(Fchg))) == nil)
-		sysfatal("realloc: %r");
+		fail("realloc: %r");
 	if((changed[nchanged].new = strdup(new)) == nil)
-		sysfatal("strdup: %r");
+		fail("strdup: %r");
 	if((changed[nchanged].old = strdup(old)) == nil)
-		sysfatal("strdup: %r");
+		fail("strdup: %r");
 	changed[nchanged].tmp = tmp;
 	nchanged++;
 }
 
-void
+int
 finish(int ok)
 {
 	Fchg *c;
-	int i, fd;
+	int i, fd, r;
 
+	r = 1;
 	for(i = 0; i < nchanged; i++){
 		c = &changed[i];
 		if(!ok){
-			if(c->tmp != nil && remove(c->tmp) == -1)
+			if(c->tmp != nil && remove(c->tmp) == -1){
+				r = 0;
 				fprint(2, "remove %s: %r\n", c->tmp);
+			}
 			goto Free;
 		}
 		if(!dryrun){
 			if(strcmp(c->new, "/dev/null") == 0){
-				if(remove(c->old) == -1)
-					sysfatal("remove %s: %r", c->old);
+				if(remove(c->old) == -1){
+					r = 0;
+					fprint(2, "remove %s: %r\n", c->old);
+					goto Free;
+				}
 				goto Print;
 			}
-			if((fd = open(c->tmp, ORDWR)) == -1)
-				sysfatal("open %s: %r", c->tmp);
+			if((fd = open(c->tmp, ORDWR)) == -1){
+				r = 0;
+				fprint(2, "open %s: %r\n", c->tmp);
+				goto Free;
+			}
 			if(strcmp(c->old, c->new) == 0 && remove(c->old) == -1)
 				sysfatal("remove %s: %r", c->old);
 			if(rename(fd, c->new) == -1)
@@ -530,9 +553,10 @@
 		free(c->new);
 	}
 	free(changed);
+	return r;
 }
 
-void
+int
 slurp(Fbuf *f, char *path)
 {
 	int n, i, fd, sz, len, nlines, linesz;
@@ -540,10 +564,12 @@
 	int *lines;
 	Dir *d;
 
-	if((fd = open(path, OREAD)) == -1)
-		sysfatal("open %s: %r", path);
+	if((fd = open(path, OREAD)) == -1){
+		fprint(2, "open %s: %r", path);
+		return -1;
+	}
 	if((d = dirfstat(fd)) == nil)
-		sysfatal("stat %s: %r", path);
+		fail("stat %s: %r", path);
 	sz = 8192;
 	len = 0;
 	buf = emalloc(sz);
@@ -556,7 +582,7 @@
 		if(n == 0)
 			break;
 		if(n == -1)
-			sysfatal("read %s: %r", path);
+			fail("read %s: %r", path);
 		len += n;
 	}
 
@@ -583,6 +609,7 @@
 	f->mode = d->mode;
 	free(d);
 	close(fd);
+	return 0;
 }
 
 char*
@@ -680,7 +707,10 @@
 				osz = 0;
 			}
 			if(!dryrun){
-				slurp(&f, h->oldpath);
+				if(slurp(&f, h->oldpath) == -1){
+					rejected(h, fname);
+					goto Next;
+				}
 				n = f.buf;
 			}
 			curfile = nextfile;
@@ -691,7 +721,7 @@
 			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);
+					fail("%s:%d: unable to find hunk offset near %s:%d", fname, h->lnum, h->oldpath, h->oldln);
 				else{
 					rejected(h, fname);
 					goto Next;
@@ -770,15 +800,15 @@
 	if(rejfile != nil){
 		rejfd = create(rejfile, OWRITE, 0644);
 		if(rejfd == -1)
-			sysfatal("open %s: %r", rejfile);
+			fail("open %s: %r", rejfile);
 	}
 	if(argc == 0){
 		if((f = Bfdopen(0, OREAD)) == nil)
-			sysfatal("open stdin: %r");
+			fail("open stdin: %r");
 		if((p = parse(f, "stdin")) == nil)
-			sysfatal("parse patch: %r");
+			fail("parse patch: %r");
 		if(workdir != nil && chdir(workdir) == -1)
-			sysfatal("chdir %s: %r", workdir);
+			fail("chdir %s: %r", workdir);
 		if(apply(p, "stdin") == -1){
 			fprint(2, "apply stdin: %r\n");
 			ok = 0;
@@ -785,15 +815,15 @@
 		}
 		freepatch(p);
 		Bterm(f);
-		finish(ok);
+		ok = finish(ok);
 	}else{
 		for(i = 0; ok && i < argc; i++){
 			if((f = Bopen(argv[i], OREAD)) == nil)
-				sysfatal("open %s: %r", argv[i]);
+				fail("open %s: %r", argv[i]);
 			if((p = parse(f, argv[i])) == nil)
-				sysfatal("parse patch: %r");
+				fail("parse patch: %r");
 			if(workdir != nil && chdir(workdir) == -1)
-				sysfatal("chdir %s: %r", workdir);
+				fail("chdir %s: %r", workdir);
 			if(apply(p, argv[i]) == -1){
 				fprint(2, "apply %s: %r\n", argv[i]);
 				ok = 0;
@@ -800,8 +830,8 @@
 			}
 			freepatch(p);
 			Bterm(f);
-			finish(ok);
+			ok = finish(ok);
 		}
 	}
-	exits(nil);
+	exits(ok ? nil : "failed");
 }
--