diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2024-12-10 21:01:52 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2024-12-10 21:01:52 +0100 |
commit | 9e364b16d1020cb7b8f8f4d4f3fe1833496b7a12 (patch) | |
tree | b1948da774eeb0f74f9cf6c7b1fc07f35ba41fde | |
parent | a208fa03de8467d1f51821f874cbf142aaad74fa (diff) | |
download | busybox-w32-9e364b16d1020cb7b8f8f4d4f3fe1833496b7a12.tar.gz busybox-w32-9e364b16d1020cb7b8f8f4d4f3fe1833496b7a12.tar.bz2 busybox-w32-9e364b16d1020cb7b8f8f4d4f3fe1833496b7a12.zip |
cut: fix -d$'\n' --output-delimiter=@@ behavior
function old new delta
cut_main 1261 1353 +92
packed_usage 34925 34901 -24
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/1 up/down: 92/-24) Total: 68 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | coreutils/cut.c | 34 | ||||
-rwxr-xr-x | testsuite/cut.tests | 26 |
2 files changed, 53 insertions, 7 deletions
diff --git a/coreutils/cut.c b/coreutils/cut.c index 9f5b649d8..2d0a6237c 100644 --- a/coreutils/cut.c +++ b/coreutils/cut.c | |||
@@ -43,11 +43,19 @@ | |||
43 | //usage: ) | 43 | //usage: ) |
44 | //usage: "\n -s Drop lines with no delimiter (else print them in full)" | 44 | //usage: "\n -s Drop lines with no delimiter (else print them in full)" |
45 | //usage: "\n -D Don't sort/collate sections or match -f"IF_FEATURE_CUT_REGEX("F")" lines without delimeter" | 45 | //usage: "\n -D Don't sort/collate sections or match -f"IF_FEATURE_CUT_REGEX("F")" lines without delimeter" |
46 | //usage: IF_LONG_OPTS( | ||
47 | //usage: IF_FEATURE_CUT_REGEX( | ||
48 | //usage: "\n --output-delimiter SEP Output field delimeter (default = -d for -f, one space for -F)" | ||
49 | //usage: ) IF_NOT_FEATURE_CUT_REGEX( | ||
50 | //usage: "\n --output-delimiter SEP Output field delimeter (default = -d)" | ||
51 | //usage: ) | ||
52 | //usage: ) IF_NOT_LONG_OPTS( | ||
46 | //usage: IF_FEATURE_CUT_REGEX( | 53 | //usage: IF_FEATURE_CUT_REGEX( |
47 | //usage: "\n -O SEP Output field delimeter (default = -d for -f, one space for -F)" | 54 | //usage: "\n -O SEP Output field delimeter (default = -d for -f, one space for -F)" |
48 | //usage: ) IF_NOT_FEATURE_CUT_REGEX( | 55 | //usage: ) IF_NOT_FEATURE_CUT_REGEX( |
49 | //usage: "\n -O SEP Output field delimeter (default = -d)" | 56 | //usage: "\n -O SEP Output field delimeter (default = -d)" |
50 | //usage: ) | 57 | //usage: ) |
58 | //usage: ) | ||
51 | //TODO: --output-delimiter=SEP | 59 | //TODO: --output-delimiter=SEP |
52 | //usage: "\n -n Ignored" | 60 | //usage: "\n -n Ignored" |
53 | //(manpage:-n with -b: don't split multibyte characters) | 61 | //(manpage:-n with -b: don't split multibyte characters) |
@@ -96,6 +104,7 @@ static void cut_file(FILE *file, const char *delim, const char *odelim, | |||
96 | { | 104 | { |
97 | char *line; | 105 | char *line; |
98 | unsigned linenum = 0; /* keep these zero-based to be consistent */ | 106 | unsigned linenum = 0; /* keep these zero-based to be consistent */ |
107 | int first_print = 1; | ||
99 | 108 | ||
100 | /* go through every line in the file */ | 109 | /* go through every line in the file */ |
101 | while ((line = xmalloc_fgetline(file)) != NULL) { | 110 | while ((line = xmalloc_fgetline(file)) != NULL) { |
@@ -130,16 +139,16 @@ static void cut_file(FILE *file, const char *delim, const char *odelim, | |||
130 | free(printed); | 139 | free(printed); |
131 | /* Cut by lines */ | 140 | /* Cut by lines */ |
132 | } else if (!opt_REGEX && *delim == '\n') { | 141 | } else if (!opt_REGEX && *delim == '\n') { |
133 | int spos = cut_list[cl_pos].startpos; | 142 | unsigned spos = cut_list[cl_pos].startpos; |
134 | 143 | ||
135 | /* get out if we have no more lists to process or if the lines | 144 | /* get out if we have no more lists to process or if the lines |
136 | * are lower than what we're interested in */ | 145 | * are lower than what we're interested in */ |
137 | if (((int)linenum < spos) || (cl_pos >= nlists)) | 146 | if ((linenum < spos) || (cl_pos >= nlists)) |
138 | goto next_line; | 147 | goto next_line; |
139 | 148 | ||
140 | /* if the line we're looking for is lower than the one we were | 149 | /* if the line we're looking for is lower than the one we were |
141 | * passed, it means we displayed it already, so move on */ | 150 | * passed, it means we displayed it already, so move on */ |
142 | while (spos < (int)linenum) { | 151 | while (spos < linenum) { |
143 | spos++; | 152 | spos++; |
144 | /* go to the next list if we're at the end of this one */ | 153 | /* go to the next list if we're at the end of this one */ |
145 | if (spos > cut_list[cl_pos].endpos) { | 154 | if (spos > cut_list[cl_pos].endpos) { |
@@ -150,20 +159,23 @@ static void cut_file(FILE *file, const char *delim, const char *odelim, | |||
150 | spos = cut_list[cl_pos].startpos; | 159 | spos = cut_list[cl_pos].startpos; |
151 | /* get out if the current line is lower than the one | 160 | /* get out if the current line is lower than the one |
152 | * we just became interested in */ | 161 | * we just became interested in */ |
153 | if ((int)linenum < spos) | 162 | if (linenum < spos) |
154 | goto next_line; | 163 | goto next_line; |
155 | } | 164 | } |
156 | } | 165 | } |
157 | 166 | ||
158 | /* If we made it here, it means we've found the line we're | 167 | /* If we made it here, it means we've found the line we're |
159 | * looking for, so print it */ | 168 | * looking for, so print it */ |
160 | puts(line); | 169 | if (first_print) { |
170 | first_print = 0; | ||
171 | fputs_stdout(line); | ||
172 | } else | ||
173 | printf("%s%s", odelim, line); | ||
161 | goto next_line; | 174 | goto next_line; |
162 | /* Cut by fields */ | 175 | /* Cut by fields */ |
163 | } else { | 176 | } else { |
164 | unsigned next = 0, start = 0, end = 0; | 177 | unsigned next = 0, start = 0, end = 0; |
165 | int dcount = 0; /* we saw Nth delimiter (0 - didn't see any yet) */ | 178 | int dcount = 0; /* we saw Nth delimiter (0 - didn't see any yet) */ |
166 | int first_print = 1; | ||
167 | 179 | ||
168 | /* Blank line? Check -s (later check for -s does not catch empty lines) */ | 180 | /* Blank line? Check -s (later check for -s does not catch empty lines) */ |
169 | if (linelen == 0) { | 181 | if (linelen == 0) { |
@@ -173,6 +185,7 @@ static void cut_file(FILE *file, const char *delim, const char *odelim, | |||
173 | 185 | ||
174 | if (!odelim) | 186 | if (!odelim) |
175 | odelim = "\t"; | 187 | odelim = "\t"; |
188 | first_print = 1; | ||
176 | 189 | ||
177 | /* Loop through bytes, finding next delimiter */ | 190 | /* Loop through bytes, finding next delimiter */ |
178 | for (;;) { | 191 | for (;;) { |
@@ -233,7 +246,10 @@ static void cut_file(FILE *file, const char *delim, const char *odelim, | |||
233 | continue; | 246 | continue; |
234 | } | 247 | } |
235 | } | 248 | } |
236 | if (end != start || !opt_REGEX) { | 249 | #if ENABLE_FEATURE_CUT_REGEX |
250 | if (end != start || !opt_REGEX) | ||
251 | #endif | ||
252 | { | ||
237 | if (first_print) { | 253 | if (first_print) { |
238 | first_print = 0; | 254 | first_print = 0; |
239 | printf("%.*s", end - start, line + start); | 255 | printf("%.*s", end - start, line + start); |
@@ -251,6 +267,10 @@ static void cut_file(FILE *file, const char *delim, const char *odelim, | |||
251 | linenum++; | 267 | linenum++; |
252 | free(line); | 268 | free(line); |
253 | } /* while (got line) */ | 269 | } /* while (got line) */ |
270 | |||
271 | /* For -d$'\n' --output-delimiter=^, the overall output is still terminated with \n, not ^ */ | ||
272 | if (!opt_REGEX && *delim == '\n' && !first_print) | ||
273 | putchar('\n'); | ||
254 | } | 274 | } |
255 | 275 | ||
256 | int cut_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 276 | int cut_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
diff --git a/testsuite/cut.tests b/testsuite/cut.tests index ba5f88d60..8da390cd7 100755 --- a/testsuite/cut.tests +++ b/testsuite/cut.tests | |||
@@ -116,4 +116,30 @@ testing "cut non-existing field" "cut -d ':' -f1,3" \ | |||
116 | "1\n" \ | 116 | "1\n" \ |
117 | "" "1:\n" | 117 | "" "1:\n" |
118 | 118 | ||
119 | # cut -d$'\n' has a special meaning: "select input lines". | ||
120 | # I didn't find any documentation for this feature. | ||
121 | testing "cut -dNEWLINE" \ | ||
122 | "cut -d' | ||
123 | ' -f4,2,6-8" \ | ||
124 | "2\n4\n6\n7\n" \ | ||
125 | "" "1\n2\n3\n4\n5\n6\n7" | ||
126 | |||
127 | testing "cut -dNEWLINE --output-delimiter" \ | ||
128 | "cut -d' | ||
129 | ' -O@@ -f4,2,6-8" \ | ||
130 | "2@@4@@6@@7\n" \ | ||
131 | "" "1\n2\n3\n4\n5\n6\n7" | ||
132 | |||
133 | testing "cut -dNEWLINE --output-delimiter 2" \ | ||
134 | "cut -d' | ||
135 | ' -O@@ -f4,2,6-8" \ | ||
136 | "2@@4@@6@@7\n" \ | ||
137 | "" "1\n2\n3\n4\n5\n6\n7\n" | ||
138 | |||
139 | testing "cut -dNEWLINE --output-delimiter EMPTY_INPUT" \ | ||
140 | "cut -d' | ||
141 | ' -O@@ -f4,2,6-8" \ | ||
142 | "" \ | ||
143 | "" "" | ||
144 | |||
119 | exit $FAILCOUNT | 145 | exit $FAILCOUNT |