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
243
editors/patch.c
243
editors/patch.c
@ -1,8 +1,7 @@
|
||||
/* Adapted from toybox's patch. */
|
||||
|
||||
/* 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>
|
||||
*
|
||||
@ -20,29 +19,41 @@
|
||||
* -f force (no questions asked)
|
||||
* -F fuzz (number, default 2)
|
||||
* [file] which file to patch
|
||||
|
||||
USE_PATCH(NEWTOY(patch, USE_TOYBOX_DEBUG("x")"up#i:R", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
config PATCH
|
||||
bool "patch"
|
||||
default y
|
||||
help
|
||||
usage: patch [-i file] [-p depth] [-Ru]
|
||||
|
||||
Apply a unified diff to one or more files.
|
||||
|
||||
-i Input file (defaults=stdin)
|
||||
-p number of '/' to strip from start of file paths (default=all)
|
||||
-R Reverse patch.
|
||||
-u Ignored (only handles "unified" diffs)
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
//applet:IF_PATCH(APPLET(patch, _BB_DIR_USR_BIN, _BB_SUID_DROP))
|
||||
|
||||
//kbuild:lib-$(CONFIG_PATCH) += patch.o
|
||||
|
||||
//config:config PATCH
|
||||
//config: bool "patch"
|
||||
//config: default y
|
||||
//config: help
|
||||
//config: Apply a unified diff formatted patch.
|
||||
|
||||
//usage:#define patch_trivial_usage
|
||||
//usage: "[OPTIONS] [ORIGFILE [PATCHFILE]]"
|
||||
//usage:#define patch_full_usage "\n\n"
|
||||
//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"
|
||||
|
||||
#include "libbb.h"
|
||||
|
||||
struct double_list {
|
||||
@ -101,125 +112,6 @@ struct double_list *dlist_add(struct double_list **list, char *data)
|
||||
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 {
|
||||
@ -229,7 +121,7 @@ struct globals {
|
||||
struct double_list *current_hunk;
|
||||
long oldline, oldlen, newline, newlen;
|
||||
long linenum;
|
||||
int context, state, filein, fileout, filepatch, hunknum;
|
||||
int context, state, filein, fileout, hunknum;
|
||||
char *tempname;
|
||||
|
||||
// was toys.foo:
|
||||
@ -263,7 +155,7 @@ struct globals {
|
||||
|
||||
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)
|
||||
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);
|
||||
|
||||
free(dlist->data);
|
||||
free(data);
|
||||
free(dlist);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void fail_hunk(void)
|
||||
{
|
||||
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);
|
||||
TT.exitval = 1;
|
||||
@ -295,7 +204,14 @@ static void fail_hunk(void)
|
||||
TT.state = 2;
|
||||
llist_free(TT.current_hunk, do_line);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -334,7 +250,7 @@ static int apply_one_hunk(void)
|
||||
plist = TT.current_hunk;
|
||||
buf = NULL;
|
||||
if (TT.context) for (;;) {
|
||||
char *data = get_line(TT.filein);
|
||||
char *data = xmalloc_reads(TT.filein, NULL, NULL);
|
||||
|
||||
TT.linenum++;
|
||||
|
||||
@ -368,7 +284,9 @@ static int apply_one_hunk(void)
|
||||
// File ended before we found a place for this hunk.
|
||||
fail_hunk();
|
||||
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);
|
||||
|
||||
// Compare this line with next expected line of hunk.
|
||||
@ -399,7 +317,7 @@ static int apply_one_hunk(void)
|
||||
// If we've reached the end of the buffer without confirming a
|
||||
// match, read more lines.
|
||||
if (check == buf) {
|
||||
buf = 0;
|
||||
buf = NULL;
|
||||
break;
|
||||
}
|
||||
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.filein = TT.fileout = -1;
|
||||
if (opts & FLAG_INPUT) {
|
||||
TT.filepatch = xopen_stdin(opt_i);
|
||||
xmove_fd(xopen_stdin(opt_i), STDIN_FILENO);
|
||||
} else {
|
||||
if (argv[0] && argv[1]) {
|
||||
TT.filepatch = xopen_stdin(argv[1]);
|
||||
xmove_fd(xopen_stdin(argv[1]), STDIN_FILENO);
|
||||
}
|
||||
}
|
||||
if (argv[0]) {
|
||||
@ -468,7 +386,7 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
|
||||
for(;;) {
|
||||
char *patchline;
|
||||
|
||||
patchline = get_line(TT.filepatch);
|
||||
patchline = xmalloc_fgetline(stdin);
|
||||
if (!patchline) break;
|
||||
|
||||
// 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.
|
||||
} 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 (!strcmp(oldname, "/dev/null") || !oldsum) {
|
||||
printf("creating %s\n", name);
|
||||
s = strrchr(name, '/');
|
||||
if (s) {
|
||||
*s = 0;
|
||||
xmkpath(name, -1);
|
||||
bb_make_directory(name, -1, FILEUTILS_RECUR);
|
||||
*s = '/';
|
||||
}
|
||||
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);
|
||||
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.hunknum = 0;
|
||||
}
|
||||
@ -620,7 +546,6 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
|
||||
finish_oldfile();
|
||||
|
||||
if (ENABLE_FEATURE_CLEAN_UP) {
|
||||
close(TT.filepatch);
|
||||
free(oldname);
|
||||
free(newname);
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ zxc
|
||||
testing "patch detects already applied hunk" \
|
||||
'patch 2>&1; echo $?; cat input' \
|
||||
"\
|
||||
Possibly reversed hunk 1 at 2
|
||||
Possibly reversed hunk 1 at 4
|
||||
Hunk 1 FAILED 1/1.
|
||||
abc
|
||||
+def
|
||||
@ -103,7 +103,7 @@ def
|
||||
testing "patch detects already applied hunk at the EOF" \
|
||||
'patch 2>&1; echo $?; cat input' \
|
||||
"\
|
||||
Possibly reversed hunk 1 at 3
|
||||
Possibly reversed hunk 1 at 4
|
||||
Hunk 1 FAILED 1/1.
|
||||
abc
|
||||
123
|
||||
|
Loading…
Reference in New Issue
Block a user