patch: busyboxify by migrating from toybox to busybox helpers
function old new delta get_line 90 128 +38 bbconfig_config_bz2 4959 4965 +6 makedevs_main 1038 1035 -3 fail_hunk 133 130 -3 finish_oldfile 174 124 -50 patch_main 2066 1987 -79 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/4 up/down: 44/-135) Total: -91 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
f718e3a0db
commit
b82ae98ea4
237
editors/patch.c
237
editors/patch.c
@ -1,8 +1,7 @@
|
|||||||
/* Adapted from toybox's patch. */
|
|
||||||
|
|
||||||
/* vi: set sw=4 ts=4:
|
/* vi: set sw=4 ts=4:
|
||||||
*
|
*
|
||||||
* patch.c - Apply a "universal" diff.
|
* Apply a "universal" diff.
|
||||||
|
* Adapted from toybox's patch implementation.
|
||||||
*
|
*
|
||||||
* Copyright 2007 Rob Landley <rob@landley.net>
|
* Copyright 2007 Rob Landley <rob@landley.net>
|
||||||
*
|
*
|
||||||
@ -20,29 +19,41 @@
|
|||||||
* -f force (no questions asked)
|
* -f force (no questions asked)
|
||||||
* -F fuzz (number, default 2)
|
* -F fuzz (number, default 2)
|
||||||
* [file] which file to patch
|
* [file] which file to patch
|
||||||
|
*/
|
||||||
|
|
||||||
USE_PATCH(NEWTOY(patch, USE_TOYBOX_DEBUG("x")"up#i:R", TOYFLAG_USR|TOYFLAG_BIN))
|
//applet:IF_PATCH(APPLET(patch, _BB_DIR_USR_BIN, _BB_SUID_DROP))
|
||||||
|
|
||||||
config PATCH
|
//kbuild:lib-$(CONFIG_PATCH) += patch.o
|
||||||
bool "patch"
|
|
||||||
default y
|
|
||||||
help
|
|
||||||
usage: patch [-i file] [-p depth] [-Ru]
|
|
||||||
|
|
||||||
Apply a unified diff to one or more files.
|
//config:config PATCH
|
||||||
|
//config: bool "patch"
|
||||||
|
//config: default y
|
||||||
|
//config: help
|
||||||
|
//config: Apply a unified diff formatted patch.
|
||||||
|
|
||||||
-i Input file (defaults=stdin)
|
//usage:#define patch_trivial_usage
|
||||||
-p number of '/' to strip from start of file paths (default=all)
|
//usage: "[OPTIONS] [ORIGFILE [PATCHFILE]]"
|
||||||
-R Reverse patch.
|
//usage:#define patch_full_usage "\n\n"
|
||||||
-u Ignored (only handles "unified" diffs)
|
//usage: IF_LONG_OPTS(
|
||||||
|
//usage: " -p,--strip N Strip N leading components from file names"
|
||||||
|
//usage: "\n -i,--input DIFF Read DIFF instead of stdin"
|
||||||
|
//usage: "\n -R,--reverse Reverse patch"
|
||||||
|
//usage: "\n -N,--forward Ignore already applied patches"
|
||||||
|
//usage: "\n --dry-run Don't actually change files"
|
||||||
|
//usage: "\n -E,--remove-empty-files Remove output files if they become empty"
|
||||||
|
//usage: )
|
||||||
|
//usage: IF_NOT_LONG_OPTS(
|
||||||
|
//usage: " -p N Strip N leading components from file names"
|
||||||
|
//usage: "\n -i DIFF Read DIFF instead of stdin"
|
||||||
|
//usage: "\n -R Reverse patch"
|
||||||
|
//usage: "\n -N Ignore already applied patches"
|
||||||
|
//usage: "\n -E Remove output files if they become empty"
|
||||||
|
//usage: )
|
||||||
|
//usage:
|
||||||
|
//usage:#define patch_example_usage
|
||||||
|
//usage: "$ patch -p1 < example.diff\n"
|
||||||
|
//usage: "$ patch -p0 -i example.diff"
|
||||||
|
|
||||||
This version of patch only handles unified diffs, and only modifies
|
|
||||||
a file when all all hunks to that file apply. Patch prints failed
|
|
||||||
hunks to stderr, and exits with nonzero status if any hunks fail.
|
|
||||||
|
|
||||||
A file compared against /dev/null (or with a date <= the epoch) is
|
|
||||||
created or deleted if -E or --remove-empty-files set.
|
|
||||||
*/
|
|
||||||
#include "libbb.h"
|
#include "libbb.h"
|
||||||
|
|
||||||
struct double_list {
|
struct double_list {
|
||||||
@ -101,125 +112,6 @@ struct double_list *dlist_add(struct double_list **list, char *data)
|
|||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure entire path exists.
|
|
||||||
// If mode != -1 set permissions on newly created dirs.
|
|
||||||
// Requires that path string be writable (for temporary null terminators).
|
|
||||||
static
|
|
||||||
void xmkpath(char *path, int mode)
|
|
||||||
{
|
|
||||||
char *p, old;
|
|
||||||
mode_t mask;
|
|
||||||
int rc;
|
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
for (p = path; ; p++) {
|
|
||||||
if (!*p || *p == '/') {
|
|
||||||
old = *p;
|
|
||||||
*p = rc = 0;
|
|
||||||
if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
|
|
||||||
if (mode != -1) {
|
|
||||||
mask = umask(0);
|
|
||||||
rc = mkdir(path, mode);
|
|
||||||
umask(mask);
|
|
||||||
} else rc = mkdir(path, 0777);
|
|
||||||
}
|
|
||||||
*p = old;
|
|
||||||
if(rc) bb_perror_msg_and_die("mkpath '%s'", path);
|
|
||||||
}
|
|
||||||
if (!*p) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slow, but small.
|
|
||||||
static
|
|
||||||
char *get_rawline(int fd, long *plen, char end)
|
|
||||||
{
|
|
||||||
char c, *buf = NULL;
|
|
||||||
long len = 0;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
if (1>read(fd, &c, 1)) break;
|
|
||||||
if (!(len & 63)) buf=xrealloc(buf, len+65);
|
|
||||||
if ((buf[len++]=c) == end) break;
|
|
||||||
}
|
|
||||||
if (buf) buf[len]=0;
|
|
||||||
if (plen) *plen = len;
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
|
||||||
char *get_line(int fd)
|
|
||||||
{
|
|
||||||
long len;
|
|
||||||
char *buf = get_rawline(fd, &len, '\n');
|
|
||||||
|
|
||||||
if (buf && buf[--len]=='\n') buf[len]=0;
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the rest of in to out and close both files.
|
|
||||||
static
|
|
||||||
void xsendfile(int in, int out)
|
|
||||||
{
|
|
||||||
long len;
|
|
||||||
char buf[4096];
|
|
||||||
|
|
||||||
if (in<0) return;
|
|
||||||
for (;;) {
|
|
||||||
len = safe_read(in, buf, 4096);
|
|
||||||
if (len<1) break;
|
|
||||||
xwrite(out, buf, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the rest of the data and replace the original with the copy.
|
|
||||||
static
|
|
||||||
void replace_tempfile(int fdin, int fdout, char **tempname)
|
|
||||||
{
|
|
||||||
char *temp = xstrdup(*tempname);
|
|
||||||
|
|
||||||
temp[strlen(temp)-6]=0;
|
|
||||||
if (fdin != -1) {
|
|
||||||
xsendfile(fdin, fdout);
|
|
||||||
xclose(fdin);
|
|
||||||
}
|
|
||||||
xclose(fdout);
|
|
||||||
rename(*tempname, temp);
|
|
||||||
free(*tempname);
|
|
||||||
free(temp);
|
|
||||||
*tempname = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open a temporary file to copy an existing file into.
|
|
||||||
static
|
|
||||||
int copy_tempfile(int fdin, char *name, char **tempname)
|
|
||||||
{
|
|
||||||
struct stat statbuf;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
*tempname = xasprintf("%sXXXXXX", name);
|
|
||||||
fd = xmkstemp(*tempname);
|
|
||||||
|
|
||||||
// Set permissions of output file
|
|
||||||
fstat(fdin, &statbuf);
|
|
||||||
fchmod(fd, statbuf.st_mode);
|
|
||||||
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Abort the copy and delete the temporary file.
|
|
||||||
static
|
|
||||||
void delete_tempfile(int fdin, int fdout, char **tempname)
|
|
||||||
{
|
|
||||||
close(fdin);
|
|
||||||
close(fdout);
|
|
||||||
unlink(*tempname);
|
|
||||||
free(*tempname);
|
|
||||||
*tempname = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct globals {
|
struct globals {
|
||||||
@ -229,7 +121,7 @@ struct globals {
|
|||||||
struct double_list *current_hunk;
|
struct double_list *current_hunk;
|
||||||
long oldline, oldlen, newline, newlen;
|
long oldline, oldlen, newline, newlen;
|
||||||
long linenum;
|
long linenum;
|
||||||
int context, state, filein, fileout, filepatch, hunknum;
|
int context, state, filein, fileout, hunknum;
|
||||||
char *tempname;
|
char *tempname;
|
||||||
|
|
||||||
// was toys.foo:
|
// was toys.foo:
|
||||||
@ -263,7 +155,7 @@ struct globals {
|
|||||||
|
|
||||||
static void do_line(void *data)
|
static void do_line(void *data)
|
||||||
{
|
{
|
||||||
struct double_list *dlist = (struct double_list *)data;
|
struct double_list *dlist = data;
|
||||||
|
|
||||||
if (TT.state>1 && *dlist->data != TT.state)
|
if (TT.state>1 && *dlist->data != TT.state)
|
||||||
fdprintf(TT.state == 2 ? 2 : TT.fileout,
|
fdprintf(TT.state == 2 ? 2 : TT.fileout,
|
||||||
@ -272,19 +164,36 @@ static void do_line(void *data)
|
|||||||
if (PATCH_DEBUG) fdprintf(2, "DO %d: %s\n", TT.state, dlist->data);
|
if (PATCH_DEBUG) fdprintf(2, "DO %d: %s\n", TT.state, dlist->data);
|
||||||
|
|
||||||
free(dlist->data);
|
free(dlist->data);
|
||||||
free(data);
|
free(dlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void finish_oldfile(void)
|
static void finish_oldfile(void)
|
||||||
{
|
{
|
||||||
if (TT.tempname) replace_tempfile(TT.filein, TT.fileout, &TT.tempname);
|
if (TT.tempname) {
|
||||||
|
// Copy the rest of the data and replace the original with the copy.
|
||||||
|
char *temp;
|
||||||
|
|
||||||
|
if (TT.filein != -1) {
|
||||||
|
bb_copyfd_eof(TT.filein, TT.fileout);
|
||||||
|
xclose(TT.filein);
|
||||||
|
}
|
||||||
|
xclose(TT.fileout);
|
||||||
|
|
||||||
|
temp = xstrdup(TT.tempname);
|
||||||
|
temp[strlen(temp) - 6] = '\0';
|
||||||
|
rename(TT.tempname, temp);
|
||||||
|
free(temp);
|
||||||
|
|
||||||
|
free(TT.tempname);
|
||||||
|
TT.tempname = NULL;
|
||||||
|
}
|
||||||
TT.fileout = TT.filein = -1;
|
TT.fileout = TT.filein = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fail_hunk(void)
|
static void fail_hunk(void)
|
||||||
{
|
{
|
||||||
if (!TT.current_hunk) return;
|
if (!TT.current_hunk) return;
|
||||||
TT.current_hunk->prev->next = 0;
|
TT.current_hunk->prev->next = NULL;
|
||||||
|
|
||||||
fdprintf(2, "Hunk %d FAILED %ld/%ld.\n", TT.hunknum, TT.oldline, TT.newline);
|
fdprintf(2, "Hunk %d FAILED %ld/%ld.\n", TT.hunknum, TT.oldline, TT.newline);
|
||||||
TT.exitval = 1;
|
TT.exitval = 1;
|
||||||
@ -295,7 +204,14 @@ static void fail_hunk(void)
|
|||||||
TT.state = 2;
|
TT.state = 2;
|
||||||
llist_free(TT.current_hunk, do_line);
|
llist_free(TT.current_hunk, do_line);
|
||||||
TT.current_hunk = NULL;
|
TT.current_hunk = NULL;
|
||||||
delete_tempfile(TT.filein, TT.fileout, &TT.tempname);
|
|
||||||
|
// Abort the copy and delete the temporary file.
|
||||||
|
close(TT.filein);
|
||||||
|
close(TT.fileout);
|
||||||
|
unlink(TT.tempname);
|
||||||
|
free(TT.tempname);
|
||||||
|
TT.tempname = NULL;
|
||||||
|
|
||||||
TT.state = 0;
|
TT.state = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,7 +250,7 @@ static int apply_one_hunk(void)
|
|||||||
plist = TT.current_hunk;
|
plist = TT.current_hunk;
|
||||||
buf = NULL;
|
buf = NULL;
|
||||||
if (TT.context) for (;;) {
|
if (TT.context) for (;;) {
|
||||||
char *data = get_line(TT.filein);
|
char *data = xmalloc_reads(TT.filein, NULL, NULL);
|
||||||
|
|
||||||
TT.linenum++;
|
TT.linenum++;
|
||||||
|
|
||||||
@ -368,7 +284,9 @@ static int apply_one_hunk(void)
|
|||||||
// File ended before we found a place for this hunk.
|
// File ended before we found a place for this hunk.
|
||||||
fail_hunk();
|
fail_hunk();
|
||||||
goto done;
|
goto done;
|
||||||
} else if (PATCH_DEBUG) fdprintf(2, "IN: %s\n", data);
|
}
|
||||||
|
|
||||||
|
if (PATCH_DEBUG) fdprintf(2, "IN: %s\n", data);
|
||||||
check = dlist_add(&buf, data);
|
check = dlist_add(&buf, data);
|
||||||
|
|
||||||
// Compare this line with next expected line of hunk.
|
// Compare this line with next expected line of hunk.
|
||||||
@ -398,8 +316,8 @@ static int apply_one_hunk(void)
|
|||||||
|
|
||||||
// If we've reached the end of the buffer without confirming a
|
// If we've reached the end of the buffer without confirming a
|
||||||
// match, read more lines.
|
// match, read more lines.
|
||||||
if (check==buf) {
|
if (check == buf) {
|
||||||
buf = 0;
|
buf = NULL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
check = buf;
|
check = buf;
|
||||||
@ -453,10 +371,10 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
TT.prefix = (opts & FLAG_PATHLEN) ? xatoi(opt_p) : 0; // can be negative!
|
TT.prefix = (opts & FLAG_PATHLEN) ? xatoi(opt_p) : 0; // can be negative!
|
||||||
TT.filein = TT.fileout = -1;
|
TT.filein = TT.fileout = -1;
|
||||||
if (opts & FLAG_INPUT) {
|
if (opts & FLAG_INPUT) {
|
||||||
TT.filepatch = xopen_stdin(opt_i);
|
xmove_fd(xopen_stdin(opt_i), STDIN_FILENO);
|
||||||
} else {
|
} else {
|
||||||
if (argv[0] && argv[1]) {
|
if (argv[0] && argv[1]) {
|
||||||
TT.filepatch = xopen_stdin(argv[1]);
|
xmove_fd(xopen_stdin(argv[1]), STDIN_FILENO);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (argv[0]) {
|
if (argv[0]) {
|
||||||
@ -468,7 +386,7 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
for(;;) {
|
for(;;) {
|
||||||
char *patchline;
|
char *patchline;
|
||||||
|
|
||||||
patchline = get_line(TT.filepatch);
|
patchline = xmalloc_fgetline(stdin);
|
||||||
if (!patchline) break;
|
if (!patchline) break;
|
||||||
|
|
||||||
// Other versions of patch accept damaged patches,
|
// Other versions of patch accept damaged patches,
|
||||||
@ -588,13 +506,15 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
}
|
}
|
||||||
// If we've got a file to open, do so.
|
// If we've got a file to open, do so.
|
||||||
} else if (!(option_mask32 & FLAG_PATHLEN) || i <= TT.prefix) {
|
} else if (!(option_mask32 & FLAG_PATHLEN) || i <= TT.prefix) {
|
||||||
|
struct stat statbuf;
|
||||||
|
|
||||||
// If the old file was null, we're creating a new one.
|
// If the old file was null, we're creating a new one.
|
||||||
if (!strcmp(oldname, "/dev/null") || !oldsum) {
|
if (!strcmp(oldname, "/dev/null") || !oldsum) {
|
||||||
printf("creating %s\n", name);
|
printf("creating %s\n", name);
|
||||||
s = strrchr(name, '/');
|
s = strrchr(name, '/');
|
||||||
if (s) {
|
if (s) {
|
||||||
*s = 0;
|
*s = 0;
|
||||||
xmkpath(name, -1);
|
bb_make_directory(name, -1, FILEUTILS_RECUR);
|
||||||
*s = '/';
|
*s = '/';
|
||||||
}
|
}
|
||||||
TT.filein = xopen(name, O_CREAT|O_EXCL|O_RDWR);
|
TT.filein = xopen(name, O_CREAT|O_EXCL|O_RDWR);
|
||||||
@ -602,7 +522,13 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
printf("patching file %s\n", name);
|
printf("patching file %s\n", name);
|
||||||
TT.filein = xopen(name, O_RDONLY);
|
TT.filein = xopen(name, O_RDONLY);
|
||||||
}
|
}
|
||||||
TT.fileout = copy_tempfile(TT.filein, name, &TT.tempname);
|
|
||||||
|
TT.tempname = xasprintf("%sXXXXXX", name);
|
||||||
|
TT.fileout = xmkstemp(TT.tempname);
|
||||||
|
// Set permissions of output file
|
||||||
|
fstat(TT.filein, &statbuf);
|
||||||
|
fchmod(TT.fileout, statbuf.st_mode);
|
||||||
|
|
||||||
TT.linenum = 0;
|
TT.linenum = 0;
|
||||||
TT.hunknum = 0;
|
TT.hunknum = 0;
|
||||||
}
|
}
|
||||||
@ -620,7 +546,6 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
finish_oldfile();
|
finish_oldfile();
|
||||||
|
|
||||||
if (ENABLE_FEATURE_CLEAN_UP) {
|
if (ENABLE_FEATURE_CLEAN_UP) {
|
||||||
close(TT.filepatch);
|
|
||||||
free(oldname);
|
free(oldname);
|
||||||
free(newname);
|
free(newname);
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ zxc
|
|||||||
testing "patch detects already applied hunk" \
|
testing "patch detects already applied hunk" \
|
||||||
'patch 2>&1; echo $?; cat input' \
|
'patch 2>&1; echo $?; cat input' \
|
||||||
"\
|
"\
|
||||||
Possibly reversed hunk 1 at 2
|
Possibly reversed hunk 1 at 4
|
||||||
Hunk 1 FAILED 1/1.
|
Hunk 1 FAILED 1/1.
|
||||||
abc
|
abc
|
||||||
+def
|
+def
|
||||||
@ -103,7 +103,7 @@ def
|
|||||||
testing "patch detects already applied hunk at the EOF" \
|
testing "patch detects already applied hunk at the EOF" \
|
||||||
'patch 2>&1; echo $?; cat input' \
|
'patch 2>&1; echo $?; cat input' \
|
||||||
"\
|
"\
|
||||||
Possibly reversed hunk 1 at 3
|
Possibly reversed hunk 1 at 4
|
||||||
Hunk 1 FAILED 1/1.
|
Hunk 1 FAILED 1/1.
|
||||||
abc
|
abc
|
||||||
123
|
123
|
||||||
|
Loading…
Reference in New Issue
Block a user