Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/editors/patch.c


Ignore:
Timestamp:
Feb 25, 2011, 9:26:54 PM (13 years ago)
Author:
Bruno Cornec
Message:
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/2.2.9/mindi-busybox/editors/patch.c

    r1765 r2725  
    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>
     1/* vi: set sw=4 ts=4:
    52 *
    6  *  Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
     3 * Apply a "universal" diff.
     4 * Adapted from toybox's patch implementation.
    75 *
    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.
     6 * Copyright 2007 Rob Landley <rob@landley.net>
    117 *
    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
     8 * see http://www.opengroup.org/onlinepubs/009695399/utilities/patch.html
     9 * (But only does -u, because who still cares about "ed"?)
    1510 *
    16  *  Issues
    17  *   - Non-interactive
    18  *   - Patches must apply cleanly or patch (not just one hunk) will fail.
    19  *   - Reject file isnt saved
     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
    2022 */
    2123
    22 #include <getopt.h>
     24//applet:IF_PATCH(APPLET(patch, _BB_DIR_USR_BIN, _BB_SUID_DROP))
     25
     26//kbuild:lib-$(CONFIG_PATCH) += patch.o
     27
     28//config:config PATCH
     29//config:   bool "patch"
     30//config:   default y
     31//config:   help
     32//config:     Apply a unified diff formatted patch.
     33
     34//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"
    2356
    2457#include "libbb.h"
    2558
    26 static unsigned int copy_lines(FILE *src_stream, FILE *dest_stream, const unsigned int lines_count)
    27 {
    28     unsigned int i = 0;
    29 
    30     while (src_stream && (i < lines_count)) {
    31         char *line;
    32         line = xmalloc_fgets(src_stream);
    33         if (line == NULL) {
    34             break;
    35         }
    36         if (fputs(line, dest_stream) == EOF) {
    37             bb_perror_msg_and_die("error writing to new file");
    38         }
    39         free(line);
    40 
    41         i++;
    42     }
    43     return i;
    44 }
    45 
    46 /* If patch_level is -1 it will remove all directory names
    47  * char *line must be greater than 4 chars
    48  * returns NULL if the file doesnt exist or error
    49  * returns malloc'ed filename
    50  */
    51 
    52 static char *extract_filename(char *line, int patch_level)
    53 {
    54     char *temp, *filename_start_ptr = line + 4;
    55     int i;
    56 
    57     /* Terminate string at end of source filename */
    58     temp = strchrnul(filename_start_ptr, '\t');
    59     *temp = '\0';
    60 
    61     /* Skip over (patch_level) number of leading directories */
    62     if (patch_level == -1)
    63         patch_level = INT_MAX;
    64     for (i = 0; i < patch_level; i++) {
    65         temp = strchr(filename_start_ptr, '/');
    66         if (!temp)
    67             break;
    68         filename_start_ptr = temp + 1;
    69     }
    70 
    71     return xstrdup(filename_start_ptr);
    72 }
    73 
    74 static int file_doesnt_exist(const char *filename)
    75 {
    76     struct stat statbuf;
    77     return stat(filename, &statbuf);
    78 }
    79 
    80 int patch_main(int argc, char **argv);
    81 int patch_main(int argc, char **argv)
    82 {
    83     int patch_level = -1;
    84     char *patch_line;
    85     int ret;
    86     FILE *patch_file = NULL;
    87 
    88     {
    89         char *p, *i;
    90         ret = getopt32(argv, "p:i:", &p, &i);
    91         if (ret & 1)
    92             patch_level = xatol_range(p, -1, USHRT_MAX);
    93         if (ret & 2) {
    94             patch_file = xfopen(i, "r");
    95         } else {
    96             patch_file = stdin;
    97         }
    98         ret = 0;
    99     }
    100 
    101     patch_line = xmalloc_getline(patch_file);
    102     while (patch_line) {
    103         FILE *src_stream;
    104         FILE *dst_stream;
    105         char *original_filename;
    106         char *new_filename;
    107         char *backup_filename;
    108         unsigned int src_cur_line = 1;
    109         unsigned int dest_cur_line = 0;
    110         unsigned int dest_beg_line;
    111         unsigned int bad_hunk_count = 0;
    112         unsigned int hunk_count = 0;
    113         char copy_trailing_lines_flag = 0;
    114 
    115         /* Skip everything upto the "---" marker
    116          * No need to parse the lines "Only in <dir>", and "diff <args>"
    117          */
    118         while (patch_line && strncmp(patch_line, "--- ", 4) != 0) {
    119             free(patch_line);
    120             patch_line = xmalloc_getline(patch_file);
    121         }
    122         /* FIXME: patch_line NULL check?? */
    123 
    124         /* Extract the filename used before the patch was generated */
    125         original_filename = extract_filename(patch_line, patch_level);
    126         free(patch_line);
    127 
    128         patch_line = xmalloc_getline(patch_file);
    129         /* FIXME: NULL check?? */
    130         if (strncmp(patch_line, "+++ ", 4) != 0) {
    131             ret = 2;
    132             bb_error_msg("invalid patch");
    133             continue;
    134         }
    135         new_filename = extract_filename(patch_line, patch_level);
    136         free(patch_line);
    137 
    138         if (file_doesnt_exist(new_filename)) {
    139             char *line_ptr;
    140             /* Create leading directories */
    141             line_ptr = strrchr(new_filename, '/');
    142             if (line_ptr) {
    143                 *line_ptr = '\0';
    144                 bb_make_directory(new_filename, -1, FILEUTILS_RECUR);
    145                 *line_ptr = '/';
     59
     60// libbb candidate?
     61
     62struct double_list {
     63    struct double_list *next;
     64    struct double_list *prev;
     65    char *data;
     66};
     67
     68// Free all the elements of a linked list
     69// Call freeit() on each element before freeing it.
     70static
     71void dlist_free(struct double_list *list, void (*freeit)(void *data))
     72{
     73    while (list) {
     74        void *pop = list;
     75        list = list->next;
     76        freeit(pop);
     77        // Bail out also if list is circular.
     78        if (list == pop) break;
     79    }
     80}
     81
     82// Add an entry before "list" element in (circular) doubly linked list
     83static
     84struct double_list *dlist_add(struct double_list **list, char *data)
     85{
     86    struct double_list *llist;
     87    struct double_list *line = xmalloc(sizeof(*line));
     88
     89    line->data = data;
     90    llist = *list;
     91    if (llist) {
     92        struct double_list *p;
     93        line->next = llist;
     94        p = line->prev = llist->prev;
     95        // (list is circular, we assume p is never NULL)
     96        p->next = line;
     97        llist->prev = line;
     98    } else
     99        *list = line->next = line->prev = line;
     100
     101    return line;
     102}
     103
     104
     105struct globals {
     106    char *infile;
     107    long prefix;
     108
     109    struct double_list *current_hunk;
     110
     111    long oldline, oldlen, newline, newlen;
     112    long linenum;
     113    int context, state, hunknum;
     114    int filein, fileout;
     115    char *tempname;
     116
     117    int exitval;
     118};
     119#define TT (*ptr_to_globals)
     120#define INIT_TT() do { \
     121    SET_PTR_TO_GLOBALS(xzalloc(sizeof(TT))); \
     122} while (0)
     123
     124
     125#define FLAG_STR "Rup:i:NEx"
     126/* FLAG_REVERSE must be == 1! Code uses this fact. */
     127#define FLAG_REVERSE (1 << 0)
     128#define FLAG_u       (1 << 1)
     129#define FLAG_PATHLEN (1 << 2)
     130#define FLAG_INPUT   (1 << 3)
     131#define FLAG_IGNORE  (1 << 4)
     132#define FLAG_RMEMPTY (1 << 5)
     133//non-standard:
     134#define FLAG_DEBUG   (1 << 6)
     135
     136// Dispose of a line of input, either by writing it out or discarding it.
     137
     138// state < 2: just free
     139// state = 2: write whole line to stderr
     140// state = 3: write whole line to fileout
     141// state > 3: write line+1 to fileout when *line != state
     142
     143#define PATCH_DEBUG (option_mask32 & FLAG_DEBUG)
     144
     145static void do_line(void *data)
     146{
     147    struct double_list *dlist = data;
     148
     149    if (TT.state>1 && *dlist->data != TT.state)
     150        fdprintf(TT.state == 2 ? 2 : TT.fileout,
     151            "%s\n", dlist->data+(TT.state>3 ? 1 : 0));
     152
     153    if (PATCH_DEBUG) fdprintf(2, "DO %d: %s\n", TT.state, dlist->data);
     154
     155    free(dlist->data);
     156    free(dlist);
     157}
     158
     159static void finish_oldfile(void)
     160{
     161    if (TT.tempname) {
     162        // Copy the rest of the data and replace the original with the copy.
     163        char *temp;
     164
     165        if (TT.filein != -1) {
     166            bb_copyfd_eof(TT.filein, TT.fileout);
     167            xclose(TT.filein);
     168        }
     169        xclose(TT.fileout);
     170
     171        temp = xstrdup(TT.tempname);
     172        temp[strlen(temp) - 6] = '\0';
     173        rename(TT.tempname, temp);
     174        free(temp);
     175
     176        free(TT.tempname);
     177        TT.tempname = NULL;
     178    }
     179    TT.fileout = TT.filein = -1;
     180}
     181
     182static void fail_hunk(void)
     183{
     184    if (!TT.current_hunk) return;
     185
     186    fdprintf(2, "Hunk %d FAILED %ld/%ld.\n", TT.hunknum, TT.oldline, TT.newline);
     187    TT.exitval = 1;
     188
     189    // If we got to this point, we've seeked to the end.  Discard changes to
     190    // this file and advance to next file.
     191
     192    TT.state = 2;
     193    TT.current_hunk->prev->next = NULL;
     194    dlist_free(TT.current_hunk, do_line);
     195    TT.current_hunk = NULL;
     196
     197    // Abort the copy and delete the temporary file.
     198    close(TT.filein);
     199    close(TT.fileout);
     200    unlink(TT.tempname);
     201    free(TT.tempname);
     202    TT.tempname = NULL;
     203
     204    TT.state = 0;
     205}
     206
     207// Given a hunk of a unified diff, make the appropriate change to the file.
     208// This does not use the location information, but instead treats a hunk
     209// as a sort of regex.  Copies data from input to output until it finds
     210// the change to be made, then outputs the changed data and returns.
     211// (Finding EOF first is an error.)  This is a single pass operation, so
     212// multiple hunks must occur in order in the file.
     213
     214static int apply_one_hunk(void)
     215{
     216    struct double_list *plist, *buf = NULL, *check;
     217    int matcheof = 0, reverse = option_mask32 & FLAG_REVERSE, backwarn = 0;
     218    /* Do we try "dummy" revert to check whether
     219     * to silently skip this hunk? Used to implement -N.
     220     */
     221    int dummy_revert = 0;
     222
     223    // Break doubly linked list so we can use singly linked traversal function.
     224    TT.current_hunk->prev->next = NULL;
     225
     226    // Match EOF if there aren't as many ending context lines as beginning
     227    for (plist = TT.current_hunk; plist; plist = plist->next) {
     228        if (plist->data[0]==' ') matcheof++;
     229        else matcheof = 0;
     230        if (PATCH_DEBUG) fdprintf(2, "HUNK:%s\n", plist->data);
     231    }
     232    matcheof = matcheof < TT.context;
     233
     234    if (PATCH_DEBUG) fdprintf(2,"MATCHEOF=%c\n", matcheof ? 'Y' : 'N');
     235
     236    // Loop through input data searching for this hunk.  Match all context
     237    // lines and all lines to be removed until we've found the end of a
     238    // complete hunk.
     239    plist = TT.current_hunk;
     240    buf = NULL;
     241    if (TT.context) for (;;) {
     242        char *data = xmalloc_reads(TT.filein, NULL, NULL);
     243
     244        TT.linenum++;
     245
     246        // Figure out which line of hunk to compare with next.  (Skip lines
     247        // of the hunk we'd be adding.)
     248        while (plist && *plist->data == "+-"[reverse]) {
     249            if (data && !strcmp(data, plist->data+1)) {
     250                if (!backwarn) {
     251                    backwarn = TT.linenum;
     252                    if (option_mask32 & FLAG_IGNORE) {
     253                        dummy_revert = 1;
     254                        reverse ^= 1;
     255                        continue;
     256                    }
     257                }
    146258            }
    147             dst_stream = xfopen(new_filename, "w+");
    148             backup_filename = NULL;
    149         } else {
    150             backup_filename = xmalloc(strlen(new_filename) + 6);
    151             strcpy(backup_filename, new_filename);
    152             strcat(backup_filename, ".orig");
    153             if (rename(new_filename, backup_filename) == -1) {
    154                 bb_perror_msg_and_die("cannot create file %s",
    155                         backup_filename);
    156             }
    157             dst_stream = xfopen(new_filename, "w");
    158         }
    159 
    160         if ((backup_filename == NULL) || file_doesnt_exist(original_filename)) {
    161             src_stream = NULL;
    162         } else {
    163             if (strcmp(original_filename, new_filename) == 0) {
    164                 src_stream = xfopen(backup_filename, "r");
    165             } else {
    166                 src_stream = xfopen(original_filename, "r");
    167             }
    168         }
    169 
    170         printf("patching file %s\n", new_filename);
    171 
    172         /* Handle each hunk */
    173         patch_line = xmalloc_fgets(patch_file);
    174         while (patch_line) {
    175             unsigned int count;
    176             unsigned int src_beg_line;
    177             unsigned int unused;
    178             unsigned int hunk_offset_start = 0;
    179             int hunk_error = 0;
    180 
    181             /* This bit should be improved */
    182             if ((sscanf(patch_line, "@@ -%d,%d +%d,%d @@", &src_beg_line, &unused, &dest_beg_line, &unused) != 4) &&
    183                 (sscanf(patch_line, "@@ -%d,%d +%d @@", &src_beg_line, &unused, &dest_beg_line) != 3) &&
    184                 (sscanf(patch_line, "@@ -%d +%d,%d @@", &src_beg_line, &dest_beg_line, &unused) != 3)) {
    185                 /* No more hunks for this file */
    186                 break;
    187             }
    188             free(patch_line);
    189             hunk_count++;
    190 
    191             if (src_beg_line && dest_beg_line) {
    192                 /* Copy unmodified lines upto start of hunk */
    193                 /* src_beg_line will be 0 if its a new file */
    194                 count = src_beg_line - src_cur_line;
    195                 if (copy_lines(src_stream, dst_stream, count) != count) {
    196                     bb_error_msg_and_die("bad src file");
    197                 }
    198                 src_cur_line += count;
    199                 dest_cur_line += count;
    200                 copy_trailing_lines_flag = 1;
    201             }
    202             hunk_offset_start = src_cur_line;
    203 
    204             while ((patch_line = xmalloc_fgets(patch_file)) != NULL) {
    205                 if ((*patch_line == '-') || (*patch_line == ' ')) {
    206                     char *src_line = NULL;
    207                     if (src_stream) {
    208                         src_line = xmalloc_fgets(src_stream);
    209                         if (!src_line) {
    210                             hunk_error++;
    211                             break;
    212                         } else {
    213                             src_cur_line++;
    214                         }
    215                         if (strcmp(src_line, patch_line + 1) != 0) {
    216                             bb_error_msg("hunk #%d FAILED at %d", hunk_count, hunk_offset_start);
    217                             hunk_error++;
    218                             free(patch_line);
    219                             /* Probably need to find next hunk, etc... */
    220                             /* but for now we just bail out */
    221                             patch_line = NULL;
    222                             break;
    223                         }
    224                         free(src_line);
    225                     }
    226                     if (*patch_line == ' ') {
    227                         fputs(patch_line + 1, dst_stream);
    228                         dest_cur_line++;
    229                     }
    230                 } else if (*patch_line == '+') {
    231                     fputs(patch_line + 1, dst_stream);
    232                     dest_cur_line++;
    233                 } else {
     259            plist = plist->next;
     260        }
     261
     262        // Is this EOF?
     263        if (!data) {
     264            if (PATCH_DEBUG) fdprintf(2, "INEOF\n");
     265
     266            // Does this hunk need to match EOF?
     267            if (!plist && matcheof) break;
     268
     269            if (backwarn)
     270                fdprintf(2,"Possibly reversed hunk %d at %ld\n",
     271                    TT.hunknum, TT.linenum);
     272
     273            // File ended before we found a place for this hunk.
     274            fail_hunk();
     275            goto done;
     276        }
     277
     278        if (PATCH_DEBUG) fdprintf(2, "IN: %s\n", data);
     279        check = dlist_add(&buf, data);
     280
     281        // Compare this line with next expected line of hunk.
     282        // todo: teach the strcmp() to ignore whitespace.
     283
     284        // A match can fail because the next line doesn't match, or because
     285        // we hit the end of a hunk that needed EOF, and this isn't EOF.
     286
     287        // If match failed, flush first line of buffered data and
     288        // recheck buffered data for a new match until we find one or run
     289        // out of buffer.
     290
     291        for (;;) {
     292            if (!plist || strcmp(check->data, plist->data+1)) {
     293                // Match failed.  Write out first line of buffered data and
     294                // recheck remaining buffered data for a new match.
     295
     296                if (PATCH_DEBUG)
     297                    fdprintf(2, "NOT: %s\n", plist->data);
     298
     299                TT.state = 3;
     300                check = buf;
     301                buf = buf->next;
     302                check->prev->next = buf;
     303                buf->prev = check->prev;
     304                do_line(check);
     305                plist = TT.current_hunk;
     306
     307                // If we've reached the end of the buffer without confirming a
     308                // match, read more lines.
     309                if (check == buf) {
     310                    buf = NULL;
    234311                    break;
    235312                }
    236                 free(patch_line);
     313                check = buf;
     314            } else {
     315                if (PATCH_DEBUG)
     316                    fdprintf(2, "MAYBE: %s\n", plist->data);
     317                // This line matches.  Advance plist, detect successful match.
     318                plist = plist->next;
     319                if (!plist && !matcheof) goto out;
     320                check = check->next;
     321                if (check == buf) break;
    237322            }
    238             if (hunk_error) {
    239                 bad_hunk_count++;
     323        }
     324    }
     325out:
     326    // We have a match.  Emit changed data.
     327    TT.state = "-+"[reverse ^ dummy_revert];
     328    dlist_free(TT.current_hunk, do_line);
     329    TT.current_hunk = NULL;
     330    TT.state = 1;
     331done:
     332    if (buf) {
     333        buf->prev->next = NULL;
     334        dlist_free(buf, do_line);
     335    }
     336
     337    return TT.state;
     338}
     339
     340// Read a patch file and find hunks, opening/creating/deleting files.
     341// Call apply_one_hunk() on each hunk.
     342
     343// state 0: Not in a hunk, look for +++.
     344// state 1: Found +++ file indicator, look for @@
     345// state 2: In hunk: counting initial context lines
     346// state 3: In hunk: getting body
     347
     348int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     349int patch_main(int argc UNUSED_PARAM, char **argv)
     350{
     351    int opts;
     352    int reverse, state = 0;
     353    char *oldname = NULL, *newname = NULL;
     354    char *opt_p, *opt_i;
     355
     356    INIT_TT();
     357
     358    opts = getopt32(argv, FLAG_STR, &opt_p, &opt_i);
     359    argv += optind;
     360    reverse = opts & FLAG_REVERSE;
     361    TT.prefix = (opts & FLAG_PATHLEN) ? xatoi(opt_p) : 0; // can be negative!
     362    TT.filein = TT.fileout = -1;
     363    if (opts & FLAG_INPUT) {
     364        xmove_fd(xopen_stdin(opt_i), STDIN_FILENO);
     365    } else {
     366        if (argv[0] && argv[1]) {
     367            xmove_fd(xopen_stdin(argv[1]), STDIN_FILENO);
     368        }
     369    }
     370    if (argv[0]) {
     371        oldname = xstrdup(argv[0]);
     372        newname = xstrdup(argv[0]);
     373    }
     374
     375    // Loop through the lines in the patch
     376    for(;;) {
     377        char *patchline;
     378
     379        patchline = xmalloc_fgetline(stdin);
     380        if (!patchline) break;
     381
     382        // Other versions of patch accept damaged patches,
     383        // so we need to also.
     384        if (!*patchline) {
     385            free(patchline);
     386            patchline = xstrdup(" ");
     387        }
     388
     389        // Are we assembling a hunk?
     390        if (state >= 2) {
     391            if (*patchline==' ' || *patchline=='+' || *patchline=='-') {
     392                dlist_add(&TT.current_hunk, patchline);
     393
     394                if (*patchline != '+') TT.oldlen--;
     395                if (*patchline != '-') TT.newlen--;
     396
     397                // Context line?
     398                if (*patchline==' ' && state==2) TT.context++;
     399                else state=3;
     400
     401                // If we've consumed all expected hunk lines, apply the hunk.
     402
     403                if (!TT.oldlen && !TT.newlen) state = apply_one_hunk();
     404                continue;
    240405            }
    241         }
    242 
    243         /* Cleanup last patched file */
    244         if (copy_trailing_lines_flag) {
    245             copy_lines(src_stream, dst_stream, -1);
    246         }
    247         if (src_stream) {
    248             fclose(src_stream);
    249         }
    250         if (dst_stream) {
    251             fclose(dst_stream);
    252         }
    253         if (bad_hunk_count) {
    254             if (!ret) {
    255                 ret = 1;
     406            fail_hunk();
     407            state = 0;
     408            continue;
     409        }
     410
     411        // Open a new file?
     412        if (!strncmp("--- ", patchline, 4) || !strncmp("+++ ", patchline, 4)) {
     413            char *s, **name = reverse ? &newname : &oldname;
     414            int i;
     415
     416            if (*patchline == '+') {
     417                name = reverse ? &oldname : &newname;
     418                state = 1;
    256419            }
    257             bb_error_msg("%d out of %d hunk FAILED", bad_hunk_count, hunk_count);
    258         } else {
    259             /* It worked, we can remove the backup */
    260             if (backup_filename) {
    261                 unlink(backup_filename);
     420
     421            finish_oldfile();
     422
     423            if (!argv[0]) {
     424                free(*name);
     425                // Trim date from end of filename (if any).  We don't care.
     426                for (s = patchline+4; *s && *s!='\t'; s++)
     427                    if (*s=='\\' && s[1]) s++;
     428                i = atoi(s);
     429                if (i>1900 && i<=1970)
     430                    *name = xstrdup("/dev/null");
     431                else {
     432                    *s = 0;
     433                    *name = xstrdup(patchline+4);
     434                }
    262435            }
    263             if ((dest_cur_line == 0) || (dest_beg_line == 0)) {
    264                 /* The new patched file is empty, remove it */
    265                 xunlink(new_filename);
    266                 if (strcmp(new_filename, original_filename) != 0)
    267                     xunlink(original_filename);
     436
     437            // We defer actually opening the file because svn produces broken
     438            // patches that don't signal they want to create a new file the
     439            // way the patch man page says, so you have to read the first hunk
     440            // and _guess_.
     441
     442        // Start a new hunk?  Usually @@ -oldline,oldlen +newline,newlen @@
     443        // but a missing ,value means the value is 1.
     444        } else if (state == 1 && !strncmp("@@ -", patchline, 4)) {
     445            int i;
     446            char *s = patchline+4;
     447
     448            // Read oldline[,oldlen] +newline[,newlen]
     449
     450            TT.oldlen = TT.newlen = 1;
     451            TT.oldline = strtol(s, &s, 10);
     452            if (*s == ',') TT.oldlen=strtol(s+1, &s, 10);
     453            TT.newline = strtol(s+2, &s, 10);
     454            if (*s == ',') TT.newlen = strtol(s+1, &s, 10);
     455
     456            TT.context = 0;
     457            state = 2;
     458
     459            // If this is the first hunk, open the file.
     460            if (TT.filein == -1) {
     461                int oldsum, newsum, empty = 0;
     462                char *name;
     463
     464                oldsum = TT.oldline + TT.oldlen;
     465                newsum = TT.newline + TT.newlen;
     466
     467                name = reverse ? oldname : newname;
     468
     469                // We're deleting oldname if new file is /dev/null (before -p)
     470                // or if new hunk is empty (zero context) after patching
     471                if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum))
     472                {
     473                    name = reverse ? newname : oldname;
     474                    empty++;
     475                }
     476
     477                // handle -p path truncation.
     478                for (i=0, s = name; *s;) {
     479                    if ((option_mask32 & FLAG_PATHLEN) && TT.prefix == i) break;
     480                    if (*(s++)=='/') {
     481                        name = s;
     482                        i++;
     483                    }
     484                }
     485
     486                if (empty) {
     487                    // File is empty after the patches have been applied
     488                    state = 0;
     489                    if (option_mask32 & FLAG_RMEMPTY) {
     490                        // If flag -E or --remove-empty-files is set
     491                        printf("removing %s\n", name);
     492                        xunlink(name);
     493                    } else {
     494                        printf("patching file %s\n", name);
     495                        xclose(xopen(name, O_WRONLY | O_TRUNC));
     496                    }
     497                // If we've got a file to open, do so.
     498                } else if (!(option_mask32 & FLAG_PATHLEN) || i <= TT.prefix) {
     499                    struct stat statbuf;
     500
     501                    // If the old file was null, we're creating a new one.
     502                    if (!strcmp(oldname, "/dev/null") || !oldsum) {
     503                        printf("creating %s\n", name);
     504                        s = strrchr(name, '/');
     505                        if (s) {
     506                            *s = 0;
     507                            bb_make_directory(name, -1, FILEUTILS_RECUR);
     508                            *s = '/';
     509                        }
     510                        TT.filein = xopen(name, O_CREAT|O_EXCL|O_RDWR);
     511                    } else {
     512                        printf("patching file %s\n", name);
     513                        TT.filein = xopen(name, O_RDONLY);
     514                    }
     515
     516                    TT.tempname = xasprintf("%sXXXXXX", name);
     517                    TT.fileout = xmkstemp(TT.tempname);
     518                    // Set permissions of output file
     519                    fstat(TT.filein, &statbuf);
     520                    fchmod(TT.fileout, statbuf.st_mode);
     521
     522                    TT.linenum = 0;
     523                    TT.hunknum = 0;
     524                }
    268525            }
    269         }
    270     }
    271 
    272     /* 0 = SUCCESS
    273      * 1 = Some hunks failed
    274      * 2 = More serious problems
    275      */
    276     return ret;
    277 }
     526
     527            TT.hunknum++;
     528
     529            continue;
     530        }
     531
     532        // If we didn't continue above, discard this line.
     533        free(patchline);
     534    }
     535
     536    finish_oldfile();
     537
     538    if (ENABLE_FEATURE_CLEAN_UP) {
     539        free(oldname);
     540        free(newname);
     541    }
     542
     543    return TT.exitval;
     544}
Note: See TracChangeset for help on using the changeset viewer.