diff options
author | Ron Yorston <rmy@pobox.com> | 2024-12-30 14:45:15 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2024-12-30 14:45:15 +0000 |
commit | 370ce52cca8e7e8a341d5e238a79fd67f6048bb6 (patch) | |
tree | 48c78d906a4467bb618946d96574a5bc21cacfba | |
parent | dc3ee8e0523a4f651f3012ae1f5a181548187709 (diff) | |
parent | 14f57f5357cb674b88e7cdaff6267bf9d84c6b80 (diff) | |
download | busybox-w32-370ce52cca8e7e8a341d5e238a79fd67f6048bb6.tar.gz busybox-w32-370ce52cca8e7e8a341d5e238a79fd67f6048bb6.tar.bz2 busybox-w32-370ce52cca8e7e8a341d5e238a79fd67f6048bb6.zip |
Merge branch 'busybox' into mergemerge
-rw-r--r-- | coreutils/cut.c | 455 | ||||
-rw-r--r-- | libbb/dump.c | 6 | ||||
-rw-r--r-- | libbb/getopt32.c | 17 | ||||
-rw-r--r-- | runit/chpst.c | 3 | ||||
-rw-r--r-- | shell/hush.c | 3 | ||||
-rwxr-xr-x | testsuite/cut.tests | 140 | ||||
-rwxr-xr-x | testsuite/hexdump.tests | 6 |
7 files changed, 422 insertions, 208 deletions
diff --git a/coreutils/cut.c b/coreutils/cut.c index f7b501a46..d81f36bcd 100644 --- a/coreutils/cut.c +++ b/coreutils/cut.c | |||
@@ -27,21 +27,34 @@ | |||
27 | //kbuild:lib-$(CONFIG_CUT) += cut.o | 27 | //kbuild:lib-$(CONFIG_CUT) += cut.o |
28 | 28 | ||
29 | //usage:#define cut_trivial_usage | 29 | //usage:#define cut_trivial_usage |
30 | //usage: "[OPTIONS] [FILE]..." | 30 | //usage: "{-b|c LIST | -f"IF_FEATURE_CUT_REGEX("|F")" LIST [-d SEP] [-s]} [-D] [-O SEP] [FILE]..." |
31 | // --output-delimiter SEP is too long to fit into 80 char-wide help ----------------^^^^^^^^ | ||
31 | //usage:#define cut_full_usage "\n\n" | 32 | //usage:#define cut_full_usage "\n\n" |
32 | //usage: "Print selected fields from FILEs to stdout\n" | 33 | //usage: "Print selected fields from FILEs to stdout\n" |
33 | //usage: "\n -b LIST Output only bytes from LIST" | 34 | //usage: "\n -b LIST Output only bytes from LIST" |
34 | //usage: "\n -c LIST Output only characters from LIST" | 35 | //usage: "\n -c LIST Output only characters from LIST" |
35 | //usage: "\n -d SEP Field delimiter for input (default -f TAB, -F run of whitespace)" | 36 | //usage: IF_FEATURE_CUT_REGEX( |
36 | //usage: "\n -O SEP Field delimeter for output (default = -d for -f, one space for -F)" | 37 | //usage: "\n -d SEP Input field delimiter (default -f TAB, -F run of whitespace)" |
37 | //usage: "\n -D Don't sort/collate sections or match -fF lines without delimeter" | 38 | //usage: ) IF_NOT_FEATURE_CUT_REGEX( |
39 | //usage: "\n -d SEP Input field delimiter (default TAB)" | ||
40 | //usage: ) | ||
38 | //usage: "\n -f LIST Print only these fields (-d is single char)" | 41 | //usage: "\n -f LIST Print only these fields (-d is single char)" |
39 | //usage: IF_FEATURE_CUT_REGEX( | 42 | //usage: IF_FEATURE_CUT_REGEX( |
40 | //usage: "\n -F LIST Print only these fields (-d is regex)" | 43 | //usage: "\n -F LIST Print only these fields (-d is regex)" |
41 | //usage: ) | 44 | //usage: ) |
42 | //usage: "\n -s Output only lines containing delimiter" | 45 | //usage: "\n -s Drop lines with no delimiter (else print them in full)" |
46 | //usage: "\n -D Don't sort ranges; line without delimiters has one field" | ||
47 | //usage: IF_LONG_OPTS( | ||
48 | //usage: "\n --output-delimiter SEP Output field delimeter" | ||
49 | //usage: ) IF_NOT_LONG_OPTS( | ||
50 | //usage: IF_FEATURE_CUT_REGEX( | ||
51 | //usage: "\n -O SEP Output field delimeter (default = -d for -f, one space for -F)" | ||
52 | //usage: ) IF_NOT_FEATURE_CUT_REGEX( | ||
53 | //usage: "\n -O SEP Output field delimeter (default = -d)" | ||
54 | //usage: ) | ||
55 | //usage: ) | ||
43 | //usage: "\n -n Ignored" | 56 | //usage: "\n -n Ignored" |
44 | //(manpage:-n with -b: don't split multibyte characters) | 57 | //(manpage:-n with -b: don't split multibyte characters) |
45 | //usage: | 58 | //usage: |
46 | //usage:#define cut_example_usage | 59 | //usage:#define cut_example_usage |
47 | //usage: "$ echo \"Hello world\" | cut -f 1 -d ' '\n" | 60 | //usage: "$ echo \"Hello world\" | cut -f 1 -d ' '\n" |
@@ -53,11 +66,6 @@ | |||
53 | 66 | ||
54 | #if ENABLE_FEATURE_CUT_REGEX | 67 | #if ENABLE_FEATURE_CUT_REGEX |
55 | #include "xregex.h" | 68 | #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 | 69 | #endif |
62 | 70 | ||
63 | /* This is a NOEXEC applet. Be very careful! */ | 71 | /* This is a NOEXEC applet. Be very careful! */ |
@@ -65,265 +73,346 @@ typedef struct { int rm_eo, rm_so; } regmatch_t; | |||
65 | 73 | ||
66 | /* option vars */ | 74 | /* option vars */ |
67 | #define OPT_STR "b:c:f:d:O:sD"IF_FEATURE_CUT_REGEX("F:")"n" | 75 | #define OPT_STR "b:c:f:d:O:sD"IF_FEATURE_CUT_REGEX("F:")"n" |
68 | #define CUT_OPT_BYTE_FLGS (1 << 0) | 76 | #define OPT_BYTE (1 << 0) |
69 | #define CUT_OPT_CHAR_FLGS (1 << 1) | 77 | #define OPT_CHAR (1 << 1) |
70 | #define CUT_OPT_FIELDS_FLGS (1 << 2) | 78 | #define OPT_FIELDS (1 << 2) |
71 | #define CUT_OPT_DELIM_FLGS (1 << 3) | 79 | #define OPT_DELIM (1 << 3) |
72 | #define CUT_OPT_ODELIM_FLGS (1 << 4) | 80 | #define OPT_ODELIM (1 << 4) |
73 | #define CUT_OPT_SUPPRESS_FLGS (1 << 5) | 81 | #define OPT_SUPPRESS (1 << 5) |
74 | #define CUT_OPT_NOSORT_FLGS (1 << 6) | 82 | #define OPT_NOSORT (1 << 6) |
75 | #define CUT_OPT_REGEX_FLGS ((1 << 7) * ENABLE_FEATURE_CUT_REGEX) | 83 | #define OPT_REGEX ((1 << 7) * ENABLE_FEATURE_CUT_REGEX) |
76 | 84 | ||
77 | struct cut_list { | 85 | #define opt_REGEX (option_mask32 & OPT_REGEX) |
78 | int startpos; | 86 | |
79 | int endpos; | 87 | struct cut_range { |
88 | unsigned startpos; | ||
89 | unsigned endpos; | ||
80 | }; | 90 | }; |
81 | 91 | ||
82 | static int cmpfunc(const void *a, const void *b) | 92 | static int cmpfunc(const void *a, const void *b) |
83 | { | 93 | { |
84 | return (((struct cut_list *) a)->startpos - | 94 | const struct cut_range *aa = a; |
85 | ((struct cut_list *) b)->startpos); | 95 | const struct cut_range *bb = b; |
96 | return aa->startpos - bb->startpos; | ||
86 | } | 97 | } |
87 | 98 | ||
99 | #define END_OF_LIST(list_elem) ((list_elem).startpos == UINT_MAX) | ||
100 | #define NOT_END_OF_LIST(list_elem) ((list_elem).startpos != UINT_MAX) | ||
101 | |||
88 | static void cut_file(FILE *file, const char *delim, const char *odelim, | 102 | static void cut_file(FILE *file, const char *delim, const char *odelim, |
89 | const struct cut_list *cut_lists, unsigned nlists) | 103 | const struct cut_range *cut_list) |
90 | { | 104 | { |
91 | char *line; | 105 | char *line; |
92 | unsigned linenum = 0; /* keep these zero-based to be consistent */ | 106 | unsigned linenum = 0; /* keep these zero-based to be consistent */ |
93 | regex_t reg; | 107 | int first_print = 1; |
94 | int spos, shoe = option_mask32 & CUT_OPT_REGEX_FLGS; | ||
95 | |||
96 | if (shoe) xregcomp(®, delim, REG_EXTENDED); | ||
97 | 108 | ||
98 | /* go through every line in the file */ | 109 | /* go through every line in the file */ |
99 | while ((line = xmalloc_fgetline(file)) != NULL) { | 110 | while ((line = xmalloc_fgetline(file)) != NULL) { |
100 | 111 | ||
101 | /* set up a list so we can keep track of what's been printed */ | 112 | /* set up a list so we can keep track of what's been printed */ |
102 | int linelen = strlen(line); | 113 | unsigned linelen = strlen(line); |
103 | char *printed = xzalloc(linelen + 1); | ||
104 | char *orig_line = line; | ||
105 | unsigned cl_pos = 0; | 114 | unsigned cl_pos = 0; |
106 | 115 | ||
107 | /* cut based on chars/bytes XXX: only works when sizeof(char) == byte */ | 116 | /* Cut based on chars/bytes XXX: only works when sizeof(char) == byte */ |
108 | if (option_mask32 & (CUT_OPT_CHAR_FLGS | CUT_OPT_BYTE_FLGS)) { | 117 | if (option_mask32 & (OPT_CHAR | OPT_BYTE)) { |
118 | char *printed = xzalloc(linelen + 1); | ||
119 | int need_odelim = 0; | ||
120 | |||
109 | /* print the chars specified in each cut list */ | 121 | /* print the chars specified in each cut list */ |
110 | for (; cl_pos < nlists; cl_pos++) { | 122 | for (; NOT_END_OF_LIST(cut_list[cl_pos]); cl_pos++) { |
111 | for (spos = cut_lists[cl_pos].startpos; spos < linelen;) { | 123 | unsigned spos = cut_list[cl_pos].startpos; |
124 | while (spos < linelen) { | ||
112 | if (!printed[spos]) { | 125 | if (!printed[spos]) { |
113 | printed[spos] = 'X'; | 126 | printed[spos] = 'X'; |
127 | if (need_odelim && spos != 0 && !printed[spos-1]) { | ||
128 | need_odelim = 0; | ||
129 | fputs_stdout(odelim); | ||
130 | } | ||
114 | putchar(line[spos]); | 131 | putchar(line[spos]); |
115 | } | 132 | } |
116 | if (++spos > cut_lists[cl_pos].endpos) { | 133 | spos++; |
134 | if (spos > cut_list[cl_pos].endpos) { | ||
135 | /* will print OSEP (if not empty) */ | ||
136 | need_odelim = (odelim && odelim[0]); | ||
117 | break; | 137 | break; |
118 | } | 138 | } |
119 | } | 139 | } |
120 | } | 140 | } |
121 | } else if (*delim == '\n') { /* cut by lines */ | 141 | free(printed); |
122 | spos = cut_lists[cl_pos].startpos; | 142 | /* Cut by lines */ |
143 | } else if (!opt_REGEX && *delim == '\n') { | ||
144 | unsigned spos = cut_list[cl_pos].startpos; | ||
123 | 145 | ||
124 | /* get out if we have no more lists to process or if the lines | 146 | linenum++; |
147 | /* get out if we have no more ranges to process or if the lines | ||
125 | * are lower than what we're interested in */ | 148 | * are lower than what we're interested in */ |
126 | if (((int)linenum < spos) || (cl_pos >= nlists)) | 149 | if (linenum <= spos || END_OF_LIST(cut_list[cl_pos])) |
127 | goto next_line; | 150 | goto next_line; |
128 | 151 | ||
129 | /* if the line we're looking for is lower than the one we were | 152 | /* if the line we're looking for is lower than the one we were |
130 | * passed, it means we displayed it already, so move on */ | 153 | * passed, it means we displayed it already, so move on */ |
131 | while (spos < (int)linenum) { | 154 | while (++spos < linenum) { |
132 | spos++; | ||
133 | /* go to the next list if we're at the end of this one */ | 155 | /* go to the next list if we're at the end of this one */ |
134 | if (spos > cut_lists[cl_pos].endpos) { | 156 | if (spos > cut_list[cl_pos].endpos) { |
135 | cl_pos++; | 157 | cl_pos++; |
136 | /* get out if there's no more lists to process */ | 158 | /* get out if there's no more ranges to process */ |
137 | if (cl_pos >= nlists) | 159 | if (END_OF_LIST(cut_list[cl_pos])) |
138 | goto next_line; | 160 | goto next_line; |
139 | spos = cut_lists[cl_pos].startpos; | 161 | spos = cut_list[cl_pos].startpos; |
140 | /* get out if the current line is lower than the one | 162 | /* get out if the current line is lower than the one |
141 | * we just became interested in */ | 163 | * we just became interested in */ |
142 | if ((int)linenum < spos) | 164 | if (linenum <= spos) |
143 | goto next_line; | 165 | goto next_line; |
144 | } | 166 | } |
145 | } | 167 | } |
146 | 168 | ||
147 | /* If we made it here, it means we've found the line we're | 169 | /* If we made it here, it means we've found the line we're |
148 | * looking for, so print it */ | 170 | * looking for, so print it */ |
149 | puts(line); | 171 | if (first_print) { |
172 | first_print = 0; | ||
173 | fputs_stdout(line); | ||
174 | } else | ||
175 | printf("%s%s", odelim, line); | ||
150 | goto next_line; | 176 | goto next_line; |
151 | } else { /* cut by fields */ | 177 | /* Cut by fields */ |
152 | unsigned uu = 0, start = 0, end = 0, out = 0; | 178 | } else { |
153 | int dcount = 0; | 179 | unsigned next = 0, start = 0, end = 0; |
180 | unsigned dcount = 0; /* we saw Nth delimiter (0 - didn't see any yet) */ | ||
181 | |||
182 | /* Blank line? Check -s (later check for -s does not catch empty lines) */ | ||
183 | if (linelen == 0) { | ||
184 | if (option_mask32 & OPT_SUPPRESS) | ||
185 | goto next_line; | ||
186 | } | ||
187 | |||
188 | if (!odelim) | ||
189 | odelim = "\t"; | ||
190 | first_print = 1; | ||
154 | 191 | ||
155 | #if ENABLE_PLATFORM_MINGW32 | ||
156 | /* An empty line can't contain a delimiter */ | ||
157 | if (linelen == 0 && (option_mask32 & CUT_OPT_SUPPRESS_FLGS)) | ||
158 | goto next_line; | ||
159 | #endif | ||
160 | /* Loop through bytes, finding next delimiter */ | 192 | /* Loop through bytes, finding next delimiter */ |
161 | for (;;) { | 193 | for (;;) { |
162 | /* End of current range? */ | 194 | /* End of current range? */ |
163 | if (end == linelen || dcount > cut_lists[cl_pos].endpos) { | 195 | if (end == linelen || dcount > cut_list[cl_pos].endpos) { |
164 | if (++cl_pos >= nlists) break; | 196 | end_of_range: |
165 | if (option_mask32 & CUT_OPT_NOSORT_FLGS) | 197 | cl_pos++; |
166 | start = dcount = uu = 0; | 198 | if (END_OF_LIST(cut_list[cl_pos])) |
167 | end = 0; | 199 | break; |
200 | if (option_mask32 & OPT_NOSORT) | ||
201 | start = dcount = next = 0; | ||
202 | end = 0; /* (why?) */ | ||
203 | //bb_error_msg("End of current range"); | ||
168 | } | 204 | } |
169 | /* End of current line? */ | 205 | /* End of current line? */ |
170 | if (uu == linelen) { | 206 | if (next == linelen) { |
171 | /* If we've seen no delimiters, check -s */ | 207 | end = linelen; /* print up to end */ |
172 | if (!cl_pos && !dcount && !shoe) { | 208 | /* If we've seen no delimiters, and no -D, check -s */ |
173 | if (option_mask32 & CUT_OPT_SUPPRESS_FLGS) | 209 | if (!(option_mask32 & OPT_NOSORT) && cl_pos == 0 && dcount == 0) { |
210 | if (option_mask32 & OPT_SUPPRESS) | ||
174 | goto next_line; | 211 | goto next_line; |
175 | } else if (dcount < cut_lists[cl_pos].startpos) | 212 | /* else: will print entire line */ |
176 | start = linelen; | 213 | } else if (dcount < cut_list[cl_pos].startpos) { |
177 | end = linelen; | 214 | /* echo 1.2 | cut -d. -f1,3: prints "1", not "1." */ |
215 | //break; | ||
216 | /* ^^^ this fails a case with -D: | ||
217 | * echo 1 2 | cut -DF 1,3,2: | ||
218 | * do not end line processing when didn't find field#3 | ||
219 | */ | ||
220 | //if (option_mask32 & OPT_NOSORT) - no, just do it always | ||
221 | goto end_of_range; | ||
222 | } | ||
223 | //bb_error_msg("End of current line: s:%d e:%d", start, end); | ||
178 | } else { | 224 | } else { |
179 | /* Find next delimiter */ | 225 | /* Find next delimiter */ |
180 | if (shoe) { | 226 | #if ENABLE_FEATURE_CUT_REGEX |
181 | regmatch_t rr = {-1, -1}; | 227 | if (opt_REGEX) { |
182 | 228 | regmatch_t rr; | |
183 | if (!regexec(®, line+uu, 1, &rr, REG_NOTBOL|REG_NOTEOL)) { | 229 | regex_t *reg = (void*) delim; |
184 | end = uu + rr.rm_so; | 230 | |
185 | uu += rr.rm_eo; | 231 | if (regexec(reg, line + next, 1, &rr, REG_NOTBOL|REG_NOTEOL) != 0) { |
186 | } else { | 232 | /* not found, go to "end of line" logic */ |
187 | uu = linelen; | 233 | next = linelen; |
188 | continue; | 234 | continue; |
189 | } | 235 | } |
190 | } else if (line[end = uu++] != *delim) | 236 | end = next + rr.rm_so; |
191 | continue; | 237 | next += (rr.rm_eo ? rr.rm_eo : 1); |
192 | 238 | /* ^^^ advancing by at least 1 prevents infinite loops */ | |
193 | /* Got delimiter. Loop if not yet within range. */ | 239 | /* testcase: echo "no at sign" | cut -d'@*' -F 1- */ |
194 | if (dcount++ < cut_lists[cl_pos].startpos) { | 240 | } else |
195 | start = uu; | 241 | #endif |
242 | { | ||
243 | end = next++; | ||
244 | if (line[end] != *delim) | ||
245 | continue; | ||
246 | } | ||
247 | /* Got delimiter */ | ||
248 | dcount++; | ||
249 | if (dcount <= cut_list[cl_pos].startpos) { | ||
250 | /* Not yet within range - loop */ | ||
251 | start = next; | ||
196 | continue; | 252 | continue; |
197 | } | 253 | } |
254 | /* -F N-M preserves intermediate delimiters: */ | ||
255 | //printf "1 2 3 4 5 6 7\n" | toybox cut -O: -F2,4-6,7 | ||
256 | //2:4 5 6:7 | ||
257 | if (opt_REGEX && dcount <= cut_list[cl_pos].endpos) | ||
258 | continue; | ||
259 | // NB: toybox does the above for -f too, but it's a compatibility bug: | ||
260 | //printf "1 2 3 4 5 6 7 8\n" | toybox cut -d' ' -O: -f2,4-6,7 | ||
261 | //2:4 5 6:7 // WRONG! | ||
262 | //printf "1 2 3 4 5 6 7 8\n" | cut -d' ' --output-delimiter=: -f2,4-6,7 | ||
263 | //2:4:5:6:7 // GNU coreutils 9.1 | ||
198 | } | 264 | } |
199 | if (end != start || !shoe) | 265 | #if ENABLE_FEATURE_CUT_REGEX |
200 | printf("%s%.*s", out++ ? odelim : "", end-start, line + start); | 266 | if (end != start || !opt_REGEX) |
201 | start = uu; | 267 | #endif |
202 | if (!dcount) | 268 | { |
203 | break; | 269 | if (first_print) { |
204 | } | 270 | first_print = 0; |
271 | printf("%.*s", end - start, line + start); | ||
272 | } else | ||
273 | printf("%s%.*s", odelim, end - start, line + start); | ||
274 | } | ||
275 | start = next; | ||
276 | //if (dcount == 0) | ||
277 | // break; - why? | ||
278 | } /* byte loop */ | ||
205 | } | 279 | } |
206 | /* if we printed anything, finish with newline */ | 280 | /* if we printed anything, finish with newline */ |
207 | putchar('\n'); | 281 | putchar('\n'); |
208 | next_line: | 282 | next_line: |
209 | linenum++; | 283 | free(line); |
210 | free(printed); | 284 | } /* while (got line) */ |
211 | free(orig_line); | 285 | |
212 | } | 286 | /* For -d$'\n' --output-delimiter=^, the overall output is still terminated with \n, not ^ */ |
287 | if (!opt_REGEX && *delim == '\n' && !first_print) | ||
288 | putchar('\n'); | ||
213 | } | 289 | } |
214 | 290 | ||
215 | int cut_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 291 | int cut_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
216 | int cut_main(int argc UNUSED_PARAM, char **argv) | 292 | int cut_main(int argc UNUSED_PARAM, char **argv) |
217 | { | 293 | { |
218 | /* growable array holding a series of lists */ | 294 | /* growable array holding a series of ranges */ |
219 | struct cut_list *cut_lists = NULL; | 295 | struct cut_range *cut_list = NULL; |
220 | unsigned nlists = 0; /* number of elements in above list */ | 296 | unsigned nranges = 0; /* number of elements in above list */ |
221 | char *sopt, *ltok; | 297 | char *LIST, *ltok; |
222 | const char *delim = NULL; | 298 | const char *delim = NULL; |
223 | const char *odelim = NULL; | 299 | const char *odelim = NULL; |
224 | unsigned opt; | 300 | unsigned opt; |
301 | #if ENABLE_FEATURE_CUT_REGEX | ||
302 | regex_t reg; | ||
303 | #endif | ||
304 | #if ENABLE_LONG_OPTS | ||
305 | static const char cut_longopts[] ALIGN1 = | ||
306 | "output-delimiter\0" Required_argument "O" | ||
307 | ; | ||
308 | #endif | ||
225 | 309 | ||
226 | #define ARG "bcf"IF_FEATURE_CUT_REGEX("F") | 310 | #define ARG "bcf"IF_FEATURE_CUT_REGEX("F") |
227 | opt = getopt32(argv, "^" | 311 | #if ENABLE_LONG_OPTS |
312 | opt = getopt32long | ||
313 | #else | ||
314 | opt = getopt32 | ||
315 | #endif | ||
316 | (argv, "^" | ||
228 | OPT_STR // = "b:c:f:d:O:sD"IF_FEATURE_CUT_REGEX("F:")"n" | 317 | OPT_STR // = "b:c:f:d:O:sD"IF_FEATURE_CUT_REGEX("F:")"n" |
229 | "\0" "b--"ARG":c--"ARG":f--"ARG IF_FEATURE_CUT_REGEX("F--"ARG), | 318 | "\0" "b:c:f:" IF_FEATURE_CUT_REGEX("F:") /* one of -bcfF is required */ |
230 | &sopt, &sopt, &sopt, &delim, &odelim IF_FEATURE_CUT_REGEX(, &sopt) | 319 | "b--"ARG":c--"ARG":f--"ARG IF_FEATURE_CUT_REGEX(":F--"ARG), /* they are mutually exclusive */ |
231 | ); | 320 | IF_LONG_OPTS(cut_longopts,) |
232 | if (!delim || !*delim) | 321 | &LIST, &LIST, &LIST, &delim, &odelim IF_FEATURE_CUT_REGEX(, &LIST) |
233 | delim = (opt & CUT_OPT_REGEX_FLGS) ? "[[:space:]]+" : "\t"; | 322 | ); |
234 | if (!odelim) odelim = (opt & CUT_OPT_REGEX_FLGS) ? " " : delim; | 323 | if (!odelim) |
324 | odelim = (opt & OPT_REGEX) ? " " : delim; | ||
325 | if (!delim) | ||
326 | delim = (opt & OPT_REGEX) ? "[[:space:]]+" : "\t"; | ||
235 | 327 | ||
236 | // argc -= optind; | 328 | // argc -= optind; |
237 | argv += optind; | 329 | argv += optind; |
238 | if (!(opt & (CUT_OPT_BYTE_FLGS | CUT_OPT_CHAR_FLGS | CUT_OPT_FIELDS_FLGS | CUT_OPT_REGEX_FLGS))) | 330 | //if (!(opt & (OPT_BYTE | OPT_CHAR | OPT_FIELDS | OPT_REGEX))) |
239 | bb_simple_error_msg_and_die("expected a list of bytes, characters, or fields"); | 331 | // bb_simple_error_msg_and_die("expected a list of bytes, characters, or fields"); |
240 | 332 | //^^^ handled by getopt32 | |
241 | /* non-field (char or byte) cutting has some special handling */ | 333 | |
242 | if (!(opt & (CUT_OPT_FIELDS_FLGS|CUT_OPT_REGEX_FLGS))) { | 334 | /* non-field (char or byte) cutting has some special handling */ |
243 | static const char _op_on_field[] ALIGN1 = " only when operating on fields"; | 335 | if (!(opt & (OPT_FIELDS|OPT_REGEX))) { |
244 | 336 | static const char requires_f[] ALIGN1 = " requires -f" | |
245 | if (opt & CUT_OPT_SUPPRESS_FLGS) { | 337 | IF_FEATURE_CUT_REGEX(" or -F"); |
246 | bb_error_msg_and_die | 338 | if (opt & OPT_SUPPRESS) |
247 | ("suppressing non-delimited lines makes sense%s", _op_on_field); | 339 | bb_error_msg_and_die("-s%s", requires_f); |
248 | } | 340 | if (opt & OPT_DELIM) |
249 | if (opt & CUT_OPT_DELIM_FLGS) { | 341 | bb_error_msg_and_die("-d DELIM%s", requires_f); |
250 | bb_error_msg_and_die | ||
251 | ("a delimiter may be specified%s", _op_on_field); | ||
252 | } | ||
253 | } | 342 | } |
254 | 343 | ||
255 | /* | 344 | /* |
256 | * parse list and put values into startpos and endpos. | 345 | * parse list and put values into startpos and endpos. |
257 | * valid list formats: N, N-, N-M, -M | 346 | * valid range formats: N, N-, N-M, -M |
258 | * more than one list can be separated by commas | 347 | * more than one range can be separated by commas |
259 | */ | 348 | */ |
260 | { | 349 | /* take apart the ranges, one by one (separated with commas) */ |
350 | while ((ltok = strsep(&LIST, ",")) != NULL) { | ||
261 | char *ntok; | 351 | char *ntok; |
262 | int s = 0, e = 0; | 352 | int s, e; |
263 | 353 | ||
264 | /* take apart the lists, one by one (they are separated with commas) */ | 354 | /* it's actually legal to pass an empty list */ |
265 | while ((ltok = strsep(&sopt, ",")) != NULL) { | 355 | //if (!ltok[0]) |
356 | // continue; | ||
357 | //^^^ testcase? | ||
358 | |||
359 | /* get the start pos */ | ||
360 | ntok = strsep(<ok, "-"); | ||
361 | if (!ntok[0]) { | ||
362 | if (!ltok) /* testcase: -f '' */ | ||
363 | bb_show_usage(); | ||
364 | if (!ltok[0]) /* testcase: -f - */ | ||
365 | bb_show_usage(); | ||
366 | s = 0; /* "-M" means "1-M" */ | ||
367 | } else { | ||
368 | /* "N" or "N-[M]" */ | ||
369 | /* arrays are zero based, while the user expects | ||
370 | * the first field/char on the line to be char #1 */ | ||
371 | s = xatoi_positive(ntok) - 1; | ||
372 | } | ||
266 | 373 | ||
267 | /* it's actually legal to pass an empty list */ | 374 | /* get the end pos */ |
268 | if (!ltok[0]) | 375 | if (!ltok) { |
269 | continue; | 376 | e = s; /* "N" means "N-N" */ |
377 | } else if (!ltok[0]) { | ||
378 | /* "N-" means "until the end of the line" */ | ||
379 | e = INT_MAX; | ||
380 | } else { | ||
381 | /* again, arrays are zero based, fields are 1 based */ | ||
382 | e = xatoi_positive(ltok) - 1; | ||
383 | } | ||
270 | 384 | ||
271 | /* get the start pos */ | 385 | if (s < 0 || e < s) |
272 | ntok = strsep(<ok, "-"); | 386 | bb_error_msg_and_die("invalid range %s-%s", ntok, ltok ?: ntok); |
273 | if (!ntok[0]) { | ||
274 | s = 0; | ||
275 | } else { | ||
276 | s = xatoi_positive(ntok); | ||
277 | /* account for the fact that arrays are zero based, while | ||
278 | * the user expects the first char on the line to be char #1 */ | ||
279 | #if !ENABLE_PLATFORM_MINGW32 | ||
280 | if (s != 0) | ||
281 | s--; | ||
282 | #else | ||
283 | s--; | ||
284 | #endif | ||
285 | } | ||
286 | 387 | ||
287 | /* get the end pos */ | 388 | /* add the new range */ |
288 | if (ltok == NULL) { | 389 | cut_list = xrealloc_vector(cut_list, 4, nranges); |
289 | e = s; | 390 | /* NB: s is always >= 0 */ |
290 | } else if (!ltok[0]) { | 391 | cut_list[nranges].startpos = s; |
291 | e = INT_MAX; | 392 | cut_list[nranges].endpos = e; |
292 | } else { | 393 | nranges++; |
293 | e = xatoi_positive(ltok); | 394 | } |
294 | /* if the user specified no end position, | 395 | cut_list[nranges].startpos = UINT_MAX; /* end indicator */ |
295 | * that means "til the end of the line" */ | ||
296 | #if !ENABLE_PLATFORM_MINGW32 | ||
297 | if (!*ltok) | ||
298 | e = INT_MAX; | ||
299 | else if (e < s) | ||
300 | bb_error_msg_and_die("%d<%d", e, s); | ||
301 | #endif | ||
302 | e--; /* again, arrays are zero based, lines are 1 based */ | ||
303 | } | ||
304 | #if ENABLE_PLATFORM_MINGW32 | ||
305 | if (s < 0 || e < s) | ||
306 | bb_error_msg_and_die("invalid range %s-%s", ntok, ltok ?: ntok); | ||
307 | #endif | ||
308 | 396 | ||
309 | /* add the new list */ | 397 | /* make sure we got some cut positions out of all that */ |
310 | cut_lists = xrealloc_vector(cut_lists, 4, nlists); | 398 | //if (nranges == 0) |
311 | /* NB: startpos is always >= 0 */ | 399 | // bb_simple_error_msg_and_die("missing list of positions"); |
312 | cut_lists[nlists].startpos = s; | 400 | //^^^ this is impossible since one of -bcfF is required, |
313 | cut_lists[nlists].endpos = e; | 401 | // they populate LIST with non-NULL string and when it is parsed, |
314 | nlists++; | 402 | // cut_list[] gets at least one element. |
315 | } | ||
316 | 403 | ||
317 | /* make sure we got some cut positions out of all that */ | 404 | /* now that the lists are parsed, we need to sort them to make life |
318 | if (nlists == 0) | 405 | * easier on us when it comes time to print the chars / fields / lines |
319 | bb_simple_error_msg_and_die("missing list of positions"); | 406 | */ |
407 | if (!(opt & OPT_NOSORT)) | ||
408 | qsort(cut_list, nranges, sizeof(cut_list[0]), cmpfunc); | ||
320 | 409 | ||
321 | /* now that the lists are parsed, we need to sort them to make life | 410 | #if ENABLE_FEATURE_CUT_REGEX |
322 | * easier on us when it comes time to print the chars / fields / lines | 411 | if (opt & OPT_REGEX) { |
323 | */ | 412 | xregcomp(®, delim, REG_EXTENDED); |
324 | if (!(opt & CUT_OPT_NOSORT_FLGS)) | 413 | delim = (void*) ® |
325 | qsort(cut_lists, nlists, sizeof(cut_lists[0]), cmpfunc); | ||
326 | } | 414 | } |
415 | #endif | ||
327 | 416 | ||
328 | { | 417 | { |
329 | exitcode_t retval = EXIT_SUCCESS; | 418 | exitcode_t retval = EXIT_SUCCESS; |
@@ -337,12 +426,12 @@ int cut_main(int argc UNUSED_PARAM, char **argv) | |||
337 | retval = EXIT_FAILURE; | 426 | retval = EXIT_FAILURE; |
338 | continue; | 427 | continue; |
339 | } | 428 | } |
340 | cut_file(file, delim, odelim, cut_lists, nlists); | 429 | cut_file(file, delim, odelim, cut_list); |
341 | fclose_if_not_stdin(file); | 430 | fclose_if_not_stdin(file); |
342 | } while (*++argv); | 431 | } while (*++argv); |
343 | 432 | ||
344 | if (ENABLE_FEATURE_CLEAN_UP) | 433 | if (ENABLE_FEATURE_CLEAN_UP) |
345 | free(cut_lists); | 434 | free(cut_list); |
346 | fflush_stdout_and_exit(retval); | 435 | fflush_stdout_and_exit(retval); |
347 | } | 436 | } |
348 | } | 437 | } |
diff --git a/libbb/dump.c b/libbb/dump.c index ffc46f6a7..aa57eca8c 100644 --- a/libbb/dump.c +++ b/libbb/dump.c | |||
@@ -204,9 +204,11 @@ static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs) | |||
204 | if (!e) | 204 | if (!e) |
205 | goto DO_BAD_CONV_CHAR; | 205 | goto DO_BAD_CONV_CHAR; |
206 | pr->flags = F_INT; | 206 | pr->flags = F_INT; |
207 | if (e > int_convs + 1) /* not d or i? */ | ||
208 | pr->flags = F_UINT; | ||
209 | byte_count_str = "\010\004\002\001"; | 207 | byte_count_str = "\010\004\002\001"; |
208 | if (e > int_convs + 1) { /* not d or i? */ | ||
209 | pr->flags = F_UINT; | ||
210 | byte_count_str++; | ||
211 | } | ||
210 | goto DO_BYTE_COUNT; | 212 | goto DO_BYTE_COUNT; |
211 | } else | 213 | } else |
212 | if (strchr(int_convs, *p1)) { /* %d etc */ | 214 | if (strchr(int_convs, *p1)) { /* %d etc */ |
diff --git a/libbb/getopt32.c b/libbb/getopt32.c index 56040e150..76d29d5eb 100644 --- a/libbb/getopt32.c +++ b/libbb/getopt32.c | |||
@@ -93,7 +93,7 @@ getopt32(char **argv, const char *applet_opts, ...) | |||
93 | 93 | ||
94 | "!" If the first character in the applet_opts string is a '!', | 94 | "!" If the first character in the applet_opts string is a '!', |
95 | report bad options, missing required options, | 95 | report bad options, missing required options, |
96 | inconsistent options with all-ones return value (instead of abort. | 96 | inconsistent options with all-ones return value instead of aborting. |
97 | 97 | ||
98 | "+" If the first character in the applet_opts string is a plus, | 98 | "+" If the first character in the applet_opts string is a plus, |
99 | then option processing will stop as soon as a non-option is | 99 | then option processing will stop as soon as a non-option is |
@@ -265,7 +265,7 @@ Special characters: | |||
265 | for "long options only" cases, such as tar --exclude=PATTERN, | 265 | for "long options only" cases, such as tar --exclude=PATTERN, |
266 | wget --header=HDR cases. | 266 | wget --header=HDR cases. |
267 | 267 | ||
268 | "a?b" A "?" between an option and a group of options means that | 268 | "a?bc" A "?" between an option and a group of options means that |
269 | at least one of them is required to occur if the first option | 269 | at least one of them is required to occur if the first option |
270 | occurs in preceding command line arguments. | 270 | occurs in preceding command line arguments. |
271 | 271 | ||
@@ -348,9 +348,6 @@ vgetopt32(char **argv, const char *applet_opts, const char *applet_long_options, | |||
348 | unsigned trigger; | 348 | unsigned trigger; |
349 | int min_arg = 0; | 349 | int min_arg = 0; |
350 | int max_arg = -1; | 350 | int max_arg = -1; |
351 | int spec_flgs = 0; | ||
352 | |||
353 | #define SHOW_USAGE_IF_ERROR 1 | ||
354 | 351 | ||
355 | on_off = complementary; | 352 | on_off = complementary; |
356 | memset(on_off, 0, sizeof(complementary)); | 353 | memset(on_off, 0, sizeof(complementary)); |
@@ -449,9 +446,7 @@ vgetopt32(char **argv, const char *applet_opts, const char *applet_long_options, | |||
449 | continue; | 446 | continue; |
450 | c = s[1]; | 447 | c = s[1]; |
451 | if (*s == '?') { | 448 | if (*s == '?') { |
452 | if (c < '0' || c > '9') { | 449 | if (c >= '0' && c <= '9') { |
453 | spec_flgs |= SHOW_USAGE_IF_ERROR; | ||
454 | } else { | ||
455 | max_arg = c - '0'; | 450 | max_arg = c - '0'; |
456 | s++; | 451 | s++; |
457 | } | 452 | } |
@@ -465,8 +460,10 @@ vgetopt32(char **argv, const char *applet_opts, const char *applet_long_options, | |||
465 | continue; | 460 | continue; |
466 | } | 461 | } |
467 | if (*s == '=') { | 462 | if (*s == '=') { |
468 | min_arg = max_arg = c - '0'; | 463 | if (c >= '0' && c <= '9') { |
469 | s++; | 464 | min_arg = max_arg = c - '0'; |
465 | s++; | ||
466 | } | ||
470 | continue; | 467 | continue; |
471 | } | 468 | } |
472 | for (on_off = complementary; on_off->opt_char; on_off++) | 469 | for (on_off = complementary; on_off->opt_char; on_off++) |
diff --git a/runit/chpst.c b/runit/chpst.c index 2be1a5775..4e3d613b7 100644 --- a/runit/chpst.c +++ b/runit/chpst.c | |||
@@ -466,7 +466,8 @@ int chpst_main(int argc UNUSED_PARAM, char **argv) | |||
466 | /* nice should be done before xsetuid */ | 466 | /* nice should be done before xsetuid */ |
467 | if (opt & OPT_n) { | 467 | if (opt & OPT_n) { |
468 | errno = 0; | 468 | errno = 0; |
469 | if (nice(xatoi(nicestr)) == -1) | 469 | nice(xatoi(nicestr)); |
470 | if (errno) | ||
470 | bb_simple_perror_msg_and_die("nice"); | 471 | bb_simple_perror_msg_and_die("nice"); |
471 | } | 472 | } |
472 | 473 | ||
diff --git a/shell/hush.c b/shell/hush.c index 04dda0734..4a97293cc 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -10361,6 +10361,9 @@ int hush_main(int argc, char **argv) | |||
10361 | _exit(0); | 10361 | _exit(0); |
10362 | } | 10362 | } |
10363 | G.argv0_for_re_execing = argv[0]; | 10363 | G.argv0_for_re_execing = argv[0]; |
10364 | if (G.argv0_for_re_execing[0] == '-') | ||
10365 | /* reexeced hush should never be a login shell */ | ||
10366 | G.argv0_for_re_execing++; | ||
10364 | #endif | 10367 | #endif |
10365 | #if ENABLE_HUSH_TRAP | 10368 | #if ENABLE_HUSH_TRAP |
10366 | # if ENABLE_HUSH_FUNCTIONS | 10369 | # if ENABLE_HUSH_FUNCTIONS |
diff --git a/testsuite/cut.tests b/testsuite/cut.tests index a31f41f7f..21cfea809 100755 --- a/testsuite/cut.tests +++ b/testsuite/cut.tests | |||
@@ -23,14 +23,30 @@ the quick brown fox jumps over the lazy dog | |||
23 | 23 | ||
24 | testing "cut -b a,a,a" "cut -b 3,3,3 input" "e\np\ne\n" "$abc" "" | 24 | testing "cut -b a,a,a" "cut -b 3,3,3 input" "e\np\ne\n" "$abc" "" |
25 | 25 | ||
26 | testing "cut -b overlaps" "cut -b 1-3,2-5,7-9,9-10 input" \ | 26 | testing "cut -b overlaps" \ |
27 | "one:to:th\nalphabeta\nthe qick \n" "$abc" "" | 27 | "cut -b 1-3,2-5,7-9,9-10 input" \ |
28 | testing "-b encapsulated" "cut -b 3-8,4-6 input" "e:two:\npha:be\ne quic\n" \ | 28 | "\ |
29 | "$abc" "" | 29 | one:to:th |
30 | # --output-delimiter not implemnted (yet?) | 30 | alphabeta |
31 | #testing "cut -bO overlaps" \ | 31 | the qick \n" \ |
32 | # "cut --output-delimiter ' ' -b 1-3,2-5,7-9,9-10 input" \ | 32 | "$abc" "" |
33 | # "one:t o:th\nalpha beta\nthe q ick \n" "$abc" "" | 33 | testing "-b encapsulated" \ |
34 | "cut -b 3-8,4-6 input" \ | ||
35 | "\ | ||
36 | e:two: | ||
37 | pha:be | ||
38 | e quic\n" \ | ||
39 | "$abc" "" | ||
40 | optional LONG_OPTS | ||
41 | testing "cut -b --output-delimiter overlaps" \ | ||
42 | "cut --output-delimiter='^' -b 1-3,2-5,7-9,9-10 input" \ | ||
43 | "\ | ||
44 | one:t^o:th | ||
45 | alpha^beta | ||
46 | the q^ick \n" \ | ||
47 | "$abc" "" | ||
48 | SKIP= | ||
49 | |||
34 | testing "cut high-low error" "cut -b 8-3 input 2>/dev/null || echo err" "err\n" \ | 50 | testing "cut high-low error" "cut -b 8-3 input 2>/dev/null || echo err" "err\n" \ |
35 | "$abc" "" | 51 | "$abc" "" |
36 | 52 | ||
@@ -68,10 +84,27 @@ testing "cut with -d -f( ) -s" "cut -d' ' -f3 -s input && echo yes" "yes\n" "$in | |||
68 | testing "cut with -d -f(a) -s" "cut -da -f3 -s input" "n\nsium:Jim\n\ncion:Ed\n" "$input" "" | 84 | testing "cut with -d -f(a) -s" "cut -da -f3 -s input" "n\nsium:Jim\n\ncion:Ed\n" "$input" "" |
69 | testing "cut with -d -f(a) -s -n" "cut -da -f3 -s -n input" "n\nsium:Jim\n\ncion:Ed\n" "$input" "" | 85 | testing "cut with -d -f(a) -s -n" "cut -da -f3 -s -n input" "n\nsium:Jim\n\ncion:Ed\n" "$input" "" |
70 | 86 | ||
87 | input="\ | ||
88 | |||
89 | foo bar baz | ||
90 | |||
91 | bing bong boop | ||
92 | |||
93 | " | ||
94 | testing "cut with -d -s omits blank lines" "cut -d' ' -f2 -s input" "bar\nbong\n" "$input" "" | ||
95 | |||
71 | # substitute for awk | 96 | # substitute for awk |
72 | optional FEATURE_CUT_REGEX | 97 | optional FEATURE_CUT_REGEX |
73 | testing "cut -DF" "cut -DF 2,7,5" \ | 98 | testing "cut -DF unordered" "cut -DF 2,7,5" \ |
74 | "said and your\nare\nis demand. supply\nforecast :\nyou you better,\n\nEm: Took hate\n" "" \ | 99 | "\ |
100 | said and your | ||
101 | are | ||
102 | is demand. supply | ||
103 | forecast : | ||
104 | you you better, | ||
105 | |||
106 | Em: Took hate | ||
107 | " "" \ | ||
75 | "Bother, said Pooh. It's your husband, and he has a gun. | 108 | "Bother, said Pooh. It's your husband, and he has a gun. |
76 | Cheerios are donut seeds. | 109 | Cheerios are donut seeds. |
77 | Talk is cheap because supply exceeds demand. | 110 | Talk is cheap because supply exceeds demand. |
@@ -79,9 +112,92 @@ Weather forecast for tonight : dark. | |||
79 | Apple: you can buy better, but you can't pay more. | 112 | Apple: you can buy better, but you can't pay more. |
80 | Subcalifragilisticexpialidocious. | 113 | Subcalifragilisticexpialidocious. |
81 | Auntie Em: Hate you, hate Kansas. Took the dog. Dorothy." | 114 | Auntie Em: Hate you, hate Kansas. Took the dog. Dorothy." |
115 | |||
116 | # No delimiter found: print entire line regardless of -F RANGES | ||
117 | testing "cut -F1" "cut -d: -F1" \ | ||
118 | "the_only_field\n" "" \ | ||
119 | "the_only_field\n" | ||
120 | testing "cut -F2" "cut -d: -F2" \ | ||
121 | "the_only_field\n" "" \ | ||
122 | "the_only_field\n" | ||
123 | # No delimiter found and -s: skip entire line | ||
124 | testing "cut -sF1" "cut -d: -sF1" \ | ||
125 | "" "" \ | ||
126 | "the_only_field\n" | ||
127 | #^^^ the above is probably mishandled by toybox, it prints the line | ||
128 | testing "cut -sF2" "cut -d: -sF2" \ | ||
129 | "" "" \ | ||
130 | "the_only_field\n" | ||
131 | # -D disables special handling of lines with no delimiters, the line is treated as the 1st field | ||
132 | testing "cut -DF1" "cut -d: -DF1" \ | ||
133 | "the_only_field\n" "" \ | ||
134 | "the_only_field\n" | ||
135 | testing "cut -DF2" "cut -d: -DF2" \ | ||
136 | "\n" "" \ | ||
137 | "the_only_field\n" | ||
138 | |||
139 | optional FEATURE_CUT_REGEX LONG_OPTS | ||
140 | testing "cut -F preserves intermediate delimiters" \ | ||
141 | "cut --output-delimiter=: -F2,4-6,7" \ | ||
142 | "2:4 5 6:7\n" \ | ||
143 | "" "1 2 3 4\t\t5 6 7 8\n" | ||
144 | SKIP= | ||
145 | |||
146 | optional LONG_OPTS | ||
147 | testing "cut -f does not preserve intermediate delimiters" \ | ||
148 | "cut --output-delimiter=: -d' ' -f2,4-6,7" \ | ||
149 | "2:4:5:6:7\n" \ | ||
150 | "" "1 2 3 4 5 6 7 8\n" | ||
151 | SKIP= | ||
152 | |||
153 | testing "cut empty field" "cut -d ':' -f 1-3" \ | ||
154 | "a::b\n" \ | ||
155 | "" "a::b\n" | ||
156 | testing "cut empty field 2" "cut -d ':' -f 3-5" \ | ||
157 | "b::c\n" \ | ||
158 | "" "a::b::c:d\n" | ||
159 | testing "cut non-existing field" "cut -d ':' -f1,3" \ | ||
160 | "1\n" \ | ||
161 | "" "1:\n" | ||
162 | |||
163 | # cut -d$'\n' has a special meaning: "select input lines". | ||
164 | # I didn't find any documentation for this feature. | ||
165 | testing "cut -dNEWLINE" \ | ||
166 | "cut -d' | ||
167 | ' -f4,2,6-8" \ | ||
168 | "2\n4\n6\n7\n" \ | ||
169 | "" "1\n2\n3\n4\n5\n6\n7" | ||
170 | |||
171 | optional LONG_OPTS | ||
172 | testing "cut -dNEWLINE --output-delimiter" \ | ||
173 | "cut -d' | ||
174 | ' --output-delimiter=@@ -f4,2,6-8" \ | ||
175 | "2@@4@@6@@7\n" \ | ||
176 | "" "1\n2\n3\n4\n5\n6\n7" | ||
177 | |||
178 | testing "cut -dNEWLINE --output-delimiter 2" \ | ||
179 | "cut -d' | ||
180 | ' --output-delimiter=@@ -f4,2,6-8" \ | ||
181 | "2@@4@@6@@7\n" \ | ||
182 | "" "1\n2\n3\n4\n5\n6\n7\n" | ||
183 | |||
184 | testing "cut -dNEWLINE --output-delimiter EMPTY_INPUT" \ | ||
185 | "cut -d' | ||
186 | ' --output-delimiter=@@ -f4,2,6-8" \ | ||
187 | "" \ | ||
188 | "" "" | ||
82 | SKIP= | 189 | SKIP= |
83 | 190 | ||
84 | testing "cut empty field" "cut -d ':' -f 1-3" "a::b\n" "" "a::b\n" | 191 | # This seems to work as if delimiter is never found. |
85 | testing "cut empty field 2" "cut -d ':' -f 3-5" "b::c\n" "" "a::b::c:d\n" | 192 | # We test here that -d '' does *not* operate as if there was no -d |
193 | # and delimiter has defaulted to TAB: | ||
194 | testing "cut -d EMPTY" \ | ||
195 | "cut -d '' -f2-" \ | ||
196 | "1 2\t3 4 5\n" \ | ||
197 | "" "1 2\t3 4 5\n" | ||
198 | testing "cut -d EMPTY -s" \ | ||
199 | "cut -d '' -f2- -s" \ | ||
200 | "" \ | ||
201 | "" "1 2\t3 4 5\n" | ||
86 | 202 | ||
87 | exit $FAILCOUNT | 203 | exit $FAILCOUNT |
diff --git a/testsuite/hexdump.tests b/testsuite/hexdump.tests index be0379cfc..517ec508b 100755 --- a/testsuite/hexdump.tests +++ b/testsuite/hexdump.tests | |||
@@ -82,4 +82,10 @@ testing "hexdump -e /2 %d" \ | |||
82 | "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"\ | 82 | "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"\ |
83 | "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"\ | 83 | "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"\ |
84 | 84 | ||
85 | testing "hexdump -n4 -e '\"%u\"'" \ | ||
86 | "hexdump -n4 -e '\"%u\"'" \ | ||
87 | "12345678" \ | ||
88 | "" \ | ||
89 | "\x4e\x61\xbc\x00AAAA" | ||
90 | |||
85 | exit $FAILCOUNT | 91 | exit $FAILCOUNT |