*
* TODO:
* -b backup
- * -l treat all whitespace as a single space
* -N ignore already applied
* -d chdir first
* -D define wrap #ifdef and #ifndef around changes
* -F fuzz (number, default 2)
* [file] which file to patch
-USE_PATCH(NEWTOY(patch, USE_TOYBOX_DEBUG("x")"ulp#i:R", TOYFLAG_USR|TOYFLAG_BIN))
+USE_PATCH(NEWTOY(patch, "(dry-run)"USE_TOYBOX_DEBUG("x")"d:ulp#i:R", TOYFLAG_USR|TOYFLAG_BIN))
config PATCH
bool "patch"
default y
help
- usage: patch [-i file] [-p depth] [-Ru]
+ usage: patch [-d DIR] [-i file] [-p depth] [-Rlu] [--dry-run]
Apply a unified diff to one or more files.
+ -d modify files in DIR
-i Input file (defaults=stdin)
-l Loose match (ignore whitespace)
-p Number of '/' to strip from start of file paths (default=all)
-R Reverse patch.
-u Ignored (only handles "unified" diffs)
+ --dry-run Don't change files, just confirm patch applies
This version of patch only handles unified diffs, and only modifies
a file when all all hunks to that file apply. Patch prints failed
GLOBALS(
char *infile;
long prefix;
+ char *dir;
struct double_list *current_hunk;
long oldline, oldlen, newline, newlen;
// state = 3: write whole line to fileout
// state > 3: write line+1 to fileout when *line != state
-#define PATCH_DEBUG (CFG_TOYBOX_DEBUG && (toys.optflags & 32))
-
static void do_line(void *data)
{
struct double_list *dlist = (struct double_list *)data;
xwrite(i, "\n", 1);
}
- if (PATCH_DEBUG) fprintf(stderr, "DO %d: %s\n", TT.state, dlist->data);
+ if (toys.optflags & FLAG_x)
+ fprintf(stderr, "DO %d: %s\n", TT.state, dlist->data);
free(dlist->data);
free(data);
static void fail_hunk(void)
{
if (!TT.current_hunk) return;
- dlist_terminate(TT.current_hunk);
fprintf(stderr, "Hunk %d FAILED %ld/%ld.\n",
TT.hunknum, TT.oldline, TT.newline);
TT.state = 2;
llist_traverse(TT.current_hunk, do_line);
TT.current_hunk = NULL;
- delete_tempfile(TT.filein, TT.fileout, &TT.tempname);
+ if (!(toys.optflags & FLAG_dry_run))
+ delete_tempfile(TT.filein, TT.fileout, &TT.tempname);
TT.state = 0;
}
-// Compare ignoring whitespace. Just returns
+// Compare ignoring whitespace. Just returns 0/1, no > or <
static int loosecmp(char *aa, char *bb)
{
int a = 0, b = 0;
static int apply_one_hunk(void)
{
struct double_list *plist, *buf = NULL, *check;
- int matcheof = 0, reverse = toys.optflags & FLAG_R, backwarn = 0;
+ int matcheof, trailing = 0, reverse = toys.optflags & FLAG_R, backwarn = 0;
int (*lcmp)(char *aa, char *bb);
lcmp = (toys.optflags & FLAG_l) ? (void *)loosecmp : (void *)strcmp;
-
dlist_terminate(TT.current_hunk);
// Match EOF if there aren't as many ending context lines as beginning
for (plist = TT.current_hunk; plist; plist = plist->next) {
- if (plist->data[0]==' ') matcheof++;
- else matcheof = 0;
- if (PATCH_DEBUG) fprintf(stderr, "HUNK:%s\n", plist->data);
+ if (plist->data[0]==' ') trailing++;
+ else trailing = 0;
+ if (toys.optflags & FLAG_x) fprintf(stderr, "HUNK:%s\n", plist->data);
}
- matcheof = matcheof < TT.context;
+ matcheof = !trailing || trailing < TT.context;
- if (PATCH_DEBUG) fprintf(stderr,"MATCHEOF=%c\n", matcheof ? 'Y' : 'N');
+ if (toys.optflags & FLAG_x)
+ fprintf(stderr,"MATCHEOF=%c\n", matcheof ? 'Y' : 'N');
// Loop through input data searching for this hunk. Match all context
// lines and all lines to be removed until we've found the end of a
// complete hunk.
plist = TT.current_hunk;
buf = NULL;
- if (TT.context) for (;;) {
+
+ for (;;) {
char *data = get_line(TT.filein);
TT.linenum++;
-
// Figure out which line of hunk to compare with next. (Skip lines
// of the hunk we'd be adding.)
while (plist && *plist->data == "+-"[reverse]) {
// Is this EOF?
if (!data) {
- if (PATCH_DEBUG) fprintf(stderr, "INEOF\n");
+ if (toys.optflags & FLAG_x) fprintf(stderr, "INEOF\n");
// Does this hunk need to match EOF?
if (!plist && matcheof) break;
// File ended before we found a place for this hunk.
fail_hunk();
goto done;
- } else if (PATCH_DEBUG) fprintf(stderr, "IN: %s\n", data);
+ } else if (toys.optflags & FLAG_x) fprintf(stderr, "IN: %s\n", data);
check = dlist_add(&buf, data);
// Compare this line with next expected line of hunk.
// Match failed. Write out first line of buffered data and
// recheck remaining buffered data for a new match.
- if (PATCH_DEBUG) {
+ if (toys.optflags & FLAG_x) {
int bug = 0;
- while (plist->data[bug] == check->data[bug]) bug++;
- fprintf(stderr, "NOT(%d:%d!=%d): %s\n", bug, plist->data[bug],
- check->data[bug], plist->data);
+ if (!plist) fprintf(stderr, "NULL plist\n");
+ else {
+ while (plist->data[bug] == check->data[bug]) bug++;
+ fprintf(stderr, "NOT(%d:%d!=%d): %s\n", bug, plist->data[bug],
+ check->data[bug], plist->data);
+ }
+ }
+
+ // If this hunk must match start of file, fail if it didn't.
+ if (!TT.context || trailing>TT.context) {
+ fail_hunk();
+ goto done;
}
TT.state = 3;
if (!buf) break;
check = buf;
} else {
- if (PATCH_DEBUG) fprintf(stderr, "MAYBE: %s\n", plist->data);
+ if (toys.optflags & FLAG_x) fprintf(stderr, "MAYBE: %s\n", plist->data);
// This line matches. Advance plist, detect successful match.
plist = plist->next;
if (!plist && !matcheof) goto out;
strip = 0;
char *oldname = NULL, *newname = NULL;
- if (TT.infile) TT.filepatch = xopen(TT.infile, O_RDONLY);
+ if (TT.infile) TT.filepatch = xopenro(TT.infile);
TT.filein = TT.fileout = -1;
+ if (TT.dir) xchdir(TT.dir);
+
// Loop through the lines in the patch
for (;;) {
char *patchline;
if (!TT.oldlen && !TT.newlen) state = apply_one_hunk();
continue;
}
+ dlist_terminate(TT.current_hunk);
fail_hunk();
state = 0;
continue;
TT.filein = xcreate(name, O_CREAT|O_EXCL|O_RDWR, 0666);
} else {
printf("patching %s\n", name);
- TT.filein = xopen(name, O_RDONLY);
+ TT.filein = xopenro(name);
}
- TT.fileout = copy_tempfile(TT.filein, name, &TT.tempname);
+ if (toys.optflags & FLAG_dry_run)
+ TT.fileout = xopen("/dev/null", O_RDWR);
+ else TT.fileout = copy_tempfile(TT.filein, name, &TT.tempname);
TT.linenum = 0;
TT.hunknum = 0;
}