libarchive: open_zipped() does not need to check extensions for e.g. gzip
[oweals/busybox.git] / editors / sed.c
index 777f38308ef4d7605a38c70af6025f87bb204a40..e18e48ab5ba0fc0265ee688456e5da9569de9186 100644 (file)
@@ -23,9 +23,6 @@
  * resulting sed_cmd_t structures are appended to a linked list
  * (G.sed_cmd_head/G.sed_cmd_tail).
  *
- * add_input_file() adds a FILE* to the list of input files.  We need to
- * know all input sources ahead of time to find the last line for the $ match.
- *
  * process_files() does actual sedding, reading data lines from each input FILE*
  * (which could be stdin) and applying the sed command list (sed_cmd_head) to
  * each of the resulting lines.
@@ -135,12 +132,15 @@ static const char semicolon_whitespace[] ALIGN1 = "; \n\r\t\v";
 struct globals {
        /* options */
        int be_quiet, regex_type;
+
        FILE *nonstdout;
        char *outname, *hold_space;
+       smallint exitcode;
 
-       /* List of input files */
-       int input_file_count, current_input_file;
-       FILE **input_file_list;
+       /* list of input files */
+       int current_input_file, last_input_file;
+       char **input_file_list;
+       FILE *current_fp;
 
        regmatch_t regmatch[10];
        regex_t *previous_regex_ptr;
@@ -148,7 +148,7 @@ struct globals {
        /* linked list of sed commands */
        sed_cmd_t *sed_cmd_head, **sed_cmd_tail;
 
-       /* Linked list of append lines */
+       /* linked list of append lines */
        llist_t *append_head;
 
        char *add_cmd_line;
@@ -200,8 +200,8 @@ static void sed_free_and_close_stuff(void)
 
        free(G.hold_space);
 
-       while (G.current_input_file < G.input_file_count)
-               fclose(G.input_file_list[G.current_input_file++]);
+       if (G.current_fp)
+               fclose(G.current_fp);
 }
 #else
 void sed_free_and_close_stuff(void);
@@ -381,7 +381,7 @@ static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr)
 
        /*
         * A substitution command should look something like this:
-        *    s/match/replace/ #gIpw
+        *    s/match/replace/ #giIpw
         *    ||     |        |||
         *    mandatory       optional
         */
@@ -429,6 +429,7 @@ static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr)
                        break;
                }
                /* Ignore case (gnu exension) */
+               case 'i':
                case 'I':
                        cflags |= REG_ICASE;
                        break;
@@ -939,8 +940,20 @@ static char *get_next_line(char *gets_char, char *last_puts_char, char last_gets
        /* will be returned if last line in the file
         * doesn't end with either '\n' or '\0' */
        gc = NO_EOL_CHAR;
-       while (G.current_input_file < G.input_file_count) {
-               FILE *fp = G.input_file_list[G.current_input_file];
+       for (; G.current_input_file <= G.last_input_file; G.current_input_file++) {
+               FILE *fp = G.current_fp;
+               if (!fp) {
+                       const char *path = G.input_file_list[G.current_input_file];
+                       fp = stdin;
+                       if (path != bb_msg_standard_input) {
+                               fp = fopen_or_warn(path, "r");
+                               if (!fp) {
+                                       G.exitcode = EXIT_FAILURE;
+                                       continue;
+                               }
+                       }
+                       G.current_fp = fp;
+               }
                /* Read line up to a newline or NUL byte, inclusive,
                 * return malloc'ed char[]. length of the chunk read
                 * is stored in len. NULL if EOF/error */
@@ -971,8 +984,8 @@ static char *get_next_line(char *gets_char, char *last_puts_char, char last_gets
                 * (note: *no* newline after "b bang"!) */
                }
                /* Close this file and advance to next one */
-               fclose(fp);
-               G.current_input_file++;
+               fclose_if_not_stdin(fp);
+               G.current_fp = NULL;
        }
        *gets_char = gc;
        return temp;
@@ -1399,12 +1412,6 @@ static void add_cmd_block(char *cmdstr)
        free(sv);
 }
 
-static void add_input_file(FILE *file)
-{
-       G.input_file_list = xrealloc_vector(G.input_file_list, 2, G.input_file_count);
-       G.input_file_list[G.input_file_count++] = file;
-}
-
 int sed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int sed_main(int argc UNUSED_PARAM, char **argv)
 {
@@ -1423,8 +1430,6 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
                "file\0"            Required_argument   "f";
 #endif
 
-       int status = EXIT_SUCCESS;
-
        INIT_G();
 
        /* destroy command strings on exit */
@@ -1488,42 +1493,38 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
        /* argv[0..(argc-1)] should be names of file to process. If no
         * files were specified or '-' was specified, take input from stdin.
         * Otherwise, we process all the files specified. */
-       if (argv[0] == NULL) {
+       G.input_file_list = argv;
+       if (!argv[0]) {
                if (opt & OPT_in_place)
                        bb_error_msg_and_die(bb_msg_requires_arg, "-i");
-               add_input_file(stdin);
+               argv[0] = (char*)bb_msg_standard_input;
+               /* G.last_input_file = 0; - already is */
        } else {
-               int i;
+               goto start;
 
-               for (i = 0; argv[i]; i++) {
+               for (; *argv; argv++) {
                        struct stat statbuf;
                        int nonstdoutfd;
-                       FILE *file;
                        sed_cmd_t *sed_cmd;
 
-                       if (LONE_DASH(argv[i]) && !(opt & OPT_in_place)) {
-                               add_input_file(stdin);
-                               process_files();
-                               continue;
-                       }
-                       file = fopen_or_warn(argv[i], "r");
-                       if (!file) {
-                               status = EXIT_FAILURE;
-                               continue;
-                       }
-                       add_input_file(file);
+                       G.last_input_file++;
+ start:
                        if (!(opt & OPT_in_place)) {
+                               if (LONE_DASH(*argv)) {
+                                       *argv = (char*)bb_msg_standard_input;
+                                       process_files();
+                               }
                                continue;
                        }
 
                        /* -i: process each FILE separately: */
 
-                       G.outname = xasprintf("%sXXXXXX", argv[i]);
+                       G.outname = xasprintf("%sXXXXXX", *argv);
                        nonstdoutfd = xmkstemp(G.outname);
                        G.nonstdout = xfdopen_for_write(nonstdoutfd);
 
                        /* Set permissions/owner of output file */
-                       fstat(fileno(file), &statbuf);
+                       stat(*argv, &statbuf);
                        /* chmod'ing AFTER chown would preserve suid/sgid bits,
                         * but GNU sed 4.2.1 does not preserve them either */
                        fchmod(nonstdoutfd, statbuf.st_mode);
@@ -1534,12 +1535,12 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
                        G.nonstdout = stdout;
 
                        if (opt_i) {
-                               char *backupname = xasprintf("%s%s", argv[i], opt_i);
-                               xrename(argv[i], backupname);
+                               char *backupname = xasprintf("%s%s", *argv, opt_i);
+                               xrename(*argv, backupname);
                                free(backupname);
                        }
-                       /* else unlink(argv[i]); - rename below does this */
-                       xrename(G.outname, argv[i]); //TODO: rollback backup on error?
+                       /* else unlink(*argv); - rename below does this */
+                       xrename(G.outname, *argv); //TODO: rollback backup on error?
                        free(G.outname);
                        G.outname = NULL;
 
@@ -1549,12 +1550,13 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
                        }
                }
                /* Here, to handle "sed 'cmds' nonexistent_file" case we did:
-                * if (G.current_input_file >= G.input_file_count)
-                *      return status;
+                * if (G.current_input_file[G.current_input_file] == NULL)
+                *      return G.exitcode;
                 * but it's not needed since process_files() works correctly
                 * in this case too. */
        }
+
        process_files();
 
-       return status;
+       return G.exitcode;
 }