diff options
-rw-r--r-- | editors/Config.in | 6 | ||||
-rw-r--r-- | editors/Makefile.in | 1 | ||||
-rw-r--r-- | editors/patch.c | 290 | ||||
-rw-r--r-- | include/applets.h | 3 | ||||
-rw-r--r-- | include/usage.h | 7 |
5 files changed, 307 insertions, 0 deletions
diff --git a/editors/Config.in b/editors/Config.in index c75267759..b7135f393 100644 --- a/editors/Config.in +++ b/editors/Config.in | |||
@@ -18,6 +18,12 @@ config CONFIG_FEATURE_AWK_MATH | |||
18 | help | 18 | help |
19 | Please submit a patch to add help text for this item. | 19 | Please submit a patch to add help text for this item. |
20 | 20 | ||
21 | config CONFIG_PATCH | ||
22 | bool "patch" | ||
23 | default n | ||
24 | help | ||
25 | Apply a unified diff formated patch. | ||
26 | |||
21 | config CONFIG_SED | 27 | config CONFIG_SED |
22 | bool "sed" | 28 | bool "sed" |
23 | default n | 29 | default n |
diff --git a/editors/Makefile.in b/editors/Makefile.in index f4f604e6a..cf6006a2c 100644 --- a/editors/Makefile.in +++ b/editors/Makefile.in | |||
@@ -24,6 +24,7 @@ endif | |||
24 | 24 | ||
25 | EDITOR-y:= | 25 | EDITOR-y:= |
26 | EDITOR-$(CONFIG_AWK) += awk.o | 26 | EDITOR-$(CONFIG_AWK) += awk.o |
27 | EDITOR-$(CONFIG_PATCH) += patch.o | ||
27 | EDITOR-$(CONFIG_SED) += sed.o | 28 | EDITOR-$(CONFIG_SED) += sed.o |
28 | EDITOR-$(CONFIG_VI) += vi.o | 29 | EDITOR-$(CONFIG_VI) += vi.o |
29 | EDITOR_SRC:= $(EDITOR-y) | 30 | EDITOR_SRC:= $(EDITOR-y) |
diff --git a/editors/patch.c b/editors/patch.c new file mode 100644 index 000000000..1587919bc --- /dev/null +++ b/editors/patch.c | |||
@@ -0,0 +1,290 @@ | |||
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@optushome.com.au> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
19 | * | ||
20 | * | ||
21 | * | ||
22 | * This applet is written to work with patches generated by GNU diff, | ||
23 | * where there is equivalent functionality busybox patch shall behave | ||
24 | * as per GNU patch. | ||
25 | * | ||
26 | * There is a SUSv3 specification for patch, however it looks to be | ||
27 | * incomplete, it doesnt even mention unified diff format. | ||
28 | * http://www.opengroup.org/onlinepubs/007904975/utilities/patch.html | ||
29 | * | ||
30 | * Issues | ||
31 | * - Non-interactive | ||
32 | * - Patches must apply cleanly or the hunk will fail. | ||
33 | * - Reject file isnt saved | ||
34 | * - | ||
35 | */ | ||
36 | |||
37 | #include <getopt.h> | ||
38 | #include <string.h> | ||
39 | #include <stdlib.h> | ||
40 | #include <unistd.h> | ||
41 | #include "busybox.h" | ||
42 | #include "libbb.h" | ||
43 | |||
44 | static int copy_lines(FILE *src_stream, FILE *dest_stream, const unsigned int lines_count) | ||
45 | { | ||
46 | int i = 0; | ||
47 | |||
48 | while (src_stream && (i < lines_count)) { | ||
49 | char *line; | ||
50 | line = bb_get_line_from_file(src_stream); | ||
51 | if (line == NULL) { | ||
52 | break; | ||
53 | } | ||
54 | if (fputs(line, dest_stream) == EOF) { | ||
55 | bb_perror_msg_and_die("Error writing to new file"); | ||
56 | } | ||
57 | free(line); | ||
58 | |||
59 | i++; | ||
60 | } | ||
61 | return(i); | ||
62 | } | ||
63 | |||
64 | /* If patch_level is -1 it will remove all directory names | ||
65 | * char *line must be greater than 4 chars | ||
66 | * returns NULL if the file doesnt exist or error | ||
67 | * returns malloc'ed filename | ||
68 | */ | ||
69 | |||
70 | static unsigned char *extract_filename(char *line, unsigned short patch_level) | ||
71 | { | ||
72 | char *filename_start_ptr = line + 4; | ||
73 | int i; | ||
74 | |||
75 | /* Terminate string at end of source filename */ | ||
76 | { | ||
77 | char *line_ptr; | ||
78 | line_ptr = strchr(filename_start_ptr, '\t'); | ||
79 | if (!line_ptr) { | ||
80 | bb_perror_msg("Malformed line %s", line); | ||
81 | return(NULL); | ||
82 | } | ||
83 | *line_ptr = '\0'; | ||
84 | } | ||
85 | |||
86 | /* Skip over (patch_level) number of leading directories */ | ||
87 | for (i = 0; i < patch_level; i++) { | ||
88 | char *dirname_ptr; | ||
89 | |||
90 | dirname_ptr = strchr(filename_start_ptr, '/'); | ||
91 | if (!dirname_ptr) { | ||
92 | break; | ||
93 | } | ||
94 | filename_start_ptr = dirname_ptr + 1; | ||
95 | } | ||
96 | |||
97 | return(bb_xstrdup(filename_start_ptr)); | ||
98 | } | ||
99 | |||
100 | static int file_doesnt_exist(const char *filename) | ||
101 | { | ||
102 | struct stat statbuf; | ||
103 | return(stat(filename, &statbuf)); | ||
104 | } | ||
105 | |||
106 | extern int patch_main(int argc, char **argv) | ||
107 | { | ||
108 | unsigned int patch_level = -1; | ||
109 | char *patch_line; | ||
110 | int ret = 0; | ||
111 | |||
112 | /* Handle 'p' option */ | ||
113 | if (argv[1] && (argv[1][0] == '-') && (argv[1][1] == 'p')) { | ||
114 | patch_level = atoi(&argv[1][2]); | ||
115 | } | ||
116 | |||
117 | patch_line = bb_get_line_from_file(stdin); | ||
118 | while (patch_line) { | ||
119 | FILE *src_stream; | ||
120 | FILE *dst_stream; | ||
121 | char *original_filename; | ||
122 | char *new_filename; | ||
123 | char *backup_filename; | ||
124 | unsigned int src_cur_line = 1; | ||
125 | unsigned int dest_cur_line = 0; | ||
126 | unsigned int dest_beg_line; | ||
127 | unsigned int bad_hunk_count = 0; | ||
128 | unsigned int hunk_count = 0; | ||
129 | char copy_trailing_lines_flag = 0; | ||
130 | |||
131 | /* Skip everything upto the "---" marker | ||
132 | * No need to parse the lines "Only in <dir>", and "diff <args>" | ||
133 | */ | ||
134 | while (patch_line && strncmp(patch_line, "--- ", 4) != 0) { | ||
135 | free(patch_line); | ||
136 | patch_line = bb_get_line_from_file(stdin); | ||
137 | } | ||
138 | |||
139 | /* Extract the filename used before the patch was generated */ | ||
140 | original_filename = extract_filename(patch_line, patch_level); | ||
141 | free(patch_line); | ||
142 | |||
143 | patch_line = bb_get_line_from_file(stdin); | ||
144 | if (strncmp(patch_line, "+++ ", 4) != 0) { | ||
145 | ret = 2; | ||
146 | bb_error_msg("Invalid patch"); | ||
147 | continue; | ||
148 | } | ||
149 | new_filename = extract_filename(patch_line, patch_level); | ||
150 | free(patch_line); | ||
151 | |||
152 | if (file_doesnt_exist(new_filename)) { | ||
153 | char *line_ptr; | ||
154 | /* Create leading directories */ | ||
155 | line_ptr = strrchr(new_filename, '/'); | ||
156 | if (line_ptr) { | ||
157 | *line_ptr = '\0'; | ||
158 | bb_make_directory(new_filename, -1, FILEUTILS_RECUR); | ||
159 | *line_ptr = '/'; | ||
160 | } | ||
161 | dst_stream = bb_xfopen(new_filename, "w+"); | ||
162 | backup_filename = NULL; | ||
163 | } else { | ||
164 | backup_filename = xmalloc(strlen(new_filename) + 6); | ||
165 | strcpy(backup_filename, new_filename); | ||
166 | strcat(backup_filename, ".orig"); | ||
167 | if (rename(new_filename, backup_filename) == -1) { | ||
168 | bb_perror_msg_and_die("Couldnt create file %s", backup_filename); | ||
169 | } | ||
170 | dst_stream = bb_xfopen(new_filename, "w"); | ||
171 | } | ||
172 | |||
173 | if ((backup_filename == NULL) || file_doesnt_exist(original_filename)) { | ||
174 | src_stream = NULL; | ||
175 | } else { | ||
176 | if (strcmp(original_filename, new_filename) == 0) { | ||
177 | src_stream = bb_xfopen(backup_filename, "r"); | ||
178 | } else { | ||
179 | src_stream = bb_xfopen(original_filename, "r"); | ||
180 | } | ||
181 | } | ||
182 | |||
183 | printf("patching file %s\n", new_filename); | ||
184 | |||
185 | /* Handle each hunk */ | ||
186 | patch_line = bb_get_line_from_file(stdin); | ||
187 | while (patch_line) { | ||
188 | unsigned int count; | ||
189 | unsigned int src_beg_line; | ||
190 | unsigned int unused; | ||
191 | unsigned int hunk_offset_start = 0; | ||
192 | int hunk_error = 0; | ||
193 | |||
194 | /* This bit should be improved */ | ||
195 | if ((sscanf(patch_line, "@@ -%d,%d +%d,%d @@", &src_beg_line, &unused, &dest_beg_line, &unused) != 4) && | ||
196 | (sscanf(patch_line, "@@ -%d,%d +%d @@", &src_beg_line, &unused, &dest_beg_line) != 3) && | ||
197 | (sscanf(patch_line, "@@ -%d +%d,%d @@", &src_beg_line, &dest_beg_line, &unused) != 3)) { | ||
198 | /* No more hunks for this file */ | ||
199 | break; | ||
200 | } | ||
201 | free(patch_line); | ||
202 | hunk_count++; | ||
203 | |||
204 | if (src_beg_line && dest_beg_line) { | ||
205 | /* Copy unmodified lines upto start of hunk */ | ||
206 | /* src_beg_line will be 0 if its a new file */ | ||
207 | count = src_beg_line - src_cur_line; | ||
208 | if (copy_lines(src_stream, dst_stream, count) != count) { | ||
209 | bb_error_msg_and_die("Bad src file"); | ||
210 | } | ||
211 | src_cur_line += count; | ||
212 | dest_cur_line += count; | ||
213 | copy_trailing_lines_flag = 1; | ||
214 | } | ||
215 | hunk_offset_start = src_cur_line; | ||
216 | |||
217 | while ((patch_line = bb_get_line_from_file(stdin)) != NULL) { | ||
218 | if ((*patch_line == '-') || (*patch_line == ' ')) { | ||
219 | char *src_line = NULL; | ||
220 | if (src_stream) { | ||
221 | src_line = bb_get_line_from_file(src_stream); | ||
222 | if (!src_line) { | ||
223 | hunk_error++; | ||
224 | break; | ||
225 | } else { | ||
226 | src_cur_line++; | ||
227 | } | ||
228 | if (strcmp(src_line, patch_line + 1) != 0) { | ||
229 | bb_error_msg("Hunk #%d FAILED at %d.", hunk_count, hunk_offset_start); | ||
230 | hunk_error++; | ||
231 | free(patch_line); | ||
232 | break; | ||
233 | } | ||
234 | free(src_line); | ||
235 | } | ||
236 | if (*patch_line == ' ') { | ||
237 | fputs(patch_line + 1, dst_stream); | ||
238 | dest_cur_line++; | ||
239 | } | ||
240 | } else if (*patch_line == '+') { | ||
241 | fputs(patch_line + 1, dst_stream); | ||
242 | dest_cur_line++; | ||
243 | } else { | ||
244 | break; | ||
245 | } | ||
246 | free(patch_line); | ||
247 | } | ||
248 | if (hunk_error) { | ||
249 | bad_hunk_count++; | ||
250 | } | ||
251 | } | ||
252 | |||
253 | /* Cleanup last patched file */ | ||
254 | if (copy_trailing_lines_flag) { | ||
255 | copy_lines(src_stream, dst_stream, -1); | ||
256 | } | ||
257 | if (src_stream) { | ||
258 | fclose(src_stream); | ||
259 | } | ||
260 | if (dst_stream) { | ||
261 | fclose(dst_stream); | ||
262 | } | ||
263 | if (bad_hunk_count) { | ||
264 | if (!ret) { | ||
265 | ret = 1; | ||
266 | } | ||
267 | bb_error_msg("%d out of %d hunk FAILED", bad_hunk_count, hunk_count); | ||
268 | } else { | ||
269 | /* It worked, we can remove the backup */ | ||
270 | if (backup_filename) { | ||
271 | unlink(backup_filename); | ||
272 | } | ||
273 | if ((dest_cur_line == 0) || (dest_beg_line == 0)) { | ||
274 | /* The new patched file is empty, remove it */ | ||
275 | if (unlink(new_filename) == -1) { | ||
276 | bb_perror_msg_and_die("Couldnt remove file %s", new_filename); | ||
277 | } | ||
278 | if (unlink(original_filename) == -1) { | ||
279 | bb_perror_msg_and_die("Couldnt remove original file %s", new_filename); | ||
280 | } | ||
281 | } | ||
282 | } | ||
283 | } | ||
284 | |||
285 | /* 0 = SUCCESS | ||
286 | * 1 = Some hunks failed | ||
287 | * 2 = More serious problems | ||
288 | */ | ||
289 | return(ret); | ||
290 | } | ||
diff --git a/include/applets.h b/include/applets.h index 01cfb8629..cfb278123 100644 --- a/include/applets.h +++ b/include/applets.h | |||
@@ -421,6 +421,9 @@ | |||
421 | #ifdef CONFIG_PASSWD | 421 | #ifdef CONFIG_PASSWD |
422 | APPLET(passwd, passwd_main, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS) | 422 | APPLET(passwd, passwd_main, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS) |
423 | #endif | 423 | #endif |
424 | #ifdef CONFIG_PATCH | ||
425 | APPLET(patch, patch_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) | ||
426 | #endif | ||
424 | #ifdef CONFIG_PIDFILEHACK | 427 | #ifdef CONFIG_PIDFILEHACK |
425 | APPLET(pidfilehack, pidfilehack_main, _BB_DIR_BIN, _BB_SUID_NEVER) | 428 | APPLET(pidfilehack, pidfilehack_main, _BB_DIR_BIN, _BB_SUID_NEVER) |
426 | #endif | 429 | #endif |
diff --git a/include/usage.h b/include/usage.h index d60a4dc78..701b40daa 100644 --- a/include/usage.h +++ b/include/usage.h | |||
@@ -1696,6 +1696,13 @@ | |||
1696 | "\t-l\tLocks (disables) the specified user account.\n" \ | 1696 | "\t-l\tLocks (disables) the specified user account.\n" \ |
1697 | "\t-u\tUnlocks (re-enables) the specified user account." | 1697 | "\t-u\tUnlocks (re-enables) the specified user account." |
1698 | 1698 | ||
1699 | #define patch_trivial_usage \ | ||
1700 | "[-p<num>]" | ||
1701 | #define patch_full_usage \ | ||
1702 | "[-p<num>]" | ||
1703 | #define patch_example_usage \ | ||
1704 | "$ patch -p1 <example.diff" | ||
1705 | |||
1699 | #define pidfilehack_trivial_usage \ | 1706 | #define pidfilehack_trivial_usage \ |
1700 | "[daemon.pid] [daemon]" | 1707 | "[daemon.pid] [daemon]" |
1701 | #define pidfilehack_full_usage \ | 1708 | #define pidfilehack_full_usage \ |