source: MondoRescue/branches/3.3/mindi-busybox/editors/patch_bbox.c@ 3625

Last change on this file since 3625 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 8.7 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * busybox patch applet to handle the unified diff format.
4 * Copyright (C) 2003 Glenn McGrath
5 *
6 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
7 *
8 * This applet is written to work with patches generated by GNU diff,
9 * where there is equivalent functionality busybox patch shall behave
10 * as per GNU patch.
11 *
12 * There is a SUSv3 specification for patch, however it looks to be
13 * incomplete, it doesnt even mention unified diff format.
14 * http://www.opengroup.org/onlinepubs/007904975/utilities/patch.html
15 *
16 * Issues
17 * - Non-interactive
18 * - Patches must apply cleanly or patch (not just one hunk) will fail.
19 * - Reject file isnt saved
20 */
21
22#include "libbb.h"
23
24static unsigned copy_lines(FILE *src_stream, FILE *dst_stream, unsigned lines_count)
25{
26 while (src_stream && lines_count) {
27 char *line;
28 line = xmalloc_fgets(src_stream);
29 if (line == NULL) {
30 break;
31 }
32 if (fputs(line, dst_stream) == EOF) {
33 bb_perror_msg_and_die("error writing to new file");
34 }
35 free(line);
36 lines_count--;
37 }
38 return lines_count;
39}
40
41/* If patch_level is -1 it will remove all directory names
42 * char *line must be greater than 4 chars
43 * returns NULL if the file doesnt exist or error
44 * returns malloc'ed filename
45 * NB: frees 1st argument!
46 */
47static char *extract_filename(char *line, int patch_level, const char *pat)
48{
49 char *temp = NULL, *filename_start_ptr = line + 4;
50
51 if (strncmp(line, pat, 4) == 0) {
52 /* Terminate string at end of source filename */
53 line[strcspn(line, "\t\n\r")] = '\0';
54
55 /* Skip over (patch_level) number of leading directories */
56 while (patch_level--) {
57 temp = strchr(filename_start_ptr, '/');
58 if (!temp)
59 break;
60 filename_start_ptr = temp + 1;
61 }
62 temp = xstrdup(filename_start_ptr);
63 }
64 free(line);
65 return temp;
66}
67
68int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
69int patch_main(int argc UNUSED_PARAM, char **argv)
70{
71 struct stat saved_stat;
72 char *patch_line;
73 FILE *patch_file;
74 int patch_level;
75 int ret = 0;
76 char plus = '+';
77 unsigned opt;
78 enum {
79 OPT_R = (1 << 2),
80 OPT_N = (1 << 3),
81 /*OPT_f = (1 << 4), ignored */
82 /*OPT_E = (1 << 5), ignored, this is the default */
83 /*OPT_g = (1 << 6), ignored */
84 OPT_dry_run = (1 << 7) * ENABLE_LONG_OPTS,
85 };
86
87 xfunc_error_retval = 2;
88 {
89 const char *p = "-1";
90 const char *i = "-"; /* compat */
91#if ENABLE_LONG_OPTS
92 static const char patch_longopts[] ALIGN1 =
93 "strip\0" Required_argument "p"
94 "input\0" Required_argument "i"
95 "reverse\0" No_argument "R"
96 "forward\0" No_argument "N"
97 /* "Assume user knows what [s]he is doing, do not ask any questions": */
98 "force\0" No_argument "f" /*ignored*/
99# if ENABLE_DESKTOP
100 "remove-empty-files\0" No_argument "E" /*ignored*/
101 /* "Controls actions when a file is under RCS or SCCS control,
102 * and does not exist or is read-only and matches the default version,
103 * or when a file is under ClearCase control and does not exist..."
104 * IOW: rather obscure option.
105 * But Gentoo's portage does use -g0 */
106 "get\0" Required_argument "g" /*ignored*/
107# endif
108 "dry-run\0" No_argument "\xfd"
109# if ENABLE_DESKTOP
110 "backup-if-mismatch\0" No_argument "\xfe" /*ignored*/
111 "no-backup-if-mismatch\0" No_argument "\xff" /*ignored*/
112# endif
113 ;
114 applet_long_options = patch_longopts;
115#endif
116 /* -f,-E,-g are ignored */
117 opt = getopt32(argv, "p:i:RN""fEg:", &p, &i, NULL);
118 if (opt & OPT_R)
119 plus = '-';
120 patch_level = xatoi(p); /* can be negative! */
121 patch_file = xfopen_stdin(i);
122 }
123
124 patch_line = xmalloc_fgetline(patch_file);
125 while (patch_line) {
126 FILE *src_stream;
127 FILE *dst_stream;
128 //char *old_filename;
129 char *new_filename;
130 char *backup_filename = NULL;
131 unsigned src_cur_line = 1;
132 unsigned dst_cur_line = 0;
133 unsigned dst_beg_line;
134 unsigned bad_hunk_count = 0;
135 unsigned hunk_count = 0;
136 smallint copy_trailing_lines_flag = 0;
137
138 /* Skip everything upto the "---" marker
139 * No need to parse the lines "Only in <dir>", and "diff <args>"
140 */
141 do {
142 /* Extract the filename used before the patch was generated */
143 new_filename = extract_filename(patch_line, patch_level, "--- ");
144 // was old_filename above
145 patch_line = xmalloc_fgetline(patch_file);
146 if (!patch_line) goto quit;
147 } while (!new_filename);
148 free(new_filename); // "source" filename is irrelevant
149
150 new_filename = extract_filename(patch_line, patch_level, "+++ ");
151 if (!new_filename) {
152 bb_error_msg_and_die("invalid patch");
153 }
154
155 /* Get access rights from the file to be patched */
156 if (stat(new_filename, &saved_stat) != 0) {
157 char *slash = strrchr(new_filename, '/');
158 if (slash) {
159 /* Create leading directories */
160 *slash = '\0';
161 bb_make_directory(new_filename, -1, FILEUTILS_RECUR);
162 *slash = '/';
163 }
164 src_stream = NULL;
165 saved_stat.st_mode = 0644;
166 } else if (!(opt & OPT_dry_run)) {
167 backup_filename = xasprintf("%s.orig", new_filename);
168 xrename(new_filename, backup_filename);
169 src_stream = xfopen_for_read(backup_filename);
170 } else
171 src_stream = xfopen_for_read(new_filename);
172
173 if (opt & OPT_dry_run) {
174 dst_stream = xfopen_for_write("/dev/null");
175 } else {
176 dst_stream = xfopen_for_write(new_filename);
177 fchmod(fileno(dst_stream), saved_stat.st_mode);
178 }
179
180 printf("patching file %s\n", new_filename);
181
182 /* Handle all hunks for this file */
183 patch_line = xmalloc_fgets(patch_file);
184 while (patch_line) {
185 unsigned count;
186 unsigned src_beg_line;
187 unsigned hunk_offset_start;
188 unsigned src_last_line = 1;
189 unsigned dst_last_line = 1;
190
191 if ((sscanf(patch_line, "@@ -%u,%u +%u,%u", &src_beg_line, &src_last_line, &dst_beg_line, &dst_last_line) < 3)
192 && (sscanf(patch_line, "@@ -%u +%u,%u", &src_beg_line, &dst_beg_line, &dst_last_line) < 2)
193 ) {
194 /* No more hunks for this file */
195 break;
196 }
197 if (plus != '+') {
198 /* reverse patch */
199 unsigned tmp = src_last_line;
200 src_last_line = dst_last_line;
201 dst_last_line = tmp;
202 tmp = src_beg_line;
203 src_beg_line = dst_beg_line;
204 dst_beg_line = tmp;
205 }
206 hunk_count++;
207
208 if (src_beg_line && dst_beg_line) {
209 /* Copy unmodified lines upto start of hunk */
210 /* src_beg_line will be 0 if it's a new file */
211 count = src_beg_line - src_cur_line;
212 if (copy_lines(src_stream, dst_stream, count)) {
213 bb_error_msg_and_die("bad src file");
214 }
215 src_cur_line += count;
216 dst_cur_line += count;
217 copy_trailing_lines_flag = 1;
218 }
219 src_last_line += hunk_offset_start = src_cur_line;
220 dst_last_line += dst_cur_line;
221
222 while (1) {
223 free(patch_line);
224 patch_line = xmalloc_fgets(patch_file);
225 if (patch_line == NULL)
226 break; /* EOF */
227 if (!*patch_line) {
228 /* whitespace-damaged patch with "" lines */
229 free(patch_line);
230 patch_line = xstrdup(" ");
231 }
232 if ((*patch_line != '-') && (*patch_line != '+')
233 && (*patch_line != ' ')
234 ) {
235 break; /* End of hunk */
236 }
237 if (*patch_line != plus) { /* '-' or ' ' */
238 char *src_line = NULL;
239 if (src_cur_line == src_last_line)
240 break;
241 if (src_stream) {
242 src_line = xmalloc_fgets(src_stream);
243 if (src_line) {
244 int diff = strcmp(src_line, patch_line + 1);
245 src_cur_line++;
246 free(src_line);
247 if (diff)
248 src_line = NULL;
249 }
250 }
251 /* Do not patch an already patched hunk with -N */
252 if (src_line == 0 && (opt & OPT_N)) {
253 continue;
254 }
255 if (!src_line) {
256 bb_error_msg("hunk #%u FAILED at %u", hunk_count, hunk_offset_start);
257 bad_hunk_count++;
258 break;
259 }
260 if (*patch_line != ' ') { /* '-' */
261 continue;
262 }
263 }
264 if (dst_cur_line == dst_last_line)
265 break;
266 fputs(patch_line + 1, dst_stream);
267 dst_cur_line++;
268 } /* end of while loop handling one hunk */
269 } /* end of while loop handling one file */
270
271 /* Cleanup last patched file */
272 if (copy_trailing_lines_flag) {
273 copy_lines(src_stream, dst_stream, (unsigned)(-1));
274 }
275 if (src_stream) {
276 fclose(src_stream);
277 }
278 fclose(dst_stream);
279 if (bad_hunk_count) {
280 ret = 1;
281 bb_error_msg("%u out of %u hunk FAILED", bad_hunk_count, hunk_count);
282 } else {
283 /* It worked, we can remove the backup */
284 if (backup_filename) {
285 unlink(backup_filename);
286 }
287 if (!(opt & OPT_dry_run)
288 && ((dst_cur_line == 0) || (dst_beg_line == 0))
289 ) {
290 /* The new patched file is empty, remove it */
291 xunlink(new_filename);
292 // /* old_filename and new_filename may be the same file */
293 // unlink(old_filename);
294 }
295 }
296 free(backup_filename);
297 //free(old_filename);
298 free(new_filename);
299 } /* end of "while there are patch lines" */
300 quit:
301 /* 0 = SUCCESS
302 * 1 = Some hunks failed
303 * 2 = More serious problems (exited earlier)
304 */
305 return ret;
306}
Note: See TracBrowser for help on using the repository browser.