diff options
author | Ron Yorston <rmy@pobox.com> | 2021-07-24 10:36:39 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2021-07-24 10:36:39 +0100 |
commit | 79ecbe5abfc8c1c9b6ef49655225d2f8afc9119c (patch) | |
tree | 7210c6fb34af20d7e9d9bd74c6809ee858571a9a | |
parent | 45863925a722c75b6ca4ba055c4a7d54f53d3f95 (diff) | |
parent | 1310d7b1d106d7ab0ec84ce88c12302cca934230 (diff) | |
download | busybox-w32-79ecbe5abfc8c1c9b6ef49655225d2f8afc9119c.tar.gz busybox-w32-79ecbe5abfc8c1c9b6ef49655225d2f8afc9119c.tar.bz2 busybox-w32-79ecbe5abfc8c1c9b6ef49655225d2f8afc9119c.zip |
Merge branch 'busybox' into merge
-rw-r--r-- | coreutils/cut.c | 207 | ||||
-rw-r--r-- | shell/ash.c | 30 | ||||
-rwxr-xr-x | testsuite/cut.tests | 64 |
3 files changed, 201 insertions, 100 deletions
diff --git a/coreutils/cut.c b/coreutils/cut.c index cc3c32576..7009e74cf 100644 --- a/coreutils/cut.c +++ b/coreutils/cut.c | |||
@@ -14,6 +14,13 @@ | |||
14 | //config: help | 14 | //config: help |
15 | //config: cut is used to print selected parts of lines from | 15 | //config: cut is used to print selected parts of lines from |
16 | //config: each file to stdout. | 16 | //config: each file to stdout. |
17 | //config: | ||
18 | //config:config FEATURE_CUT_REGEX | ||
19 | //config: bool "cut -F" | ||
20 | //config: default y | ||
21 | //config: depends on CUT | ||
22 | //config: help | ||
23 | //config: Allow regex based delimiters. | ||
17 | 24 | ||
18 | //applet:IF_CUT(APPLET_NOEXEC(cut, cut, BB_DIR_USR_BIN, BB_SUID_DROP, cut)) | 25 | //applet:IF_CUT(APPLET_NOEXEC(cut, cut, BB_DIR_USR_BIN, BB_SUID_DROP, cut)) |
19 | 26 | ||
@@ -25,9 +32,14 @@ | |||
25 | //usage: "Print selected fields from FILEs to stdout\n" | 32 | //usage: "Print selected fields from FILEs to stdout\n" |
26 | //usage: "\n -b LIST Output only bytes from LIST" | 33 | //usage: "\n -b LIST Output only bytes from LIST" |
27 | //usage: "\n -c LIST Output only characters from LIST" | 34 | //usage: "\n -c LIST Output only characters from LIST" |
28 | //usage: "\n -d CHAR Use CHAR instead of tab as field delimiter" | 35 | //usage: "\n -d SEP Field delimiter for input (default -f TAB, -F run of whitespace)" |
36 | //usage: "\n -O SEP Field delimeter for output (default = -d for -f, one space for -F)" | ||
37 | //usage: "\n -D Don't sort/collate sections or match -fF lines without delimeter" | ||
38 | //usage: "\n -f LIST Print only these fields (-d is single char)" | ||
39 | //usage: IF_FEATURE_CUT_REGEX( | ||
40 | //usage: "\n -F LIST Print only these fields (-d is regex)" | ||
41 | //usage: ) | ||
29 | //usage: "\n -s Output only lines containing delimiter" | 42 | //usage: "\n -s Output only lines containing delimiter" |
30 | //usage: "\n -f LIST Print only these fields" | ||
31 | //usage: "\n -n Ignored" | 43 | //usage: "\n -n Ignored" |
32 | //(manpage:-n with -b: don't split multibyte characters) | 44 | //(manpage:-n with -b: don't split multibyte characters) |
33 | //usage: | 45 | //usage: |
@@ -39,38 +51,49 @@ | |||
39 | 51 | ||
40 | #include "libbb.h" | 52 | #include "libbb.h" |
41 | 53 | ||
54 | #if ENABLE_FEATURE_CUT_REGEX | ||
55 | #include "xregex.h" | ||
56 | #else | ||
57 | #define regex_t int | ||
58 | typedef struct { int rm_eo, rm_so; } regmatch_t; | ||
59 | #define xregcomp(x, ...) *(x) = 0 | ||
60 | #define regexec(...) 0 | ||
61 | #endif | ||
62 | |||
42 | /* This is a NOEXEC applet. Be very careful! */ | 63 | /* This is a NOEXEC applet. Be very careful! */ |
43 | 64 | ||
44 | 65 | ||
45 | /* option vars */ | 66 | /* option vars */ |
46 | #define OPT_STR "b:c:f:d:sn" | 67 | #define OPT_STR "b:c:f:d:O:sD"IF_FEATURE_CUT_REGEX("F:")"n" |
47 | #define CUT_OPT_BYTE_FLGS (1 << 0) | 68 | #define CUT_OPT_BYTE_FLGS (1 << 0) |
48 | #define CUT_OPT_CHAR_FLGS (1 << 1) | 69 | #define CUT_OPT_CHAR_FLGS (1 << 1) |
49 | #define CUT_OPT_FIELDS_FLGS (1 << 2) | 70 | #define CUT_OPT_FIELDS_FLGS (1 << 2) |
50 | #define CUT_OPT_DELIM_FLGS (1 << 3) | 71 | #define CUT_OPT_DELIM_FLGS (1 << 3) |
51 | #define CUT_OPT_SUPPRESS_FLGS (1 << 4) | 72 | #define CUT_OPT_ODELIM_FLGS (1 << 4) |
73 | #define CUT_OPT_SUPPRESS_FLGS (1 << 5) | ||
74 | #define CUT_OPT_NOSORT_FLGS (1 << 6) | ||
75 | #define CUT_OPT_REGEX_FLGS ((1 << 7) * ENABLE_FEATURE_CUT_REGEX) | ||
52 | 76 | ||
53 | struct cut_list { | 77 | struct cut_list { |
54 | int startpos; | 78 | int startpos; |
55 | int endpos; | 79 | int endpos; |
56 | }; | 80 | }; |
57 | 81 | ||
58 | enum { | ||
59 | BOL = 0, | ||
60 | EOL = INT_MAX, | ||
61 | NON_RANGE = -1 | ||
62 | }; | ||
63 | |||
64 | static int cmpfunc(const void *a, const void *b) | 82 | static int cmpfunc(const void *a, const void *b) |
65 | { | 83 | { |
66 | return (((struct cut_list *) a)->startpos - | 84 | return (((struct cut_list *) a)->startpos - |
67 | ((struct cut_list *) b)->startpos); | 85 | ((struct cut_list *) b)->startpos); |
68 | } | 86 | } |
69 | 87 | ||
70 | static void cut_file(FILE *file, char delim, const struct cut_list *cut_lists, unsigned nlists) | 88 | static void cut_file(FILE *file, const char *delim, const char *odelim, |
89 | const struct cut_list *cut_lists, unsigned nlists) | ||
71 | { | 90 | { |
72 | char *line; | 91 | char *line; |
73 | unsigned linenum = 0; /* keep these zero-based to be consistent */ | 92 | unsigned linenum = 0; /* keep these zero-based to be consistent */ |
93 | regex_t reg; | ||
94 | int spos, shoe = option_mask32 & CUT_OPT_REGEX_FLGS; | ||
95 | |||
96 | if (shoe) xregcomp(®, delim, REG_EXTENDED); | ||
74 | 97 | ||
75 | /* go through every line in the file */ | 98 | /* go through every line in the file */ |
76 | while ((line = xmalloc_fgetline(file)) != NULL) { | 99 | while ((line = xmalloc_fgetline(file)) != NULL) { |
@@ -80,29 +103,22 @@ static void cut_file(FILE *file, char delim, const struct cut_list *cut_lists, u | |||
80 | char *printed = xzalloc(linelen + 1); | 103 | char *printed = xzalloc(linelen + 1); |
81 | char *orig_line = line; | 104 | char *orig_line = line; |
82 | unsigned cl_pos = 0; | 105 | unsigned cl_pos = 0; |
83 | int spos; | ||
84 | 106 | ||
85 | /* cut based on chars/bytes XXX: only works when sizeof(char) == byte */ | 107 | /* cut based on chars/bytes XXX: only works when sizeof(char) == byte */ |
86 | if (option_mask32 & (CUT_OPT_CHAR_FLGS | CUT_OPT_BYTE_FLGS)) { | 108 | if (option_mask32 & (CUT_OPT_CHAR_FLGS | CUT_OPT_BYTE_FLGS)) { |
87 | /* print the chars specified in each cut list */ | 109 | /* print the chars specified in each cut list */ |
88 | for (; cl_pos < nlists; cl_pos++) { | 110 | for (; cl_pos < nlists; cl_pos++) { |
89 | spos = cut_lists[cl_pos].startpos; | 111 | for (spos = cut_lists[cl_pos].startpos; spos < linelen;) { |
90 | while (spos < linelen) { | ||
91 | if (!printed[spos]) { | 112 | if (!printed[spos]) { |
92 | printed[spos] = 'X'; | 113 | printed[spos] = 'X'; |
93 | putchar(line[spos]); | 114 | putchar(line[spos]); |
94 | } | 115 | } |
95 | spos++; | 116 | if (++spos > cut_lists[cl_pos].endpos) { |
96 | if (spos > cut_lists[cl_pos].endpos | ||
97 | /* NON_RANGE is -1, so if below is true, | ||
98 | * the above was true too (spos is >= 0) */ | ||
99 | /* || cut_lists[cl_pos].endpos == NON_RANGE */ | ||
100 | ) { | ||
101 | break; | 117 | break; |
102 | } | 118 | } |
103 | } | 119 | } |
104 | } | 120 | } |
105 | } else if (delim == '\n') { /* cut by lines */ | 121 | } else if (*delim == '\n') { /* cut by lines */ |
106 | spos = cut_lists[cl_pos].startpos; | 122 | spos = cut_lists[cl_pos].startpos; |
107 | 123 | ||
108 | /* get out if we have no more lists to process or if the lines | 124 | /* get out if we have no more lists to process or if the lines |
@@ -115,9 +131,7 @@ static void cut_file(FILE *file, char delim, const struct cut_list *cut_lists, u | |||
115 | while (spos < (int)linenum) { | 131 | while (spos < (int)linenum) { |
116 | spos++; | 132 | spos++; |
117 | /* go to the next list if we're at the end of this one */ | 133 | /* go to the next list if we're at the end of this one */ |
118 | if (spos > cut_lists[cl_pos].endpos | 134 | if (spos > cut_lists[cl_pos].endpos) { |
119 | || cut_lists[cl_pos].endpos == NON_RANGE | ||
120 | ) { | ||
121 | cl_pos++; | 135 | cl_pos++; |
122 | /* get out if there's no more lists to process */ | 136 | /* get out if there's no more lists to process */ |
123 | if (cl_pos >= nlists) | 137 | if (cl_pos >= nlists) |
@@ -135,55 +149,56 @@ static void cut_file(FILE *file, char delim, const struct cut_list *cut_lists, u | |||
135 | puts(line); | 149 | puts(line); |
136 | goto next_line; | 150 | goto next_line; |
137 | } else { /* cut by fields */ | 151 | } else { /* cut by fields */ |
138 | int ndelim = -1; /* zero-based / one-based problem */ | 152 | unsigned uu = 0, start = 0, end = 0, out = 0; |
139 | int nfields_printed = 0; | 153 | int dcount = 0; |
140 | char *field = NULL; | 154 | |
141 | char delimiter[2]; | 155 | /* Loop through bytes, finding next delimiter */ |
142 | 156 | for (;;) { | |
143 | delimiter[0] = delim; | 157 | /* End of current range? */ |
144 | delimiter[1] = 0; | 158 | if (end == linelen || dcount > cut_lists[cl_pos].endpos) { |
145 | 159 | if (++cl_pos >= nlists) break; | |
146 | /* does this line contain any delimiters? */ | 160 | if (option_mask32 & CUT_OPT_NOSORT_FLGS) |
147 | if (strchr(line, delim) == NULL) { | 161 | start = dcount = uu = 0; |
148 | if (!(option_mask32 & CUT_OPT_SUPPRESS_FLGS)) | 162 | end = 0; |
149 | puts(line); | 163 | } |
150 | goto next_line; | 164 | /* End of current line? */ |
151 | } | 165 | if (uu == linelen) { |
152 | 166 | /* If we've seen no delimiters, check -s */ | |
153 | /* process each list on this line, for as long as we've got | 167 | if (!cl_pos && !dcount && !shoe) { |
154 | * a line to process */ | 168 | if (option_mask32 & CUT_OPT_SUPPRESS_FLGS) |
155 | for (; cl_pos < nlists && line; cl_pos++) { | 169 | goto next_line; |
156 | spos = cut_lists[cl_pos].startpos; | 170 | } else if (dcount<cut_lists[cl_pos].startpos) |
157 | do { | 171 | start = linelen; |
158 | /* find the field we're looking for */ | 172 | end = linelen; |
159 | while (line && ndelim < spos) { | 173 | } else { |
160 | field = strsep(&line, delimiter); | 174 | /* Find next delimiter */ |
161 | ndelim++; | 175 | if (shoe) { |
162 | } | 176 | regmatch_t rr = {-1, -1}; |
163 | 177 | ||
164 | /* we found it, and it hasn't been printed yet */ | 178 | if (!regexec(®, line+uu, 1, &rr, REG_NOTBOL|REG_NOTEOL)) { |
165 | if (field && ndelim == spos && !printed[ndelim]) { | 179 | end = uu + rr.rm_so; |
166 | /* if this isn't our first time through, we need to | 180 | uu += rr.rm_eo; |
167 | * print the delimiter after the last field that was | 181 | } else { |
168 | * printed */ | 182 | uu = linelen; |
169 | if (nfields_printed > 0) | 183 | continue; |
170 | putchar(delim); | 184 | } |
171 | fputs_stdout(field); | 185 | } else if (line[end = uu++] != *delim) |
172 | printed[ndelim] = 'X'; | 186 | continue; |
173 | nfields_printed++; /* shouldn't overflow.. */ | 187 | |
188 | /* Got delimiter. Loop if not yet within range. */ | ||
189 | if (dcount++ < cut_lists[cl_pos].startpos) { | ||
190 | start = uu; | ||
191 | continue; | ||
174 | } | 192 | } |
175 | 193 | } | |
176 | spos++; | 194 | if (end != start || !shoe) |
177 | 195 | printf("%s%.*s", out++ ? odelim : "", end-start, line + start); | |
178 | /* keep going as long as we have a line to work with, | 196 | start = uu; |
179 | * this is a list, and we're not at the end of that | 197 | if (!dcount) |
180 | * list */ | 198 | break; |
181 | } while (spos <= cut_lists[cl_pos].endpos && line | ||
182 | && cut_lists[cl_pos].endpos != NON_RANGE); | ||
183 | } | 199 | } |
184 | } | 200 | } |
185 | /* if we printed anything at all, we need to finish it with a | 201 | /* if we printed anything, finish with newline */ |
186 | * newline cuz we were handed a chomped line */ | ||
187 | putchar('\n'); | 202 | putchar('\n'); |
188 | next_line: | 203 | next_line: |
189 | linenum++; | 204 | linenum++; |
@@ -198,37 +213,35 @@ int cut_main(int argc UNUSED_PARAM, char **argv) | |||
198 | /* growable array holding a series of lists */ | 213 | /* growable array holding a series of lists */ |
199 | struct cut_list *cut_lists = NULL; | 214 | struct cut_list *cut_lists = NULL; |
200 | unsigned nlists = 0; /* number of elements in above list */ | 215 | unsigned nlists = 0; /* number of elements in above list */ |
201 | char delim = '\t'; /* delimiter, default is tab */ | ||
202 | char *sopt, *ltok; | 216 | char *sopt, *ltok; |
217 | const char *delim = NULL; | ||
218 | const char *odelim = NULL; | ||
203 | unsigned opt; | 219 | unsigned opt; |
204 | 220 | ||
221 | #define ARG "bcf"IF_FEATURE_CUT_REGEX("F") | ||
205 | opt = getopt32(argv, "^" | 222 | opt = getopt32(argv, "^" |
206 | OPT_STR | 223 | OPT_STR // = "b:c:f:d:O:sD"IF_FEATURE_CUT_REGEX("F:")"n" |
207 | "\0" "b--bcf:c--bcf:f--bcf", | 224 | "\0" "b--"ARG":c--"ARG":f--"ARG IF_FEATURE_CUT_REGEX("F--"ARG), |
208 | &sopt, &sopt, &sopt, <ok | 225 | &sopt, &sopt, &sopt, &delim, &odelim IF_FEATURE_CUT_REGEX(, &sopt) |
209 | ); | 226 | ); |
227 | if (!delim || !*delim) | ||
228 | delim = (opt & CUT_OPT_REGEX_FLGS) ? "[[:space:]]+" : "\t"; | ||
229 | if (!odelim) odelim = (opt & CUT_OPT_REGEX_FLGS) ? " " : delim; | ||
230 | |||
210 | // argc -= optind; | 231 | // argc -= optind; |
211 | argv += optind; | 232 | argv += optind; |
212 | if (!(opt & (CUT_OPT_BYTE_FLGS | CUT_OPT_CHAR_FLGS | CUT_OPT_FIELDS_FLGS))) | 233 | if (!(opt & (CUT_OPT_BYTE_FLGS | CUT_OPT_CHAR_FLGS | CUT_OPT_FIELDS_FLGS | CUT_OPT_REGEX_FLGS))) |
213 | bb_simple_error_msg_and_die("expected a list of bytes, characters, or fields"); | 234 | bb_simple_error_msg_and_die("expected a list of bytes, characters, or fields"); |
214 | 235 | ||
215 | if (opt & CUT_OPT_DELIM_FLGS) { | ||
216 | if (ltok[0] && ltok[1]) { /* more than 1 char? */ | ||
217 | bb_simple_error_msg_and_die("the delimiter must be a single character"); | ||
218 | } | ||
219 | delim = ltok[0]; | ||
220 | } | ||
221 | |||
222 | /* non-field (char or byte) cutting has some special handling */ | 236 | /* non-field (char or byte) cutting has some special handling */ |
223 | if (!(opt & CUT_OPT_FIELDS_FLGS)) { | 237 | if (!(opt & (CUT_OPT_FIELDS_FLGS|CUT_OPT_REGEX_FLGS))) { |
224 | static const char _op_on_field[] ALIGN1 = " only when operating on fields"; | 238 | static const char _op_on_field[] ALIGN1 = " only when operating on fields"; |
225 | 239 | ||
226 | if (opt & CUT_OPT_SUPPRESS_FLGS) { | 240 | if (opt & CUT_OPT_SUPPRESS_FLGS) { |
227 | bb_error_msg_and_die | 241 | bb_error_msg_and_die |
228 | ("suppressing non-delimited lines makes sense%s", | 242 | ("suppressing non-delimited lines makes sense%s", _op_on_field); |
229 | _op_on_field); | ||
230 | } | 243 | } |
231 | if (delim != '\t') { | 244 | if (opt & CUT_OPT_DELIM_FLGS) { |
232 | bb_error_msg_and_die | 245 | bb_error_msg_and_die |
233 | ("a delimiter may be specified%s", _op_on_field); | 246 | ("a delimiter may be specified%s", _op_on_field); |
234 | } | 247 | } |
@@ -253,7 +266,7 @@ int cut_main(int argc UNUSED_PARAM, char **argv) | |||
253 | /* get the start pos */ | 266 | /* get the start pos */ |
254 | ntok = strsep(<ok, "-"); | 267 | ntok = strsep(<ok, "-"); |
255 | if (!ntok[0]) { | 268 | if (!ntok[0]) { |
256 | s = BOL; | 269 | s = 0; |
257 | } else { | 270 | } else { |
258 | s = xatoi_positive(ntok); | 271 | s = xatoi_positive(ntok); |
259 | /* account for the fact that arrays are zero based, while | 272 | /* account for the fact that arrays are zero based, while |
@@ -264,24 +277,23 @@ int cut_main(int argc UNUSED_PARAM, char **argv) | |||
264 | 277 | ||
265 | /* get the end pos */ | 278 | /* get the end pos */ |
266 | if (ltok == NULL) { | 279 | if (ltok == NULL) { |
267 | e = NON_RANGE; | 280 | e = s; |
268 | } else if (!ltok[0]) { | 281 | } else if (!ltok[0]) { |
269 | e = EOL; | 282 | e = INT_MAX; |
270 | } else { | 283 | } else { |
271 | e = xatoi_positive(ltok); | 284 | e = xatoi_positive(ltok); |
272 | /* if the user specified and end position of 0, | 285 | /* if the user specified and end position of 0, |
273 | * that means "til the end of the line" */ | 286 | * that means "til the end of the line" */ |
274 | if (e == 0) | 287 | if (!*ltok) |
275 | e = EOL; | 288 | e = INT_MAX; |
289 | else if (e < s) | ||
290 | bb_error_msg_and_die("%d<%d", e, s); | ||
276 | e--; /* again, arrays are zero based, lines are 1 based */ | 291 | e--; /* again, arrays are zero based, lines are 1 based */ |
277 | if (e == s) | ||
278 | e = NON_RANGE; | ||
279 | } | 292 | } |
280 | 293 | ||
281 | /* add the new list */ | 294 | /* add the new list */ |
282 | cut_lists = xrealloc_vector(cut_lists, 4, nlists); | 295 | cut_lists = xrealloc_vector(cut_lists, 4, nlists); |
283 | /* NB: startpos is always >= 0, | 296 | /* NB: startpos is always >= 0 */ |
284 | * while endpos may be = NON_RANGE (-1) */ | ||
285 | cut_lists[nlists].startpos = s; | 297 | cut_lists[nlists].startpos = s; |
286 | cut_lists[nlists].endpos = e; | 298 | cut_lists[nlists].endpos = e; |
287 | nlists++; | 299 | nlists++; |
@@ -294,7 +306,8 @@ int cut_main(int argc UNUSED_PARAM, char **argv) | |||
294 | /* now that the lists are parsed, we need to sort them to make life | 306 | /* now that the lists are parsed, we need to sort them to make life |
295 | * easier on us when it comes time to print the chars / fields / lines | 307 | * easier on us when it comes time to print the chars / fields / lines |
296 | */ | 308 | */ |
297 | qsort(cut_lists, nlists, sizeof(cut_lists[0]), cmpfunc); | 309 | if (!(opt & CUT_OPT_NOSORT_FLGS)) |
310 | qsort(cut_lists, nlists, sizeof(cut_lists[0]), cmpfunc); | ||
298 | } | 311 | } |
299 | 312 | ||
300 | { | 313 | { |
@@ -309,7 +322,7 @@ int cut_main(int argc UNUSED_PARAM, char **argv) | |||
309 | retval = EXIT_FAILURE; | 322 | retval = EXIT_FAILURE; |
310 | continue; | 323 | continue; |
311 | } | 324 | } |
312 | cut_file(file, delim, cut_lists, nlists); | 325 | cut_file(file, delim, odelim, cut_lists, nlists); |
313 | fclose_if_not_stdin(file); | 326 | fclose_if_not_stdin(file); |
314 | } while (*++argv); | 327 | } while (*++argv); |
315 | 328 | ||
diff --git a/shell/ash.c b/shell/ash.c index 8b8c5f1dc..3e02a4e1f 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -7538,7 +7538,8 @@ scanright(char *startp, char *rmesc, char *rmescend, | |||
7538 | * Logic: | 7538 | * Logic: |
7539 | * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc, | 7539 | * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc, |
7540 | * and on each iteration they go back two/one char until they reach the beginning. | 7540 | * and on each iteration they go back two/one char until they reach the beginning. |
7541 | * We try to find a match in "raw_value_of_v", "raw_value_of_", "raw_value_of" etc. | 7541 | * We try to match "raw_value_of_v", "raw_value_of_", "raw_value_of" etc. |
7542 | * If one of these matches, return pointer past last matched char in startp. | ||
7542 | */ | 7543 | */ |
7543 | /* TODO: document in what other circumstances we are called. */ | 7544 | /* TODO: document in what other circumstances we are called. */ |
7544 | 7545 | ||
@@ -7826,6 +7827,7 @@ subevalvar(char *start, char *str, int strloc, | |||
7826 | #if BASH_PATTERN_SUBST | 7827 | #if BASH_PATTERN_SUBST |
7827 | workloc = expdest - (char *)stackblock(); | 7828 | workloc = expdest - (char *)stackblock(); |
7828 | if (subtype == VSREPLACE || subtype == VSREPLACEALL) { | 7829 | if (subtype == VSREPLACE || subtype == VSREPLACEALL) { |
7830 | size_t no_meta_len; | ||
7829 | int len; | 7831 | int len; |
7830 | char *idx, *end; | 7832 | char *idx, *end; |
7831 | 7833 | ||
@@ -7843,16 +7845,38 @@ subevalvar(char *start, char *str, int strloc, | |||
7843 | if (str[0] == '\0') | 7845 | if (str[0] == '\0') |
7844 | goto out1; | 7846 | goto out1; |
7845 | 7847 | ||
7848 | no_meta_len = (ENABLE_ASH_OPTIMIZE_FOR_SIZE || strpbrk(str, "*?[\\")) ? 0 : strlen(str); | ||
7846 | len = 0; | 7849 | len = 0; |
7847 | idx = startp; | 7850 | idx = startp; |
7848 | end = str - 1; | 7851 | end = str - 1; |
7849 | while (idx <= end) { | 7852 | while (idx <= end) { |
7850 | try_to_match: | 7853 | try_to_match: |
7851 | loc = scanright(idx, rmesc, rmescend, str, quotes, 1); | 7854 | if (no_meta_len == 0) { |
7855 | /* pattern has meta chars, have to glob; or ENABLE_ASH_OPTIMIZE_FOR_SIZE */ | ||
7856 | loc = scanright(idx, rmesc, rmescend, str, quotes, /*match_at_start:*/ 1); | ||
7857 | } else { | ||
7858 | /* Testcase for very slow replace (performs about 22k replaces): | ||
7859 | * x=:::::::::::::::::::::: | ||
7860 | * x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;echo ${#x} | ||
7861 | * echo "${x//:/|}" | ||
7862 | */ | ||
7863 | size_t n; | ||
7864 | if (strncmp(rmesc, str, no_meta_len) != 0) | ||
7865 | goto no_match; | ||
7866 | n = no_meta_len; | ||
7867 | loc = idx; | ||
7868 | do { | ||
7869 | if (quotes && (unsigned char)*loc == CTLESC) | ||
7870 | loc++; | ||
7871 | loc++; | ||
7872 | } while (--n != 0); | ||
7873 | } | ||
7852 | //bb_error_msg("scanright('%s'):'%s'", str, loc); | 7874 | //bb_error_msg("scanright('%s'):'%s'", str, loc); |
7853 | if (!loc) { | 7875 | if (!loc) { |
7876 | char *restart_detect; | ||
7877 | no_match: | ||
7854 | /* No match, advance */ | 7878 | /* No match, advance */ |
7855 | char *restart_detect = stackblock(); | 7879 | restart_detect = stackblock(); |
7856 | skip_matching: | 7880 | skip_matching: |
7857 | if (idx >= end) | 7881 | if (idx >= end) |
7858 | break; | 7882 | break; |
diff --git a/testsuite/cut.tests b/testsuite/cut.tests index 110340277..d705b91c3 100755 --- a/testsuite/cut.tests +++ b/testsuite/cut.tests | |||
@@ -15,4 +15,68 @@ testing "cut '-' (stdin) and multi file handling" \ | |||
15 | "the quick brown fox\n" \ | 15 | "the quick brown fox\n" \ |
16 | "jumps over the lazy dog\n" \ | 16 | "jumps over the lazy dog\n" \ |
17 | 17 | ||
18 | abc="\ | ||
19 | one:two:three:four:five:six:seven | ||
20 | alpha:beta:gamma:delta:epsilon:zeta:eta:theta:iota:kappa:lambda:mu | ||
21 | the quick brown fox jumps over the lazy dog | ||
22 | " | ||
23 | |||
24 | testing "cut -b a,a,a" "cut -b 3,3,3 input" "e\np\ne\n" "$abc" "" | ||
25 | |||
26 | testing "cut -b overlaps" "cut -b 1-3,2-5,7-9,9-10 input" \ | ||
27 | "one:to:th\nalphabeta\nthe qick \n" "$abc" "" | ||
28 | testing "-b encapsulated" "cut -b 3-8,4-6 input" "e:two:\npha:be\ne quic\n" \ | ||
29 | "$abc" "" | ||
30 | # --output-delimiter not implemnted (yet?) | ||
31 | #testing "cut -bO overlaps" \ | ||
32 | # "cut --output-delimiter ' ' -b 1-3,2-5,7-9,9-10 input" \ | ||
33 | # "one:t o:th\nalpha beta\nthe q ick \n" "$abc" "" | ||
34 | testing "cut high-low error" "cut -b 8-3 abc.txt 2>/dev/null || echo err" "err\n" \ | ||
35 | "$abc" "" | ||
36 | |||
37 | testing "cut -c a-b" "cut -c 4-10 input" ":two:th\nha:beta\n quick \n" "$abc" "" | ||
38 | testing "cut -c a-" "cut -c 41- input" "\ntheta:iota:kappa:lambda:mu\ndog\n" "$abc" "" | ||
39 | testing "cut -c -b" "cut -c -39 input" \ | ||
40 | "one:two:three:four:five:six:seven\nalpha:beta:gamma:delta:epsilon:zeta:eta\nthe quick brown fox jumps over the lazy\n" \ | ||
41 | "$abc" "" | ||
42 | testing "cut -c a" "cut -c 40 input" "\n:\n \n" "$abc" "" | ||
43 | testing "cut -c a,b-c,d" "cut -c 3,5-7,10 input" "etwoh\npa:ba\nequi \n" "$abc" "" | ||
44 | |||
45 | testing "cut -f a-" "cut -d ':' -f 5- input" "five:six:seven\nepsilon:zeta:eta:theta:iota:kappa:lambda:mu\nthe quick brown fox jumps over the lazy dog\n" "$abc" "" | ||
46 | |||
47 | testing "cut show whole line with no delim" "cut -d ' ' -f 3 input" \ | ||
48 | "one:two:three:four:five:six:seven\nalpha:beta:gamma:delta:epsilon:zeta:eta:theta:iota:kappa:lambda:mu\nbrown\n" "$abc" "" | ||
49 | |||
50 | testing "cut with echo, -c (a-b)" "echo 'ref_categorie=test' | cut -c 1-15 " "ref_categorie=t\n" "" "" | ||
51 | testing "cut with echo, -c (a)" "echo 'ref_categorie=test' | cut -c 14" "=\n" "" "" | ||
52 | |||
53 | testing "cut with -c (a,b,c)" "cut -c 4,5,20 input" "det\n" "abcdefghijklmnopqrstuvwxyz" "" | ||
54 | |||
55 | testing "cut with -b (a,b,c)" "cut -b 4,5,20 input" "det\n" "abcdefghijklmnopqrstuvwxyz" "" | ||
56 | |||
57 | input="\ | ||
58 | 406378:Sales:Itorre:Jan | ||
59 | 031762:Marketing:Nasium:Jim | ||
60 | 636496:Research:Ancholie:Mel | ||
61 | 396082:Sales:Jucacion:Ed | ||
62 | " | ||
63 | testing "cut with -d -f(:) -s" "cut -d: -f3 -s input" "Itorre\nNasium\nAncholie\nJucacion\n" "$input" "" | ||
64 | testing "cut with -d -f( ) -s" "cut -d' ' -f3 -s input && echo yes" "yes\n" "$input" "" | ||
65 | testing "cut with -d -f(a) -s" "cut -da -f3 -s input" "n\nsium:Jim\n\ncion:Ed\n" "$input" "" | ||
66 | testing "cut with -d -f(a) -s -n" "cut -da -f3 -s -n input" "n\nsium:Jim\n\ncion:Ed\n" "$input" "" | ||
67 | |||
68 | # substitute for awk | ||
69 | testing "cut -DF" "cut -DF 2,7,5" \ | ||
70 | "said and your\nare\nis demand. supply\nforecast :\nyou you better,\n\nEm: Took hate\n" "" \ | ||
71 | "Bother, said Pooh. It's your husband, and he has a gun. | ||
72 | Cheerios are donut seeds. | ||
73 | Talk is cheap because supply exceeds demand. | ||
74 | Weather forecast for tonight : dark. | ||
75 | Apple: you can buy better, but you can't pay more. | ||
76 | Subcalifragilisticexpialidocious. | ||
77 | Auntie Em: Hate you, hate Kansas. Took the dog. Dorothy." | ||
78 | |||
79 | testing "cut empty field" "cut -d ':' -f 1-3" "a::b\n" "" "a::b\n" | ||
80 | testing "cut empty field 2" "cut -d ':' -f 3-5" "b::c\n" "" "a::b::c:d\n" | ||
81 | |||
18 | exit $FAILCOUNT | 82 | exit $FAILCOUNT |