Changeset 3621 in MondoRescue for branches/3.3/mindi-busybox/editors/sed.c


Ignore:
Timestamp:
Dec 20, 2016, 4:07:32 PM (7 years ago)
Author:
Bruno Cornec
Message:

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

Location:
branches/3.3
Files:
1 edited
1 copied

Legend:

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

    r3232 r3621  
    2323 * resulting sed_cmd_t structures are appended to a linked list
    2424 * (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.
    2825 *
    2926 * process_files() does actual sedding, reading data lines from each input FILE*
     
    5754 * http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html
    5855 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html
     56 * http://sed.sourceforge.net/sedfaq3.html
    5957 */
    6058
     59//config:config SED
     60//config:   bool "sed"
     61//config:   default y
     62//config:   help
     63//config:     sed is used to perform text transformations on a file
     64//config:     or input from a pipeline.
     65
     66//kbuild:lib-$(CONFIG_SED) += sed.o
     67
     68//applet:IF_SED(APPLET(sed, BB_DIR_BIN, BB_SUID_DROP))
     69
    6170//usage:#define sed_trivial_usage
    62 //usage:       "[-inr] [-f FILE]... [-e CMD]... [FILE]...\n"
    63 //usage:       "or: sed [-inr] CMD [FILE]..."
     71//usage:       "[-inrE] [-f FILE]... [-e CMD]... [FILE]...\n"
     72//usage:       "or: sed [-inrE] CMD [FILE]..."
    6473//usage:#define sed_full_usage "\n\n"
    6574//usage:       "    -e CMD  Add CMD to sed commands to be executed"
     
    6877//usage:     "\n        Optionally back files up, appending SFX"
    6978//usage:     "\n    -n  Suppress automatic printing of pattern space"
    70 //usage:     "\n    -r  Use extended regex syntax"
     79//usage:     "\n    -r,-E   Use extended regex syntax"
    7180//usage:     "\n"
    7281//usage:     "\nIf no -e or -f, the first non-option argument is the sed command string."
     
    7887
    7988#include "libbb.h"
     89#include "common_bufsiz.h"
    8090#include "xregex.h"
    8191
     
    102112    int beg_line;           /* 'sed 1p'   0 == apply commands to all lines */
    103113    int beg_line_orig;      /* copy of the above, needed for -i */
    104     int end_line;           /* 'sed 1,3p' 0 == one line only. -1 = last line ($) */
    105 
    106     FILE *sw_file;          /* File (sw) command writes to, -1 for none. */
     114    int end_line;           /* 'sed 1,3p' 0 == one line only. -1 = last line ($). -2-N = +N */
     115    int end_line_orig;
     116
     117    FILE *sw_file;          /* File (sw) command writes to, NULL for none. */
    107118    char *string;           /* Data string for (saicytb) commands. */
    108119
     
    125136    /* options */
    126137    int be_quiet, regex_type;
     138
    127139    FILE *nonstdout;
    128140    char *outname, *hold_space;
    129 
    130     /* List of input files */
    131     int input_file_count, current_input_file;
    132     FILE **input_file_list;
     141    smallint exitcode;
     142
     143    /* list of input files */
     144    int current_input_file, last_input_file;
     145    char **input_file_list;
     146    FILE *current_fp;
    133147
    134148    regmatch_t regmatch[10];
     
    138152    sed_cmd_t *sed_cmd_head, **sed_cmd_tail;
    139153
    140     /* Linked list of append lines */
     154    /* linked list of append lines */
    141155    llist_t *append_head;
    142156
     
    149163    } pipeline;
    150164} FIX_ALIASING;
    151 #define G (*(struct globals*)&bb_common_bufsiz1)
    152 struct BUG_G_too_big {
    153     char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
    154 };
     165#define G (*(struct globals*)bb_common_bufsiz1)
    155166#define INIT_G() do { \
     167    setup_common_bufsiz(); \
     168    BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
    156169    G.sed_cmd_tail = &G.sed_cmd_head; \
    157170} while (0)
     
    169182
    170183        if (sed_cmd->sw_file)
    171             xprint_and_close_file(sed_cmd->sw_file);
     184            fclose(sed_cmd->sw_file);
    172185
    173186        if (sed_cmd->beg_match) {
     
    190203    free(G.hold_space);
    191204
    192     while (G.current_input_file < G.input_file_count)
    193         fclose(G.input_file_list[G.current_input_file++]);
     205    if (G.current_fp)
     206        fclose(G.current_fp);
    194207}
    195208#else
     
    206219/* strcpy, replacing "\from" with 'to'. If to is NUL, replacing "\any" with 'any' */
    207220
    208 static void parse_escapes(char *dest, const char *string, int len, char from, char to)
    209 {
     221static unsigned parse_escapes(char *dest, const char *string, int len, char from, char to)
     222{
     223    char *d = dest;
    210224    int i = 0;
     225
     226    if (len == -1)
     227        len = strlen(string);
    211228
    212229    while (i < len) {
    213230        if (string[i] == '\\') {
    214231            if (!to || string[i+1] == from) {
    215                 *dest++ = to ? to : string[i+1];
     232                if ((*d = to ? to : string[i+1]) == '\0')
     233                    return d - dest;
    216234                i += 2;
     235                d++;
    217236                continue;
    218237            }
    219             *dest++ = string[i++];
    220         }
    221         /* TODO: is it safe wrt a string with trailing '\\' ? */
    222         *dest++ = string[i++];
    223     }
    224     *dest = '\0';
     238            i++; /* skip backslash in string[] */
     239            *d++ = '\\';
     240            /* fall through: copy next char verbatim */
     241        }
     242        if ((*d = string[i++]) == '\0')
     243            return d - dest;
     244        d++;
     245    }
     246    *d = '\0';
     247    return d - dest;
    225248}
    226249
     
    233256    /* GNU sed also recognizes \t and \r */
    234257    for (s = "\nn\tt\rr"; *s; s += 2) {
    235         parse_escapes(dest, string, len, s[1], s[0]);
     258        len = parse_escapes(dest, string, len, s[1], s[0]);
    236259        string = dest;
    237         len = strlen(dest);
    238260    }
    239261    return dest;
     
    331353        temp = copy_parsing_escapes(pos, next);
    332354        *regex = xzalloc(sizeof(regex_t));
    333         xregcomp(*regex, temp, G.regex_type|REG_NEWLINE);
     355        xregcomp(*regex, temp, G.regex_type);
    334356        free(temp);
    335357        /* Move position to next character after last delimiter */
     
    371393    /*
    372394     * A substitution command should look something like this:
    373      *    s/match/replace/ #gIpw
     395     *    s/match/replace/ #giIpw
    374396     *    ||     |        |||
    375397     *    mandatory       optional
     
    385407
    386408    sed_cmd->which_match = 1;
     409    dbg("s flags:'%s'", substr + idx + 1);
    387410    while (substr[++idx]) {
     411        dbg("s flag:'%c'", substr[idx]);
    388412        /* Parse match number */
    389413        if (isdigit(substr[idx])) {
     
    393417/* FIXME: error check? */
    394418                sed_cmd->which_match = (unsigned)strtol(substr+idx, (char**) &pos, 10);
    395                 idx = pos - substr;
     419                idx = pos - substr - 1;
    396420            }
    397421            continue;
     
    414438        case 'w':
    415439        {
    416             char *temp;
    417             idx += parse_file_cmd(/*sed_cmd,*/ substr+idx, &temp);
     440            char *fname;
     441            idx += parse_file_cmd(/*sed_cmd,*/ substr+idx+1, &fname);
     442            sed_cmd->sw_file = xfopen_for_write(fname);
     443            sed_cmd->sw_last_char = '\n';
     444            free(fname);
    418445            break;
    419446        }
    420447        /* Ignore case (gnu exension) */
     448        case 'i':
    421449        case 'I':
    422450            cflags |= REG_ICASE;
     
    432460            goto out;
    433461        default:
     462            dbg("s bad flags:'%s'", substr + idx);
    434463            bb_error_msg_and_die("bad option in substitution expression");
    435464        }
     
    454483static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
    455484{
    456     static const char cmd_letters[] = "saicrw:btTydDgGhHlnNpPqx={}";
     485    static const char cmd_letters[] ALIGN1 = "saicrw:btTydDgGhHlnNpPqx={}";
    457486    enum {
    458487        IDX_s = 0,
     
    485514        IDX_nul
    486515    };
    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;
     516    unsigned idx;
     517
     518    BUILD_BUG_ON(sizeof(cmd_letters)-1 != IDX_nul);
     519
     520    idx = strchrnul(cmd_letters, sed_cmd->cmd) - cmd_letters;
    490521
    491522    /* handle (s)ubstitution command */
     
    495526    /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */
    496527    else if (idx <= IDX_c) { /* a,i,c */
     528        unsigned len;
     529
    497530        if (idx < IDX_c) { /* a,i */
    498531            if (sed_cmd->end_line || sed_cmd->end_match)
     
    508541            cmdstr++;
    509542        }
    510         sed_cmd->string = xstrdup(cmdstr);
     543        len = strlen(cmdstr);
     544        sed_cmd->string = copy_parsing_escapes(cmdstr, len);
     545        cmdstr += len;
    511546        /* "\anychar" -> "anychar" */
    512         parse_escapes(sed_cmd->string, sed_cmd->string, strlen(cmdstr), '\0', '\0');
    513         cmdstr += strlen(cmdstr);
     547        parse_escapes(sed_cmd->string, sed_cmd->string, -1, '\0', '\0');
    514548    }
    515549    /* handle file cmds: (r)ead */
     
    543577        cmdstr += parse_regex_delim(cmdstr, &match, &replace)+1;
    544578        /* \n already parsed, but \delimiter needs unescaping. */
    545         parse_escapes(match, match, strlen(match), i, i);
    546         parse_escapes(replace, replace, strlen(replace), i, i);
     579        parse_escapes(match,   match,   -1, i, i);
     580        parse_escapes(replace, replace, -1, i, i);
    547581
    548582        sed_cmd->string = xzalloc((strlen(match) + 1) * 2);
     
    626660
    627661            cmdstr++;
    628             idx = get_address(cmdstr, &sed_cmd->end_line, &sed_cmd->end_match);
    629             if (!idx)
     662            if (*cmdstr == '+' && isdigit(cmdstr[1])) {
     663                /* http://sed.sourceforge.net/sedfaq3.html#s3.3
     664                 * Under GNU sed 3.02+, ssed, and sed15+, <address2>
     665                 * may also be a notation of the form +num,
     666                 * indicating the next num lines after <address1> is
     667                 * matched.
     668                 * GNU sed 4.2.1 accepts even "+" (meaning "+0").
     669                 * We don't (we check for isdigit, see above), think
     670                 * about the "+-3" case.
     671                 */
     672                char *end;
     673                /* code is smaller compared to using &cmdstr here: */
     674                idx = strtol(cmdstr+1, &end, 10);
     675                sed_cmd->end_line = -2 - idx;
     676                cmdstr = end;
     677            } else {
     678                idx = get_address(cmdstr, &sed_cmd->end_line, &sed_cmd->end_match);
     679                cmdstr += idx;
     680                idx--; /* if 0, trigger error check below */
     681            }
     682            if (idx < 0)
    630683                bb_error_msg_and_die("no address after comma");
    631             cmdstr += idx;
     684            sed_cmd->end_line_orig = sed_cmd->end_line;
    632685        }
    633686
     
    649702        sed_cmd->cmd = *cmdstr++;
    650703        cmdstr = parse_cmd_args(sed_cmd, cmdstr);
     704
     705        /* cmdstr now points past args.
     706         * GNU sed requires a separator, if there are more commands,
     707         * else it complains "char N: extra characters after command".
     708         * Example: "sed 'p;d'". We also allow "sed 'pd'".
     709         */
    651710
    652711        /* Add the command to the command array */
     
    843902static void append(char *s)
    844903{
    845     llist_add_to_end(&G.append_head, xstrdup(s));
    846 }
    847 
    848 static void flush_append(void)
    849 {
    850     char *data;
    851 
    852     /* Output appended lines. */
    853     while ((data = (char *)llist_pop(&G.append_head))) {
    854         fprintf(G.nonstdout, "%s\n", data);
    855         free(data);
    856     }
    857 }
    858 
    859 static void add_input_file(FILE *file)
    860 {
    861     G.input_file_list = xrealloc_vector(G.input_file_list, 2, G.input_file_count);
    862     G.input_file_list[G.input_file_count++] = file;
    863 }
    864 
    865 /* Get next line of input from G.input_file_list, flushing append buffer and
    866  * noting if we ran out of files without a newline on the last line we read.
     904    llist_add_to_end(&G.append_head, s);
     905}
     906
     907/* Output line of text. */
     908/* Note:
     909 * The tricks with NO_EOL_CHAR and last_puts_char are there to emulate gnu sed.
     910 * Without them, we had this:
     911 * echo -n thingy >z1
     912 * echo -n again >z2
     913 * >znull
     914 * sed "s/i/z/" z1 z2 znull | hexdump -vC
     915 * output:
     916 * gnu sed 4.1.5:
     917 * 00000000  74 68 7a 6e 67 79 0a 61  67 61 7a 6e              |thzngy.agazn|
     918 * bbox:
     919 * 00000000  74 68 7a 6e 67 79 61 67  61 7a 6e                 |thzngyagazn|
    867920 */
    868921enum {
     
    870923    LAST_IS_NUL = 2,
    871924};
    872 static char *get_next_line(char *gets_char)
     925static void puts_maybe_newline(char *s, FILE *file, char *last_puts_char, char last_gets_char)
     926{
     927    char lpc = *last_puts_char;
     928
     929    /* Need to insert a '\n' between two files because first file's
     930     * last line wasn't terminated? */
     931    if (lpc != '\n' && lpc != '\0') {
     932        fputc('\n', file);
     933        lpc = '\n';
     934    }
     935    fputs(s, file);
     936
     937    /* 'x' - just something which is not '\n', '\0' or NO_EOL_CHAR */
     938    if (s[0])
     939        lpc = 'x';
     940
     941    /* had trailing '\0' and it was last char of file? */
     942    if (last_gets_char == LAST_IS_NUL) {
     943        fputc('\0', file);
     944        lpc = 'x'; /* */
     945    } else
     946    /* had trailing '\n' or '\0'? */
     947    if (last_gets_char != NO_EOL_CHAR) {
     948        fputc(last_gets_char, file);
     949        lpc = last_gets_char;
     950    }
     951
     952    if (ferror(file)) {
     953        xfunc_error_retval = 4;  /* It's what gnu sed exits with... */
     954        bb_error_msg_and_die(bb_msg_write_error);
     955    }
     956    *last_puts_char = lpc;
     957}
     958
     959static void flush_append(char *last_puts_char)
     960{
     961    char *data;
     962
     963    /* Output appended lines. */
     964    while ((data = (char *)llist_pop(&G.append_head)) != NULL) {
     965        /* Append command does not respect "nonterminated-ness"
     966         * of last line. Try this:
     967         * $ echo -n "woot" | sed -e '/woot/a woo' -
     968         * woot
     969         * woo
     970         * (both lines are terminated with \n)
     971         * Therefore we do not propagate "last_gets_char" here,
     972         * pass '\n' instead:
     973         */
     974        puts_maybe_newline(data, G.nonstdout, last_puts_char, '\n');
     975        free(data);
     976    }
     977}
     978
     979/* Get next line of input from G.input_file_list, flushing append buffer and
     980 * noting if we ran out of files without a newline on the last line we read.
     981 */
     982static char *get_next_line(char *gets_char, char *last_puts_char)
    873983{
    874984    char *temp = NULL;
     
    876986    char gc;
    877987
    878     flush_append();
     988    flush_append(last_puts_char);
    879989
    880990    /* will be returned if last line in the file
    881991     * doesn't end with either '\n' or '\0' */
    882992    gc = NO_EOL_CHAR;
    883     while (G.current_input_file < G.input_file_count) {
    884         FILE *fp = G.input_file_list[G.current_input_file];
     993    for (; G.current_input_file <= G.last_input_file; G.current_input_file++) {
     994        FILE *fp = G.current_fp;
     995        if (!fp) {
     996            const char *path = G.input_file_list[G.current_input_file];
     997            fp = stdin;
     998            if (path != bb_msg_standard_input) {
     999                fp = fopen_or_warn(path, "r");
     1000                if (!fp) {
     1001                    G.exitcode = EXIT_FAILURE;
     1002                    continue;
     1003                }
     1004            }
     1005            G.current_fp = fp;
     1006        }
    8851007        /* Read line up to a newline or NUL byte, inclusive,
    8861008         * return malloc'ed char[]. length of the chunk read
     
    9131035        }
    9141036        /* Close this file and advance to next one */
    915         fclose(fp);
    916         G.current_input_file++;
     1037        fclose_if_not_stdin(fp);
     1038        G.current_fp = NULL;
    9171039    }
    9181040    *gets_char = gc;
    9191041    return temp;
    920 }
    921 
    922 /* Output line of text. */
    923 /* Note:
    924  * The tricks with NO_EOL_CHAR and last_puts_char are there to emulate gnu sed.
    925  * Without them, we had this:
    926  * echo -n thingy >z1
    927  * echo -n again >z2
    928  * >znull
    929  * sed "s/i/z/" z1 z2 znull | hexdump -vC
    930  * output:
    931  * gnu sed 4.1.5:
    932  * 00000000  74 68 7a 6e 67 79 0a 61  67 61 7a 6e              |thzngy.agazn|
    933  * bbox:
    934  * 00000000  74 68 7a 6e 67 79 61 67  61 7a 6e                 |thzngyagazn|
    935  */
    936 static void puts_maybe_newline(char *s, FILE *file, char *last_puts_char, char last_gets_char)
    937 {
    938     char lpc = *last_puts_char;
    939 
    940     /* Need to insert a '\n' between two files because first file's
    941      * last line wasn't terminated? */
    942     if (lpc != '\n' && lpc != '\0') {
    943         fputc('\n', file);
    944         lpc = '\n';
    945     }
    946     fputs(s, file);
    947 
    948     /* 'x' - just something which is not '\n', '\0' or NO_EOL_CHAR */
    949     if (s[0])
    950         lpc = 'x';
    951 
    952     /* had trailing '\0' and it was last char of file? */
    953     if (last_gets_char == LAST_IS_NUL) {
    954         fputc('\0', file);
    955         lpc = 'x'; /* */
    956     } else
    957     /* had trailing '\n' or '\0'? */
    958     if (last_gets_char != NO_EOL_CHAR) {
    959         fputc(last_gets_char, file);
    960         lpc = last_gets_char;
    961     }
    962 
    963     if (ferror(file)) {
    964         xfunc_error_retval = 4;  /* It's what gnu sed exits with... */
    965         bb_error_msg_and_die(bb_msg_write_error);
    966     }
    967     *last_puts_char = lpc;
    9681042}
    9691043
     
    9901064
    9911065    /* Prime the pump */
    992     next_line = get_next_line(&next_gets_char);
     1066    next_line = get_next_line(&next_gets_char, &last_puts_char);
    9931067
    9941068    /* Go through every line in each file */
     
    10041078    /* Read one line in advance so we can act on the last line,
    10051079     * the '$' address */
    1006     next_line = get_next_line(&next_gets_char);
     1080    next_line = get_next_line(&next_gets_char, &last_puts_char);
    10071081    linenum++;
    10081082
     
    10631137
    10641138        if (matched) {
     1139            if (sed_cmd->end_line <= -2) {
     1140                /* address2 is +N, i.e. N lines from beg_line */
     1141                sed_cmd->end_line = linenum + (-sed_cmd->end_line - 2);
     1142            }
    10651143            /* once matched, "n,xxx" range is dead, disabling it */
    10661144            if (sed_cmd->beg_line > 0) {
    10671145                sed_cmd->beg_line = -2;
    10681146            }
     1147            dbg("end1:%d", sed_cmd->end_line ? sed_cmd->end_line == -1
     1148                        ? !next_line : (sed_cmd->end_line <= linenum)
     1149                    : !sed_cmd->end_match);
     1150            dbg("end2:%d", sed_cmd->end_match && old_matched
     1151                    && !regexec(sed_cmd->end_match,pattern_space, 0, NULL, 0));
    10691152            sed_cmd->in_match = !(
    10701153                /* has the ending line come, or is this a single address command? */
     
    11761259        /* Append line to linked list to be printed later */
    11771260        case 'a':
    1178             append(sed_cmd->string);
     1261            append(xstrdup(sed_cmd->string));
    11791262            break;
    11801263
     
    11981281            if (rfile) {
    11991282                char *line;
    1200 
    12011283                while ((line = xmalloc_fgetline(rfile))
    12021284                        != NULL)
    12031285                    append(line);
    1204                 xprint_and_close_file(rfile);
     1286                fclose(rfile);
    12051287            }
    12061288
     
    12231305                pattern_space = next_line;
    12241306                last_gets_char = next_gets_char;
    1225                 next_line = get_next_line(&next_gets_char);
     1307                next_line = get_next_line(&next_gets_char, &last_puts_char);
    12261308                substituted = 0;
    12271309                linenum++;
     
    12591341            strcpy(pattern_space + len+1, next_line);
    12601342            last_gets_char = next_gets_char;
    1261             next_line = get_next_line(&next_gets_char);
     1343            next_line = get_next_line(&next_gets_char, &last_puts_char);
    12621344            linenum++;
    12631345            break;
     
    13631445    /* Delete and such jump here. */
    13641446 discard_line:
    1365     flush_append();
     1447    flush_append(&last_puts_char /*,last_gets_char*/);
    13661448    free(pattern_space);
    13671449
     
    13721454 * newlines.  This counts as multiple command lines.
    13731455 * However, newline can be escaped: 's/e/z\<newline>z/'
    1374  * We check for this.
     1456 * add_cmd() handles this.
    13751457 */
    13761458
     
    13821464    do {
    13831465        eol = strchr(cmdstr, '\n');
    1384  next:
    1385         if (eol) {
    1386             /* Count preceding slashes */
    1387             int slashes = 0;
    1388             char *sl = eol;
    1389 
    1390             while (sl != cmdstr && *--sl == '\\')
    1391                 slashes++;
    1392             /* Odd number of preceding slashes - newline is escaped */
    1393             if (slashes & 1) {
    1394                 overlapping_strcpy(eol - 1, eol);
    1395                 eol = strchr(eol, '\n');
    1396                 goto next;
    1397             }
     1466        if (eol)
    13981467            *eol = '\0';
    1399         }
    14001468        add_cmd(cmdstr);
    14011469        cmdstr = eol + 1;
     
    14221490#endif
    14231491
    1424     int status = EXIT_SUCCESS;
    1425 
    14261492    INIT_G();
    14271493
     
    14441510
    14451511    /* -i must be first, to match OPT_in_place definition */
    1446     opt = getopt32(argv, "i::rne:f:", &opt_i, &opt_e, &opt_f,
     1512    /* -E is a synonym of -r:
     1513     * GNU sed 4.2.1 mentions it in neither --help
     1514     * nor manpage, but does recognize it.
     1515     */
     1516    opt = getopt32(argv, "i::rEne:f:", &opt_i, &opt_e, &opt_f,
    14471517                &G.be_quiet); /* counter for -n */
    14481518    //argc -= optind;
     
    14511521        atexit(cleanup_outname);
    14521522    }
    1453     if (opt & 0x2) G.regex_type |= REG_EXTENDED; // -r
    1454     //if (opt & 0x4) G.be_quiet++; // -n
     1523    if (opt & (2|4))
     1524        G.regex_type |= REG_EXTENDED; // -r or -E
     1525    //if (opt & 8)
     1526    //  G.be_quiet++; // -n (implemented with a counter instead)
    14551527    while (opt_e) { // -e
    14561528        add_cmd_block(llist_pop(&opt_e));
     
    14591531        char *line;
    14601532        FILE *cmdfile;
    1461         cmdfile = xfopen_for_read(llist_pop(&opt_f));
     1533        cmdfile = xfopen_stdin(llist_pop(&opt_f));
    14621534        while ((line = xmalloc_fgetline(cmdfile)) != NULL) {
    14631535            add_cmd(line);
    14641536            free(line);
    14651537        }
    1466         fclose(cmdfile);
     1538        fclose_if_not_stdin(cmdfile);
    14671539    }
    14681540    /* if we didn't get a pattern from -e or -f, use argv[0] */
    1469     if (!(opt & 0x18)) {
     1541    if (!(opt & 0x30)) {
    14701542        if (!*argv)
    14711543            bb_show_usage();
     
    14811553     * files were specified or '-' was specified, take input from stdin.
    14821554     * Otherwise, we process all the files specified. */
    1483     if (argv[0] == NULL) {
     1555    G.input_file_list = argv;
     1556    if (!argv[0]) {
    14841557        if (opt & OPT_in_place)
    14851558            bb_error_msg_and_die(bb_msg_requires_arg, "-i");
    1486         add_input_file(stdin);
     1559        argv[0] = (char*)bb_msg_standard_input;
     1560        /* G.last_input_file = 0; - already is */
    14871561    } else {
    1488         int i;
    1489 
    1490         for (i = 0; argv[i]; i++) {
     1562        goto start;
     1563
     1564        for (; *argv; argv++) {
    14911565            struct stat statbuf;
    14921566            int nonstdoutfd;
    1493             FILE *file;
    14941567            sed_cmd_t *sed_cmd;
    14951568
    1496             if (LONE_DASH(argv[i]) && !(opt & OPT_in_place)) {
    1497                 add_input_file(stdin);
    1498                 process_files();
     1569            G.last_input_file++;
     1570 start:
     1571            if (!(opt & OPT_in_place)) {
     1572                if (LONE_DASH(*argv)) {
     1573                    *argv = (char*)bb_msg_standard_input;
     1574                    process_files();
     1575                }
    14991576                continue;
    15001577            }
    1501             file = fopen_or_warn(argv[i], "r");
    1502             if (!file) {
    1503                 status = EXIT_FAILURE;
     1578
     1579            /* -i: process each FILE separately: */
     1580
     1581            if (stat(*argv, &statbuf) != 0) {
     1582                bb_simple_perror_msg(*argv);
     1583                G.exitcode = EXIT_FAILURE;
     1584                G.current_input_file++;
    15041585                continue;
    15051586            }
    1506             add_input_file(file);
    1507             if (!(opt & OPT_in_place)) {
    1508                 continue;
    1509             }
    1510 
    1511             /* -i: process each FILE separately: */
    1512 
    1513             G.outname = xasprintf("%sXXXXXX", argv[i]);
     1587            G.outname = xasprintf("%sXXXXXX", *argv);
    15141588            nonstdoutfd = xmkstemp(G.outname);
    15151589            G.nonstdout = xfdopen_for_write(nonstdoutfd);
    1516 
    15171590            /* Set permissions/owner of output file */
    1518             fstat(fileno(file), &statbuf);
    15191591            /* chmod'ing AFTER chown would preserve suid/sgid bits,
    15201592             * but GNU sed 4.2.1 does not preserve them either */
     
    15271599
    15281600            if (opt_i) {
    1529                 char *backupname = xasprintf("%s%s", argv[i], opt_i);
    1530                 xrename(argv[i], backupname);
     1601                char *backupname = xasprintf("%s%s", *argv, opt_i);
     1602                xrename(*argv, backupname);
    15311603                free(backupname);
    15321604            }
    1533             /* else unlink(argv[i]); - rename below does this */
    1534             xrename(G.outname, argv[i]); //TODO: rollback backup on error?
     1605            /* else unlink(*argv); - rename below does this */
     1606            xrename(G.outname, *argv); //TODO: rollback backup on error?
    15351607            free(G.outname);
    15361608            G.outname = NULL;
    15371609
    1538             /* Re-enable disabled range matches */
     1610            /* Fix disabled range matches and mangled ",+N" ranges */
    15391611            for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) {
    15401612                sed_cmd->beg_line = sed_cmd->beg_line_orig;
     1613                sed_cmd->end_line = sed_cmd->end_line_orig;
    15411614            }
    15421615        }
    15431616        /* Here, to handle "sed 'cmds' nonexistent_file" case we did:
    1544          * if (G.current_input_file >= G.input_file_count)
    1545          *  return status;
     1617         * if (G.current_input_file[G.current_input_file] == NULL)
     1618         *  return G.exitcode;
    15461619         * but it's not needed since process_files() works correctly
    15471620         * in this case too. */
    15481621    }
     1622
    15491623    process_files();
    15501624
    1551     return status;
    1552 }
     1625    return G.exitcode;
     1626}
Note: See TracChangeset for help on using the changeset viewer.