#include "libbb.h"
-static unsigned copy_lines(FILE *src_stream, FILE *dest_stream, unsigned lines_count)
+static unsigned copy_lines(FILE *src_stream, FILE *dst_stream, unsigned lines_count)
{
while (src_stream && lines_count) {
char *line;
if (line == NULL) {
break;
}
- if (fputs(line, dest_stream) == EOF) {
+ if (fputs(line, dst_stream) == EOF) {
bb_perror_msg_and_die("error writing to new file");
}
free(line);
* returns malloc'ed filename
* NB: frees 1st argument!
*/
-static char *extract_filename_and_free_line(char *line, int patch_level)
+static char *extract_filename(char *line, int patch_level, const char *pat)
{
- char *temp, *filename_start_ptr = line + 4;
+ char *temp = NULL, *filename_start_ptr = line + 4;
- /* Terminate string at end of source filename */
- temp = strchrnul(filename_start_ptr, '\t');
- *temp = '\0';
+ if (strncmp(line, pat, 4) == 0) {
+ /* Terminate string at end of source filename */
+ line[strcspn(line, "\t\n\r")] = '\0';
- /* Skip over (patch_level) number of leading directories */
- while (patch_level--) {
- temp = strchr(filename_start_ptr, '/');
- if (!temp)
- break;
- filename_start_ptr = temp + 1;
+ /* Skip over (patch_level) number of leading directories */
+ while (patch_level--) {
+ temp = strchr(filename_start_ptr, '/');
+ if (!temp)
+ break;
+ filename_start_ptr = temp + 1;
+ }
+ temp = xstrdup(filename_start_ptr);
}
-
- temp = xstrdup(filename_start_ptr);
free(line);
return temp;
}
int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int patch_main(int argc ATTRIBUTE_UNUSED, char **argv)
+int patch_main(int argc UNUSED_PARAM, char **argv)
{
struct stat saved_stat;
char *patch_line;
FILE *patch_file;
int patch_level;
int ret = 0;
+ char plus = '+';
+ unsigned opt;
+ enum {
+ OPT_R = (1 << 2),
+ OPT_N = (1 << 3),
+ OPT_dry_run = (1 << 4) * ENABLE_LONG_OPTS,
+ };
xfunc_error_retval = 2;
{
const char *p = "-1";
const char *i = "-"; /* compat */
- getopt32(argv, "p:i:", &p, &i);
+#if ENABLE_LONG_OPTS
+ static const char patch_longopts[] ALIGN1 =
+ "strip\0" Required_argument "p"
+ "input\0" Required_argument "i"
+ "reverse\0" No_argument "R"
+ "forward\0" No_argument "N"
+ "dry-run\0" No_argument "\xff"
+ ;
+ applet_long_options = patch_longopts;
+#endif
+ opt = getopt32(argv, "p:i:RN", &p, &i);
+ if (opt & OPT_R)
+ plus = '-';
patch_level = xatoi(p); /* can be negative! */
patch_file = xfopen_stdin(i);
}
- patch_line = xmalloc_getline(patch_file);
+ patch_line = xmalloc_fgetline(patch_file);
while (patch_line) {
FILE *src_stream;
FILE *dst_stream;
- char *original_filename;
+ //char *old_filename;
char *new_filename;
- char *backup_filename;
+ char *backup_filename = NULL;
unsigned src_cur_line = 1;
- unsigned dest_cur_line = 0;
- unsigned dest_beg_line;
+ unsigned dst_cur_line = 0;
+ unsigned dst_beg_line;
unsigned bad_hunk_count = 0;
unsigned hunk_count = 0;
smallint copy_trailing_lines_flag = 0;
/* Skip everything upto the "---" marker
* No need to parse the lines "Only in <dir>", and "diff <args>"
*/
- while (strncmp(patch_line, "--- ", 4) != 0) {
- free(patch_line);
- patch_line = xmalloc_getline(patch_file);
- if (!patch_line)
- bb_error_msg_and_die("invalid patch");
- }
-
- /* Extract the filename used before the patch was generated */
- original_filename = extract_filename_and_free_line(patch_line, patch_level);
+ do {
+ /* Extract the filename used before the patch was generated */
+ new_filename = extract_filename(patch_line, patch_level, "--- ");
+ // was old_filename above
+ patch_line = xmalloc_fgetline(patch_file);
+ if (!patch_line) goto quit;
+ } while (!new_filename);
+ free(new_filename); // "source" filename is irrelevant
- patch_line = xmalloc_getline(patch_file);
- if (!patch_line || strncmp(patch_line, "+++ ", 4) != 0)
+ new_filename = extract_filename(patch_line, patch_level, "+++ ");
+ if (!new_filename) {
bb_error_msg_and_die("invalid patch");
- new_filename = extract_filename_and_free_line(patch_line, patch_level);
+ }
- /* Get access rights from the file to be patched, -1 file does not exist */
+ /* Get access rights from the file to be patched */
if (stat(new_filename, &saved_stat) != 0) {
- char *line_ptr;
- /* Create leading directories */
- line_ptr = strrchr(new_filename, '/');
- if (line_ptr) {
- *line_ptr = '\0';
+ char *slash = strrchr(new_filename, '/');
+ if (slash) {
+ /* Create leading directories */
+ *slash = '\0';
bb_make_directory(new_filename, -1, FILEUTILS_RECUR);
- *line_ptr = '/';
+ *slash = '/';
}
- backup_filename = NULL;
+ src_stream = NULL;
saved_stat.st_mode = 0644;
- } else {
+ } else if (!(opt & OPT_dry_run)) {
backup_filename = xasprintf("%s.orig", new_filename);
xrename(new_filename, backup_filename);
- }
- dst_stream = xfopen(new_filename, "w");
- fchmod(fileno(dst_stream), saved_stat.st_mode);
- src_stream = NULL;
+ src_stream = xfopen_for_read(backup_filename);
+ } else
+ src_stream = xfopen_for_read(new_filename);
- if (backup_filename && !stat(original_filename, &saved_stat)) {
- src_stream = xfopen((strcmp(original_filename, new_filename)) ?
- original_filename : backup_filename, "r");
+ if (opt & OPT_dry_run) {
+ dst_stream = xfopen_for_write("/dev/null");
+ } else {
+ dst_stream = xfopen_for_write(new_filename);
+ fchmod(fileno(dst_stream), saved_stat.st_mode);
}
printf("patching file %s\n", new_filename);
while (patch_line) {
unsigned count;
unsigned src_beg_line;
- unsigned unused;
- unsigned hunk_offset_start = 0;
- smallint hunk_error = 0;
-
- /* This bit should be improved */
- if ((sscanf(patch_line, "@@ -%d,%d +%d,%d @@", &src_beg_line, &unused, &dest_beg_line, &unused) != 4)
- && (sscanf(patch_line, "@@ -%d,%d +%d @@", &src_beg_line, &unused, &dest_beg_line) != 3)
- && (sscanf(patch_line, "@@ -%d +%d,%d @@", &src_beg_line, &dest_beg_line, &unused) != 3)
+ unsigned hunk_offset_start;
+ unsigned src_last_line = 1;
+ unsigned dst_last_line = 1;
+
+ if ((sscanf(patch_line, "@@ -%d,%d +%d,%d", &src_beg_line, &src_last_line, &dst_beg_line, &dst_last_line) < 3)
+ && (sscanf(patch_line, "@@ -%d +%d,%d", &src_beg_line, &dst_beg_line, &dst_last_line) < 2)
) {
/* No more hunks for this file */
break;
}
+ if (plus != '+') {
+ /* reverse patch */
+ unsigned tmp = src_last_line;
+ src_last_line = dst_last_line;
+ dst_last_line = tmp;
+ tmp = src_beg_line;
+ src_beg_line = dst_beg_line;
+ dst_beg_line = tmp;
+ }
hunk_count++;
- if (src_beg_line && dest_beg_line) {
+ if (src_beg_line && dst_beg_line) {
/* Copy unmodified lines upto start of hunk */
/* src_beg_line will be 0 if it's a new file */
count = src_beg_line - src_cur_line;
bb_error_msg_and_die("bad src file");
}
src_cur_line += count;
- dest_cur_line += count;
+ dst_cur_line += count;
copy_trailing_lines_flag = 1;
}
- hunk_offset_start = src_cur_line;
+ src_last_line += hunk_offset_start = src_cur_line;
+ dst_last_line += dst_cur_line;
while (1) {
free(patch_line);
- patch_line = xmalloc_fgets(patch_file);
- if (patch_line == NULL) break;
- if ((*patch_line == '-') || (*patch_line == ' ')) {
+ patch_line = xmalloc_fgets(patch_file);
+ if (patch_line == NULL)
+ break; /* EOF */
+ if (!*patch_line) {
+ /* whitespace-damaged patch with "" lines */
+ free(patch_line);
+ patch_line = xstrdup(" ");
+ }
+ if ((*patch_line != '-') && (*patch_line != '+')
+ && (*patch_line != ' ')
+ ) {
+ break; /* End of hunk */
+ }
+ if (*patch_line != plus) { /* '-' or ' ' */
char *src_line = NULL;
+ if (src_cur_line == src_last_line)
+ break;
if (src_stream) {
src_line = xmalloc_fgets(src_stream);
if (src_line) {
int diff = strcmp(src_line, patch_line + 1);
src_cur_line++;
free(src_line);
- if (diff) src_line = NULL;
- }
- if (!src_line) {
- bb_error_msg("hunk #%d FAILED at %d", hunk_count, hunk_offset_start);
- hunk_error = 1;
- break;
+ if (diff)
+ src_line = NULL;
}
}
- if (*patch_line == ' ') {
- fputs(patch_line + 1, dst_stream);
- dest_cur_line++;
+ /* Do not patch an already patched hunk with -N */
+ if (src_line == 0 && (opt & OPT_N)) {
+ continue;
+ }
+ if (!src_line) {
+ bb_error_msg("hunk #%u FAILED at %u", hunk_count, hunk_offset_start);
+ bad_hunk_count++;
+ break;
+ }
+ if (*patch_line != ' ') { /* '-' */
+ continue;
}
- } else if (*patch_line == '+') {
- fputs(patch_line + 1, dst_stream);
- dest_cur_line++;
- } else {
- break;
}
+ if (dst_cur_line == dst_last_line)
+ break;
+ fputs(patch_line + 1, dst_stream);
+ dst_cur_line++;
} /* end of while loop handling one hunk */
- if (hunk_error) {
- bad_hunk_count++;
- }
} /* end of while loop handling one file */
/* Cleanup last patched file */
if (src_stream) {
fclose(src_stream);
}
- if (dst_stream) {
- fclose(dst_stream);
- }
+ fclose(dst_stream);
if (bad_hunk_count) {
ret = 1;
bb_error_msg("%u out of %u hunk FAILED", bad_hunk_count, hunk_count);
/* It worked, we can remove the backup */
if (backup_filename) {
unlink(backup_filename);
- free(backup_filename);
}
- if ((dest_cur_line == 0) || (dest_beg_line == 0)) {
+ if (!(opt & OPT_dry_run)
+ && ((dst_cur_line == 0) || (dst_beg_line == 0))
+ ) {
/* The new patched file is empty, remove it */
xunlink(new_filename);
- if (strcmp(new_filename, original_filename) != 0)
- xunlink(original_filename);
+ // /* old_filename and new_filename may be the same file */
+ // unlink(old_filename);
}
}
+ free(backup_filename);
+ //free(old_filename);
+ free(new_filename);
} /* end of "while there are patch lines" */
-
+ quit:
/* 0 = SUCCESS
* 1 = Some hunks failed
* 2 = More serious problems (exited earlier)