Magellan Linux

Annotation of /trunk/mkinitrd-magellan/busybox/editors/patch.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 984 - (hide annotations) (download)
Sun May 30 11:32:42 2010 UTC (14 years ago) by niro
File MIME type: text/plain
File size: 8941 byte(s)
-updated to busybox-1.16.1 and enabled blkid/uuid support in default config
1 niro 532 /* vi: set sw=4 ts=4: */
2     /*
3     * busybox patch applet to handle the unified diff format.
4 niro 816 * Copyright (C) 2003 Glenn McGrath
5 niro 532 *
6     * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
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 niro 816 #include "libbb.h"
23 niro 532
24 niro 816 static unsigned copy_lines(FILE *src_stream, FILE *dst_stream, unsigned lines_count)
25 niro 532 {
26 niro 816 while (src_stream && lines_count) {
27 niro 532 char *line;
28     line = xmalloc_fgets(src_stream);
29     if (line == NULL) {
30     break;
31     }
32 niro 816 if (fputs(line, dst_stream) == EOF) {
33 niro 532 bb_perror_msg_and_die("error writing to new file");
34     }
35     free(line);
36 niro 816 lines_count--;
37 niro 532 }
38 niro 816 return lines_count;
39 niro 532 }
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 niro 816 * NB: frees 1st argument!
46 niro 532 */
47 niro 816 static char *extract_filename(char *line, int patch_level, const char *pat)
48 niro 532 {
49 niro 816 char *temp = NULL, *filename_start_ptr = line + 4;
50 niro 532
51 niro 816 if (strncmp(line, pat, 4) == 0) {
52     /* Terminate string at end of source filename */
53     line[strcspn(line, "\t\n\r")] = '\0';
54 niro 532
55 niro 816 /* 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 niro 532 }
64 niro 816 free(line);
65     return temp;
66 niro 532 }
67    
68 niro 816 int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
69     int patch_main(int argc UNUSED_PARAM, char **argv)
70 niro 532 {
71 niro 816 struct stat saved_stat;
72 niro 532 char *patch_line;
73 niro 816 FILE *patch_file;
74     int patch_level;
75     int ret = 0;
76     char plus = '+';
77 niro 984 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 niro 532
87 niro 816 xfunc_error_retval = 2;
88 niro 532 {
89 niro 816 const char *p = "-1";
90     const char *i = "-"; /* compat */
91 niro 984 #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 niro 816 plus = '-';
120     patch_level = xatoi(p); /* can be negative! */
121     patch_file = xfopen_stdin(i);
122 niro 532 }
123    
124 niro 816 patch_line = xmalloc_fgetline(patch_file);
125 niro 532 while (patch_line) {
126     FILE *src_stream;
127     FILE *dst_stream;
128 niro 816 //char *old_filename;
129 niro 532 char *new_filename;
130 niro 984 char *backup_filename = NULL;
131 niro 816 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 niro 532
138     /* Skip everything upto the "---" marker
139     * No need to parse the lines "Only in <dir>", and "diff <args>"
140     */
141 niro 816 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 niro 532
150 niro 816 new_filename = extract_filename(patch_line, patch_level, "+++ ");
151     if (!new_filename) {
152     bb_error_msg_and_die("invalid patch");
153 niro 532 }
154    
155 niro 816 /* 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 niro 532 bb_make_directory(new_filename, -1, FILEUTILS_RECUR);
162 niro 816 *slash = '/';
163 niro 532 }
164     src_stream = NULL;
165 niro 816 saved_stat.st_mode = 0644;
166 niro 984 } else if (!(opt & OPT_dry_run)) {
167 niro 816 backup_filename = xasprintf("%s.orig", new_filename);
168     xrename(new_filename, backup_filename);
169     src_stream = xfopen_for_read(backup_filename);
170 niro 984 } 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 niro 532 }
179    
180     printf("patching file %s\n", new_filename);
181    
182 niro 816 /* Handle all hunks for this file */
183 niro 532 patch_line = xmalloc_fgets(patch_file);
184     while (patch_line) {
185 niro 816 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 niro 532
191 niro 816 if ((sscanf(patch_line, "@@ -%d,%d +%d,%d", &src_beg_line, &src_last_line, &dst_beg_line, &dst_last_line) < 3)
192     && (sscanf(patch_line, "@@ -%d +%d,%d", &src_beg_line, &dst_beg_line, &dst_last_line) < 2)
193     ) {
194 niro 532 /* No more hunks for this file */
195     break;
196     }
197 niro 816 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 niro 532 hunk_count++;
207    
208 niro 816 if (src_beg_line && dst_beg_line) {
209 niro 532 /* Copy unmodified lines upto start of hunk */
210 niro 816 /* src_beg_line will be 0 if it's a new file */
211 niro 532 count = src_beg_line - src_cur_line;
212 niro 816 if (copy_lines(src_stream, dst_stream, count)) {
213 niro 532 bb_error_msg_and_die("bad src file");
214     }
215     src_cur_line += count;
216 niro 816 dst_cur_line += count;
217 niro 532 copy_trailing_lines_flag = 1;
218     }
219 niro 816 src_last_line += hunk_offset_start = src_cur_line;
220     dst_last_line += dst_cur_line;
221 niro 532
222 niro 816 while (1) {
223     free(patch_line);
224     patch_line = xmalloc_fgets(patch_file);
225     if (patch_line == NULL)
226     break; /* EOF */
227 niro 984 if (!*patch_line) {
228     /* whitespace-damaged patch with "" lines */
229     free(patch_line);
230     patch_line = xstrdup(" ");
231     }
232 niro 816 if ((*patch_line != '-') && (*patch_line != '+')
233     && (*patch_line != ' ')
234     ) {
235     break; /* End of hunk */
236     }
237     if (*patch_line != plus) { /* '-' or ' ' */
238 niro 532 char *src_line = NULL;
239 niro 816 if (src_cur_line == src_last_line)
240     break;
241 niro 532 if (src_stream) {
242     src_line = xmalloc_fgets(src_stream);
243 niro 816 if (src_line) {
244     int diff = strcmp(src_line, patch_line + 1);
245 niro 532 src_cur_line++;
246 niro 816 free(src_line);
247     if (diff)
248     src_line = NULL;
249 niro 532 }
250     }
251 niro 984 /* Do not patch an already patched hunk with -N */
252     if (src_line == 0 && (opt & OPT_N)) {
253     continue;
254     }
255 niro 816 if (!src_line) {
256     bb_error_msg("hunk #%u FAILED at %u", hunk_count, hunk_offset_start);
257     bad_hunk_count++;
258     break;
259 niro 532 }
260 niro 816 if (*patch_line != ' ') { /* '-' */
261     continue;
262     }
263     }
264     if (dst_cur_line == dst_last_line)
265 niro 532 break;
266 niro 816 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 niro 532
271     /* Cleanup last patched file */
272     if (copy_trailing_lines_flag) {
273 niro 816 copy_lines(src_stream, dst_stream, (unsigned)(-1));
274 niro 532 }
275     if (src_stream) {
276     fclose(src_stream);
277     }
278 niro 816 fclose(dst_stream);
279 niro 532 if (bad_hunk_count) {
280 niro 816 ret = 1;
281     bb_error_msg("%u out of %u hunk FAILED", bad_hunk_count, hunk_count);
282 niro 532 } else {
283     /* It worked, we can remove the backup */
284     if (backup_filename) {
285     unlink(backup_filename);
286     }
287 niro 984 if (!(opt & OPT_dry_run)
288     && ((dst_cur_line == 0) || (dst_beg_line == 0))
289     ) {
290 niro 532 /* The new patched file is empty, remove it */
291 niro 816 xunlink(new_filename);
292     // /* old_filename and new_filename may be the same file */
293     // unlink(old_filename);
294 niro 532 }
295     }
296 niro 816 free(backup_filename);
297     //free(old_filename);
298     free(new_filename);
299     } /* end of "while there are patch lines" */
300     quit:
301 niro 532 /* 0 = SUCCESS
302     * 1 = Some hunks failed
303 niro 816 * 2 = More serious problems (exited earlier)
304 niro 532 */
305     return ret;
306     }