patch: implement --dry-run
function old new delta static.patch_longopts - 137 +137 patch_main 2053 2135 +82 fail_hunk 132 139 +7 finish_oldfile 119 124 +5 packed_usage 32807 32787 -20 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 3/1 up/down: 231/-20) Total: 211 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
		| @@ -15,7 +15,6 @@ | |||||||
|  * -D define wrap #ifdef and #ifndef around changes |  * -D define wrap #ifdef and #ifndef around changes | ||||||
|  * -o outfile output here instead of in place |  * -o outfile output here instead of in place | ||||||
|  * -r rejectfile write rejected hunks to this file |  * -r rejectfile write rejected hunks to this file | ||||||
|  * --dry-run (regression!) |  | ||||||
|  * |  * | ||||||
|  * -f force (no questions asked) |  * -f force (no questions asked) | ||||||
|  * -F fuzz (number, default 2) |  * -F fuzz (number, default 2) | ||||||
| @@ -34,23 +33,15 @@ | |||||||
| //usage:#define patch_trivial_usage | //usage:#define patch_trivial_usage | ||||||
| //usage:       "[OPTIONS] [ORIGFILE [PATCHFILE]]" | //usage:       "[OPTIONS] [ORIGFILE [PATCHFILE]]" | ||||||
| //usage:#define patch_full_usage "\n\n" | //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" - TODO */ |  | ||||||
| //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:       "	-p N	Strip N leading components from file names" | ||||||
| //usage:     "\n	-i DIFF	Read DIFF instead of stdin" | //usage:     "\n	-i DIFF	Read DIFF instead of stdin" | ||||||
| //usage:     "\n	-R	Reverse patch" | //usage:     "\n	-R	Reverse patch" | ||||||
| //usage:     "\n	-N	Ignore already applied patches" | //usage:     "\n	-N	Ignore already applied patches" | ||||||
| //usage:     "\n	-E	Remove output files if they become empty" | //usage:     "\n	-E	Remove output files if they become empty" | ||||||
|  | //usage:	IF_LONG_OPTS( | ||||||
|  | //usage:     "\n	--dry-run	Don't actually change files" | ||||||
| //usage:	) | //usage:	) | ||||||
| /* -u "interpret as unified diff" is supported but not documented: this info is not useful for --help */ | /* -u "interpret as unified diff" is supported but not documented: this info is not useful for --help */ | ||||||
| /* -x "debug" is supported but does nothing */ |  | ||||||
| //usage: | //usage: | ||||||
| //usage:#define patch_example_usage | //usage:#define patch_example_usage | ||||||
| //usage:       "$ patch -p1 < example.diff\n" | //usage:       "$ patch -p1 < example.diff\n" | ||||||
| @@ -58,6 +49,7 @@ | |||||||
|  |  | ||||||
| #include "libbb.h" | #include "libbb.h" | ||||||
|  |  | ||||||
|  | #define PATCH_DEBUG  0 | ||||||
|  |  | ||||||
| // libbb candidate? | // libbb candidate? | ||||||
|  |  | ||||||
| @@ -122,7 +114,7 @@ struct globals { | |||||||
| } while (0) | } while (0) | ||||||
|  |  | ||||||
|  |  | ||||||
| #define FLAG_STR "Rup:i:NEx" | #define FLAG_STR "Rup:i:NEfg" | ||||||
| /* FLAG_REVERSE must be == 1! Code uses this fact. */ | /* FLAG_REVERSE must be == 1! Code uses this fact. */ | ||||||
| #define FLAG_REVERSE  (1 << 0) | #define FLAG_REVERSE  (1 << 0) | ||||||
| #define FLAG_u        (1 << 1) | #define FLAG_u        (1 << 1) | ||||||
| @@ -130,8 +122,10 @@ struct globals { | |||||||
| #define FLAG_INPUT    (1 << 3) | #define FLAG_INPUT    (1 << 3) | ||||||
| #define FLAG_IGNORE   (1 << 4) | #define FLAG_IGNORE   (1 << 4) | ||||||
| #define FLAG_RMEMPTY  (1 << 5) | #define FLAG_RMEMPTY  (1 << 5) | ||||||
| /* Enable this bit and use -x for debug output: */ | #define FLAG_f_unused (1 << 6) | ||||||
| #define FLAG_DEBUG   (0 << 6) | #define FLAG_g_unused (1 << 7) | ||||||
|  | #define FLAG_dry_run  ((1 << 8) * ENABLE_LONG_OPTS) | ||||||
|  |  | ||||||
|  |  | ||||||
| // Dispose of a line of input, either by writing it out or discarding it. | // Dispose of a line of input, either by writing it out or discarding it. | ||||||
|  |  | ||||||
| @@ -140,8 +134,6 @@ struct globals { | |||||||
| // state = 3: write whole line to fileout | // state = 3: write whole line to fileout | ||||||
| // state > 3: write line+1 to fileout when *line != state | // state > 3: write line+1 to fileout when *line != state | ||||||
|  |  | ||||||
| #define PATCH_DEBUG (option_mask32 & FLAG_DEBUG) |  | ||||||
|  |  | ||||||
| static void do_line(void *data) | static void do_line(void *data) | ||||||
| { | { | ||||||
| 	struct double_list *dlist = data; | 	struct double_list *dlist = data; | ||||||
| @@ -168,12 +160,14 @@ static void finish_oldfile(void) | |||||||
| 		} | 		} | ||||||
| 		xclose(TT.fileout); | 		xclose(TT.fileout); | ||||||
|  |  | ||||||
|  | 		if (!ENABLE_LONG_OPTS || TT.tempname[0]) { /* not --dry-run? */ | ||||||
| 			temp = xstrdup(TT.tempname); | 			temp = xstrdup(TT.tempname); | ||||||
| 			temp[strlen(temp) - 6] = '\0'; | 			temp[strlen(temp) - 6] = '\0'; | ||||||
| 			rename(TT.tempname, temp); | 			rename(TT.tempname, temp); | ||||||
| 			free(temp); | 			free(temp); | ||||||
|  |  | ||||||
| 			free(TT.tempname); | 			free(TT.tempname); | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		TT.tempname = NULL; | 		TT.tempname = NULL; | ||||||
| 	} | 	} | ||||||
| 	TT.fileout = TT.filein = -1; | 	TT.fileout = TT.filein = -1; | ||||||
| @@ -197,8 +191,10 @@ static void fail_hunk(void) | |||||||
| 	// Abort the copy and delete the temporary file. | 	// Abort the copy and delete the temporary file. | ||||||
| 	close(TT.filein); | 	close(TT.filein); | ||||||
| 	close(TT.fileout); | 	close(TT.fileout); | ||||||
|  | 	if (!ENABLE_LONG_OPTS || TT.tempname[0]) { /* not --dry-run? */ | ||||||
| 		unlink(TT.tempname); | 		unlink(TT.tempname); | ||||||
| 		free(TT.tempname); | 		free(TT.tempname); | ||||||
|  | 	} | ||||||
| 	TT.tempname = NULL; | 	TT.tempname = NULL; | ||||||
|  |  | ||||||
| 	TT.state = 0; | 	TT.state = 0; | ||||||
| @@ -239,6 +235,7 @@ static int apply_one_hunk(void) | |||||||
| 	plist = TT.current_hunk; | 	plist = TT.current_hunk; | ||||||
| 	buf = NULL; | 	buf = NULL; | ||||||
| 	if (reverse ? TT.oldlen : TT.newlen) for (;;) { | 	if (reverse ? TT.oldlen : TT.newlen) for (;;) { | ||||||
|  | //FIXME: this performs 1-byte reads: | ||||||
| 		char *data = xmalloc_reads(TT.filein, NULL); | 		char *data = xmalloc_reads(TT.filein, NULL); | ||||||
|  |  | ||||||
| 		TT.linenum++; | 		TT.linenum++; | ||||||
| @@ -369,9 +366,45 @@ int patch_main(int argc UNUSED_PARAM, char **argv) | |||||||
| 	long oldlen = oldlen; /* for compiler */ | 	long oldlen = oldlen; /* for compiler */ | ||||||
| 	long newlen = newlen; /* for compiler */ | 	long newlen = newlen; /* for compiler */ | ||||||
|  |  | ||||||
|  | #if ENABLE_LONG_OPTS | ||||||
|  | 	static const char patch_longopts[] ALIGN1 = | ||||||
|  | 		"reverse\0"               No_argument       "R" | ||||||
|  | 		"unified\0"               No_argument       "u" | ||||||
|  | 		"strip\0"                 Required_argument "p" | ||||||
|  | 		"input\0"                 Required_argument "i" | ||||||
|  | 		"forward\0"               No_argument       "N" | ||||||
|  | # if ENABLE_DESKTOP | ||||||
|  | 		"remove-empty-files\0"    No_argument       "E" /*ignored*/ | ||||||
|  | 		/* "debug"                Required_argument "x" */ | ||||||
|  | # endif | ||||||
|  | 		/* "Assume user knows what [s]he is doing, do not ask any questions": */ | ||||||
|  | 		"force\0"                 No_argument       "f" /*ignored*/ | ||||||
|  | # if ENABLE_DESKTOP | ||||||
|  | 		/* "Controls actions when a file is under RCS or SCCS control, | ||||||
|  | 		 * and does not exist or is read-only and matches the default version, | ||||||
|  | 		 * or when a file is under ClearCase control and does not exist..." | ||||||
|  | 		 * IOW: rather obscure option. | ||||||
|  | 		 * But Gentoo's portage does use -g0 | ||||||
|  | 		 */ | ||||||
|  | 		"get\0"                   Required_argument "g" /*ignored*/ | ||||||
|  | # endif | ||||||
|  | 		"dry-run\0"               No_argument       "\xfd" | ||||||
|  | # if ENABLE_DESKTOP | ||||||
|  | 		"backup-if-mismatch\0"    No_argument       "\xfe" /*ignored*/ | ||||||
|  | 		"no-backup-if-mismatch\0" No_argument       "\xff" /*ignored*/ | ||||||
|  | # endif | ||||||
|  | 		; | ||||||
|  | #endif | ||||||
|  |  | ||||||
| 	INIT_TT(); | 	INIT_TT(); | ||||||
|  |  | ||||||
|  | #if ENABLE_LONG_OPTS | ||||||
|  | 	opts = getopt32long(argv, FLAG_STR, patch_longopts, &opt_p, &opt_i); | ||||||
|  | #else | ||||||
| 	opts = getopt32(argv, FLAG_STR, &opt_p, &opt_i); | 	opts = getopt32(argv, FLAG_STR, &opt_p, &opt_i); | ||||||
|  | #endif | ||||||
|  | 	//bb_error_msg_and_die("opts:%x", opts); | ||||||
|  |  | ||||||
| 	argv += optind; | 	argv += optind; | ||||||
| 	reverse = opts & FLAG_REVERSE; | 	reverse = opts & FLAG_REVERSE; | ||||||
| 	TT.prefix = (opts & FLAG_PATHLEN) ? xatoi(opt_p) : 0; // can be negative! | 	TT.prefix = (opts & FLAG_PATHLEN) ? xatoi(opt_p) : 0; // can be negative! | ||||||
| @@ -517,9 +550,11 @@ int patch_main(int argc UNUSED_PARAM, char **argv) | |||||||
| 					if (option_mask32 & FLAG_RMEMPTY) { | 					if (option_mask32 & FLAG_RMEMPTY) { | ||||||
| 						// If flag -E or --remove-empty-files is set | 						// If flag -E or --remove-empty-files is set | ||||||
| 						printf("removing %s\n", name); | 						printf("removing %s\n", name); | ||||||
|  | 						if (!(opts & FLAG_dry_run)) | ||||||
| 							xunlink(name); | 							xunlink(name); | ||||||
| 					} else { | 					} else { | ||||||
| 						printf("patching file %s\n", name); | 						printf("patching file %s\n", name); | ||||||
|  | 						if (!(opts & FLAG_dry_run)) | ||||||
| 							xclose(xopen(name, O_WRONLY | O_TRUNC)); | 							xclose(xopen(name, O_WRONLY | O_TRUNC)); | ||||||
| 					} | 					} | ||||||
| 				// If we've got a file to open, do so. | 				// If we've got a file to open, do so. | ||||||
| @@ -529,24 +564,32 @@ int patch_main(int argc UNUSED_PARAM, char **argv) | |||||||
| 					// 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") == 0 || !oldsum) { | 					if (strcmp(oldname, "/dev/null") == 0 || !oldsum) { | ||||||
| 						printf("creating %s\n", name); | 						printf("creating %s\n", name); | ||||||
|  | 						if (!(opts & FLAG_dry_run)) { | ||||||
| 							s = strrchr(name, '/'); | 							s = strrchr(name, '/'); | ||||||
| 							if (s) { | 							if (s) { | ||||||
| 							*s = 0; | 								*s = '\0'; | ||||||
| 								bb_make_directory(name, -1, FILEUTILS_RECUR); | 								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); | ||||||
|  | 						} else { | ||||||
|  | 							TT.filein = xopen("/dev/null", O_RDONLY); | ||||||
|  | 						} | ||||||
| 					} else { | 					} else { | ||||||
| 						printf("patching file %s\n", name); | 						printf("patching file %s\n", name); | ||||||
| 						TT.filein = xopen(name, O_RDONLY); | 						TT.filein = xopen(name, O_RDONLY); | ||||||
| 					} | 					} | ||||||
|  |  | ||||||
|  | 					if (!(opts & FLAG_dry_run)) { | ||||||
| 						TT.tempname = xasprintf("%sXXXXXX", name); | 						TT.tempname = xasprintf("%sXXXXXX", name); | ||||||
| 						TT.fileout = xmkstemp(TT.tempname); | 						TT.fileout = xmkstemp(TT.tempname); | ||||||
| 						// Set permissions of output file | 						// Set permissions of output file | ||||||
| 						fstat(TT.filein, &statbuf); | 						fstat(TT.filein, &statbuf); | ||||||
| 						fchmod(TT.fileout, statbuf.st_mode); | 						fchmod(TT.fileout, statbuf.st_mode); | ||||||
|  | 					} else { | ||||||
|  | 						TT.tempname = (char*)""; | ||||||
|  | 						TT.fileout = xopen("/dev/null", O_WRONLY); | ||||||
|  | 					} | ||||||
| 					TT.linenum = 0; | 					TT.linenum = 0; | ||||||
| 					TT.hunknum = 0; | 					TT.hunknum = 0; | ||||||
| 				} | 				} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user