aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--coreutils/cut.c34
-rwxr-xr-xtestsuite/cut.tests26
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
256int cut_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 276int 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.
121testing "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
127testing "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
133testing "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
139testing "cut -dNEWLINE --output-delimiter EMPTY_INPUT" \
140 "cut -d'
141' -O@@ -f4,2,6-8" \
142 "" \
143 "" ""
144
119exit $FAILCOUNT 145exit $FAILCOUNT