~mmach/netext73/busybox

« back to all changes in this revision

Viewing changes to editors/patch.c

  • Committer: mmach
  • Date: 2021-04-14 13:54:24 UTC
  • Revision ID: netbit73@gmail.com-20210414135424-8x3fxf716zs4wflb
1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* vi: set sw=4 ts=4:
 
2
 *
 
3
 * Apply a "universal" diff.
 
4
 * Adapted from toybox's patch implementation.
 
5
 *
 
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
 
14
 * -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
 *
 
19
 * -f force (no questions asked)
 
20
 * -F fuzz (number, default 2)
 
21
 * [file] which file to patch
 
22
 */
 
23
//config:config PATCH
 
24
//config:       bool "patch (9.4 kb)"
 
25
//config:       default y
 
26
//config:       help
 
27
//config:       Apply a unified diff formatted patch.
 
28
 
 
29
//applet:IF_PATCH(APPLET(patch, BB_DIR_USR_BIN, BB_SUID_DROP))
 
30
 
 
31
//kbuild:lib-$(CONFIG_PATCH) += patch.o
 
32
 
 
33
//usage:#define patch_trivial_usage
 
34
//usage:       "[OPTIONS] [ORIGFILE [PATCHFILE]]"
 
35
//usage:#define patch_full_usage "\n\n"
 
36
//usage:       "        -p N    Strip N leading components from file names"
 
37
//usage:     "\n        -i DIFF Read DIFF instead of stdin"
 
38
//usage:     "\n        -R      Reverse patch"
 
39
//usage:     "\n        -N      Ignore already applied patches"
 
40
//usage:     "\n        -E      Remove output files if they become empty"
 
41
//usage:        IF_LONG_OPTS(
 
42
//usage:     "\n        --dry-run       Don't actually change files"
 
43
//usage:        )
 
44
/* -u "interpret as unified diff" is supported but not documented: this info is not useful for --help */
 
45
//usage:
 
46
//usage:#define patch_example_usage
 
47
//usage:       "$ patch -p1 < example.diff\n"
 
48
//usage:       "$ patch -p0 -i example.diff"
 
49
 
 
50
#include "libbb.h"
 
51
 
 
52
#define PATCH_DEBUG  0
 
53
 
 
54
// libbb candidate?
 
55
 
 
56
struct double_list {
 
57
        struct double_list *next;
 
58
        struct double_list *prev;
 
59
        char *data;
 
60
};
 
61
 
 
62
// Free all the elements of a linked list
 
63
// Call freeit() on each element before freeing it.
 
64
static void dlist_free(struct double_list *list, void (*freeit)(void *data))
 
65
{
 
66
        while (list) {
 
67
                void *pop = list;
 
68
                list = list->next;
 
69
                freeit(pop);
 
70
                // Bail out also if list is circular.
 
71
                if (list == pop) break;
 
72
        }
 
73
}
 
74
 
 
75
// Add an entry before "list" element in (circular) doubly linked list
 
76
static struct double_list *dlist_add(struct double_list **list, char *data)
 
77
{
 
78
        struct double_list *llist;
 
79
        struct double_list *line = xmalloc(sizeof(*line));
 
80
 
 
81
        line->data = data;
 
82
        llist = *list;
 
83
        if (llist) {
 
84
                struct double_list *p;
 
85
                line->next = llist;
 
86
                p = line->prev = llist->prev;
 
87
                // (list is circular, we assume p is never NULL)
 
88
                p->next = line;
 
89
                llist->prev = line;
 
90
        } else
 
91
                *list = line->next = line->prev = line;
 
92
 
 
93
        return line;
 
94
}
 
95
 
 
96
 
 
97
struct globals {
 
98
        char *infile;
 
99
        long prefix;
 
100
 
 
101
        struct double_list *current_hunk;
 
102
 
 
103
        long oldline, oldlen, newline, newlen;
 
104
        long linenum;
 
105
        int context, state, hunknum;
 
106
        int filein, fileout;
 
107
        char *tempname;
 
108
 
 
109
        int exitval;
 
110
};
 
111
#define TT (*ptr_to_globals)
 
112
#define INIT_TT() do { \
 
113
        SET_PTR_TO_GLOBALS(xzalloc(sizeof(TT))); \
 
114
} while (0)
 
115
 
 
116
 
 
117
#define FLAG_STR "Rup:i:NEfg"
 
118
/* FLAG_REVERSE must be == 1! Code uses this fact. */
 
119
#define FLAG_REVERSE  (1 << 0)
 
120
#define FLAG_u        (1 << 1)
 
121
#define FLAG_PATHLEN  (1 << 2)
 
122
#define FLAG_INPUT    (1 << 3)
 
123
#define FLAG_IGNORE   (1 << 4)
 
124
#define FLAG_RMEMPTY  (1 << 5)
 
125
#define FLAG_f_unused (1 << 6)
 
126
#define FLAG_g_unused (1 << 7)
 
127
#define FLAG_dry_run  ((1 << 8) * ENABLE_LONG_OPTS)
 
128
 
 
129
 
 
130
// Dispose of a line of input, either by writing it out or discarding it.
 
131
 
 
132
// state < 2: just free
 
133
// state = 2: write whole line to stderr
 
134
// state = 3: write whole line to fileout
 
135
// state > 3: write line+1 to fileout when *line != state
 
136
 
 
137
static void do_line(void *data)
 
138
{
 
139
        struct double_list *dlist = data;
 
140
 
 
141
        if (TT.state>1 && *dlist->data != TT.state)
 
142
                fdprintf(TT.state == 2 ? 2 : TT.fileout,
 
143
                        "%s\n", dlist->data+(TT.state>3 ? 1 : 0));
 
144
 
 
145
        if (PATCH_DEBUG) fdprintf(2, "DO %d: %s\n", TT.state, dlist->data);
 
146
 
 
147
        free(dlist->data);
 
148
        free(dlist);
 
149
}
 
150
 
 
151
static void finish_oldfile(void)
 
152
{
 
153
        if (TT.tempname) {
 
154
                // Copy the rest of the data and replace the original with the copy.
 
155
                char *temp;
 
156
 
 
157
                if (TT.filein != -1) {
 
158
                        bb_copyfd_eof(TT.filein, TT.fileout);
 
159
                        xclose(TT.filein);
 
160
                }
 
161
                xclose(TT.fileout);
 
162
 
 
163
                if (!ENABLE_LONG_OPTS || TT.tempname[0]) { /* not --dry-run? */
 
164
                        temp = xstrdup(TT.tempname);
 
165
                        temp[strlen(temp) - 6] = '\0';
 
166
                        rename(TT.tempname, temp);
 
167
                        free(temp);
 
168
                        free(TT.tempname);
 
169
                }
 
170
 
 
171
                TT.tempname = NULL;
 
172
        }
 
173
        TT.fileout = TT.filein = -1;
 
174
}
 
175
 
 
176
static void fail_hunk(void)
 
177
{
 
178
        if (!TT.current_hunk) return;
 
179
 
 
180
        fdprintf(2, "Hunk %d FAILED %ld/%ld.\n", TT.hunknum, TT.oldline, TT.newline);
 
181
        TT.exitval = 1;
 
182
 
 
183
        // If we got to this point, we've seeked to the end.  Discard changes to
 
184
        // this file and advance to next file.
 
185
 
 
186
        TT.state = 2;
 
187
        TT.current_hunk->prev->next = NULL;
 
188
        dlist_free(TT.current_hunk, do_line);
 
189
        TT.current_hunk = NULL;
 
190
 
 
191
        // Abort the copy and delete the temporary file.
 
192
        close(TT.filein);
 
193
        close(TT.fileout);
 
194
        if (!ENABLE_LONG_OPTS || TT.tempname[0]) { /* not --dry-run? */
 
195
                unlink(TT.tempname);
 
196
                free(TT.tempname);
 
197
        }
 
198
        TT.tempname = NULL;
 
199
 
 
200
        TT.state = 0;
 
201
}
 
202
 
 
203
// Given a hunk of a unified diff, make the appropriate change to the file.
 
204
// This does not use the location information, but instead treats a hunk
 
205
// as a sort of regex.  Copies data from input to output until it finds
 
206
// the change to be made, then outputs the changed data and returns.
 
207
// (Finding EOF first is an error.)  This is a single pass operation, so
 
208
// multiple hunks must occur in order in the file.
 
209
 
 
210
static int apply_one_hunk(void)
 
211
{
 
212
        struct double_list *plist, *buf = NULL, *check;
 
213
        int matcheof = 0, reverse = option_mask32 & FLAG_REVERSE, backwarn = 0;
 
214
        /* Do we try "dummy" revert to check whether
 
215
         * to silently skip this hunk? Used to implement -N.
 
216
         */
 
217
        int dummy_revert = 0;
 
218
 
 
219
        // Break doubly linked list so we can use singly linked traversal function.
 
220
        TT.current_hunk->prev->next = NULL;
 
221
 
 
222
        // Match EOF if there aren't as many ending context lines as beginning
 
223
        for (plist = TT.current_hunk; plist; plist = plist->next) {
 
224
                if (plist->data[0]==' ') matcheof++;
 
225
                else matcheof = 0;
 
226
                if (PATCH_DEBUG) fdprintf(2, "HUNK:%s\n", plist->data);
 
227
        }
 
228
        matcheof = !matcheof || matcheof < TT.context;
 
229
 
 
230
        if (PATCH_DEBUG) fdprintf(2,"MATCHEOF=%c\n", matcheof ? 'Y' : 'N');
 
231
 
 
232
        // Loop through input data searching for this hunk.  Match all context
 
233
        // lines and all lines to be removed until we've found the end of a
 
234
        // complete hunk.
 
235
        plist = TT.current_hunk;
 
236
        buf = NULL;
 
237
        if (reverse ? TT.oldlen : TT.newlen) for (;;) {
 
238
//FIXME: this performs 1-byte reads:
 
239
                char *data = xmalloc_reads(TT.filein, NULL);
 
240
 
 
241
                TT.linenum++;
 
242
 
 
243
                // Figure out which line of hunk to compare with next.  (Skip lines
 
244
                // of the hunk we'd be adding.)
 
245
                while (plist && *plist->data == "+-"[reverse]) {
 
246
                        if (data && strcmp(data, plist->data+1) == 0) {
 
247
                                if (!backwarn) {
 
248
                                        backwarn = TT.linenum;
 
249
                                        if (option_mask32 & FLAG_IGNORE) {
 
250
                                                dummy_revert = 1;
 
251
                                                reverse ^= 1;
 
252
                                                continue;
 
253
                                        }
 
254
                                }
 
255
                        }
 
256
                        plist = plist->next;
 
257
                }
 
258
 
 
259
                // Is this EOF?
 
260
                if (!data) {
 
261
                        if (PATCH_DEBUG) fdprintf(2, "INEOF\n");
 
262
 
 
263
                        // Does this hunk need to match EOF?
 
264
                        if (!plist && matcheof) break;
 
265
 
 
266
                        if (backwarn)
 
267
                                fdprintf(2,"Possibly reversed hunk %d at %ld\n",
 
268
                                        TT.hunknum, TT.linenum);
 
269
 
 
270
                        // File ended before we found a place for this hunk.
 
271
                        fail_hunk();
 
272
                        goto done;
 
273
                }
 
274
 
 
275
                if (PATCH_DEBUG) fdprintf(2, "IN: %s\n", data);
 
276
                check = dlist_add(&buf, data);
 
277
 
 
278
                // Compare this line with next expected line of hunk.
 
279
                // todo: teach the strcmp() to ignore whitespace.
 
280
 
 
281
                // A match can fail because the next line doesn't match, or because
 
282
                // we hit the end of a hunk that needed EOF, and this isn't EOF.
 
283
 
 
284
                // If match failed, flush first line of buffered data and
 
285
                // recheck buffered data for a new match until we find one or run
 
286
                // out of buffer.
 
287
 
 
288
                for (;;) {
 
289
                        while (plist && *plist->data == "+-"[reverse]) {
 
290
                                if (strcmp(check->data, plist->data+1) == 0
 
291
                                 && !backwarn
 
292
                                ) {
 
293
                                        backwarn = TT.linenum;
 
294
                                        if (option_mask32 & FLAG_IGNORE) {
 
295
                                                dummy_revert = 1;
 
296
                                                reverse ^= 1;
 
297
                                        }
 
298
                                }
 
299
                                plist = plist->next;
 
300
                        }
 
301
                        if (!plist || strcmp(check->data, plist->data+1)) {
 
302
                                // Match failed.  Write out first line of buffered data and
 
303
                                // recheck remaining buffered data for a new match.
 
304
 
 
305
                                if (PATCH_DEBUG)
 
306
                                        fdprintf(2, "NOT: %s\n", plist ? plist->data : "EOF");
 
307
 
 
308
                                TT.state = 3;
 
309
                                check = buf;
 
310
                                buf = buf->next;
 
311
                                check->prev->next = buf;
 
312
                                buf->prev = check->prev;
 
313
                                do_line(check);
 
314
                                plist = TT.current_hunk;
 
315
 
 
316
                                // If we've reached the end of the buffer without confirming a
 
317
                                // match, read more lines.
 
318
                                if (check == buf) {
 
319
                                        buf = NULL;
 
320
                                        break;
 
321
                                }
 
322
                                check = buf;
 
323
                        } else {
 
324
                                if (PATCH_DEBUG)
 
325
                                        fdprintf(2, "MAYBE: %s\n", plist->data);
 
326
                                // This line matches.  Advance plist, detect successful match.
 
327
                                plist = plist->next;
 
328
                                if (!plist && !matcheof) goto out;
 
329
                                check = check->next;
 
330
                                if (check == buf) break;
 
331
                        }
 
332
                }
 
333
        }
 
334
out:
 
335
        // We have a match.  Emit changed data.
 
336
        TT.state = "-+"[reverse ^ dummy_revert];
 
337
        dlist_free(TT.current_hunk, do_line);
 
338
        TT.current_hunk = NULL;
 
339
        TT.state = 1;
 
340
done:
 
341
        if (buf) {
 
342
                buf->prev->next = NULL;
 
343
                dlist_free(buf, do_line);
 
344
        }
 
345
 
 
346
        return TT.state;
 
347
}
 
348
 
 
349
// Read a patch file and find hunks, opening/creating/deleting files.
 
350
// Call apply_one_hunk() on each hunk.
 
351
 
 
352
// state 0: Not in a hunk, look for +++.
 
353
// state 1: Found +++ file indicator, look for @@
 
354
// state 2: In hunk: counting initial context lines
 
355
// state 3: In hunk: getting body
 
356
// Like GNU patch, we don't require a --- line before the +++, and
 
357
// also allow the --- after the +++ line.
 
358
 
 
359
int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 
360
int patch_main(int argc UNUSED_PARAM, char **argv)
 
361
{
 
362
        int opts;
 
363
        int reverse, state = 0;
 
364
        char *oldname = NULL, *newname = NULL;
 
365
        char *opt_p, *opt_i;
 
366
        long oldlen = oldlen; /* for compiler */
 
367
        long newlen = newlen; /* for compiler */
 
368
 
 
369
#if ENABLE_LONG_OPTS
 
370
        static const char patch_longopts[] ALIGN1 =
 
371
                "reverse\0"               No_argument       "R"
 
372
                "unified\0"               No_argument       "u"
 
373
                "strip\0"                 Required_argument "p"
 
374
                "input\0"                 Required_argument "i"
 
375
                "forward\0"               No_argument       "N"
 
376
# if ENABLE_DESKTOP
 
377
                "remove-empty-files\0"    No_argument       "E" /*ignored*/
 
378
                /* "debug"                Required_argument "x" */
 
379
# endif
 
380
                /* "Assume user knows what [s]he is doing, do not ask any questions": */
 
381
                "force\0"                 No_argument       "f" /*ignored*/
 
382
# if ENABLE_DESKTOP
 
383
                /* "Controls actions when a file is under RCS or SCCS control,
 
384
                 * and does not exist or is read-only and matches the default version,
 
385
                 * or when a file is under ClearCase control and does not exist..."
 
386
                 * IOW: rather obscure option.
 
387
                 * But Gentoo's portage does use -g0
 
388
                 */
 
389
                "get\0"                   Required_argument "g" /*ignored*/
 
390
# endif
 
391
                "dry-run\0"               No_argument       "\xfd"
 
392
# if ENABLE_DESKTOP
 
393
                "backup-if-mismatch\0"    No_argument       "\xfe" /*ignored*/
 
394
                "no-backup-if-mismatch\0" No_argument       "\xff" /*ignored*/
 
395
# endif
 
396
                ;
 
397
#endif
 
398
 
 
399
        INIT_TT();
 
400
 
 
401
#if ENABLE_LONG_OPTS
 
402
        opts = getopt32long(argv, FLAG_STR, patch_longopts, &opt_p, &opt_i);
 
403
#else
 
404
        opts = getopt32(argv, FLAG_STR, &opt_p, &opt_i);
 
405
#endif
 
406
        //bb_error_msg_and_die("opts:%x", opts);
 
407
 
 
408
        argv += optind;
 
409
        reverse = opts & FLAG_REVERSE;
 
410
        TT.prefix = (opts & FLAG_PATHLEN) ? xatoi(opt_p) : 0; // can be negative!
 
411
        TT.filein = TT.fileout = -1;
 
412
        if (opts & FLAG_INPUT) {
 
413
                xmove_fd(xopen_stdin(opt_i), STDIN_FILENO);
 
414
        } else {
 
415
                if (argv[0] && argv[1]) {
 
416
                        xmove_fd(xopen_stdin(argv[1]), STDIN_FILENO);
 
417
                }
 
418
        }
 
419
 
 
420
        // Loop through the lines in the patch
 
421
        for(;;) {
 
422
                char *patchline;
 
423
 
 
424
                patchline = xmalloc_fgetline(stdin);
 
425
                if (!patchline) break;
 
426
 
 
427
                // Other versions of patch accept damaged patches,
 
428
                // so we need to also.
 
429
                if (!*patchline) {
 
430
                        free(patchline);
 
431
                        patchline = xstrdup(" ");
 
432
                }
 
433
 
 
434
                // Are we assembling a hunk?
 
435
                if (state >= 2) {
 
436
                        if (*patchline==' ' || *patchline=='+' || *patchline=='-') {
 
437
                                dlist_add(&TT.current_hunk, patchline);
 
438
 
 
439
                                if (*patchline != '+') oldlen--;
 
440
                                if (*patchline != '-') newlen--;
 
441
 
 
442
                                // Context line?
 
443
                                if (*patchline==' ' && state==2) TT.context++;
 
444
                                else state=3;
 
445
 
 
446
                                // If we've consumed all expected hunk lines, apply the hunk.
 
447
 
 
448
                                if (!oldlen && !newlen) state = apply_one_hunk();
 
449
                                continue;
 
450
                        }
 
451
                        fail_hunk();
 
452
                        state = 0;
 
453
                        continue;
 
454
                }
 
455
 
 
456
                // Open a new file?
 
457
                if (is_prefixed_with(patchline, "--- ") || is_prefixed_with(patchline, "+++ ")) {
 
458
                        char *s, **name = reverse ? &newname : &oldname;
 
459
                        int i;
 
460
 
 
461
                        if (*patchline == '+') {
 
462
                                name = reverse ? &oldname : &newname;
 
463
                                state = 1;
 
464
                        }
 
465
 
 
466
                        finish_oldfile();
 
467
 
 
468
                        if (!argv[0]) {
 
469
                                free(*name);
 
470
                                // Trim date from end of filename (if any).  We don't care.
 
471
                                for (s = patchline+4; *s && *s!='\t'; s++)
 
472
                                        if (*s=='\\' && s[1]) s++;
 
473
                                i = atoi(s);
 
474
                                if (i>1900 && i<=1970)
 
475
                                        *name = xstrdup("/dev/null");
 
476
                                else {
 
477
                                        *s = 0;
 
478
                                        *name = xstrdup(patchline+4);
 
479
                                }
 
480
                        }
 
481
 
 
482
                        // We defer actually opening the file because svn produces broken
 
483
                        // patches that don't signal they want to create a new file the
 
484
                        // way the patch man page says, so you have to read the first hunk
 
485
                        // and _guess_.
 
486
 
 
487
                // Start a new hunk?  Usually @@ -oldline,oldlen +newline,newlen @@
 
488
                // but a missing ,value means the value is 1.
 
489
                } else if (state == 1 && is_prefixed_with(patchline, "@@ -")) {
 
490
                        int i;
 
491
                        char *s = patchline+4;
 
492
 
 
493
                        // Read oldline[,oldlen] +newline[,newlen]
 
494
 
 
495
                        TT.oldlen = oldlen = TT.newlen = newlen = 1;
 
496
                        TT.oldline = strtol(s, &s, 10);
 
497
                        if (*s == ',') TT.oldlen = oldlen = strtol(s+1, &s, 10);
 
498
                        TT.newline = strtol(s+2, &s, 10);
 
499
                        if (*s == ',') TT.newlen = newlen = strtol(s+1, &s, 10);
 
500
 
 
501
                        if (oldlen < 1 && newlen < 1)
 
502
                                bb_error_msg_and_die("Really? %s", patchline);
 
503
 
 
504
                        TT.context = 0;
 
505
                        state = 2;
 
506
 
 
507
                        // If the --- line is missing or malformed, either oldname
 
508
                        // or (for -R) newname could be NULL -- but not both.  Like
 
509
                        // GNU patch, proceed based on the +++ line, and avoid SEGVs.
 
510
                        if (!oldname)
 
511
                                oldname = xstrdup("MISSING_FILENAME");
 
512
                        if (!newname)
 
513
                                newname = xstrdup("MISSING_FILENAME");
 
514
 
 
515
                        // If this is the first hunk, open the file.
 
516
                        if (TT.filein == -1) {
 
517
                                int oldsum, newsum, empty = 0;
 
518
                                char *name;
 
519
 
 
520
                                oldsum = TT.oldline + oldlen;
 
521
                                newsum = TT.newline + newlen;
 
522
 
 
523
                                name = reverse ? oldname : newname;
 
524
 
 
525
                                // We're deleting oldname if new file is /dev/null (before -p)
 
526
                                // or if new hunk is empty (zero context) after patching
 
527
                                if (strcmp(name, "/dev/null") == 0 || !(reverse ? oldsum : newsum)) {
 
528
                                        name = reverse ? newname : oldname;
 
529
                                        empty = 1;
 
530
                                }
 
531
 
 
532
                                // Handle -p path truncation.
 
533
                                for (i = 0, s = name; *s;) {
 
534
                                        if ((option_mask32 & FLAG_PATHLEN) && TT.prefix == i)
 
535
                                                break;
 
536
                                        if (*s++ != '/')
 
537
                                                continue;
 
538
                                        while (*s == '/')
 
539
                                                s++;
 
540
                                        i++;
 
541
                                        name = s;
 
542
                                }
 
543
                                // If "patch FILE_TO_PATCH", completely ignore name from patch
 
544
                                if (argv[0])
 
545
                                        name = argv[0];
 
546
 
 
547
                                if (empty) {
 
548
                                        // File is empty after the patches have been applied
 
549
                                        state = 0;
 
550
                                        if (option_mask32 & FLAG_RMEMPTY) {
 
551
                                                // If flag -E or --remove-empty-files is set
 
552
                                                printf("removing %s\n", name);
 
553
                                                if (!(opts & FLAG_dry_run))
 
554
                                                        xunlink(name);
 
555
                                        } else {
 
556
                                                printf("patching file %s\n", name);
 
557
                                                if (!(opts & FLAG_dry_run))
 
558
                                                        xclose(xopen(name, O_WRONLY | O_TRUNC));
 
559
                                        }
 
560
                                // If we've got a file to open, do so.
 
561
                                } else if (!(option_mask32 & FLAG_PATHLEN) || i <= TT.prefix) {
 
562
                                        struct stat statbuf;
 
563
 
 
564
                                        // If the old file was null, we're creating a new one.
 
565
                                        if (strcmp(oldname, "/dev/null") == 0 || !oldsum) {
 
566
                                                printf("creating %s\n", name);
 
567
                                                if (!(opts & FLAG_dry_run)) {
 
568
                                                        s = strrchr(name, '/');
 
569
                                                        if (s) {
 
570
                                                                *s = '\0';
 
571
                                                                bb_make_directory(name, -1, FILEUTILS_RECUR);
 
572
                                                                *s = '/';
 
573
                                                        }
 
574
                                                        TT.filein = xopen(name, O_CREAT|O_EXCL|O_RDWR);
 
575
                                                } else {
 
576
                                                        TT.filein = xopen("/dev/null", O_RDONLY);
 
577
                                                }
 
578
                                        } else {
 
579
                                                printf("patching file %s\n", name);
 
580
                                                TT.filein = xopen(name, O_RDONLY);
 
581
                                        }
 
582
 
 
583
                                        if (!(opts & FLAG_dry_run)) {
 
584
                                                TT.tempname = xasprintf("%sXXXXXX", name);
 
585
                                                TT.fileout = xmkstemp(TT.tempname);
 
586
                                                // Set permissions of output file
 
587
                                                fstat(TT.filein, &statbuf);
 
588
                                                fchmod(TT.fileout, statbuf.st_mode);
 
589
                                        } else {
 
590
                                                TT.tempname = (char*)"";
 
591
                                                TT.fileout = xopen("/dev/null", O_WRONLY);
 
592
                                        }
 
593
                                        TT.linenum = 0;
 
594
                                        TT.hunknum = 0;
 
595
                                }
 
596
                        }
 
597
 
 
598
                        TT.hunknum++;
 
599
 
 
600
                        continue;
 
601
                }
 
602
 
 
603
                // If we didn't continue above, discard this line.
 
604
                free(patchline);
 
605
        }
 
606
 
 
607
        finish_oldfile();
 
608
 
 
609
        if (ENABLE_FEATURE_CLEAN_UP) {
 
610
                free(oldname);
 
611
                free(newname);
 
612
        }
 
613
 
 
614
        return TT.exitval;
 
615
}