blob: 4fadbb8fe991d4357e6ae457bb2f6de048064b5b [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
18 *
Rob Landley1bbc0cd2010-08-13 15:50:26 +020019 * -f force (no questions asked)
20 * -F fuzz (number, default 2)
21 * [file] which file to patch
Denys Vlasenkob82ae982010-11-21 05:53:34 +010022 */
Rob Landley1bbc0cd2010-08-13 15:50:26 +020023
Denys Vlasenkob82ae982010-11-21 05:53:34 +010024//applet:IF_PATCH(APPLET(patch, _BB_DIR_USR_BIN, _BB_SUID_DROP))
Rob Landley1bbc0cd2010-08-13 15:50:26 +020025
Denys Vlasenkob82ae982010-11-21 05:53:34 +010026//kbuild:lib-$(CONFIG_PATCH) += patch.o
Rob Landley1bbc0cd2010-08-13 15:50:26 +020027
Denys Vlasenkob82ae982010-11-21 05:53:34 +010028//config:config PATCH
29//config: bool "patch"
30//config: default y
31//config: help
32//config: Apply a unified diff formatted patch.
Rob Landley1bbc0cd2010-08-13 15:50:26 +020033
Denys Vlasenkob82ae982010-11-21 05:53:34 +010034//usage:#define patch_trivial_usage
35//usage: "[OPTIONS] [ORIGFILE [PATCHFILE]]"
36//usage:#define patch_full_usage "\n\n"
37//usage: IF_LONG_OPTS(
38//usage: " -p,--strip N Strip N leading components from file names"
39//usage: "\n -i,--input DIFF Read DIFF instead of stdin"
40//usage: "\n -R,--reverse Reverse patch"
41//usage: "\n -N,--forward Ignore already applied patches"
42//usage: "\n --dry-run Don't actually change files"
43//usage: "\n -E,--remove-empty-files Remove output files if they become empty"
44//usage: )
45//usage: IF_NOT_LONG_OPTS(
46//usage: " -p N Strip N leading components from file names"
47//usage: "\n -i DIFF Read DIFF instead of stdin"
48//usage: "\n -R Reverse patch"
49//usage: "\n -N Ignore already applied patches"
50//usage: "\n -E Remove output files if they become empty"
51//usage: )
52//usage:
53//usage:#define patch_example_usage
54//usage: "$ patch -p1 < example.diff\n"
55//usage: "$ patch -p0 -i example.diff"
Rob Landley1bbc0cd2010-08-13 15:50:26 +020056
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000057#include "libbb.h"
Glenn L McGrath655d8142003-06-22 15:32:41 +000058
Rob Landley1bbc0cd2010-08-13 15:50:26 +020059struct double_list {
60 struct double_list *next;
61 struct double_list *prev;
62 char *data;
63};
64
65// Return the first item from the list, advancing the list (which must be called
66// as &list)
67static
68void *TOY_llist_pop(void *list)
Glenn L McGrath655d8142003-06-22 15:32:41 +000069{
Rob Landley1bbc0cd2010-08-13 15:50:26 +020070 // I'd use a void ** for the argument, and even accept the typecast in all
71 // callers as documentation you need the &, except the stupid compiler
72 // would then scream about type-punned pointers. Screw it.
73 void **llist = (void **)list;
74 void **next = (void **)*llist;
75 *llist = *next;
76
77 return (void *)next;
Glenn L McGrath655d8142003-06-22 15:32:41 +000078}
79
Rob Landley1bbc0cd2010-08-13 15:50:26 +020080// Free all the elements of a linked list
81// if freeit!=NULL call freeit() on each element before freeing it.
82static
83void TOY_llist_free(void *list, void (*freeit)(void *data))
Glenn L McGrath655d8142003-06-22 15:32:41 +000084{
Rob Landley1bbc0cd2010-08-13 15:50:26 +020085 while (list) {
86 void *pop = TOY_llist_pop(&list);
87 if (freeit) freeit(pop);
88 else free(pop);
Glenn L McGrath655d8142003-06-22 15:32:41 +000089
Rob Landley1bbc0cd2010-08-13 15:50:26 +020090 // End doubly linked list too.
91 if (list==pop) break;
Glenn L McGrath655d8142003-06-22 15:32:41 +000092 }
Glenn L McGrath655d8142003-06-22 15:32:41 +000093}
Denys Vlasenko6373bb72010-08-13 16:41:15 +020094//Override bbox's names
95#define llist_pop TOY_llist_pop
96#define llist_free TOY_llist_free
Glenn L McGrath655d8142003-06-22 15:32:41 +000097
Rob Landley1bbc0cd2010-08-13 15:50:26 +020098// Add an entry to the end off a doubly linked list
99static
100struct double_list *dlist_add(struct double_list **list, char *data)
101{
102 struct double_list *line = xmalloc(sizeof(struct double_list));
103
104 line->data = data;
105 if (*list) {
106 line->next = *list;
107 line->prev = (*list)->prev;
108 (*list)->prev->next = line;
109 (*list)->prev = line;
110 } else *list = line->next = line->prev = line;
111
112 return line;
113}
114
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200115
116
117struct globals {
118 char *infile;
119 long prefix;
120
121 struct double_list *current_hunk;
Rob Landley760d0eb2010-08-13 16:40:21 +0200122 long oldline, oldlen, newline, newlen;
123 long linenum;
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100124 int context, state, filein, fileout, hunknum;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200125 char *tempname;
126
127 // was toys.foo:
128 int exitval;
129};
130#define TT (*ptr_to_globals)
131#define INIT_TT() do { \
132 SET_PTR_TO_GLOBALS(xzalloc(sizeof(TT))); \
133} while (0)
134
135
Lukas Huba08187352010-10-21 00:43:00 +0200136#define FLAG_STR "Rup:i:NEx"
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200137/* FLAG_REVERSE must be == 1! Code uses this fact. */
138#define FLAG_REVERSE (1 << 0)
139#define FLAG_u (1 << 1)
140#define FLAG_PATHLEN (1 << 2)
141#define FLAG_INPUT (1 << 3)
Denys Vlasenkoa4160e12010-08-16 01:33:57 +0200142#define FLAG_IGNORE (1 << 4)
Lukas Huba08187352010-10-21 00:43:00 +0200143#define FLAG_RMEMPTY (1 << 5)
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200144//non-standard:
Lukas Huba08187352010-10-21 00:43:00 +0200145#define FLAG_DEBUG (1 << 6)
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200146
147// Dispose of a line of input, either by writing it out or discarding it.
148
149// state < 2: just free
150// state = 2: write whole line to stderr
151// state = 3: write whole line to fileout
152// state > 3: write line+1 to fileout when *line != state
153
154#define PATCH_DEBUG (option_mask32 & FLAG_DEBUG)
155
156static void do_line(void *data)
157{
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100158 struct double_list *dlist = data;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200159
160 if (TT.state>1 && *dlist->data != TT.state)
161 fdprintf(TT.state == 2 ? 2 : TT.fileout,
162 "%s\n", dlist->data+(TT.state>3 ? 1 : 0));
163
164 if (PATCH_DEBUG) fdprintf(2, "DO %d: %s\n", TT.state, dlist->data);
165
166 free(dlist->data);
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100167 free(dlist);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200168}
169
170static void finish_oldfile(void)
171{
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100172 if (TT.tempname) {
173 // Copy the rest of the data and replace the original with the copy.
174 char *temp;
175
176 if (TT.filein != -1) {
177 bb_copyfd_eof(TT.filein, TT.fileout);
178 xclose(TT.filein);
179 }
180 xclose(TT.fileout);
181
182 temp = xstrdup(TT.tempname);
183 temp[strlen(temp) - 6] = '\0';
184 rename(TT.tempname, temp);
185 free(temp);
186
187 free(TT.tempname);
188 TT.tempname = NULL;
189 }
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200190 TT.fileout = TT.filein = -1;
191}
192
193static void fail_hunk(void)
194{
195 if (!TT.current_hunk) return;
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100196 TT.current_hunk->prev->next = NULL;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200197
198 fdprintf(2, "Hunk %d FAILED %ld/%ld.\n", TT.hunknum, TT.oldline, TT.newline);
199 TT.exitval = 1;
200
201 // If we got to this point, we've seeked to the end. Discard changes to
202 // this file and advance to next file.
203
204 TT.state = 2;
Denys Vlasenko6373bb72010-08-13 16:41:15 +0200205 llist_free(TT.current_hunk, do_line);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200206 TT.current_hunk = NULL;
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100207
208 // Abort the copy and delete the temporary file.
209 close(TT.filein);
210 close(TT.fileout);
211 unlink(TT.tempname);
212 free(TT.tempname);
213 TT.tempname = NULL;
214
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200215 TT.state = 0;
216}
217
218// Given a hunk of a unified diff, make the appropriate change to the file.
219// This does not use the location information, but instead treats a hunk
220// as a sort of regex. Copies data from input to output until it finds
221// the change to be made, then outputs the changed data and returns.
222// (Finding EOF first is an error.) This is a single pass operation, so
223// multiple hunks must occur in order in the file.
224
225static int apply_one_hunk(void)
226{
227 struct double_list *plist, *buf = NULL, *check;
228 int matcheof = 0, reverse = option_mask32 & FLAG_REVERSE, backwarn = 0;
Denys Vlasenkocda81592010-08-17 01:31:40 +0200229 /* Do we try "dummy" revert to check whether
230 * to silently skip this hunk? Used to implement -N.
231 */
232 int dummy_revert = 0;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200233
234 // Break doubly linked list so we can use singly linked traversal function.
235 TT.current_hunk->prev->next = NULL;
236
237 // Match EOF if there aren't as many ending context lines as beginning
238 for (plist = TT.current_hunk; plist; plist = plist->next) {
239 if (plist->data[0]==' ') matcheof++;
240 else matcheof = 0;
241 if (PATCH_DEBUG) fdprintf(2, "HUNK:%s\n", plist->data);
242 }
243 matcheof = matcheof < TT.context;
244
245 if (PATCH_DEBUG) fdprintf(2,"MATCHEOF=%c\n", matcheof ? 'Y' : 'N');
246
247 // Loop through input data searching for this hunk. Match all context
248 // lines and all lines to be removed until we've found the end of a
249 // complete hunk.
250 plist = TT.current_hunk;
251 buf = NULL;
252 if (TT.context) for (;;) {
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100253 char *data = xmalloc_reads(TT.filein, NULL, NULL);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200254
255 TT.linenum++;
256
257 // Figure out which line of hunk to compare with next. (Skip lines
258 // of the hunk we'd be adding.)
259 while (plist && *plist->data == "+-"[reverse]) {
260 if (data && !strcmp(data, plist->data+1)) {
261 if (!backwarn) {
Rob Landley9d113ca2010-10-04 00:49:48 +0200262 backwarn = TT.linenum;
Denys Vlasenkocda81592010-08-17 01:31:40 +0200263 if (option_mask32 & FLAG_IGNORE) {
264 dummy_revert = 1;
265 reverse ^= 1;
266 continue;
267 }
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200268 }
269 }
270 plist = plist->next;
271 }
272
273 // Is this EOF?
274 if (!data) {
275 if (PATCH_DEBUG) fdprintf(2, "INEOF\n");
276
277 // Does this hunk need to match EOF?
278 if (!plist && matcheof) break;
279
Rob Landley9d113ca2010-10-04 00:49:48 +0200280 if (backwarn)
281 fdprintf(2,"Possibly reversed hunk %d at %ld\n",
282 TT.hunknum, TT.linenum);
283
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200284 // File ended before we found a place for this hunk.
285 fail_hunk();
286 goto done;
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100287 }
288
289 if (PATCH_DEBUG) fdprintf(2, "IN: %s\n", data);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200290 check = dlist_add(&buf, data);
291
292 // Compare this line with next expected line of hunk.
293 // todo: teach the strcmp() to ignore whitespace.
294
295 // A match can fail because the next line doesn't match, or because
296 // we hit the end of a hunk that needed EOF, and this isn't EOF.
297
298 // If match failed, flush first line of buffered data and
299 // recheck buffered data for a new match until we find one or run
300 // out of buffer.
301
302 for (;;) {
303 if (!plist || strcmp(check->data, plist->data+1)) {
304 // Match failed. Write out first line of buffered data and
305 // recheck remaining buffered data for a new match.
306
307 if (PATCH_DEBUG)
308 fdprintf(2, "NOT: %s\n", plist->data);
309
310 TT.state = 3;
Denys Vlasenko6373bb72010-08-13 16:41:15 +0200311 check = llist_pop(&buf);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200312 check->prev->next = buf;
313 buf->prev = check->prev;
314 do_line(check);
315 plist = TT.current_hunk;
316
317 // If we've reached the end of the buffer without confirming a
318 // match, read more lines.
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100319 if (check == buf) {
320 buf = NULL;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200321 break;
322 }
323 check = buf;
324 } else {
325 if (PATCH_DEBUG)
326 fdprintf(2, "MAYBE: %s\n", plist->data);
327 // This line matches. Advance plist, detect successful match.
328 plist = plist->next;
329 if (!plist && !matcheof) goto out;
330 check = check->next;
331 if (check == buf) break;
332 }
333 }
334 }
335out:
336 // We have a match. Emit changed data.
Denys Vlasenkocda81592010-08-17 01:31:40 +0200337 TT.state = "-+"[reverse ^ dummy_revert];
Denys Vlasenko6373bb72010-08-13 16:41:15 +0200338 llist_free(TT.current_hunk, do_line);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200339 TT.current_hunk = NULL;
340 TT.state = 1;
341done:
342 if (buf) {
343 buf->prev->next = NULL;
Denys Vlasenko6373bb72010-08-13 16:41:15 +0200344 llist_free(buf, do_line);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200345 }
346
347 return TT.state;
348}
349
350// Read a patch file and find hunks, opening/creating/deleting files.
351// Call apply_one_hunk() on each hunk.
352
353// state 0: Not in a hunk, look for +++.
354// state 1: Found +++ file indicator, look for @@
355// state 2: In hunk: counting initial context lines
356// state 3: In hunk: getting body
357
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000358int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000359int patch_main(int argc UNUSED_PARAM, char **argv)
Glenn L McGrath655d8142003-06-22 15:32:41 +0000360{
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200361 int opts;
362 int reverse, state = 0;
363 char *oldname = NULL, *newname = NULL;
364 char *opt_p, *opt_i;
Denis Vlasenkoc93b1622008-03-23 22:55:25 +0000365
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200366 INIT_TT();
367
368 opts = getopt32(argv, FLAG_STR, &opt_p, &opt_i);
Denys Vlasenkoe7b0a9e2010-08-22 05:39:15 +0200369 argv += optind;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200370 reverse = opts & FLAG_REVERSE;
371 TT.prefix = (opts & FLAG_PATHLEN) ? xatoi(opt_p) : 0; // can be negative!
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200372 TT.filein = TT.fileout = -1;
Denys Vlasenkoe7b0a9e2010-08-22 05:39:15 +0200373 if (opts & FLAG_INPUT) {
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100374 xmove_fd(xopen_stdin(opt_i), STDIN_FILENO);
Denys Vlasenkoe7b0a9e2010-08-22 05:39:15 +0200375 } else {
376 if (argv[0] && argv[1]) {
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100377 xmove_fd(xopen_stdin(argv[1]), STDIN_FILENO);
Denys Vlasenkoe7b0a9e2010-08-22 05:39:15 +0200378 }
379 }
380 if (argv[0]) {
381 oldname = xstrdup(argv[0]);
382 newname = xstrdup(argv[0]);
383 }
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200384
385 // Loop through the lines in the patch
386 for(;;) {
387 char *patchline;
388
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100389 patchline = xmalloc_fgetline(stdin);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200390 if (!patchline) break;
391
392 // Other versions of patch accept damaged patches,
393 // so we need to also.
394 if (!*patchline) {
395 free(patchline);
396 patchline = xstrdup(" ");
397 }
398
399 // Are we assembling a hunk?
400 if (state >= 2) {
401 if (*patchline==' ' || *patchline=='+' || *patchline=='-') {
402 dlist_add(&TT.current_hunk, patchline);
403
404 if (*patchline != '+') TT.oldlen--;
405 if (*patchline != '-') TT.newlen--;
406
407 // Context line?
408 if (*patchline==' ' && state==2) TT.context++;
409 else state=3;
410
411 // If we've consumed all expected hunk lines, apply the hunk.
412
413 if (!TT.oldlen && !TT.newlen) state = apply_one_hunk();
414 continue;
415 }
416 fail_hunk();
417 state = 0;
418 continue;
419 }
420
421 // Open a new file?
422 if (!strncmp("--- ", patchline, 4) || !strncmp("+++ ", patchline, 4)) {
423 char *s, **name = reverse ? &newname : &oldname;
424 int i;
425
426 if (*patchline == '+') {
427 name = reverse ? &oldname : &newname;
428 state = 1;
429 }
430
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200431 finish_oldfile();
432
Denys Vlasenkoe7b0a9e2010-08-22 05:39:15 +0200433 if (!argv[0]) {
434 free(*name);
435 // Trim date from end of filename (if any). We don't care.
436 for (s = patchline+4; *s && *s!='\t'; s++)
437 if (*s=='\\' && s[1]) s++;
438 i = atoi(s);
439 if (i>1900 && i<=1970)
440 *name = xstrdup("/dev/null");
441 else {
442 *s = 0;
443 *name = xstrdup(patchline+4);
444 }
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200445 }
446
447 // We defer actually opening the file because svn produces broken
448 // patches that don't signal they want to create a new file the
449 // way the patch man page says, so you have to read the first hunk
450 // and _guess_.
451
Rob Landley760d0eb2010-08-13 16:40:21 +0200452 // Start a new hunk? Usually @@ -oldline,oldlen +newline,newlen @@
453 // but a missing ,value means the value is 1.
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200454 } else if (state == 1 && !strncmp("@@ -", patchline, 4)) {
455 int i;
Rob Landley760d0eb2010-08-13 16:40:21 +0200456 char *s = patchline+4;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200457
Rob Landley760d0eb2010-08-13 16:40:21 +0200458 // Read oldline[,oldlen] +newline[,newlen]
459
460 TT.oldlen = TT.newlen = 1;
461 TT.oldline = strtol(s, &s, 10);
462 if (*s == ',') TT.oldlen=strtol(s+1, &s, 10);
463 TT.newline = strtol(s+2, &s, 10);
464 if (*s == ',') TT.newlen = strtol(s+1, &s, 10);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200465
466 TT.context = 0;
467 state = 2;
468
469 // If this is the first hunk, open the file.
470 if (TT.filein == -1) {
Lukas Huba08187352010-10-21 00:43:00 +0200471 int oldsum, newsum, empty = 0;
Rob Landley760d0eb2010-08-13 16:40:21 +0200472 char *name;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200473
Denys Vlasenkocacb2cd2010-10-05 00:13:02 +0200474 oldsum = TT.oldline + TT.oldlen;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200475 newsum = TT.newline + TT.newlen;
476
477 name = reverse ? oldname : newname;
478
479 // We're deleting oldname if new file is /dev/null (before -p)
480 // or if new hunk is empty (zero context) after patching
481 if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum))
482 {
483 name = reverse ? newname : oldname;
Lukas Huba08187352010-10-21 00:43:00 +0200484 empty++;
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200485 }
486
487 // handle -p path truncation.
488 for (i=0, s = name; *s;) {
489 if ((option_mask32 & FLAG_PATHLEN) && TT.prefix == i) break;
490 if (*(s++)=='/') {
491 name = s;
492 i++;
493 }
494 }
495
Lukas Huba08187352010-10-21 00:43:00 +0200496 if (empty) {
497 // File is empty after the patches have been applied
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200498 state = 0;
Lukas Huba08187352010-10-21 00:43:00 +0200499 if (option_mask32 & FLAG_RMEMPTY) {
500 // If flag -E or --remove-empty-files is set
501 printf("removing %s\n", name);
502 xunlink(name);
503 } else {
504 printf("patching file %s\n", name);
505 xclose(xopen(name, O_WRONLY | O_TRUNC));
506 }
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200507 // If we've got a file to open, do so.
508 } else if (!(option_mask32 & FLAG_PATHLEN) || i <= TT.prefix) {
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100509 struct stat statbuf;
510
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200511 // If the old file was null, we're creating a new one.
512 if (!strcmp(oldname, "/dev/null") || !oldsum) {
513 printf("creating %s\n", name);
514 s = strrchr(name, '/');
515 if (s) {
516 *s = 0;
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100517 bb_make_directory(name, -1, FILEUTILS_RECUR);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200518 *s = '/';
519 }
Denys Vlasenkoc05387d2010-10-18 02:38:27 +0200520 TT.filein = xopen(name, O_CREAT|O_EXCL|O_RDWR);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200521 } else {
522 printf("patching file %s\n", name);
Rob Landley9d113ca2010-10-04 00:49:48 +0200523 TT.filein = xopen(name, O_RDONLY);
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200524 }
Denys Vlasenkob82ae982010-11-21 05:53:34 +0100525
526 TT.tempname = xasprintf("%sXXXXXX", name);
527 TT.fileout = xmkstemp(TT.tempname);
528 // Set permissions of output file
529 fstat(TT.filein, &statbuf);
530 fchmod(TT.fileout, statbuf.st_mode);
531
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200532 TT.linenum = 0;
533 TT.hunknum = 0;
534 }
535 }
536
537 TT.hunknum++;
538
539 continue;
540 }
541
542 // If we didn't continue above, discard this line.
543 free(patchline);
Glenn L McGrath655d8142003-06-22 15:32:41 +0000544 }
545
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200546 finish_oldfile();
Glenn L McGrath655d8142003-06-22 15:32:41 +0000547
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200548 if (ENABLE_FEATURE_CLEAN_UP) {
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200549 free(oldname);
550 free(newname);
551 }
Denis Vlasenko64a76d72008-03-24 18:18:03 +0000552
Rob Landley1bbc0cd2010-08-13 15:50:26 +0200553 return TT.exitval;
Glenn L McGrath655d8142003-06-22 15:32:41 +0000554}