blob: ec5b8e7adf584cf5b04fe6ff85156c7e2f193b11 [file] [log] [blame]
Rob Landley1bbc0cd2010-08-13 15:50:26 +02001/* vi: set sw=4 ts=4:
2 *
Denys Vlasenkob82ae982010-11-21 05:53:34 +01003 * Apply a "universal" diff.
4 * Adapted from toybox's patch implementation.
Rob Landley1bbc0cd2010-08-13 15:50:26 +02005 *
6 * Copyright 2007 Rob Landley <rob@landley.net>
7 *
8 * see http://www.opengroup.org/onlinepubs/009695399/utilities/patch.html
9 * (But only does -u, because who still cares about "ed"?)
10 *
11 * TODO:
12 * -b backup
13 * -l treat all whitespace as a single space
Rob Landley1bbc0cd2010-08-13 15:50:26 +020014 * -d chdir first
15 * -D define wrap #ifdef and #ifndef around changes
16 * -o outfile output here instead of in place
17 * -r rejectfile write rejected hunks to this file
Denys Vlasenko416e9782011-06-19 01:40:31 +020018 * --dry-run (regression!)
Rob Landley1bbc0cd2010-08-13 15:50:26 +020019 *
Rob Landley1bbc0cd2010-08-13 15:50:26 +020020 * -f force (no questions asked)
21 * -F fuzz (number, default 2)
22 * [file] which file to patch
Denys Vlasenkob82ae982010-11-21 05:53:34 +010023 */
Rob Landley1bbc0cd2010-08-13 15:50:26 +020024
Denys Vlasenkob82ae982010-11-21 05:53:34 +010025//config:config PATCH
26//config: bool "patch"
27//config: default y
28//config: help
29//config: Apply a unified diff formatted patch.
Rob Landley1bbc0cd2010-08-13 15:50:26 +020030
Denys Vlasenko416e9782011-06-19 01:40:31 +020031//applet:IF_PATCH(APPLET(patch, BB_DIR_USR_BIN, BB_SUID_DROP))
32
33//kbuild:lib-$(CONFIG_PATCH) += patch.o
34
Denys Vlasenkob82ae982010-11-21 05:53:34 +010035//usage:#define patch_trivial_usage
36//usage: "[OPTIONS] [ORIGFILE [PATCHFILE]]"
37//usage:#define patch_full_usage "\n\n"
38//usage: IF_LONG_OPTS(
39//usage: " -p,--strip N Strip N leading components from file names"
40//usage: "\n -i,--input DIFF Read DIFF instead of stdin"
41//usage: "\n -R,--reverse Reverse patch"
42//usage: "\n -N,--forward Ignore already applied patches"
Denys Vlasenko416e9782011-06-19 01:40:31 +020043/*usage: "\n --dry-run Don't actually change files" - TODO */
Denys Vlasenkob82ae982010-11-21 05:53:34 +010044//usage: "\n -E,--remove-empty-files Remove output files if they become empty"
45//usage: )
46//usage: IF_NOT_LONG_OPTS(
47//usage: " -p N Strip N leading components from file names"
48//usage: "\n -i DIFF Read DIFF instead of stdin"
49//usage: "\n -R Reverse patch"
50//usage: "\n -N Ignore already applied patches"
51//usage: "\n -E Remove output files if they become empty"
52//usage: )
Denys Vlasenko416e9782011-06-19 01:40:31 +020053/* -u "interpret as unified diff" is supported but not documented: this info is not useful for --help */
54/* -x "debug" is supported but does nothing */
Denys Vlasenkob82ae982010-11-21 05:53:34 +010055//usage:
56//usage:#define patch_example_usage
57//usage: "$ patch -p1 < example.diff\n"
58//usage: "$ patch -p0 -i example.diff"
Rob Landley1bbc0cd2010-08-13 15:50:26 +020059
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000060#include "libbb.h"
Glenn L McGrath655d8142003-06-22 15:32:41 +000061
Denys Vlasenko400ff222010-11-21 05:54:28 +010062
63// libbb candidate?
64
Rob Landley1bbc0cd2010-08-13 15:50:26 +020065struct double_list {
66 struct double_list *next;
67 struct double_list *prev;
68 char *data;
69};
70
Rob Landley1bbc0cd2010-08-13 15:50:26 +020071// Free all the elements of a linked list
Denys Vlasenko400ff222010-11-21 05:54:28 +010072// Call freeit() on each element before freeing it.
Rob Landley1bbc0cd2010-08-13 15:50:26 +020073static
Denys Vlasenko400ff222010-11-21 05:54:28 +010074void dlist_free(struct double_list *list, void (*freeit)(void *data))
Glenn L McGrath655d8142003-06-22 15:32:41 +000075{
Rob Landley1bbc0cd2010-08-13 15:50:26 +020076 while (list) {
Denys Vlasenko400ff222010-11-21 05:54:28 +010077 void *pop = list;
78 list = list->next;
79 freeit(pop);
80 // Bail out also if list is circular.
81 if (list == pop) break;
Glenn L McGrath655d8142003-06-22 15:32:41 +000082 }
Glenn L McGrath655d8142003-06-22 15:32:41 +000083}
84
Denys Vlasenko400ff222010-11-21 05:54:28 +010085// Add an entry before "list" element in (circular) doubly linked list
Rob Landley1bbc0cd2010-08-13 15:50:26 +020086static
87struct double_list *dlist_add(struct double_list **list, char *data)
88{
Denys Vlasenko400ff222010-11-21 05:54:28 +010089 struct double_list *llist;
90 struct double_list *line = xmalloc(sizeof(*line));
Rob Landley1bbc0cd2010-08-13 15:50:26 +020091
92 line->data = data;
Denys Vlasenko400ff222010-11-21 05:54:28 +010093 llist = *list;
94 if (llist) {
95 struct double_list *p;
96 line->next = llist;
97 p = line->prev = llist->prev;
98 // (list is circular, we assume p is never NULL)
99 p->next = line;
100 llist->prev = line;
101 } else
102 *list = line->next = line->prev = line;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200103
104 return line;
105}
106
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200107
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200108struct globals {
109 char *infile;
110 long prefix;
111
112 struct double_list *current_hunk;
Denys Vlasenko400ff222010-11-21 05:54:28 +0100113
Rob Landley760d0eb2010-08-13 16:40:21 +0200114 long oldline, oldlen, newline, newlen;
115 long linenum;
Denys Vlasenko400ff222010-11-21 05:54:28 +0100116 int context, state, hunknum;
117 int filein, fileout;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200118 char *tempname;
119
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200120 int exitval;
121};
122#define TT (*ptr_to_globals)
123#define INIT_TT() do { \
124 SET_PTR_TO_GLOBALS(xzalloc(sizeof(TT))); \
125} while (0)
126
127
Lukas Huba08187352010-10-21 00:43:00 +0200128#define FLAG_STR "Rup:i:NEx"
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200129/* FLAG_REVERSE must be == 1! Code uses this fact. */
130#define FLAG_REVERSE (1 << 0)
131#define FLAG_u (1 << 1)
132#define FLAG_PATHLEN (1 << 2)
133#define FLAG_INPUT (1 << 3)
Denys Vlasenkoa4160e12010-08-16 01:33:57 +0200134#define FLAG_IGNORE (1 << 4)
Lukas Huba08187352010-10-21 00:43:00 +0200135#define FLAG_RMEMPTY (1 << 5)
Denys Vlasenko416e9782011-06-19 01:40:31 +0200136/* Enable this bit and use -x for debug output: */
137#define FLAG_DEBUG (0 << 6)
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200138
139// Dispose of a line of input, either by writing it out or discarding it.
140
141// state < 2: just free
142// state = 2: write whole line to stderr
143// state = 3: write whole line to fileout
144// state > 3: write line+1 to fileout when *line != state
145
146#define PATCH_DEBUG (option_mask32 & FLAG_DEBUG)
147
148static void do_line(void *data)
149{
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100150 struct double_list *dlist = data;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200151
152 if (TT.state>1 && *dlist->data != TT.state)
153 fdprintf(TT.state == 2 ? 2 : TT.fileout,
154 "%s\n", dlist->data+(TT.state>3 ? 1 : 0));
155
156 if (PATCH_DEBUG) fdprintf(2, "DO %d: %s\n", TT.state, dlist->data);
157
158 free(dlist->data);
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100159 free(dlist);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200160}
161
162static void finish_oldfile(void)
163{
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100164 if (TT.tempname) {
165 // Copy the rest of the data and replace the original with the copy.
166 char *temp;
167
168 if (TT.filein != -1) {
169 bb_copyfd_eof(TT.filein, TT.fileout);
170 xclose(TT.filein);
171 }
172 xclose(TT.fileout);
173
174 temp = xstrdup(TT.tempname);
175 temp[strlen(temp) - 6] = '\0';
176 rename(TT.tempname, temp);
177 free(temp);
178
179 free(TT.tempname);
180 TT.tempname = NULL;
181 }
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200182 TT.fileout = TT.filein = -1;
183}
184
185static void fail_hunk(void)
186{
187 if (!TT.current_hunk) return;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200188
189 fdprintf(2, "Hunk %d FAILED %ld/%ld.\n", TT.hunknum, TT.oldline, TT.newline);
190 TT.exitval = 1;
191
192 // If we got to this point, we've seeked to the end. Discard changes to
193 // this file and advance to next file.
194
195 TT.state = 2;
Denys Vlasenko400ff222010-11-21 05:54:28 +0100196 TT.current_hunk->prev->next = NULL;
197 dlist_free(TT.current_hunk, do_line);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200198 TT.current_hunk = NULL;
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100199
200 // Abort the copy and delete the temporary file.
201 close(TT.filein);
202 close(TT.fileout);
203 unlink(TT.tempname);
204 free(TT.tempname);
205 TT.tempname = NULL;
206
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200207 TT.state = 0;
208}
209
210// Given a hunk of a unified diff, make the appropriate change to the file.
211// This does not use the location information, but instead treats a hunk
212// as a sort of regex. Copies data from input to output until it finds
213// the change to be made, then outputs the changed data and returns.
214// (Finding EOF first is an error.) This is a single pass operation, so
215// multiple hunks must occur in order in the file.
216
217static int apply_one_hunk(void)
218{
219 struct double_list *plist, *buf = NULL, *check;
220 int matcheof = 0, reverse = option_mask32 & FLAG_REVERSE, backwarn = 0;
Denys Vlasenkocda81592010-08-17 01:31:40 +0200221 /* Do we try "dummy" revert to check whether
222 * to silently skip this hunk? Used to implement -N.
223 */
224 int dummy_revert = 0;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200225
226 // Break doubly linked list so we can use singly linked traversal function.
227 TT.current_hunk->prev->next = NULL;
228
229 // Match EOF if there aren't as many ending context lines as beginning
230 for (plist = TT.current_hunk; plist; plist = plist->next) {
231 if (plist->data[0]==' ') matcheof++;
232 else matcheof = 0;
233 if (PATCH_DEBUG) fdprintf(2, "HUNK:%s\n", plist->data);
234 }
235 matcheof = matcheof < TT.context;
236
237 if (PATCH_DEBUG) fdprintf(2,"MATCHEOF=%c\n", matcheof ? 'Y' : 'N');
238
239 // Loop through input data searching for this hunk. Match all context
240 // lines and all lines to be removed until we've found the end of a
241 // complete hunk.
242 plist = TT.current_hunk;
243 buf = NULL;
Rob Landley8027a202010-11-29 03:24:51 +0100244 if (reverse ? TT.oldlen : TT.newlen) for (;;) {
Denys Vlasenko80c5b682011-05-08 21:21:10 +0200245 char *data = xmalloc_reads(TT.filein, NULL);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200246
247 TT.linenum++;
248
249 // Figure out which line of hunk to compare with next. (Skip lines
250 // of the hunk we'd be adding.)
251 while (plist && *plist->data == "+-"[reverse]) {
252 if (data && !strcmp(data, plist->data+1)) {
253 if (!backwarn) {
Rob Landley9d113ca2010-10-04 00:49:48 +0200254 backwarn = TT.linenum;
Denys Vlasenkocda81592010-08-17 01:31:40 +0200255 if (option_mask32 & FLAG_IGNORE) {
256 dummy_revert = 1;
257 reverse ^= 1;
258 continue;
259 }
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200260 }
261 }
262 plist = plist->next;
263 }
264
265 // Is this EOF?
266 if (!data) {
267 if (PATCH_DEBUG) fdprintf(2, "INEOF\n");
268
269 // Does this hunk need to match EOF?
270 if (!plist && matcheof) break;
271
Rob Landley9d113ca2010-10-04 00:49:48 +0200272 if (backwarn)
273 fdprintf(2,"Possibly reversed hunk %d at %ld\n",
274 TT.hunknum, TT.linenum);
275
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200276 // File ended before we found a place for this hunk.
277 fail_hunk();
278 goto done;
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100279 }
280
281 if (PATCH_DEBUG) fdprintf(2, "IN: %s\n", data);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200282 check = dlist_add(&buf, data);
283
284 // Compare this line with next expected line of hunk.
285 // todo: teach the strcmp() to ignore whitespace.
286
287 // A match can fail because the next line doesn't match, or because
288 // we hit the end of a hunk that needed EOF, and this isn't EOF.
289
290 // If match failed, flush first line of buffered data and
291 // recheck buffered data for a new match until we find one or run
292 // out of buffer.
293
294 for (;;) {
295 if (!plist || strcmp(check->data, plist->data+1)) {
296 // Match failed. Write out first line of buffered data and
297 // recheck remaining buffered data for a new match.
298
299 if (PATCH_DEBUG)
300 fdprintf(2, "NOT: %s\n", plist->data);
301
302 TT.state = 3;
Denys Vlasenko400ff222010-11-21 05:54:28 +0100303 check = buf;
304 buf = buf->next;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200305 check->prev->next = buf;
306 buf->prev = check->prev;
307 do_line(check);
308 plist = TT.current_hunk;
309
310 // If we've reached the end of the buffer without confirming a
311 // match, read more lines.
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100312 if (check == buf) {
313 buf = NULL;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200314 break;
315 }
316 check = buf;
317 } else {
318 if (PATCH_DEBUG)
319 fdprintf(2, "MAYBE: %s\n", plist->data);
320 // This line matches. Advance plist, detect successful match.
321 plist = plist->next;
322 if (!plist && !matcheof) goto out;
323 check = check->next;
324 if (check == buf) break;
325 }
326 }
327 }
328out:
329 // We have a match. Emit changed data.
Denys Vlasenkocda81592010-08-17 01:31:40 +0200330 TT.state = "-+"[reverse ^ dummy_revert];
Denys Vlasenko400ff222010-11-21 05:54:28 +0100331 dlist_free(TT.current_hunk, do_line);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200332 TT.current_hunk = NULL;
333 TT.state = 1;
334done:
335 if (buf) {
336 buf->prev->next = NULL;
Denys Vlasenko400ff222010-11-21 05:54:28 +0100337 dlist_free(buf, do_line);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200338 }
339
340 return TT.state;
341}
342
343// Read a patch file and find hunks, opening/creating/deleting files.
344// Call apply_one_hunk() on each hunk.
345
346// state 0: Not in a hunk, look for +++.
347// state 1: Found +++ file indicator, look for @@
348// state 2: In hunk: counting initial context lines
349// state 3: In hunk: getting body
350
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000351int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000352int patch_main(int argc UNUSED_PARAM, char **argv)
Glenn L McGrath655d8142003-06-22 15:32:41 +0000353{
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200354 int opts;
355 int reverse, state = 0;
356 char *oldname = NULL, *newname = NULL;
357 char *opt_p, *opt_i;
Rob Landley8027a202010-11-29 03:24:51 +0100358 long oldlen = oldlen; /* for compiler */
359 long newlen = newlen; /* for compiler */
Denis Vlasenkoc93b1622008-03-23 22:55:25 +0000360
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200361 INIT_TT();
362
363 opts = getopt32(argv, FLAG_STR, &opt_p, &opt_i);
Denys Vlasenkoe7b0a9e2010-08-22 05:39:15 +0200364 argv += optind;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200365 reverse = opts & FLAG_REVERSE;
366 TT.prefix = (opts & FLAG_PATHLEN) ? xatoi(opt_p) : 0; // can be negative!
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200367 TT.filein = TT.fileout = -1;
Denys Vlasenkoe7b0a9e2010-08-22 05:39:15 +0200368 if (opts & FLAG_INPUT) {
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100369 xmove_fd(xopen_stdin(opt_i), STDIN_FILENO);
Denys Vlasenkoe7b0a9e2010-08-22 05:39:15 +0200370 } else {
371 if (argv[0] && argv[1]) {
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100372 xmove_fd(xopen_stdin(argv[1]), STDIN_FILENO);
Denys Vlasenkoe7b0a9e2010-08-22 05:39:15 +0200373 }
374 }
375 if (argv[0]) {
376 oldname = xstrdup(argv[0]);
377 newname = xstrdup(argv[0]);
378 }
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200379
380 // Loop through the lines in the patch
381 for(;;) {
382 char *patchline;
383
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100384 patchline = xmalloc_fgetline(stdin);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200385 if (!patchline) break;
386
387 // Other versions of patch accept damaged patches,
388 // so we need to also.
389 if (!*patchline) {
390 free(patchline);
391 patchline = xstrdup(" ");
392 }
393
394 // Are we assembling a hunk?
395 if (state >= 2) {
396 if (*patchline==' ' || *patchline=='+' || *patchline=='-') {
397 dlist_add(&TT.current_hunk, patchline);
398
Rob Landley8027a202010-11-29 03:24:51 +0100399 if (*patchline != '+') oldlen--;
400 if (*patchline != '-') newlen--;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200401
402 // Context line?
403 if (*patchline==' ' && state==2) TT.context++;
404 else state=3;
405
406 // If we've consumed all expected hunk lines, apply the hunk.
407
Rob Landley8027a202010-11-29 03:24:51 +0100408 if (!oldlen && !newlen) state = apply_one_hunk();
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200409 continue;
410 }
411 fail_hunk();
412 state = 0;
413 continue;
414 }
415
416 // Open a new file?
417 if (!strncmp("--- ", patchline, 4) || !strncmp("+++ ", patchline, 4)) {
418 char *s, **name = reverse ? &newname : &oldname;
419 int i;
420
421 if (*patchline == '+') {
422 name = reverse ? &oldname : &newname;
423 state = 1;
424 }
425
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200426 finish_oldfile();
427
Denys Vlasenkoe7b0a9e2010-08-22 05:39:15 +0200428 if (!argv[0]) {
429 free(*name);
430 // Trim date from end of filename (if any). We don't care.
431 for (s = patchline+4; *s && *s!='\t'; s++)
432 if (*s=='\\' && s[1]) s++;
433 i = atoi(s);
434 if (i>1900 && i<=1970)
435 *name = xstrdup("/dev/null");
436 else {
437 *s = 0;
438 *name = xstrdup(patchline+4);
439 }
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200440 }
441
442 // We defer actually opening the file because svn produces broken
443 // patches that don't signal they want to create a new file the
444 // way the patch man page says, so you have to read the first hunk
445 // and _guess_.
446
Rob Landley760d0eb2010-08-13 16:40:21 +0200447 // Start a new hunk? Usually @@ -oldline,oldlen +newline,newlen @@
448 // but a missing ,value means the value is 1.
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200449 } else if (state == 1 && !strncmp("@@ -", patchline, 4)) {
450 int i;
Rob Landley760d0eb2010-08-13 16:40:21 +0200451 char *s = patchline+4;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200452
Rob Landley760d0eb2010-08-13 16:40:21 +0200453 // Read oldline[,oldlen] +newline[,newlen]
454
Rob Landley8027a202010-11-29 03:24:51 +0100455 TT.oldlen = oldlen = TT.newlen = newlen = 1;
Rob Landley760d0eb2010-08-13 16:40:21 +0200456 TT.oldline = strtol(s, &s, 10);
Rob Landley8027a202010-11-29 03:24:51 +0100457 if (*s == ',') TT.oldlen = oldlen = strtol(s+1, &s, 10);
Rob Landley760d0eb2010-08-13 16:40:21 +0200458 TT.newline = strtol(s+2, &s, 10);
Rob Landley8027a202010-11-29 03:24:51 +0100459 if (*s == ',') TT.newlen = newlen = strtol(s+1, &s, 10);
460
461 if (oldlen < 1 && newlen < 1)
462 bb_error_msg_and_die("Really? %s", patchline);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200463
464 TT.context = 0;
465 state = 2;
466
467 // If this is the first hunk, open the file.
468 if (TT.filein == -1) {
Lukas Huba08187352010-10-21 00:43:00 +0200469 int oldsum, newsum, empty = 0;
Rob Landley760d0eb2010-08-13 16:40:21 +0200470 char *name;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200471
Rob Landley8027a202010-11-29 03:24:51 +0100472 oldsum = TT.oldline + oldlen;
473 newsum = TT.newline + newlen;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200474
475 name = reverse ? oldname : newname;
476
477 // We're deleting oldname if new file is /dev/null (before -p)
478 // or if new hunk is empty (zero context) after patching
479 if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum))
480 {
481 name = reverse ? newname : oldname;
Lukas Huba08187352010-10-21 00:43:00 +0200482 empty++;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200483 }
484
485 // handle -p path truncation.
486 for (i=0, s = name; *s;) {
487 if ((option_mask32 & FLAG_PATHLEN) && TT.prefix == i) break;
488 if (*(s++)=='/') {
489 name = s;
490 i++;
491 }
492 }
493
Lukas Huba08187352010-10-21 00:43:00 +0200494 if (empty) {
495 // File is empty after the patches have been applied
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200496 state = 0;
Lukas Huba08187352010-10-21 00:43:00 +0200497 if (option_mask32 & FLAG_RMEMPTY) {
498 // If flag -E or --remove-empty-files is set
499 printf("removing %s\n", name);
500 xunlink(name);
501 } else {
502 printf("patching file %s\n", name);
503 xclose(xopen(name, O_WRONLY | O_TRUNC));
504 }
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200505 // If we've got a file to open, do so.
506 } else if (!(option_mask32 & FLAG_PATHLEN) || i <= TT.prefix) {
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100507 struct stat statbuf;
508
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200509 // If the old file was null, we're creating a new one.
510 if (!strcmp(oldname, "/dev/null") || !oldsum) {
511 printf("creating %s\n", name);
512 s = strrchr(name, '/');
513 if (s) {
514 *s = 0;
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100515 bb_make_directory(name, -1, FILEUTILS_RECUR);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200516 *s = '/';
517 }
Denys Vlasenkoc05387d2010-10-18 02:38:27 +0200518 TT.filein = xopen(name, O_CREAT|O_EXCL|O_RDWR);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200519 } else {
520 printf("patching file %s\n", name);
Rob Landley9d113ca2010-10-04 00:49:48 +0200521 TT.filein = xopen(name, O_RDONLY);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200522 }
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100523
524 TT.tempname = xasprintf("%sXXXXXX", name);
525 TT.fileout = xmkstemp(TT.tempname);
526 // Set permissions of output file
527 fstat(TT.filein, &statbuf);
528 fchmod(TT.fileout, statbuf.st_mode);
529
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200530 TT.linenum = 0;
531 TT.hunknum = 0;
532 }
533 }
534
535 TT.hunknum++;
536
537 continue;
538 }
539
540 // If we didn't continue above, discard this line.
541 free(patchline);
Glenn L McGrath655d8142003-06-22 15:32:41 +0000542 }
543
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200544 finish_oldfile();
Glenn L McGrath655d8142003-06-22 15:32:41 +0000545
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200546 if (ENABLE_FEATURE_CLEAN_UP) {
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200547 free(oldname);
548 free(newname);
549 }
Denis Vlasenko64a76d72008-03-24 18:18:03 +0000550
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200551 return TT.exitval;
Glenn L McGrath655d8142003-06-22 15:32:41 +0000552}