Contents of /trunk/mkinitrd-magellan/busybox/editors/patch.c
Parent Directory | Revision Log
Revision 532 -
(show annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 7364 byte(s)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 7364 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * busybox patch applet to handle the unified diff format. |
4 | * Copyright (C) 2003 Glenn McGrath <bug1@iinet.net.au> |
5 | * |
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 | #include <getopt.h> |
23 | #include <string.h> |
24 | #include <stdlib.h> |
25 | #include <unistd.h> |
26 | #include "busybox.h" |
27 | |
28 | static unsigned int copy_lines(FILE *src_stream, FILE *dest_stream, const unsigned int lines_count) |
29 | { |
30 | unsigned int i = 0; |
31 | |
32 | while (src_stream && (i < lines_count)) { |
33 | char *line; |
34 | line = xmalloc_fgets(src_stream); |
35 | if (line == NULL) { |
36 | break; |
37 | } |
38 | if (fputs(line, dest_stream) == EOF) { |
39 | bb_perror_msg_and_die("error writing to new file"); |
40 | } |
41 | free(line); |
42 | |
43 | i++; |
44 | } |
45 | return i; |
46 | } |
47 | |
48 | /* If patch_level is -1 it will remove all directory names |
49 | * char *line must be greater than 4 chars |
50 | * returns NULL if the file doesnt exist or error |
51 | * returns malloc'ed filename |
52 | */ |
53 | |
54 | static char *extract_filename(char *line, int patch_level) |
55 | { |
56 | char *temp, *filename_start_ptr = line + 4; |
57 | int i; |
58 | |
59 | /* Terminate string at end of source filename */ |
60 | temp = strchr(filename_start_ptr, '\t'); |
61 | if (temp) *temp = 0; |
62 | |
63 | /* skip over (patch_level) number of leading directories */ |
64 | for (i = 0; i < patch_level; i++) { |
65 | if(!(temp = strchr(filename_start_ptr, '/'))) break; |
66 | filename_start_ptr = temp + 1; |
67 | } |
68 | |
69 | return xstrdup(filename_start_ptr); |
70 | } |
71 | |
72 | static int file_doesnt_exist(const char *filename) |
73 | { |
74 | struct stat statbuf; |
75 | return stat(filename, &statbuf); |
76 | } |
77 | |
78 | int patch_main(int argc, char **argv) |
79 | { |
80 | int patch_level = -1; |
81 | char *patch_line; |
82 | int ret; |
83 | FILE *patch_file = NULL; |
84 | |
85 | { |
86 | char *p, *i; |
87 | ret = getopt32(argc, argv, "p:i:", &p, &i); |
88 | if (ret & 1) |
89 | patch_level = xatol_range(p, -1, USHRT_MAX); |
90 | if (ret & 2) { |
91 | patch_file = xfopen(i, "r"); |
92 | } else { |
93 | patch_file = stdin; |
94 | } |
95 | ret = 0; |
96 | } |
97 | |
98 | patch_line = xmalloc_fgets(patch_file); |
99 | while (patch_line) { |
100 | FILE *src_stream; |
101 | FILE *dst_stream; |
102 | char *original_filename; |
103 | char *new_filename; |
104 | char *backup_filename; |
105 | unsigned int src_cur_line = 1; |
106 | unsigned int dest_cur_line = 0; |
107 | unsigned int dest_beg_line; |
108 | unsigned int bad_hunk_count = 0; |
109 | unsigned int hunk_count = 0; |
110 | char copy_trailing_lines_flag = 0; |
111 | |
112 | /* Skip everything upto the "---" marker |
113 | * No need to parse the lines "Only in <dir>", and "diff <args>" |
114 | */ |
115 | while (patch_line && strncmp(patch_line, "--- ", 4) != 0) { |
116 | free(patch_line); |
117 | patch_line = xmalloc_fgets(patch_file); |
118 | } |
119 | /* FIXME: patch_line NULL check?? */ |
120 | |
121 | /* Extract the filename used before the patch was generated */ |
122 | original_filename = extract_filename(patch_line, patch_level); |
123 | free(patch_line); |
124 | |
125 | patch_line = xmalloc_fgets(patch_file); |
126 | /* FIXME: NULL check?? */ |
127 | if (strncmp(patch_line, "+++ ", 4) != 0) { |
128 | ret = 2; |
129 | bb_error_msg("invalid patch"); |
130 | continue; |
131 | } |
132 | new_filename = extract_filename(patch_line, patch_level); |
133 | free(patch_line); |
134 | |
135 | if (file_doesnt_exist(new_filename)) { |
136 | char *line_ptr; |
137 | /* Create leading directories */ |
138 | line_ptr = strrchr(new_filename, '/'); |
139 | if (line_ptr) { |
140 | *line_ptr = '\0'; |
141 | bb_make_directory(new_filename, -1, FILEUTILS_RECUR); |
142 | *line_ptr = '/'; |
143 | } |
144 | dst_stream = xfopen(new_filename, "w+"); |
145 | backup_filename = NULL; |
146 | } else { |
147 | backup_filename = xmalloc(strlen(new_filename) + 6); |
148 | strcpy(backup_filename, new_filename); |
149 | strcat(backup_filename, ".orig"); |
150 | if (rename(new_filename, backup_filename) == -1) { |
151 | bb_perror_msg_and_die("cannot create file %s", |
152 | backup_filename); |
153 | } |
154 | dst_stream = xfopen(new_filename, "w"); |
155 | } |
156 | |
157 | if ((backup_filename == NULL) || file_doesnt_exist(original_filename)) { |
158 | src_stream = NULL; |
159 | } else { |
160 | if (strcmp(original_filename, new_filename) == 0) { |
161 | src_stream = xfopen(backup_filename, "r"); |
162 | } else { |
163 | src_stream = xfopen(original_filename, "r"); |
164 | } |
165 | } |
166 | |
167 | printf("patching file %s\n", new_filename); |
168 | |
169 | /* Handle each hunk */ |
170 | patch_line = xmalloc_fgets(patch_file); |
171 | while (patch_line) { |
172 | unsigned int count; |
173 | unsigned int src_beg_line; |
174 | unsigned int unused; |
175 | unsigned int hunk_offset_start = 0; |
176 | int hunk_error = 0; |
177 | |
178 | /* This bit should be improved */ |
179 | if ((sscanf(patch_line, "@@ -%d,%d +%d,%d @@", &src_beg_line, &unused, &dest_beg_line, &unused) != 4) && |
180 | (sscanf(patch_line, "@@ -%d,%d +%d @@", &src_beg_line, &unused, &dest_beg_line) != 3) && |
181 | (sscanf(patch_line, "@@ -%d +%d,%d @@", &src_beg_line, &dest_beg_line, &unused) != 3)) { |
182 | /* No more hunks for this file */ |
183 | break; |
184 | } |
185 | free(patch_line); |
186 | hunk_count++; |
187 | |
188 | if (src_beg_line && dest_beg_line) { |
189 | /* Copy unmodified lines upto start of hunk */ |
190 | /* src_beg_line will be 0 if its a new file */ |
191 | count = src_beg_line - src_cur_line; |
192 | if (copy_lines(src_stream, dst_stream, count) != count) { |
193 | bb_error_msg_and_die("bad src file"); |
194 | } |
195 | src_cur_line += count; |
196 | dest_cur_line += count; |
197 | copy_trailing_lines_flag = 1; |
198 | } |
199 | hunk_offset_start = src_cur_line; |
200 | |
201 | while ((patch_line = xmalloc_fgets(patch_file)) != NULL) { |
202 | if ((*patch_line == '-') || (*patch_line == ' ')) { |
203 | char *src_line = NULL; |
204 | if (src_stream) { |
205 | src_line = xmalloc_fgets(src_stream); |
206 | if (!src_line) { |
207 | hunk_error++; |
208 | break; |
209 | } else { |
210 | src_cur_line++; |
211 | } |
212 | if (strcmp(src_line, patch_line + 1) != 0) { |
213 | bb_error_msg("hunk #%d FAILED at %d", hunk_count, hunk_offset_start); |
214 | hunk_error++; |
215 | free(patch_line); |
216 | /* Probably need to find next hunk, etc... */ |
217 | /* but for now we just bail out */ |
218 | patch_line = NULL; |
219 | break; |
220 | } |
221 | free(src_line); |
222 | } |
223 | if (*patch_line == ' ') { |
224 | fputs(patch_line + 1, dst_stream); |
225 | dest_cur_line++; |
226 | } |
227 | } else if (*patch_line == '+') { |
228 | fputs(patch_line + 1, dst_stream); |
229 | dest_cur_line++; |
230 | } else { |
231 | break; |
232 | } |
233 | free(patch_line); |
234 | } |
235 | if (hunk_error) { |
236 | bad_hunk_count++; |
237 | } |
238 | } |
239 | |
240 | /* Cleanup last patched file */ |
241 | if (copy_trailing_lines_flag) { |
242 | copy_lines(src_stream, dst_stream, -1); |
243 | } |
244 | if (src_stream) { |
245 | fclose(src_stream); |
246 | } |
247 | if (dst_stream) { |
248 | fclose(dst_stream); |
249 | } |
250 | if (bad_hunk_count) { |
251 | if (!ret) { |
252 | ret = 1; |
253 | } |
254 | bb_error_msg("%d out of %d hunk FAILED", bad_hunk_count, hunk_count); |
255 | } else { |
256 | /* It worked, we can remove the backup */ |
257 | if (backup_filename) { |
258 | unlink(backup_filename); |
259 | } |
260 | if ((dest_cur_line == 0) || (dest_beg_line == 0)) { |
261 | /* The new patched file is empty, remove it */ |
262 | if (unlink(new_filename) == -1) { |
263 | bb_perror_msg_and_die("cannot remove file %s", new_filename); |
264 | } |
265 | if (unlink(original_filename) == -1) { |
266 | bb_perror_msg_and_die("cannot remove original file %s", new_filename); |
267 | } |
268 | } |
269 | } |
270 | } |
271 | |
272 | /* 0 = SUCCESS |
273 | * 1 = Some hunks failed |
274 | * 2 = More serious problems |
275 | */ |
276 | return ret; |
277 | } |