diff options
Diffstat (limited to 'editors')
-rw-r--r-- | editors/Config.src | 60 | ||||
-rw-r--r-- | editors/Kbuild.src | 5 | ||||
-rw-r--r-- | editors/awk.c | 125 | ||||
-rw-r--r-- | editors/cmp.c | 11 | ||||
-rw-r--r-- | editors/diff.c | 27 | ||||
-rw-r--r-- | editors/ed.c | 12 | ||||
-rw-r--r-- | editors/patch_bbox.c | 4 | ||||
-rw-r--r-- | editors/sed.c | 256 |
8 files changed, 285 insertions, 215 deletions
diff --git a/editors/Config.src b/editors/Config.src index af1e1de5e..c6e9d92af 100644 --- a/editors/Config.src +++ b/editors/Config.src | |||
@@ -7,66 +7,6 @@ menu "Editors" | |||
7 | 7 | ||
8 | INSERT | 8 | INSERT |
9 | 9 | ||
10 | config AWK | ||
11 | bool "awk" | ||
12 | default y | ||
13 | help | ||
14 | Awk is used as a pattern scanning and processing language. This is | ||
15 | the BusyBox implementation of that programming language. | ||
16 | |||
17 | config FEATURE_AWK_LIBM | ||
18 | bool "Enable math functions (requires libm)" | ||
19 | default y | ||
20 | depends on AWK | ||
21 | help | ||
22 | Enable math functions of the Awk programming language. | ||
23 | NOTE: This will require libm to be present for linking. | ||
24 | |||
25 | config CMP | ||
26 | bool "cmp" | ||
27 | default y | ||
28 | help | ||
29 | cmp is used to compare two files and returns the result | ||
30 | to standard output. | ||
31 | |||
32 | config DIFF | ||
33 | bool "diff" | ||
34 | default y | ||
35 | help | ||
36 | diff compares two files or directories and outputs the | ||
37 | differences between them in a form that can be given to | ||
38 | the patch command. | ||
39 | |||
40 | config FEATURE_DIFF_LONG_OPTIONS | ||
41 | bool "Enable long options" | ||
42 | default y | ||
43 | depends on DIFF && LONG_OPTS | ||
44 | help | ||
45 | Enable use of long options. | ||
46 | |||
47 | config FEATURE_DIFF_DIR | ||
48 | bool "Enable directory support" | ||
49 | default y | ||
50 | depends on DIFF | ||
51 | help | ||
52 | This option enables support for directory and subdirectory | ||
53 | comparison. | ||
54 | |||
55 | config ED | ||
56 | bool "ed" | ||
57 | default y | ||
58 | help | ||
59 | The original 1970's Unix text editor, from the days of teletypes. | ||
60 | Small, simple, evil. Part of SUSv3. If you're not already using | ||
61 | this, you don't need it. | ||
62 | |||
63 | config SED | ||
64 | bool "sed" | ||
65 | default y | ||
66 | help | ||
67 | sed is used to perform text transformations on a file | ||
68 | or input from a pipeline. | ||
69 | |||
70 | config FEATURE_ALLOW_EXEC | 10 | config FEATURE_ALLOW_EXEC |
71 | bool "Allow vi and awk to execute shell commands" | 11 | bool "Allow vi and awk to execute shell commands" |
72 | default y | 12 | default y |
diff --git a/editors/Kbuild.src b/editors/Kbuild.src index 8888cba12..6b4fb7470 100644 --- a/editors/Kbuild.src +++ b/editors/Kbuild.src | |||
@@ -7,8 +7,3 @@ | |||
7 | lib-y:= | 7 | lib-y:= |
8 | 8 | ||
9 | INSERT | 9 | INSERT |
10 | lib-$(CONFIG_AWK) += awk.o | ||
11 | lib-$(CONFIG_CMP) += cmp.o | ||
12 | lib-$(CONFIG_DIFF) += diff.o | ||
13 | lib-$(CONFIG_ED) += ed.o | ||
14 | lib-$(CONFIG_SED) += sed.o | ||
diff --git a/editors/awk.c b/editors/awk.c index 77784dfc1..d0e3781e7 100644 --- a/editors/awk.c +++ b/editors/awk.c | |||
@@ -7,12 +7,45 @@ | |||
7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
8 | */ | 8 | */ |
9 | 9 | ||
10 | //config:config AWK | ||
11 | //config: bool "awk" | ||
12 | //config: default y | ||
13 | //config: help | ||
14 | //config: Awk is used as a pattern scanning and processing language. This is | ||
15 | //config: the BusyBox implementation of that programming language. | ||
16 | //config: | ||
17 | //config:config FEATURE_AWK_LIBM | ||
18 | //config: bool "Enable math functions (requires libm)" | ||
19 | //config: default y | ||
20 | //config: depends on AWK | ||
21 | //config: help | ||
22 | //config: Enable math functions of the Awk programming language. | ||
23 | //config: NOTE: This will require libm to be present for linking. | ||
24 | //config: | ||
25 | //config:config FEATURE_AWK_GNU_EXTENSIONS | ||
26 | //config: bool "Enable a few GNU extensions" | ||
27 | //config: default y | ||
28 | //config: depends on AWK | ||
29 | //config: help | ||
30 | //config: Enable a few features from gawk: | ||
31 | //config: * command line option -e AWK_PROGRAM | ||
32 | //config: * simultaneous use of -f and -e on the command line. | ||
33 | //config: This enables the use of awk library files. | ||
34 | //config: Ex: awk -f mylib.awk -e '{print myfunction($1);}' ... | ||
35 | |||
36 | //applet:IF_AWK(APPLET_NOEXEC(awk, awk, BB_DIR_USR_BIN, BB_SUID_DROP, awk)) | ||
37 | |||
38 | //kbuild:lib-$(CONFIG_AWK) += awk.o | ||
39 | |||
10 | //usage:#define awk_trivial_usage | 40 | //usage:#define awk_trivial_usage |
11 | //usage: "[OPTIONS] [AWK_PROGRAM] [FILE]..." | 41 | //usage: "[OPTIONS] [AWK_PROGRAM] [FILE]..." |
12 | //usage:#define awk_full_usage "\n\n" | 42 | //usage:#define awk_full_usage "\n\n" |
13 | //usage: " -v VAR=VAL Set variable" | 43 | //usage: " -v VAR=VAL Set variable" |
14 | //usage: "\n -F SEP Use SEP as field separator" | 44 | //usage: "\n -F SEP Use SEP as field separator" |
15 | //usage: "\n -f FILE Read program from FILE" | 45 | //usage: "\n -f FILE Read program from FILE" |
46 | //usage: IF_FEATURE_AWK_GNU_EXTENSIONS( | ||
47 | //usage: "\n -e AWK_PROGRAM" | ||
48 | //usage: ) | ||
16 | 49 | ||
17 | #include "libbb.h" | 50 | #include "libbb.h" |
18 | #include "xregex.h" | 51 | #include "xregex.h" |
@@ -38,6 +71,25 @@ | |||
38 | #endif | 71 | #endif |
39 | 72 | ||
40 | 73 | ||
74 | #define OPTSTR_AWK \ | ||
75 | "F:v:f:" \ | ||
76 | IF_FEATURE_AWK_GNU_EXTENSIONS("e:") \ | ||
77 | "W:" | ||
78 | #define OPTCOMPLSTR_AWK \ | ||
79 | "v::f::" \ | ||
80 | IF_FEATURE_AWK_GNU_EXTENSIONS("e::") | ||
81 | enum { | ||
82 | OPTBIT_F, /* define field separator */ | ||
83 | OPTBIT_v, /* define variable */ | ||
84 | OPTBIT_f, /* pull in awk program from file */ | ||
85 | IF_FEATURE_AWK_GNU_EXTENSIONS(OPTBIT_e,) /* -e AWK_PROGRAM */ | ||
86 | OPTBIT_W, /* -W ignored */ | ||
87 | OPT_F = 1 << OPTBIT_F, | ||
88 | OPT_v = 1 << OPTBIT_v, | ||
89 | OPT_f = 1 << OPTBIT_f, | ||
90 | OPT_e = IF_FEATURE_AWK_GNU_EXTENSIONS((1 << OPTBIT_e)) + 0, | ||
91 | OPT_W = 1 << OPTBIT_W | ||
92 | }; | ||
41 | 93 | ||
42 | #define MAXVARFMT 240 | 94 | #define MAXVARFMT 240 |
43 | #define MINNVBLOCK 64 | 95 | #define MINNVBLOCK 64 |
@@ -2784,8 +2836,16 @@ static var *evaluate(node *op, var *res) | |||
2784 | break; | 2836 | break; |
2785 | 2837 | ||
2786 | case F_le: | 2838 | case F_le: |
2787 | if (!op1) | 2839 | debug_printf_eval("length: L.s:'%s'\n", L.s); |
2840 | if (!op1) { | ||
2788 | L.s = getvar_s(intvar[F0]); | 2841 | L.s = getvar_s(intvar[F0]); |
2842 | debug_printf_eval("length: L.s='%s'\n", L.s); | ||
2843 | } | ||
2844 | else if (L.v->type & VF_ARRAY) { | ||
2845 | R_d = L.v->x.array->nel; | ||
2846 | debug_printf_eval("length: array_len:%d\n", L.v->x.array->nel); | ||
2847 | break; | ||
2848 | } | ||
2789 | R_d = strlen(L.s); | 2849 | R_d = strlen(L.s); |
2790 | break; | 2850 | break; |
2791 | 2851 | ||
@@ -3079,6 +3139,9 @@ int awk_main(int argc, char **argv) | |||
3079 | char *opt_F; | 3139 | char *opt_F; |
3080 | llist_t *list_v = NULL; | 3140 | llist_t *list_v = NULL; |
3081 | llist_t *list_f = NULL; | 3141 | llist_t *list_f = NULL; |
3142 | #if ENABLE_FEATURE_AWK_GNU_EXTENSIONS | ||
3143 | llist_t *list_e = NULL; | ||
3144 | #endif | ||
3082 | int i, j; | 3145 | int i, j; |
3083 | var *v; | 3146 | var *v; |
3084 | var tv; | 3147 | var tv; |
@@ -3137,47 +3200,51 @@ int awk_main(int argc, char **argv) | |||
3137 | *s1 = '='; | 3200 | *s1 = '='; |
3138 | } | 3201 | } |
3139 | } | 3202 | } |
3140 | opt_complementary = "v::f::"; /* -v and -f can occur multiple times */ | 3203 | opt_complementary = OPTCOMPLSTR_AWK; |
3141 | opt = getopt32(argv, "F:v:f:W:", &opt_F, &list_v, &list_f, NULL); | 3204 | opt = getopt32(argv, OPTSTR_AWK, &opt_F, &list_v, &list_f, IF_FEATURE_AWK_GNU_EXTENSIONS(&list_e,) NULL); |
3142 | argv += optind; | 3205 | argv += optind; |
3143 | argc -= optind; | 3206 | argc -= optind; |
3144 | if (opt & 0x1) { /* -F */ | 3207 | if (opt & OPT_W) |
3208 | bb_error_msg("warning: option -W is ignored"); | ||
3209 | if (opt & OPT_F) { | ||
3145 | unescape_string_in_place(opt_F); | 3210 | unescape_string_in_place(opt_F); |
3146 | setvar_s(intvar[FS], opt_F); | 3211 | setvar_s(intvar[FS], opt_F); |
3147 | } | 3212 | } |
3148 | while (list_v) { /* -v */ | 3213 | while (list_v) { |
3149 | if (!is_assignment(llist_pop(&list_v))) | 3214 | if (!is_assignment(llist_pop(&list_v))) |
3150 | bb_show_usage(); | 3215 | bb_show_usage(); |
3151 | } | 3216 | } |
3152 | if (list_f) { /* -f */ | 3217 | while (list_f) { |
3153 | do { | 3218 | char *s = NULL; |
3154 | char *s = NULL; | 3219 | FILE *from_file; |
3155 | FILE *from_file; | 3220 | |
3156 | 3221 | g_progname = llist_pop(&list_f); | |
3157 | g_progname = llist_pop(&list_f); | 3222 | from_file = xfopen_stdin(g_progname); |
3158 | from_file = xfopen_stdin(g_progname); | 3223 | /* one byte is reserved for some trick in next_token */ |
3159 | /* one byte is reserved for some trick in next_token */ | 3224 | for (i = j = 1; j > 0; i += j) { |
3160 | for (i = j = 1; j > 0; i += j) { | 3225 | s = xrealloc(s, i + 4096); |
3161 | s = xrealloc(s, i + 4096); | 3226 | j = fread(s + i, 1, 4094, from_file); |
3162 | j = fread(s + i, 1, 4094, from_file); | 3227 | } |
3163 | } | 3228 | s[i] = '\0'; |
3164 | s[i] = '\0'; | 3229 | fclose(from_file); |
3165 | fclose(from_file); | 3230 | parse_program(s + 1); |
3166 | parse_program(s + 1); | 3231 | free(s); |
3167 | free(s); | 3232 | } |
3168 | } while (list_f); | 3233 | g_progname = "cmd. line"; |
3169 | argc++; | 3234 | #if ENABLE_FEATURE_AWK_GNU_EXTENSIONS |
3170 | } else { // no -f: take program from 1st parameter | 3235 | while (list_e) { |
3171 | if (!argc) | 3236 | parse_program(llist_pop(&list_e)); |
3237 | } | ||
3238 | #endif | ||
3239 | if (!(opt & (OPT_f | OPT_e))) { | ||
3240 | if (!*argv) | ||
3172 | bb_show_usage(); | 3241 | bb_show_usage(); |
3173 | g_progname = "cmd. line"; | ||
3174 | parse_program(*argv++); | 3242 | parse_program(*argv++); |
3243 | argc--; | ||
3175 | } | 3244 | } |
3176 | if (opt & 0x8) // -W | ||
3177 | bb_error_msg("warning: option -W is ignored"); | ||
3178 | 3245 | ||
3179 | /* fill in ARGV array */ | 3246 | /* fill in ARGV array */ |
3180 | setvar_i(intvar[ARGC], argc); | 3247 | setvar_i(intvar[ARGC], argc + 1); |
3181 | setari_u(intvar[ARGV], 0, "awk"); | 3248 | setari_u(intvar[ARGV], 0, "awk"); |
3182 | i = 0; | 3249 | i = 0; |
3183 | while (*argv) | 3250 | while (*argv) |
diff --git a/editors/cmp.c b/editors/cmp.c index fbe6b9753..a4af6f480 100644 --- a/editors/cmp.c +++ b/editors/cmp.c | |||
@@ -10,6 +10,17 @@ | |||
10 | /* BB_AUDIT SUSv3 (virtually) compliant -- uses nicer GNU format for -l. */ | 10 | /* BB_AUDIT SUSv3 (virtually) compliant -- uses nicer GNU format for -l. */ |
11 | /* http://www.opengroup.org/onlinepubs/007904975/utilities/cmp.html */ | 11 | /* http://www.opengroup.org/onlinepubs/007904975/utilities/cmp.html */ |
12 | 12 | ||
13 | //config:config CMP | ||
14 | //config: bool "cmp" | ||
15 | //config: default y | ||
16 | //config: help | ||
17 | //config: cmp is used to compare two files and returns the result | ||
18 | //config: to standard output. | ||
19 | |||
20 | //kbuild:lib-$(CONFIG_CMP) += cmp.o | ||
21 | |||
22 | //applet:IF_CMP(APPLET(cmp, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
23 | |||
13 | //usage:#define cmp_trivial_usage | 24 | //usage:#define cmp_trivial_usage |
14 | //usage: "[-l] [-s] FILE1 [FILE2" IF_DESKTOP(" [SKIP1 [SKIP2]]") "]" | 25 | //usage: "[-l] [-s] FILE1 [FILE2" IF_DESKTOP(" [SKIP1 [SKIP2]]") "]" |
15 | //usage:#define cmp_full_usage "\n\n" | 26 | //usage:#define cmp_full_usage "\n\n" |
diff --git a/editors/diff.c b/editors/diff.c index b08ded3a1..a78a0ee28 100644 --- a/editors/diff.c +++ b/editors/diff.c | |||
@@ -76,6 +76,33 @@ | |||
76 | * 6n words for files of length n. | 76 | * 6n words for files of length n. |
77 | */ | 77 | */ |
78 | 78 | ||
79 | //config:config DIFF | ||
80 | //config: bool "diff" | ||
81 | //config: default y | ||
82 | //config: help | ||
83 | //config: diff compares two files or directories and outputs the | ||
84 | //config: differences between them in a form that can be given to | ||
85 | //config: the patch command. | ||
86 | //config: | ||
87 | //config:config FEATURE_DIFF_LONG_OPTIONS | ||
88 | //config: bool "Enable long options" | ||
89 | //config: default y | ||
90 | //config: depends on DIFF && LONG_OPTS | ||
91 | //config: help | ||
92 | //config: Enable use of long options. | ||
93 | //config: | ||
94 | //config:config FEATURE_DIFF_DIR | ||
95 | //config: bool "Enable directory support" | ||
96 | //config: default y | ||
97 | //config: depends on DIFF | ||
98 | //config: help | ||
99 | //config: This option enables support for directory and subdirectory | ||
100 | //config: comparison. | ||
101 | |||
102 | //kbuild:lib-$(CONFIG_DIFF) += diff.o | ||
103 | |||
104 | //applet:IF_DIFF(APPLET(diff, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
105 | |||
79 | //usage:#define diff_trivial_usage | 106 | //usage:#define diff_trivial_usage |
80 | //usage: "[-abBdiNqrTstw] [-L LABEL] [-S FILE] [-U LINES] FILE1 FILE2" | 107 | //usage: "[-abBdiNqrTstw] [-L LABEL] [-S FILE] [-U LINES] FILE1 FILE2" |
81 | //usage:#define diff_full_usage "\n\n" | 108 | //usage:#define diff_full_usage "\n\n" |
diff --git a/editors/ed.c b/editors/ed.c index dbb51306c..3087fb0b9 100644 --- a/editors/ed.c +++ b/editors/ed.c | |||
@@ -7,6 +7,18 @@ | |||
7 | * The "ed" built-in command (much simplified) | 7 | * The "ed" built-in command (much simplified) |
8 | */ | 8 | */ |
9 | 9 | ||
10 | //config:config ED | ||
11 | //config: bool "ed" | ||
12 | //config: default y | ||
13 | //config: help | ||
14 | //config: The original 1970's Unix text editor, from the days of teletypes. | ||
15 | //config: Small, simple, evil. Part of SUSv3. If you're not already using | ||
16 | //config: this, you don't need it. | ||
17 | |||
18 | //kbuild:lib-$(CONFIG_ED) += ed.o | ||
19 | |||
20 | //applet:IF_ED(APPLET(ed, BB_DIR_BIN, BB_SUID_DROP)) | ||
21 | |||
10 | //usage:#define ed_trivial_usage "" | 22 | //usage:#define ed_trivial_usage "" |
11 | //usage:#define ed_full_usage "" | 23 | //usage:#define ed_full_usage "" |
12 | 24 | ||
diff --git a/editors/patch_bbox.c b/editors/patch_bbox.c index 78aa5fde8..aae7b7987 100644 --- a/editors/patch_bbox.c +++ b/editors/patch_bbox.c | |||
@@ -188,8 +188,8 @@ int patch_main(int argc UNUSED_PARAM, char **argv) | |||
188 | unsigned src_last_line = 1; | 188 | unsigned src_last_line = 1; |
189 | unsigned dst_last_line = 1; | 189 | unsigned dst_last_line = 1; |
190 | 190 | ||
191 | if ((sscanf(patch_line, "@@ -%d,%d +%d,%d", &src_beg_line, &src_last_line, &dst_beg_line, &dst_last_line) < 3) | 191 | if ((sscanf(patch_line, "@@ -%u,%u +%u,%u", &src_beg_line, &src_last_line, &dst_beg_line, &dst_last_line) < 3) |
192 | && (sscanf(patch_line, "@@ -%d +%d,%d", &src_beg_line, &dst_beg_line, &dst_last_line) < 2) | 192 | && (sscanf(patch_line, "@@ -%u +%u,%u", &src_beg_line, &dst_beg_line, &dst_last_line) < 2) |
193 | ) { | 193 | ) { |
194 | /* No more hunks for this file */ | 194 | /* No more hunks for this file */ |
195 | break; | 195 | break; |
diff --git a/editors/sed.c b/editors/sed.c index 3a0d917aa..e18e48ab5 100644 --- a/editors/sed.c +++ b/editors/sed.c | |||
@@ -23,9 +23,6 @@ | |||
23 | * resulting sed_cmd_t structures are appended to a linked list | 23 | * resulting sed_cmd_t structures are appended to a linked list |
24 | * (G.sed_cmd_head/G.sed_cmd_tail). | 24 | * (G.sed_cmd_head/G.sed_cmd_tail). |
25 | * | 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* | 26 | * 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 | 27 | * (which could be stdin) and applying the sed command list (sed_cmd_head) to |
31 | * each of the resulting lines. | 28 | * each of the resulting lines. |
@@ -58,16 +55,27 @@ | |||
58 | * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html | 55 | * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html |
59 | */ | 56 | */ |
60 | 57 | ||
58 | //config:config SED | ||
59 | //config: bool "sed" | ||
60 | //config: default y | ||
61 | //config: help | ||
62 | //config: sed is used to perform text transformations on a file | ||
63 | //config: or input from a pipeline. | ||
64 | |||
65 | //kbuild:lib-$(CONFIG_SED) += sed.o | ||
66 | |||
67 | //applet:IF_SED(APPLET(sed, BB_DIR_BIN, BB_SUID_DROP)) | ||
68 | |||
61 | //usage:#define sed_trivial_usage | 69 | //usage:#define sed_trivial_usage |
62 | //usage: "[-inr] [-f FILE]... [-e CMD]... [FILE]...\n" | 70 | //usage: "[-inrE] [-f FILE]... [-e CMD]... [FILE]...\n" |
63 | //usage: "or: sed [-inr] CMD [FILE]..." | 71 | //usage: "or: sed [-inrE] CMD [FILE]..." |
64 | //usage:#define sed_full_usage "\n\n" | 72 | //usage:#define sed_full_usage "\n\n" |
65 | //usage: " -e CMD Add CMD to sed commands to be executed" | 73 | //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" | 74 | //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)" | 75 | //usage: "\n -i[SFX] Edit files in-place (otherwise sends to stdout)" |
68 | //usage: "\n Optionally back files up, appending SFX" | 76 | //usage: "\n Optionally back files up, appending SFX" |
69 | //usage: "\n -n Suppress automatic printing of pattern space" | 77 | //usage: "\n -n Suppress automatic printing of pattern space" |
70 | //usage: "\n -r Use extended regex syntax" | 78 | //usage: "\n -r,-E Use extended regex syntax" |
71 | //usage: "\n" | 79 | //usage: "\n" |
72 | //usage: "\nIf no -e or -f, the first non-option argument is the sed command string." | 80 | //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)." | 81 | //usage: "\nRemaining arguments are input files (stdin if none)." |
@@ -124,12 +132,15 @@ static const char semicolon_whitespace[] ALIGN1 = "; \n\r\t\v"; | |||
124 | struct globals { | 132 | struct globals { |
125 | /* options */ | 133 | /* options */ |
126 | int be_quiet, regex_type; | 134 | int be_quiet, regex_type; |
135 | |||
127 | FILE *nonstdout; | 136 | FILE *nonstdout; |
128 | char *outname, *hold_space; | 137 | char *outname, *hold_space; |
138 | smallint exitcode; | ||
129 | 139 | ||
130 | /* List of input files */ | 140 | /* list of input files */ |
131 | int input_file_count, current_input_file; | 141 | int current_input_file, last_input_file; |
132 | FILE **input_file_list; | 142 | char **input_file_list; |
143 | FILE *current_fp; | ||
133 | 144 | ||
134 | regmatch_t regmatch[10]; | 145 | regmatch_t regmatch[10]; |
135 | regex_t *previous_regex_ptr; | 146 | regex_t *previous_regex_ptr; |
@@ -137,7 +148,7 @@ struct globals { | |||
137 | /* linked list of sed commands */ | 148 | /* linked list of sed commands */ |
138 | sed_cmd_t *sed_cmd_head, **sed_cmd_tail; | 149 | sed_cmd_t *sed_cmd_head, **sed_cmd_tail; |
139 | 150 | ||
140 | /* Linked list of append lines */ | 151 | /* linked list of append lines */ |
141 | llist_t *append_head; | 152 | llist_t *append_head; |
142 | 153 | ||
143 | char *add_cmd_line; | 154 | char *add_cmd_line; |
@@ -189,8 +200,8 @@ static void sed_free_and_close_stuff(void) | |||
189 | 200 | ||
190 | free(G.hold_space); | 201 | free(G.hold_space); |
191 | 202 | ||
192 | while (G.current_input_file < G.input_file_count) | 203 | if (G.current_fp) |
193 | fclose(G.input_file_list[G.current_input_file++]); | 204 | fclose(G.current_fp); |
194 | } | 205 | } |
195 | #else | 206 | #else |
196 | void sed_free_and_close_stuff(void); | 207 | void sed_free_and_close_stuff(void); |
@@ -370,7 +381,7 @@ static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr) | |||
370 | 381 | ||
371 | /* | 382 | /* |
372 | * A substitution command should look something like this: | 383 | * A substitution command should look something like this: |
373 | * s/match/replace/ #gIpw | 384 | * s/match/replace/ #giIpw |
374 | * || | ||| | 385 | * || | ||| |
375 | * mandatory optional | 386 | * mandatory optional |
376 | */ | 387 | */ |
@@ -418,6 +429,7 @@ static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr) | |||
418 | break; | 429 | break; |
419 | } | 430 | } |
420 | /* Ignore case (gnu exension) */ | 431 | /* Ignore case (gnu exension) */ |
432 | case 'i': | ||
421 | case 'I': | 433 | case 'I': |
422 | cflags |= REG_ICASE; | 434 | cflags |= REG_ICASE; |
423 | break; | 435 | break; |
@@ -848,46 +860,100 @@ static sed_cmd_t *branch_to(char *label) | |||
848 | 860 | ||
849 | static void append(char *s) | 861 | static void append(char *s) |
850 | { | 862 | { |
851 | llist_add_to_end(&G.append_head, xstrdup(s)); | 863 | llist_add_to_end(&G.append_head, s); |
852 | } | 864 | } |
853 | 865 | ||
854 | static void flush_append(void) | 866 | /* Output line of text. */ |
867 | /* Note: | ||
868 | * The tricks with NO_EOL_CHAR and last_puts_char are there to emulate gnu sed. | ||
869 | * Without them, we had this: | ||
870 | * echo -n thingy >z1 | ||
871 | * echo -n again >z2 | ||
872 | * >znull | ||
873 | * sed "s/i/z/" z1 z2 znull | hexdump -vC | ||
874 | * output: | ||
875 | * gnu sed 4.1.5: | ||
876 | * 00000000 74 68 7a 6e 67 79 0a 61 67 61 7a 6e |thzngy.agazn| | ||
877 | * bbox: | ||
878 | * 00000000 74 68 7a 6e 67 79 61 67 61 7a 6e |thzngyagazn| | ||
879 | */ | ||
880 | enum { | ||
881 | NO_EOL_CHAR = 1, | ||
882 | LAST_IS_NUL = 2, | ||
883 | }; | ||
884 | static void puts_maybe_newline(char *s, FILE *file, char *last_puts_char, char last_gets_char) | ||
885 | { | ||
886 | char lpc = *last_puts_char; | ||
887 | |||
888 | /* Need to insert a '\n' between two files because first file's | ||
889 | * last line wasn't terminated? */ | ||
890 | if (lpc != '\n' && lpc != '\0') { | ||
891 | fputc('\n', file); | ||
892 | lpc = '\n'; | ||
893 | } | ||
894 | fputs(s, file); | ||
895 | |||
896 | /* 'x' - just something which is not '\n', '\0' or NO_EOL_CHAR */ | ||
897 | if (s[0]) | ||
898 | lpc = 'x'; | ||
899 | |||
900 | /* had trailing '\0' and it was last char of file? */ | ||
901 | if (last_gets_char == LAST_IS_NUL) { | ||
902 | fputc('\0', file); | ||
903 | lpc = 'x'; /* */ | ||
904 | } else | ||
905 | /* had trailing '\n' or '\0'? */ | ||
906 | if (last_gets_char != NO_EOL_CHAR) { | ||
907 | fputc(last_gets_char, file); | ||
908 | lpc = last_gets_char; | ||
909 | } | ||
910 | |||
911 | if (ferror(file)) { | ||
912 | xfunc_error_retval = 4; /* It's what gnu sed exits with... */ | ||
913 | bb_error_msg_and_die(bb_msg_write_error); | ||
914 | } | ||
915 | *last_puts_char = lpc; | ||
916 | } | ||
917 | |||
918 | static void flush_append(char *last_puts_char, char last_gets_char) | ||
855 | { | 919 | { |
856 | char *data; | 920 | char *data; |
857 | 921 | ||
858 | /* Output appended lines. */ | 922 | /* Output appended lines. */ |
859 | while ((data = (char *)llist_pop(&G.append_head))) { | 923 | while ((data = (char *)llist_pop(&G.append_head))) { |
860 | fprintf(G.nonstdout, "%s\n", data); | 924 | puts_maybe_newline(data, G.nonstdout, last_puts_char, last_gets_char); |
861 | free(data); | 925 | free(data); |
862 | } | 926 | } |
863 | } | 927 | } |
864 | 928 | ||
865 | static void add_input_file(FILE *file) | ||
866 | { | ||
867 | G.input_file_list = xrealloc_vector(G.input_file_list, 2, G.input_file_count); | ||
868 | G.input_file_list[G.input_file_count++] = file; | ||
869 | } | ||
870 | |||
871 | /* Get next line of input from G.input_file_list, flushing append buffer and | 929 | /* Get next line of input from G.input_file_list, flushing append buffer and |
872 | * noting if we ran out of files without a newline on the last line we read. | 930 | * noting if we ran out of files without a newline on the last line we read. |
873 | */ | 931 | */ |
874 | enum { | 932 | static char *get_next_line(char *gets_char, char *last_puts_char, char last_gets_char) |
875 | NO_EOL_CHAR = 1, | ||
876 | LAST_IS_NUL = 2, | ||
877 | }; | ||
878 | static char *get_next_line(char *gets_char) | ||
879 | { | 933 | { |
880 | char *temp = NULL; | 934 | char *temp = NULL; |
881 | int len; | 935 | int len; |
882 | char gc; | 936 | char gc; |
883 | 937 | ||
884 | flush_append(); | 938 | flush_append(last_puts_char, last_gets_char); |
885 | 939 | ||
886 | /* will be returned if last line in the file | 940 | /* will be returned if last line in the file |
887 | * doesn't end with either '\n' or '\0' */ | 941 | * doesn't end with either '\n' or '\0' */ |
888 | gc = NO_EOL_CHAR; | 942 | gc = NO_EOL_CHAR; |
889 | while (G.current_input_file < G.input_file_count) { | 943 | for (; G.current_input_file <= G.last_input_file; G.current_input_file++) { |
890 | FILE *fp = G.input_file_list[G.current_input_file]; | 944 | FILE *fp = G.current_fp; |
945 | if (!fp) { | ||
946 | const char *path = G.input_file_list[G.current_input_file]; | ||
947 | fp = stdin; | ||
948 | if (path != bb_msg_standard_input) { | ||
949 | fp = fopen_or_warn(path, "r"); | ||
950 | if (!fp) { | ||
951 | G.exitcode = EXIT_FAILURE; | ||
952 | continue; | ||
953 | } | ||
954 | } | ||
955 | G.current_fp = fp; | ||
956 | } | ||
891 | /* Read line up to a newline or NUL byte, inclusive, | 957 | /* Read line up to a newline or NUL byte, inclusive, |
892 | * return malloc'ed char[]. length of the chunk read | 958 | * return malloc'ed char[]. length of the chunk read |
893 | * is stored in len. NULL if EOF/error */ | 959 | * is stored in len. NULL if EOF/error */ |
@@ -918,61 +984,13 @@ static char *get_next_line(char *gets_char) | |||
918 | * (note: *no* newline after "b bang"!) */ | 984 | * (note: *no* newline after "b bang"!) */ |
919 | } | 985 | } |
920 | /* Close this file and advance to next one */ | 986 | /* Close this file and advance to next one */ |
921 | fclose(fp); | 987 | fclose_if_not_stdin(fp); |
922 | G.current_input_file++; | 988 | G.current_fp = NULL; |
923 | } | 989 | } |
924 | *gets_char = gc; | 990 | *gets_char = gc; |
925 | return temp; | 991 | return temp; |
926 | } | 992 | } |
927 | 993 | ||
928 | /* Output line of text. */ | ||
929 | /* Note: | ||
930 | * The tricks with NO_EOL_CHAR and last_puts_char are there to emulate gnu sed. | ||
931 | * Without them, we had this: | ||
932 | * echo -n thingy >z1 | ||
933 | * echo -n again >z2 | ||
934 | * >znull | ||
935 | * sed "s/i/z/" z1 z2 znull | hexdump -vC | ||
936 | * output: | ||
937 | * gnu sed 4.1.5: | ||
938 | * 00000000 74 68 7a 6e 67 79 0a 61 67 61 7a 6e |thzngy.agazn| | ||
939 | * bbox: | ||
940 | * 00000000 74 68 7a 6e 67 79 61 67 61 7a 6e |thzngyagazn| | ||
941 | */ | ||
942 | static void puts_maybe_newline(char *s, FILE *file, char *last_puts_char, char last_gets_char) | ||
943 | { | ||
944 | char lpc = *last_puts_char; | ||
945 | |||
946 | /* Need to insert a '\n' between two files because first file's | ||
947 | * last line wasn't terminated? */ | ||
948 | if (lpc != '\n' && lpc != '\0') { | ||
949 | fputc('\n', file); | ||
950 | lpc = '\n'; | ||
951 | } | ||
952 | fputs(s, file); | ||
953 | |||
954 | /* 'x' - just something which is not '\n', '\0' or NO_EOL_CHAR */ | ||
955 | if (s[0]) | ||
956 | lpc = 'x'; | ||
957 | |||
958 | /* had trailing '\0' and it was last char of file? */ | ||
959 | if (last_gets_char == LAST_IS_NUL) { | ||
960 | fputc('\0', file); | ||
961 | lpc = 'x'; /* */ | ||
962 | } else | ||
963 | /* had trailing '\n' or '\0'? */ | ||
964 | if (last_gets_char != NO_EOL_CHAR) { | ||
965 | fputc(last_gets_char, file); | ||
966 | lpc = last_gets_char; | ||
967 | } | ||
968 | |||
969 | if (ferror(file)) { | ||
970 | xfunc_error_retval = 4; /* It's what gnu sed exits with... */ | ||
971 | bb_error_msg_and_die(bb_msg_write_error); | ||
972 | } | ||
973 | *last_puts_char = lpc; | ||
974 | } | ||
975 | |||
976 | #define sed_puts(s, n) (puts_maybe_newline(s, G.nonstdout, &last_puts_char, n)) | 994 | #define sed_puts(s, n) (puts_maybe_newline(s, G.nonstdout, &last_puts_char, n)) |
977 | 995 | ||
978 | static int beg_match(sed_cmd_t *sed_cmd, const char *pattern_space) | 996 | static int beg_match(sed_cmd_t *sed_cmd, const char *pattern_space) |
@@ -995,7 +1013,7 @@ static void process_files(void) | |||
995 | int substituted; | 1013 | int substituted; |
996 | 1014 | ||
997 | /* Prime the pump */ | 1015 | /* Prime the pump */ |
998 | next_line = get_next_line(&next_gets_char); | 1016 | next_line = get_next_line(&next_gets_char, &last_puts_char, '\n' /*last_gets_char*/); |
999 | 1017 | ||
1000 | /* Go through every line in each file */ | 1018 | /* Go through every line in each file */ |
1001 | again: | 1019 | again: |
@@ -1009,7 +1027,7 @@ static void process_files(void) | |||
1009 | 1027 | ||
1010 | /* Read one line in advance so we can act on the last line, | 1028 | /* Read one line in advance so we can act on the last line, |
1011 | * the '$' address */ | 1029 | * the '$' address */ |
1012 | next_line = get_next_line(&next_gets_char); | 1030 | next_line = get_next_line(&next_gets_char, &last_puts_char, last_gets_char); |
1013 | linenum++; | 1031 | linenum++; |
1014 | 1032 | ||
1015 | /* For every line, go through all the commands */ | 1033 | /* For every line, go through all the commands */ |
@@ -1181,7 +1199,7 @@ static void process_files(void) | |||
1181 | 1199 | ||
1182 | /* Append line to linked list to be printed later */ | 1200 | /* Append line to linked list to be printed later */ |
1183 | case 'a': | 1201 | case 'a': |
1184 | append(sed_cmd->string); | 1202 | append(xstrdup(sed_cmd->string)); |
1185 | break; | 1203 | break; |
1186 | 1204 | ||
1187 | /* Insert text before this line */ | 1205 | /* Insert text before this line */ |
@@ -1203,11 +1221,10 @@ static void process_files(void) | |||
1203 | rfile = fopen_for_read(sed_cmd->string); | 1221 | rfile = fopen_for_read(sed_cmd->string); |
1204 | if (rfile) { | 1222 | if (rfile) { |
1205 | char *line; | 1223 | char *line; |
1206 | |||
1207 | while ((line = xmalloc_fgetline(rfile)) | 1224 | while ((line = xmalloc_fgetline(rfile)) |
1208 | != NULL) | 1225 | != NULL) |
1209 | append(line); | 1226 | append(line); |
1210 | xprint_and_close_file(rfile); | 1227 | fclose(rfile); |
1211 | } | 1228 | } |
1212 | 1229 | ||
1213 | break; | 1230 | break; |
@@ -1228,7 +1245,7 @@ static void process_files(void) | |||
1228 | free(pattern_space); | 1245 | free(pattern_space); |
1229 | pattern_space = next_line; | 1246 | pattern_space = next_line; |
1230 | last_gets_char = next_gets_char; | 1247 | last_gets_char = next_gets_char; |
1231 | next_line = get_next_line(&next_gets_char); | 1248 | next_line = get_next_line(&next_gets_char, &last_puts_char, last_gets_char); |
1232 | substituted = 0; | 1249 | substituted = 0; |
1233 | linenum++; | 1250 | linenum++; |
1234 | break; | 1251 | break; |
@@ -1264,7 +1281,7 @@ static void process_files(void) | |||
1264 | pattern_space[len] = '\n'; | 1281 | pattern_space[len] = '\n'; |
1265 | strcpy(pattern_space + len+1, next_line); | 1282 | strcpy(pattern_space + len+1, next_line); |
1266 | last_gets_char = next_gets_char; | 1283 | last_gets_char = next_gets_char; |
1267 | next_line = get_next_line(&next_gets_char); | 1284 | next_line = get_next_line(&next_gets_char, &last_puts_char, last_gets_char); |
1268 | linenum++; | 1285 | linenum++; |
1269 | break; | 1286 | break; |
1270 | } | 1287 | } |
@@ -1368,7 +1385,7 @@ static void process_files(void) | |||
1368 | 1385 | ||
1369 | /* Delete and such jump here. */ | 1386 | /* Delete and such jump here. */ |
1370 | discard_line: | 1387 | discard_line: |
1371 | flush_append(); | 1388 | flush_append(&last_puts_char, last_gets_char); |
1372 | free(pattern_space); | 1389 | free(pattern_space); |
1373 | 1390 | ||
1374 | goto again; | 1391 | goto again; |
@@ -1413,8 +1430,6 @@ int sed_main(int argc UNUSED_PARAM, char **argv) | |||
1413 | "file\0" Required_argument "f"; | 1430 | "file\0" Required_argument "f"; |
1414 | #endif | 1431 | #endif |
1415 | 1432 | ||
1416 | int status = EXIT_SUCCESS; | ||
1417 | |||
1418 | INIT_G(); | 1433 | INIT_G(); |
1419 | 1434 | ||
1420 | /* destroy command strings on exit */ | 1435 | /* destroy command strings on exit */ |
@@ -1435,15 +1450,21 @@ int sed_main(int argc UNUSED_PARAM, char **argv) | |||
1435 | IF_LONG_OPTS(applet_long_options = sed_longopts); | 1450 | IF_LONG_OPTS(applet_long_options = sed_longopts); |
1436 | 1451 | ||
1437 | /* -i must be first, to match OPT_in_place definition */ | 1452 | /* -i must be first, to match OPT_in_place definition */ |
1438 | opt = getopt32(argv, "i::rne:f:", &opt_i, &opt_e, &opt_f, | 1453 | /* -E is a synonym of -r: |
1454 | * GNU sed 4.2.1 mentions it in neither --help | ||
1455 | * nor manpage, but does recognize it. | ||
1456 | */ | ||
1457 | opt = getopt32(argv, "i::rEne:f:", &opt_i, &opt_e, &opt_f, | ||
1439 | &G.be_quiet); /* counter for -n */ | 1458 | &G.be_quiet); /* counter for -n */ |
1440 | //argc -= optind; | 1459 | //argc -= optind; |
1441 | argv += optind; | 1460 | argv += optind; |
1442 | if (opt & OPT_in_place) { // -i | 1461 | if (opt & OPT_in_place) { // -i |
1443 | atexit(cleanup_outname); | 1462 | atexit(cleanup_outname); |
1444 | } | 1463 | } |
1445 | if (opt & 0x2) G.regex_type |= REG_EXTENDED; // -r | 1464 | if (opt & (2|4)) |
1446 | //if (opt & 0x4) G.be_quiet++; // -n | 1465 | G.regex_type |= REG_EXTENDED; // -r or -E |
1466 | //if (opt & 8) | ||
1467 | // G.be_quiet++; // -n (implemented with a counter instead) | ||
1447 | while (opt_e) { // -e | 1468 | while (opt_e) { // -e |
1448 | add_cmd_block(llist_pop(&opt_e)); | 1469 | add_cmd_block(llist_pop(&opt_e)); |
1449 | } | 1470 | } |
@@ -1458,7 +1479,7 @@ int sed_main(int argc UNUSED_PARAM, char **argv) | |||
1458 | fclose(cmdfile); | 1479 | fclose(cmdfile); |
1459 | } | 1480 | } |
1460 | /* if we didn't get a pattern from -e or -f, use argv[0] */ | 1481 | /* if we didn't get a pattern from -e or -f, use argv[0] */ |
1461 | if (!(opt & 0x18)) { | 1482 | if (!(opt & 0x30)) { |
1462 | if (!*argv) | 1483 | if (!*argv) |
1463 | bb_show_usage(); | 1484 | bb_show_usage(); |
1464 | add_cmd_block(*argv++); | 1485 | add_cmd_block(*argv++); |
@@ -1472,42 +1493,38 @@ int sed_main(int argc UNUSED_PARAM, char **argv) | |||
1472 | /* argv[0..(argc-1)] should be names of file to process. If no | 1493 | /* argv[0..(argc-1)] should be names of file to process. If no |
1473 | * files were specified or '-' was specified, take input from stdin. | 1494 | * files were specified or '-' was specified, take input from stdin. |
1474 | * Otherwise, we process all the files specified. */ | 1495 | * Otherwise, we process all the files specified. */ |
1475 | if (argv[0] == NULL) { | 1496 | G.input_file_list = argv; |
1497 | if (!argv[0]) { | ||
1476 | if (opt & OPT_in_place) | 1498 | if (opt & OPT_in_place) |
1477 | bb_error_msg_and_die(bb_msg_requires_arg, "-i"); | 1499 | bb_error_msg_and_die(bb_msg_requires_arg, "-i"); |
1478 | add_input_file(stdin); | 1500 | argv[0] = (char*)bb_msg_standard_input; |
1501 | /* G.last_input_file = 0; - already is */ | ||
1479 | } else { | 1502 | } else { |
1480 | int i; | 1503 | goto start; |
1481 | 1504 | ||
1482 | for (i = 0; argv[i]; i++) { | 1505 | for (; *argv; argv++) { |
1483 | struct stat statbuf; | 1506 | struct stat statbuf; |
1484 | int nonstdoutfd; | 1507 | int nonstdoutfd; |
1485 | FILE *file; | ||
1486 | sed_cmd_t *sed_cmd; | 1508 | sed_cmd_t *sed_cmd; |
1487 | 1509 | ||
1488 | if (LONE_DASH(argv[i]) && !(opt & OPT_in_place)) { | 1510 | G.last_input_file++; |
1489 | add_input_file(stdin); | 1511 | start: |
1490 | process_files(); | ||
1491 | continue; | ||
1492 | } | ||
1493 | file = fopen_or_warn(argv[i], "r"); | ||
1494 | if (!file) { | ||
1495 | status = EXIT_FAILURE; | ||
1496 | continue; | ||
1497 | } | ||
1498 | add_input_file(file); | ||
1499 | if (!(opt & OPT_in_place)) { | 1512 | if (!(opt & OPT_in_place)) { |
1513 | if (LONE_DASH(*argv)) { | ||
1514 | *argv = (char*)bb_msg_standard_input; | ||
1515 | process_files(); | ||
1516 | } | ||
1500 | continue; | 1517 | continue; |
1501 | } | 1518 | } |
1502 | 1519 | ||
1503 | /* -i: process each FILE separately: */ | 1520 | /* -i: process each FILE separately: */ |
1504 | 1521 | ||
1505 | G.outname = xasprintf("%sXXXXXX", argv[i]); | 1522 | G.outname = xasprintf("%sXXXXXX", *argv); |
1506 | nonstdoutfd = xmkstemp(G.outname); | 1523 | nonstdoutfd = xmkstemp(G.outname); |
1507 | G.nonstdout = xfdopen_for_write(nonstdoutfd); | 1524 | G.nonstdout = xfdopen_for_write(nonstdoutfd); |
1508 | 1525 | ||
1509 | /* Set permissions/owner of output file */ | 1526 | /* Set permissions/owner of output file */ |
1510 | fstat(fileno(file), &statbuf); | 1527 | stat(*argv, &statbuf); |
1511 | /* chmod'ing AFTER chown would preserve suid/sgid bits, | 1528 | /* chmod'ing AFTER chown would preserve suid/sgid bits, |
1512 | * but GNU sed 4.2.1 does not preserve them either */ | 1529 | * but GNU sed 4.2.1 does not preserve them either */ |
1513 | fchmod(nonstdoutfd, statbuf.st_mode); | 1530 | fchmod(nonstdoutfd, statbuf.st_mode); |
@@ -1518,12 +1535,12 @@ int sed_main(int argc UNUSED_PARAM, char **argv) | |||
1518 | G.nonstdout = stdout; | 1535 | G.nonstdout = stdout; |
1519 | 1536 | ||
1520 | if (opt_i) { | 1537 | if (opt_i) { |
1521 | char *backupname = xasprintf("%s%s", argv[i], opt_i); | 1538 | char *backupname = xasprintf("%s%s", *argv, opt_i); |
1522 | xrename(argv[i], backupname); | 1539 | xrename(*argv, backupname); |
1523 | free(backupname); | 1540 | free(backupname); |
1524 | } | 1541 | } |
1525 | /* else unlink(argv[i]); - rename below does this */ | 1542 | /* else unlink(*argv); - rename below does this */ |
1526 | xrename(G.outname, argv[i]); //TODO: rollback backup on error? | 1543 | xrename(G.outname, *argv); //TODO: rollback backup on error? |
1527 | free(G.outname); | 1544 | free(G.outname); |
1528 | G.outname = NULL; | 1545 | G.outname = NULL; |
1529 | 1546 | ||
@@ -1533,12 +1550,13 @@ int sed_main(int argc UNUSED_PARAM, char **argv) | |||
1533 | } | 1550 | } |
1534 | } | 1551 | } |
1535 | /* Here, to handle "sed 'cmds' nonexistent_file" case we did: | 1552 | /* Here, to handle "sed 'cmds' nonexistent_file" case we did: |
1536 | * if (G.current_input_file >= G.input_file_count) | 1553 | * if (G.current_input_file[G.current_input_file] == NULL) |
1537 | * return status; | 1554 | * return G.exitcode; |
1538 | * but it's not needed since process_files() works correctly | 1555 | * but it's not needed since process_files() works correctly |
1539 | * in this case too. */ | 1556 | * in this case too. */ |
1540 | } | 1557 | } |
1558 | |||
1541 | process_files(); | 1559 | process_files(); |
1542 | 1560 | ||
1543 | return status; | 1561 | return G.exitcode; |
1544 | } | 1562 | } |