Changeset 3232 in MondoRescue for branches/3.2/mindi-busybox/editors/sed.c


Ignore:
Timestamp:
Jan 1, 2014, 12:47:38 AM (10 years ago)
Author:
Bruno Cornec
Message:
  • Update mindi-busybox to 1.21.1
File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/3.2/mindi-busybox/editors/sed.c

    r2725 r3232  
    1515
    1616/* Code overview.
    17 
    18   Files are laid out to avoid unnecessary function declarations.  So for
    19   example, every function add_cmd calls occurs before add_cmd in this file.
    20 
    21   add_cmd() is called on each line of sed command text (from a file or from
    22   the command line).  It calls get_address() and parse_cmd_args().  The
    23   resulting sed_cmd_t structures are appended to a linked list
    24   (G.sed_cmd_head/G.sed_cmd_tail).
    25 
    26   add_input_file() adds a FILE* to the list of input files.  We need to
    27   know all input sources ahead of time to find the last line for the $ match.
    28 
    29   process_files() does actual sedding, reading data lines from each input FILE *
    30   (which could be stdin) and applying the sed command list (sed_cmd_head) to
    31   each of the resulting lines.
    32 
    33   sed_main() is where external code calls into this, with a command line.
    34 */
    35 
    36 
    37 /*
    38     Supported features and commands in this version of sed:
    39 
    40      - comments ('#')
    41      - address matching: num|/matchstr/[,num|/matchstr/|$]command
    42      - commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags)
    43      - edit commands: (a)ppend, (i)nsert, (c)hange
    44      - file commands: (r)ead
    45      - backreferences in substitution expressions (\0, \1, \2...\9)
    46      - grouped commands: {cmd1;cmd2}
    47      - transliteration (y/source-chars/dest-chars/)
    48      - pattern space hold space storing / swapping (g, h, x)
    49      - labels / branching (: label, b, t, T)
    50 
    51      (Note: Specifying an address (range) to match is *optional*; commands
    52      default to the whole pattern space if no specific address match was
    53      requested.)
    54 
    55     Todo:
    56      - Create a wrapper around regex to make libc's regex conform with sed
    57 
    58     Reference http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html
    59 */
     17 *
     18 * Files are laid out to avoid unnecessary function declarations.  So for
     19 * example, every function add_cmd calls occurs before add_cmd in this file.
     20 *
     21 * add_cmd() is called on each line of sed command text (from a file or from
     22 * the command line).  It calls get_address() and parse_cmd_args().  The
     23 * resulting sed_cmd_t structures are appended to a linked list
     24 * (G.sed_cmd_head/G.sed_cmd_tail).
     25 *
     26 * add_input_file() adds a FILE* to the list of input files.  We need to
     27 * know all input sources ahead of time to find the last line for the $ match.
     28 *
     29 * process_files() does actual sedding, reading data lines from each input FILE*
     30 * (which could be stdin) and applying the sed command list (sed_cmd_head) to
     31 * each of the resulting lines.
     32 *
     33 * sed_main() is where external code calls into this, with a command line.
     34 */
     35
     36/* Supported features and commands in this version of sed:
     37 *
     38 * - comments ('#')
     39 * - address matching: num|/matchstr/[,num|/matchstr/|$]command
     40 * - commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags)
     41 * - edit commands: (a)ppend, (i)nsert, (c)hange
     42 * - file commands: (r)ead
     43 * - backreferences in substitution expressions (\0, \1, \2...\9)
     44 * - grouped commands: {cmd1;cmd2}
     45 * - transliteration (y/source-chars/dest-chars/)
     46 * - pattern space hold space storing / swapping (g, h, x)
     47 * - labels / branching (: label, b, t, T)
     48 *
     49 * (Note: Specifying an address (range) to match is *optional*; commands
     50 * default to the whole pattern space if no specific address match was
     51 * requested.)
     52 *
     53 * Todo:
     54 * - Create a wrapper around regex to make libc's regex conform with sed
     55 *
     56 * Reference
     57 * http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html
     58 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html
     59 */
     60
     61//usage:#define sed_trivial_usage
     62//usage:       "[-inr] [-f FILE]... [-e CMD]... [FILE]...\n"
     63//usage:       "or: sed [-inr] CMD [FILE]..."
     64//usage:#define sed_full_usage "\n\n"
     65//usage:       "    -e CMD  Add CMD to sed commands to be executed"
     66//usage:     "\n    -f FILE Add FILE contents to sed commands to be executed"
     67//usage:     "\n    -i[SFX] Edit files in-place (otherwise sends to stdout)"
     68//usage:     "\n        Optionally back files up, appending SFX"
     69//usage:     "\n    -n  Suppress automatic printing of pattern space"
     70//usage:     "\n    -r  Use extended regex syntax"
     71//usage:     "\n"
     72//usage:     "\nIf no -e or -f, the first non-option argument is the sed command string."
     73//usage:     "\nRemaining arguments are input files (stdin if none)."
     74//usage:
     75//usage:#define sed_example_usage
     76//usage:       "$ echo \"foo\" | sed -e 's/f[a-zA-Z]o/bar/g'\n"
     77//usage:       "bar\n"
    6078
    6179#include "libbb.h"
    6280#include "xregex.h"
     81
     82#if 0
     83# define dbg(...) bb_error_msg(__VA_ARGS__)
     84#else
     85# define dbg(...) ((void)0)
     86#endif
     87
    6388
    6489enum {
     
    76101    regex_t *sub_match;     /* For 's/sub_match/string/' */
    77102    int beg_line;           /* 'sed 1p'   0 == apply commands to all lines */
     103    int beg_line_orig;      /* copy of the above, needed for -i */
    78104    int end_line;           /* 'sed 1,3p' 0 == one line only. -1 = last line ($) */
    79105
     
    110136
    111137    /* linked list of sed commands */
    112     sed_cmd_t sed_cmd_head, *sed_cmd_tail;
     138    sed_cmd_t *sed_cmd_head, **sed_cmd_tail;
    113139
    114140    /* Linked list of append lines */
     
    125151#define G (*(struct globals*)&bb_common_bufsiz1)
    126152struct BUG_G_too_big {
    127         char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
     153    char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
    128154};
    129155#define INIT_G() do { \
     
    135161static void sed_free_and_close_stuff(void)
    136162{
    137     sed_cmd_t *sed_cmd = G.sed_cmd_head.next;
     163    sed_cmd_t *sed_cmd = G.sed_cmd_head;
    138164
    139165    llist_free(G.append_head, free);
     
    201227static char *copy_parsing_escapes(const char *string, int len)
    202228{
     229    const char *s;
    203230    char *dest = xmalloc(len + 1);
    204231
    205     parse_escapes(dest, string, len, 'n', '\n');
    206     /* GNU sed also recognizes \t */
    207     parse_escapes(dest, dest, strlen(dest), 't', '\t');
     232    /* sed recognizes \n */
     233    /* GNU sed also recognizes \t and \r */
     234    for (s = "\nn\tt\rr"; *s; s += 2) {
     235        parse_escapes(dest, string, len, s[1], s[0]);
     236        string = dest;
     237        len = strlen(dest);
     238    }
    208239    return dest;
    209240}
     
    228259    }
    229260
    230     for (; (ch = str[idx]); idx++) {
     261    for (; (ch = str[idx]) != '\0'; idx++) {
    231262        if (bracket >= 0) {
    232             if (ch == ']' && !(bracket == idx - 1 || (bracket == idx - 2
    233                     && str[idx - 1] == '^')))
     263            if (ch == ']'
     264             && !(bracket == idx - 1 || (bracket == idx - 2 && str[idx - 1] == '^'))
     265            ) {
    234266                bracket = -1;
     267            }
    235268        } else if (escaped)
    236269            escaped = 0;
     
    253286{
    254287    const char *cmdstr_ptr = cmdstr;
    255     char delimiter;
     288    unsigned char delimiter;
    256289    int idx = 0;
    257290
     
    268301    /* save the replacement string */
    269302    cmdstr_ptr += idx + 1;
    270     idx = index_of_next_unescaped_regexp_delim(-delimiter, cmdstr_ptr);
     303    idx = index_of_next_unescaped_regexp_delim(- (int)delimiter, cmdstr_ptr);
    271304    *replace = copy_parsing_escapes(cmdstr_ptr, idx);
    272305
     
    293326
    294327        delimiter = '/';
    295         if (*my_str == '\\') delimiter = *++pos;
     328        if (*my_str == '\\')
     329            delimiter = *++pos;
    296330        next = index_of_next_unescaped_regexp_delim(delimiter, ++pos);
    297331        temp = copy_parsing_escapes(pos, next);
    298         *regex = xmalloc(sizeof(regex_t));
     332        *regex = xzalloc(sizeof(regex_t));
    299333        xregcomp(*regex, temp, G.regex_type|REG_NEWLINE);
    300334        free(temp);
     
    405439    if (*match != '\0') {
    406440        /* If match is empty, we use last regex used at runtime */
    407         sed_cmd->sub_match = xmalloc(sizeof(regex_t));
     441        sed_cmd->sub_match = xzalloc(sizeof(regex_t));
     442        dbg("xregcomp('%s',%x)", match, cflags);
    408443        xregcomp(sed_cmd->sub_match, match, cflags);
     444        dbg("regcomp ok");
    409445    }
    410446    free(match);
     
    418454static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
    419455{
     456    static const char cmd_letters[] = "saicrw:btTydDgGhHlnNpPqx={}";
     457    enum {
     458        IDX_s = 0,
     459        IDX_a,
     460        IDX_i,
     461        IDX_c,
     462        IDX_r,
     463        IDX_w,
     464        IDX_colon,
     465        IDX_b,
     466        IDX_t,
     467        IDX_T,
     468        IDX_y,
     469        IDX_d,
     470        IDX_D,
     471        IDX_g,
     472        IDX_G,
     473        IDX_h,
     474        IDX_H,
     475        IDX_l,
     476        IDX_n,
     477        IDX_N,
     478        IDX_p,
     479        IDX_P,
     480        IDX_q,
     481        IDX_x,
     482        IDX_equal,
     483        IDX_lbrace,
     484        IDX_rbrace,
     485        IDX_nul
     486    };
     487    struct chk { char chk[sizeof(cmd_letters)-1 == IDX_nul ? 1 : -1]; };
     488
     489    unsigned idx = strchrnul(cmd_letters, sed_cmd->cmd) - cmd_letters;
     490
    420491    /* handle (s)ubstitution command */
    421     if (sed_cmd->cmd == 's')
     492    if (idx == IDX_s) {
    422493        cmdstr += parse_subst_cmd(sed_cmd, cmdstr);
     494    }
    423495    /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */
    424     else if (strchr("aic", sed_cmd->cmd)) {
    425         if ((sed_cmd->end_line || sed_cmd->end_match) && sed_cmd->cmd != 'c')
    426             bb_error_msg_and_die("only a beginning address can be specified for edit commands");
     496    else if (idx <= IDX_c) { /* a,i,c */
     497        if (idx < IDX_c) { /* a,i */
     498            if (sed_cmd->end_line || sed_cmd->end_match)
     499                bb_error_msg_and_die("command '%c' uses only one address", sed_cmd->cmd);
     500        }
    427501        for (;;) {
    428502            if (*cmdstr == '\n' || *cmdstr == '\\') {
     
    438512        parse_escapes(sed_cmd->string, sed_cmd->string, strlen(cmdstr), '\0', '\0');
    439513        cmdstr += strlen(cmdstr);
     514    }
    440515    /* handle file cmds: (r)ead */
    441     } else if (strchr("rw", sed_cmd->cmd)) {
    442         if (sed_cmd->end_line || sed_cmd->end_match)
    443             bb_error_msg_and_die("command only uses one address");
     516    else if (idx <= IDX_w) { /* r,w */
     517        if (idx < IDX_w) { /* r */
     518            if (sed_cmd->end_line || sed_cmd->end_match)
     519                bb_error_msg_and_die("command '%c' uses only one address", sed_cmd->cmd);
     520        }
    444521        cmdstr += parse_file_cmd(/*sed_cmd,*/ cmdstr, &sed_cmd->string);
    445522        if (sed_cmd->cmd == 'w') {
     
    447524            sed_cmd->sw_last_char = '\n';
    448525        }
     526    }
    449527    /* handle branch commands */
    450     } else if (strchr(":btT", sed_cmd->cmd)) {
     528    else if (idx <= IDX_T) { /* :,b,t,T */
    451529        int length;
    452530
     
    459537    }
    460538    /* translation command */
    461     else if (sed_cmd->cmd == 'y') {
     539    else if (idx == IDX_y) {
    462540        char *match, *replace;
    463541        int i = cmdstr[0];
     
    479557     * then it must be an invalid command.
    480558     */
    481     else if (strchr("dDgGhHlnNpPqx={}", sed_cmd->cmd) == 0) {
     559    else if (idx >= IDX_nul) { /* not d,D,g,G,h,H,l,n,N,p,P,q,x,=,{,} */
    482560        bb_error_msg_and_die("unsupported command %c", sed_cmd->cmd);
    483561    }
     
    541619        /* first part (if present) is an address: either a '$', a number or a /regex/ */
    542620        cmdstr += get_address(cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match);
     621        sed_cmd->beg_line_orig = sed_cmd->beg_line;
    543622
    544623        /* second part (if present) will begin with a comma */
     
    572651
    573652        /* Add the command to the command array */
    574         G.sed_cmd_tail->next = sed_cmd;
    575         G.sed_cmd_tail = G.sed_cmd_tail->next;
     653        *G.sed_cmd_tail = sed_cmd;
     654        G.sed_cmd_tail = &sed_cmd->next;
    576655    }
    577656
     
    601680    /* go through the replacement string */
    602681    for (i = 0; replace[i]; i++) {
    603         /* if we find a backreference (\1, \2, etc.) print the backref'ed * text */
     682        /* if we find a backreference (\1, \2, etc.) print the backref'ed text */
    604683        if (replace[i] == '\\') {
    605684            unsigned backref = replace[++i] - '0';
     
    635714{
    636715    char *line = *line_p;
    637     int altered = 0;
    638716    unsigned match_count = 0;
     717    bool altered = 0;
     718    bool prev_match_empty = 1;
     719    bool tried_at_eol = 0;
    639720    regex_t *current_regex;
    640721
     
    649730
    650731    /* Find the first match */
    651     if (REG_NOMATCH == regexec(current_regex, line, 10, G.regmatch, 0))
     732    dbg("matching '%s'", line);
     733    if (REG_NOMATCH == regexec(current_regex, line, 10, G.regmatch, 0)) {
     734        dbg("no match");
    652735        return 0;
     736    }
     737    dbg("match");
    653738
    654739    /* Initialize temporary output buffer. */
     
    659744    /* Now loop through, substituting for matches */
    660745    do {
     746        int start = G.regmatch[0].rm_so;
     747        int end = G.regmatch[0].rm_eo;
    661748        int i;
    662749
    663         /* Work around bug in glibc regexec, demonstrated by:
    664            echo " a.b" | busybox sed 's [^ .]* x g'
    665            The match_count check is so not to break
    666            echo "hi" | busybox sed 's/^/!/g' */
    667         if (!G.regmatch[0].rm_so && !G.regmatch[0].rm_eo && match_count) {
    668             pipe_putc(*line++);
    669             continue;
    670         }
    671 
    672750        match_count++;
    673751
    674752        /* If we aren't interested in this match, output old line to
    675            end of match and continue */
     753         * end of match and continue */
    676754        if (sed_cmd->which_match
    677755         && (sed_cmd->which_match != match_count)
    678756        ) {
    679             for (i = 0; i < G.regmatch[0].rm_eo; i++)
     757            for (i = 0; i < end; i++)
    680758                pipe_putc(*line++);
    681             continue;
    682         }
    683 
    684         /* print everything before the match */
    685         for (i = 0; i < G.regmatch[0].rm_so; i++)
     759            /* Null match? Print one more char */
     760            if (start == end && *line)
     761                pipe_putc(*line++);
     762            goto next;
     763        }
     764
     765        /* Print everything before the match */
     766        for (i = 0; i < start; i++)
    686767            pipe_putc(line[i]);
    687768
    688         /* then print the substitution string */
    689         do_subst_w_backrefs(line, sed_cmd->string);
    690 
    691         /* advance past the match */
    692         line += G.regmatch[0].rm_eo;
    693         /* flag that something has changed */
    694         altered++;
     769        /* Then print the substitution string,
     770         * unless we just matched empty string after non-empty one.
     771         * Example: string "cccd", pattern "c*", repl "R":
     772         * result is "RdR", not "RRdR": first match "ccc",
     773         * second is "" before "d", third is "" after "d".
     774         * Second match is NOT replaced!
     775         */
     776        if (prev_match_empty || start != 0 || start != end) {
     777            //dbg("%d %d %d", prev_match_empty, start, end);
     778            dbg("inserting replacement at %d in '%s'", start, line);
     779            do_subst_w_backrefs(line, sed_cmd->string);
     780            /* Flag that something has changed */
     781            altered = 1;
     782        } else {
     783            dbg("NOT inserting replacement at %d in '%s'", start, line);
     784        }
     785
     786        /* If matched string is empty (f.e. "c*" pattern),
     787         * copy verbatim one char after it before attempting more matches
     788         */
     789        prev_match_empty = (start == end);
     790        if (prev_match_empty) {
     791            if (!line[end]) {
     792                tried_at_eol = 1;
     793            } else {
     794                pipe_putc(line[end]);
     795                end++;
     796            }
     797        }
     798
     799        /* Advance past the match */
     800        dbg("line += %d", end);
     801        line += end;
    695802
    696803        /* if we're not doing this globally, get out now */
    697         if (sed_cmd->which_match)
    698             break;
    699 
    700 //maybe (G.regmatch[0].rm_eo ? REG_NOTBOL : 0) instead of unconditional REG_NOTBOL?
    701     } while (*line && regexec(current_regex, line, 10, G.regmatch, REG_NOTBOL) != REG_NOMATCH);
     804        if (sed_cmd->which_match != 0)
     805            break;
     806 next:
     807        /* Exit if we are at EOL and already tried matching at it */
     808        if (*line == '\0') {
     809            if (tried_at_eol)
     810                break;
     811            tried_at_eol = 1;
     812        }
     813
     814//maybe (end ? REG_NOTBOL : 0) instead of unconditional REG_NOTBOL?
     815    } while (regexec(current_regex, line, 10, G.regmatch, REG_NOTBOL) != REG_NOMATCH);
    702816
    703817    /* Copy rest of string into output pipeline */
     
    719833    sed_cmd_t *sed_cmd;
    720834
    721     for (sed_cmd = G.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {
     835    for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) {
    722836        if (sed_cmd->cmd == ':' && sed_cmd->string && !strcmp(sed_cmd->string, label)) {
    723837            return sed_cmd;
     
    8951009    /* For every line, go through all the commands */
    8961010 restart:
    897     for (sed_cmd = G.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {
     1011    for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) {
    8981012        int old_matched, matched;
    8991013
     
    9021016        /* Determine if this command matches this line: */
    9031017
    904         //bb_error_msg("match1:%d", sed_cmd->in_match);
    905         //bb_error_msg("match2:%d", (!sed_cmd->beg_line && !sed_cmd->end_line
    906         //      && !sed_cmd->beg_match && !sed_cmd->end_match));
    907         //bb_error_msg("match3:%d", (sed_cmd->beg_line > 0
    908         //  && (sed_cmd->end_line || sed_cmd->end_match
    909         //      ? (sed_cmd->beg_line <= linenum)
    910         //      : (sed_cmd->beg_line == linenum)
    911         //      )
    912         //  )
    913         //bb_error_msg("match4:%d", (beg_match(sed_cmd, pattern_space)));
    914         //bb_error_msg("match5:%d", (sed_cmd->beg_line == -1 && next_line == NULL));
     1018        dbg("match1:%d", sed_cmd->in_match);
     1019        dbg("match2:%d", (!sed_cmd->beg_line && !sed_cmd->end_line
     1020                && !sed_cmd->beg_match && !sed_cmd->end_match));
     1021        dbg("match3:%d", (sed_cmd->beg_line > 0
     1022            && (sed_cmd->end_line || sed_cmd->end_match
     1023                ? (sed_cmd->beg_line <= linenum)
     1024                : (sed_cmd->beg_line == linenum)
     1025                )
     1026            ));
     1027        dbg("match4:%d", (beg_match(sed_cmd, pattern_space)));
     1028        dbg("match5:%d", (sed_cmd->beg_line == -1 && next_line == NULL));
    9151029
    9161030        /* Are we continuing a previous multi-line match? */
     
    9231037                && (sed_cmd->end_line || sed_cmd->end_match
    9241038                  /* note: even if end is numeric and is < linenum too,
    925                    * GNU sed matches! We match too */
     1039                   * GNU sed matches! We match too, therefore we don't
     1040                   * check here that linenum <= end.
     1041                   * Example:
     1042                   * printf '1\n2\n3\n4\n' | sed -n '1{N;N;d};1p;2,3p;3p;4p'
     1043                   * first three input lines are deleted;
     1044                   * 4th line is matched and printed
     1045                   * by "2,3" (!) and by "4" ranges
     1046                   */
    9261047                ? (sed_cmd->beg_line <= linenum)    /* N,end */
    9271048                : (sed_cmd->beg_line == linenum)    /* N */
     
    9361057        matched = sed_cmd->in_match;
    9371058
    938         //bb_error_msg("cmd:'%c' matched:%d beg_line:%d end_line:%d linenum:%d",
    939         //sed_cmd->cmd, matched, sed_cmd->beg_line, sed_cmd->end_line, linenum);
     1059        dbg("cmd:'%c' matched:%d beg_line:%d end_line:%d linenum:%d",
     1060            sed_cmd->cmd, matched, sed_cmd->beg_line, sed_cmd->end_line, linenum);
    9401061
    9411062        /* Is this line the end of the current match? */
     
    9431064        if (matched) {
    9441065            /* once matched, "n,xxx" range is dead, disabling it */
    945             if (sed_cmd->beg_line > 0
    946              && !(option_mask32 & OPT_in_place) /* but not for -i */
    947             ) {
     1066            if (sed_cmd->beg_line > 0) {
    9481067                sed_cmd->beg_line = -2;
    9491068            }
    9501069            sed_cmd->in_match = !(
    9511070                /* has the ending line come, or is this a single address command? */
    952                 (sed_cmd->end_line ?
    953                     sed_cmd->end_line == -1 ?
    954                         !next_line
     1071                (sed_cmd->end_line
     1072                    ? sed_cmd->end_line == -1
     1073                        ? !next_line
    9551074                        : (sed_cmd->end_line <= linenum)
    9561075                    : !sed_cmd->end_match
     
    9591078                || (sed_cmd->end_match && old_matched
    9601079                     && (regexec(sed_cmd->end_match,
    961                                  pattern_space, 0, NULL, 0) == 0))
     1080                        pattern_space, 0, NULL, 0) == 0)
     1081                )
    9621082            );
    9631083        }
     
    9931113
    9941114        /* actual sedding */
    995         //bb_error_msg("pattern_space:'%s' next_line:'%s' cmd:%c",
    996         //pattern_space, next_line, sed_cmd->cmd);
     1115        dbg("pattern_space:'%s' next_line:'%s' cmd:%c",
     1116                pattern_space, next_line, sed_cmd->cmd);
    9971117        switch (sed_cmd->cmd) {
    9981118
     
    10411161            if (!do_subst_command(sed_cmd, &pattern_space))
    10421162                break;
     1163            dbg("do_subst_command succeeded:'%s'", pattern_space);
    10431164            substituted |= 1;
    10441165
     
    12881409    unsigned opt;
    12891410    llist_t *opt_e, *opt_f;
     1411    char *opt_i;
     1412
     1413#if ENABLE_LONG_OPTS
     1414    static const char sed_longopts[] ALIGN1 =
     1415        /* name             has_arg             short */
     1416        "in-place\0"        Optional_argument   "i"
     1417        "regexp-extended\0" No_argument         "r"
     1418        "quiet\0"           No_argument         "n"
     1419        "silent\0"          No_argument         "n"
     1420        "expression\0"      Required_argument   "e"
     1421        "file\0"            Required_argument   "f";
     1422#endif
     1423
    12901424    int status = EXIT_SUCCESS;
    12911425
     
    12961430
    12971431    /* Lie to autoconf when it starts asking stupid questions. */
    1298     if (argv[1] && !strcmp(argv[1], "--version")) {
     1432    if (argv[1] && strcmp(argv[1], "--version") == 0) {
    12991433        puts("This is not GNU sed version 4.0");
    13001434        return 0;
     
    13031437    /* do normal option parsing */
    13041438    opt_e = opt_f = NULL;
     1439    opt_i = NULL;
    13051440    opt_complementary = "e::f::" /* can occur multiple times */
    13061441                        "nn"; /* count -n */
     1442
     1443    IF_LONG_OPTS(applet_long_options = sed_longopts);
     1444
    13071445    /* -i must be first, to match OPT_in_place definition */
    1308     opt = getopt32(argv, "irne:f:", &opt_e, &opt_f,
     1446    opt = getopt32(argv, "i::rne:f:", &opt_i, &opt_e, &opt_f,
    13091447                &G.be_quiet); /* counter for -n */
    13101448    //argc -= optind;
     
    13491487    } else {
    13501488        int i;
    1351         FILE *file;
    13521489
    13531490        for (i = 0; argv[i]; i++) {
    13541491            struct stat statbuf;
    13551492            int nonstdoutfd;
     1493            FILE *file;
     1494            sed_cmd_t *sed_cmd;
    13561495
    13571496            if (LONE_DASH(argv[i]) && !(opt & OPT_in_place)) {
     
    13651504                continue;
    13661505            }
     1506            add_input_file(file);
    13671507            if (!(opt & OPT_in_place)) {
    1368                 add_input_file(file);
    13691508                continue;
    13701509            }
     1510
     1511            /* -i: process each FILE separately: */
    13711512
    13721513            G.outname = xasprintf("%sXXXXXX", argv[i]);
     
    13801521            fchmod(nonstdoutfd, statbuf.st_mode);
    13811522            fchown(nonstdoutfd, statbuf.st_uid, statbuf.st_gid);
    1382             add_input_file(file);
     1523
    13831524            process_files();
    13841525            fclose(G.nonstdout);
    1385 
    13861526            G.nonstdout = stdout;
    1387             /* unlink(argv[i]); */
    1388             xrename(G.outname, argv[i]);
     1527
     1528            if (opt_i) {
     1529                char *backupname = xasprintf("%s%s", argv[i], opt_i);
     1530                xrename(argv[i], backupname);
     1531                free(backupname);
     1532            }
     1533            /* else unlink(argv[i]); - rename below does this */
     1534            xrename(G.outname, argv[i]); //TODO: rollback backup on error?
    13891535            free(G.outname);
    13901536            G.outname = NULL;
     1537
     1538            /* Re-enable disabled range matches */
     1539            for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) {
     1540                sed_cmd->beg_line = sed_cmd->beg_line_orig;
     1541            }
    13911542        }
    13921543        /* Here, to handle "sed 'cmds' nonexistent_file" case we did:
Note: See TracChangeset for help on using the changeset viewer.