Mercurial > docs > cut
comparison code/cut.c__gnu.2015-05-01 @ 14:21ad1c1548c4
Code ausgewaehlter Implementierungen eingefuegt
Das Datum entspricht dem Dateiaenderungsdatum.
author | markus schnalke <meillo@marmaro.de> |
---|---|
date | Tue, 12 May 2015 06:46:59 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
13:bf5e41260f89 | 14:21ad1c1548c4 |
---|---|
1 /* cut - remove parts of lines of files | |
2 Copyright (C) 1997-2015 Free Software Foundation, Inc. | |
3 Copyright (C) 1984 David M. Ihnat | |
4 | |
5 This program is free software: you can redistribute it and/or modify | |
6 it under the terms of the GNU General Public License as published by | |
7 the Free Software Foundation, either version 3 of the License, or | |
8 (at your option) any later version. | |
9 | |
10 This program is distributed in the hope that it will be useful, | |
11 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 GNU General Public License for more details. | |
14 | |
15 You should have received a copy of the GNU General Public License | |
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
17 | |
18 /* Written by David Ihnat. */ | |
19 | |
20 /* POSIX changes, bug fixes, long-named options, and cleanup | |
21 by David MacKenzie <djm@gnu.ai.mit.edu>. | |
22 | |
23 Rewrite cut_fields and cut_bytes -- Jim Meyering. */ | |
24 | |
25 #include <config.h> | |
26 | |
27 #include <stdio.h> | |
28 #include <assert.h> | |
29 #include <getopt.h> | |
30 #include <sys/types.h> | |
31 #include "system.h" | |
32 | |
33 #include "error.h" | |
34 #include "fadvise.h" | |
35 #include "getndelim2.h" | |
36 #include "hash.h" | |
37 #include "quote.h" | |
38 #include "xstrndup.h" | |
39 | |
40 /* The official name of this program (e.g., no 'g' prefix). */ | |
41 #define PROGRAM_NAME "cut" | |
42 | |
43 #define AUTHORS \ | |
44 proper_name ("David M. Ihnat"), \ | |
45 proper_name ("David MacKenzie"), \ | |
46 proper_name ("Jim Meyering") | |
47 | |
48 #define FATAL_ERROR(Message) \ | |
49 do \ | |
50 { \ | |
51 error (0, 0, (Message)); \ | |
52 usage (EXIT_FAILURE); \ | |
53 } \ | |
54 while (0) | |
55 | |
56 | |
57 struct range_pair | |
58 { | |
59 size_t lo; | |
60 size_t hi; | |
61 }; | |
62 | |
63 /* Array of `struct range_pair' holding all the finite ranges. */ | |
64 static struct range_pair *rp; | |
65 | |
66 /* Pointer inside RP. When checking if a byte or field is selected | |
67 by a finite range, we check if it is between CURRENT_RP.LO | |
68 and CURRENT_RP.HI. If the byte or field index is greater than | |
69 CURRENT_RP.HI then we make CURRENT_RP to point to the next range pair. */ | |
70 static struct range_pair *current_rp; | |
71 | |
72 /* Number of finite ranges specified by the user. */ | |
73 static size_t n_rp; | |
74 | |
75 /* Number of `struct range_pair's allocated. */ | |
76 static size_t n_rp_allocated; | |
77 | |
78 | |
79 /* Append LOW, HIGH to the list RP of range pairs, allocating additional | |
80 space if necessary. Update global variable N_RP. When allocating, | |
81 update global variable N_RP_ALLOCATED. */ | |
82 | |
83 static void | |
84 add_range_pair (size_t lo, size_t hi) | |
85 { | |
86 if (n_rp == n_rp_allocated) | |
87 rp = X2NREALLOC (rp, &n_rp_allocated); | |
88 rp[n_rp].lo = lo; | |
89 rp[n_rp].hi = hi; | |
90 ++n_rp; | |
91 } | |
92 | |
93 /* This buffer is used to support the semantics of the -s option | |
94 (or lack of same) when the specified field list includes (does | |
95 not include) the first field. In both of those cases, the entire | |
96 first field must be read into this buffer to determine whether it | |
97 is followed by a delimiter or a newline before any of it may be | |
98 output. Otherwise, cut_fields can do the job without using this | |
99 buffer. */ | |
100 static char *field_1_buffer; | |
101 | |
102 /* The number of bytes allocated for FIELD_1_BUFFER. */ | |
103 static size_t field_1_bufsize; | |
104 | |
105 enum operating_mode | |
106 { | |
107 undefined_mode, | |
108 | |
109 /* Output characters that are in the given bytes. */ | |
110 byte_mode, | |
111 | |
112 /* Output the given delimiter-separated fields. */ | |
113 field_mode | |
114 }; | |
115 | |
116 static enum operating_mode operating_mode; | |
117 | |
118 /* If true do not output lines containing no delimiter characters. | |
119 Otherwise, all such lines are printed. This option is valid only | |
120 with field mode. */ | |
121 static bool suppress_non_delimited; | |
122 | |
123 /* If true, print all bytes, characters, or fields _except_ | |
124 those that were specified. */ | |
125 static bool complement; | |
126 | |
127 /* The delimiter character for field mode. */ | |
128 static unsigned char delim; | |
129 | |
130 /* True if the --output-delimiter=STRING option was specified. */ | |
131 static bool output_delimiter_specified; | |
132 | |
133 /* The length of output_delimiter_string. */ | |
134 static size_t output_delimiter_length; | |
135 | |
136 /* The output field separator string. Defaults to the 1-character | |
137 string consisting of the input delimiter. */ | |
138 static char *output_delimiter_string; | |
139 | |
140 /* True if we have ever read standard input. */ | |
141 static bool have_read_stdin; | |
142 | |
143 /* For long options that have no equivalent short option, use a | |
144 non-character as a pseudo short option, starting with CHAR_MAX + 1. */ | |
145 enum | |
146 { | |
147 OUTPUT_DELIMITER_OPTION = CHAR_MAX + 1, | |
148 COMPLEMENT_OPTION | |
149 }; | |
150 | |
151 static struct option const longopts[] = | |
152 { | |
153 {"bytes", required_argument, NULL, 'b'}, | |
154 {"characters", required_argument, NULL, 'c'}, | |
155 {"fields", required_argument, NULL, 'f'}, | |
156 {"delimiter", required_argument, NULL, 'd'}, | |
157 {"only-delimited", no_argument, NULL, 's'}, | |
158 {"output-delimiter", required_argument, NULL, OUTPUT_DELIMITER_OPTION}, | |
159 {"complement", no_argument, NULL, COMPLEMENT_OPTION}, | |
160 {GETOPT_HELP_OPTION_DECL}, | |
161 {GETOPT_VERSION_OPTION_DECL}, | |
162 {NULL, 0, NULL, 0} | |
163 }; | |
164 | |
165 void | |
166 usage (int status) | |
167 { | |
168 if (status != EXIT_SUCCESS) | |
169 emit_try_help (); | |
170 else | |
171 { | |
172 printf (_("\ | |
173 Usage: %s OPTION... [FILE]...\n\ | |
174 "), | |
175 program_name); | |
176 fputs (_("\ | |
177 Print selected parts of lines from each FILE to standard output.\n\ | |
178 "), stdout); | |
179 | |
180 emit_stdin_note (); | |
181 emit_mandatory_arg_note (); | |
182 | |
183 fputs (_("\ | |
184 -b, --bytes=LIST select only these bytes\n\ | |
185 -c, --characters=LIST select only these characters\n\ | |
186 -d, --delimiter=DELIM use DELIM instead of TAB for field delimiter\n\ | |
187 "), stdout); | |
188 fputs (_("\ | |
189 -f, --fields=LIST select only these fields; also print any line\n\ | |
190 that contains no delimiter character, unless\n\ | |
191 the -s option is specified\n\ | |
192 -n (ignored)\n\ | |
193 "), stdout); | |
194 fputs (_("\ | |
195 --complement complement the set of selected bytes, characters\n\ | |
196 or fields\n\ | |
197 "), stdout); | |
198 fputs (_("\ | |
199 -s, --only-delimited do not print lines not containing delimiters\n\ | |
200 --output-delimiter=STRING use STRING as the output delimiter\n\ | |
201 the default is to use the input delimiter\n\ | |
202 "), stdout); | |
203 fputs (HELP_OPTION_DESCRIPTION, stdout); | |
204 fputs (VERSION_OPTION_DESCRIPTION, stdout); | |
205 fputs (_("\ | |
206 \n\ | |
207 Use one, and only one of -b, -c or -f. Each LIST is made up of one\n\ | |
208 range, or many ranges separated by commas. Selected input is written\n\ | |
209 in the same order that it is read, and is written exactly once.\n\ | |
210 "), stdout); | |
211 fputs (_("\ | |
212 Each range is one of:\n\ | |
213 \n\ | |
214 N N'th byte, character or field, counted from 1\n\ | |
215 N- from N'th byte, character or field, to end of line\n\ | |
216 N-M from N'th to M'th (included) byte, character or field\n\ | |
217 -M from first to M'th (included) byte, character or field\n\ | |
218 "), stdout); | |
219 emit_ancillary_info (PROGRAM_NAME); | |
220 } | |
221 exit (status); | |
222 } | |
223 | |
224 /* Comparison function for qsort to order the list of | |
225 struct range_pairs. */ | |
226 static int | |
227 compare_ranges (const void *a, const void *b) | |
228 { | |
229 int a_start = ((const struct range_pair *) a)->lo; | |
230 int b_start = ((const struct range_pair *) b)->lo; | |
231 return a_start < b_start ? -1 : a_start > b_start; | |
232 } | |
233 | |
234 /* Reallocate Range Pair entries, with corresponding | |
235 entries outside the range of each specified entry. */ | |
236 | |
237 static void | |
238 complement_rp (void) | |
239 { | |
240 if (complement) | |
241 { | |
242 struct range_pair *c = rp; | |
243 size_t n = n_rp; | |
244 size_t i; | |
245 | |
246 rp = NULL; | |
247 n_rp = 0; | |
248 n_rp_allocated = 0; | |
249 | |
250 if (c[0].lo > 1) | |
251 add_range_pair (1, c[0].lo - 1); | |
252 | |
253 for (i = 1; i < n; ++i) | |
254 { | |
255 if (c[i-1].hi + 1 == c[i].lo) | |
256 continue; | |
257 | |
258 add_range_pair (c[i-1].hi + 1, c[i].lo - 1); | |
259 } | |
260 | |
261 if (c[n-1].hi < SIZE_MAX) | |
262 add_range_pair (c[n-1].hi + 1, SIZE_MAX); | |
263 | |
264 free (c); | |
265 } | |
266 } | |
267 | |
268 /* Given the list of field or byte range specifications FIELDSTR, | |
269 allocate and initialize the RP array. FIELDSTR should | |
270 be composed of one or more numbers or ranges of numbers, separated | |
271 by blanks or commas. Incomplete ranges may be given: '-m' means '1-m'; | |
272 'n-' means 'n' through end of line. | |
273 Return true if FIELDSTR contains at least one field specification, | |
274 false otherwise. */ | |
275 | |
276 static bool | |
277 set_fields (const char *fieldstr) | |
278 { | |
279 size_t initial = 1; /* Value of first number in a range. */ | |
280 size_t value = 0; /* If nonzero, a number being accumulated. */ | |
281 bool lhs_specified = false; | |
282 bool rhs_specified = false; | |
283 bool dash_found = false; /* True if a '-' is found in this field. */ | |
284 bool field_found = false; /* True if at least one field spec | |
285 has been processed. */ | |
286 | |
287 size_t i; | |
288 bool in_digits = false; | |
289 | |
290 /* Collect and store in RP the range end points. */ | |
291 | |
292 while (true) | |
293 { | |
294 if (*fieldstr == '-') | |
295 { | |
296 in_digits = false; | |
297 /* Starting a range. */ | |
298 if (dash_found) | |
299 FATAL_ERROR (_("invalid byte, character or field list")); | |
300 dash_found = true; | |
301 fieldstr++; | |
302 | |
303 if (lhs_specified && !value) | |
304 FATAL_ERROR (_("fields and positions are numbered from 1")); | |
305 | |
306 initial = (lhs_specified ? value : 1); | |
307 value = 0; | |
308 } | |
309 else if (*fieldstr == ',' | |
310 || isblank (to_uchar (*fieldstr)) || *fieldstr == '\0') | |
311 { | |
312 in_digits = false; | |
313 /* Ending the string, or this field/byte sublist. */ | |
314 if (dash_found) | |
315 { | |
316 dash_found = false; | |
317 | |
318 if (!lhs_specified && !rhs_specified) | |
319 FATAL_ERROR (_("invalid range with no endpoint: -")); | |
320 | |
321 /* A range. Possibilities: -n, m-n, n-. | |
322 In any case, 'initial' contains the start of the range. */ | |
323 if (!rhs_specified) | |
324 { | |
325 /* 'n-'. From 'initial' to end of line. */ | |
326 add_range_pair (initial, SIZE_MAX); | |
327 field_found = true; | |
328 } | |
329 else | |
330 { | |
331 /* 'm-n' or '-n' (1-n). */ | |
332 if (value < initial) | |
333 FATAL_ERROR (_("invalid decreasing range")); | |
334 | |
335 add_range_pair (initial, value); | |
336 field_found = true; | |
337 } | |
338 value = 0; | |
339 } | |
340 else | |
341 { | |
342 /* A simple field number, not a range. */ | |
343 if (value == 0) | |
344 FATAL_ERROR (_("fields and positions are numbered from 1")); | |
345 add_range_pair (value, value); | |
346 value = 0; | |
347 field_found = true; | |
348 } | |
349 | |
350 if (*fieldstr == '\0') | |
351 break; | |
352 | |
353 fieldstr++; | |
354 lhs_specified = false; | |
355 rhs_specified = false; | |
356 } | |
357 else if (ISDIGIT (*fieldstr)) | |
358 { | |
359 /* Record beginning of digit string, in case we have to | |
360 complain about it. */ | |
361 static char const *num_start; | |
362 if (!in_digits || !num_start) | |
363 num_start = fieldstr; | |
364 in_digits = true; | |
365 | |
366 if (dash_found) | |
367 rhs_specified = 1; | |
368 else | |
369 lhs_specified = 1; | |
370 | |
371 /* Detect overflow. */ | |
372 if (!DECIMAL_DIGIT_ACCUMULATE (value, *fieldstr - '0', size_t) | |
373 || value == SIZE_MAX) | |
374 { | |
375 /* In case the user specified -c$(echo 2^64|bc),22, | |
376 complain only about the first number. */ | |
377 /* Determine the length of the offending number. */ | |
378 size_t len = strspn (num_start, "0123456789"); | |
379 char *bad_num = xstrndup (num_start, len); | |
380 if (operating_mode == byte_mode) | |
381 error (0, 0, | |
382 _("byte offset %s is too large"), quote (bad_num)); | |
383 else | |
384 error (0, 0, | |
385 _("field number %s is too large"), quote (bad_num)); | |
386 free (bad_num); | |
387 exit (EXIT_FAILURE); | |
388 } | |
389 | |
390 fieldstr++; | |
391 } | |
392 else | |
393 FATAL_ERROR (_("invalid byte, character or field list")); | |
394 } | |
395 | |
396 qsort (rp, n_rp, sizeof (rp[0]), compare_ranges); | |
397 | |
398 /* Merge range pairs (e.g. `2-5,3-4' becomes `2-5'). */ | |
399 for (i = 0; i < n_rp; ++i) | |
400 { | |
401 for (size_t j = i + 1; j < n_rp; ++j) | |
402 { | |
403 if (rp[j].lo <= rp[i].hi) | |
404 { | |
405 rp[i].hi = MAX (rp[j].hi, rp[i].hi); | |
406 memmove (rp + j, rp + j + 1, (n_rp - j - 1) * sizeof *rp); | |
407 n_rp--; | |
408 j--; | |
409 } | |
410 else | |
411 break; | |
412 } | |
413 } | |
414 | |
415 complement_rp (); | |
416 | |
417 /* After merging, reallocate RP so we release memory to the system. | |
418 Also add a sentinel at the end of RP, to avoid out of bounds access | |
419 and for performance reasons. */ | |
420 ++n_rp; | |
421 rp = xrealloc (rp, n_rp * sizeof (struct range_pair)); | |
422 rp[n_rp - 1].lo = rp[n_rp - 1].hi = SIZE_MAX; | |
423 | |
424 return field_found; | |
425 } | |
426 | |
427 /* Increment *ITEM_IDX (i.e., a field or byte index), | |
428 and if required CURRENT_RP. */ | |
429 | |
430 static inline void | |
431 next_item (size_t *item_idx) | |
432 { | |
433 (*item_idx)++; | |
434 if ((*item_idx) > current_rp->hi) | |
435 current_rp++; | |
436 } | |
437 | |
438 /* Return nonzero if the K'th field or byte is printable. */ | |
439 | |
440 static inline bool | |
441 print_kth (size_t k) | |
442 { | |
443 return current_rp->lo <= k; | |
444 } | |
445 | |
446 /* Return nonzero if K'th byte is the beginning of a range. */ | |
447 | |
448 static inline bool | |
449 is_range_start_index (size_t k) | |
450 { | |
451 return k == current_rp->lo; | |
452 } | |
453 | |
454 /* Read from stream STREAM, printing to standard output any selected bytes. */ | |
455 | |
456 static void | |
457 cut_bytes (FILE *stream) | |
458 { | |
459 size_t byte_idx; /* Number of bytes in the line so far. */ | |
460 /* Whether to begin printing delimiters between ranges for the current line. | |
461 Set after we've begun printing data corresponding to the first range. */ | |
462 bool print_delimiter; | |
463 | |
464 byte_idx = 0; | |
465 print_delimiter = false; | |
466 current_rp = rp; | |
467 while (true) | |
468 { | |
469 int c; /* Each character from the file. */ | |
470 | |
471 c = getc (stream); | |
472 | |
473 if (c == '\n') | |
474 { | |
475 putchar ('\n'); | |
476 byte_idx = 0; | |
477 print_delimiter = false; | |
478 current_rp = rp; | |
479 } | |
480 else if (c == EOF) | |
481 { | |
482 if (byte_idx > 0) | |
483 putchar ('\n'); | |
484 break; | |
485 } | |
486 else | |
487 { | |
488 next_item (&byte_idx); | |
489 if (print_kth (byte_idx)) | |
490 { | |
491 if (output_delimiter_specified) | |
492 { | |
493 if (print_delimiter && is_range_start_index (byte_idx)) | |
494 { | |
495 fwrite (output_delimiter_string, sizeof (char), | |
496 output_delimiter_length, stdout); | |
497 } | |
498 print_delimiter = true; | |
499 } | |
500 | |
501 putchar (c); | |
502 } | |
503 } | |
504 } | |
505 } | |
506 | |
507 /* Read from stream STREAM, printing to standard output any selected fields. */ | |
508 | |
509 static void | |
510 cut_fields (FILE *stream) | |
511 { | |
512 int c; | |
513 size_t field_idx = 1; | |
514 bool found_any_selected_field = false; | |
515 bool buffer_first_field; | |
516 | |
517 current_rp = rp; | |
518 | |
519 c = getc (stream); | |
520 if (c == EOF) | |
521 return; | |
522 | |
523 ungetc (c, stream); | |
524 c = 0; | |
525 | |
526 /* To support the semantics of the -s flag, we may have to buffer | |
527 all of the first field to determine whether it is 'delimited.' | |
528 But that is unnecessary if all non-delimited lines must be printed | |
529 and the first field has been selected, or if non-delimited lines | |
530 must be suppressed and the first field has *not* been selected. | |
531 That is because a non-delimited line has exactly one field. */ | |
532 buffer_first_field = (suppress_non_delimited ^ !print_kth (1)); | |
533 | |
534 while (1) | |
535 { | |
536 if (field_idx == 1 && buffer_first_field) | |
537 { | |
538 ssize_t len; | |
539 size_t n_bytes; | |
540 | |
541 len = getndelim2 (&field_1_buffer, &field_1_bufsize, 0, | |
542 GETNLINE_NO_LIMIT, delim, '\n', stream); | |
543 if (len < 0) | |
544 { | |
545 free (field_1_buffer); | |
546 field_1_buffer = NULL; | |
547 if (ferror (stream) || feof (stream)) | |
548 break; | |
549 xalloc_die (); | |
550 } | |
551 | |
552 n_bytes = len; | |
553 assert (n_bytes != 0); | |
554 | |
555 c = 0; | |
556 | |
557 /* If the first field extends to the end of line (it is not | |
558 delimited) and we are printing all non-delimited lines, | |
559 print this one. */ | |
560 if (to_uchar (field_1_buffer[n_bytes - 1]) != delim) | |
561 { | |
562 if (suppress_non_delimited) | |
563 { | |
564 /* Empty. */ | |
565 } | |
566 else | |
567 { | |
568 fwrite (field_1_buffer, sizeof (char), n_bytes, stdout); | |
569 /* Make sure the output line is newline terminated. */ | |
570 if (field_1_buffer[n_bytes - 1] != '\n') | |
571 putchar ('\n'); | |
572 c = '\n'; | |
573 } | |
574 continue; | |
575 } | |
576 if (print_kth (1)) | |
577 { | |
578 /* Print the field, but not the trailing delimiter. */ | |
579 fwrite (field_1_buffer, sizeof (char), n_bytes - 1, stdout); | |
580 | |
581 /* With -d$'\n' don't treat the last '\n' as a delimiter. */ | |
582 if (delim == '\n') | |
583 { | |
584 int last_c = getc (stream); | |
585 if (last_c != EOF) | |
586 { | |
587 ungetc (last_c, stream); | |
588 found_any_selected_field = true; | |
589 } | |
590 } | |
591 else | |
592 found_any_selected_field = true; | |
593 } | |
594 next_item (&field_idx); | |
595 } | |
596 | |
597 int prev_c = c; | |
598 | |
599 if (print_kth (field_idx)) | |
600 { | |
601 if (found_any_selected_field) | |
602 { | |
603 fwrite (output_delimiter_string, sizeof (char), | |
604 output_delimiter_length, stdout); | |
605 } | |
606 found_any_selected_field = true; | |
607 | |
608 while ((c = getc (stream)) != delim && c != '\n' && c != EOF) | |
609 { | |
610 putchar (c); | |
611 prev_c = c; | |
612 } | |
613 } | |
614 else | |
615 { | |
616 while ((c = getc (stream)) != delim && c != '\n' && c != EOF) | |
617 { | |
618 prev_c = c; | |
619 } | |
620 } | |
621 | |
622 /* With -d$'\n' don't treat the last '\n' as a delimiter. */ | |
623 if (delim == '\n' && c == delim) | |
624 { | |
625 int last_c = getc (stream); | |
626 if (last_c != EOF) | |
627 ungetc (last_c, stream); | |
628 else | |
629 c = last_c; | |
630 } | |
631 | |
632 if (c == delim) | |
633 next_item (&field_idx); | |
634 else if (c == '\n' || c == EOF) | |
635 { | |
636 if (found_any_selected_field | |
637 || !(suppress_non_delimited && field_idx == 1)) | |
638 { | |
639 if (c == '\n' || prev_c != '\n' || delim == '\n') | |
640 putchar ('\n'); | |
641 } | |
642 if (c == EOF) | |
643 break; | |
644 field_idx = 1; | |
645 current_rp = rp; | |
646 found_any_selected_field = false; | |
647 } | |
648 } | |
649 } | |
650 | |
651 static void | |
652 cut_stream (FILE *stream) | |
653 { | |
654 if (operating_mode == byte_mode) | |
655 cut_bytes (stream); | |
656 else | |
657 cut_fields (stream); | |
658 } | |
659 | |
660 /* Process file FILE to standard output. | |
661 Return true if successful. */ | |
662 | |
663 static bool | |
664 cut_file (char const *file) | |
665 { | |
666 FILE *stream; | |
667 | |
668 if (STREQ (file, "-")) | |
669 { | |
670 have_read_stdin = true; | |
671 stream = stdin; | |
672 } | |
673 else | |
674 { | |
675 stream = fopen (file, "r"); | |
676 if (stream == NULL) | |
677 { | |
678 error (0, errno, "%s", file); | |
679 return false; | |
680 } | |
681 } | |
682 | |
683 fadvise (stream, FADVISE_SEQUENTIAL); | |
684 | |
685 cut_stream (stream); | |
686 | |
687 if (ferror (stream)) | |
688 { | |
689 error (0, errno, "%s", file); | |
690 return false; | |
691 } | |
692 if (STREQ (file, "-")) | |
693 clearerr (stream); /* Also clear EOF. */ | |
694 else if (fclose (stream) == EOF) | |
695 { | |
696 error (0, errno, "%s", file); | |
697 return false; | |
698 } | |
699 return true; | |
700 } | |
701 | |
702 int | |
703 main (int argc, char **argv) | |
704 { | |
705 int optc; | |
706 bool ok; | |
707 bool delim_specified = false; | |
708 char *spec_list_string IF_LINT ( = NULL); | |
709 | |
710 initialize_main (&argc, &argv); | |
711 set_program_name (argv[0]); | |
712 setlocale (LC_ALL, ""); | |
713 bindtextdomain (PACKAGE, LOCALEDIR); | |
714 textdomain (PACKAGE); | |
715 | |
716 atexit (close_stdout); | |
717 | |
718 operating_mode = undefined_mode; | |
719 | |
720 /* By default, all non-delimited lines are printed. */ | |
721 suppress_non_delimited = false; | |
722 | |
723 delim = '\0'; | |
724 have_read_stdin = false; | |
725 | |
726 while ((optc = getopt_long (argc, argv, "b:c:d:f:ns", longopts, NULL)) != -1) | |
727 { | |
728 switch (optc) | |
729 { | |
730 case 'b': | |
731 case 'c': | |
732 /* Build the byte list. */ | |
733 if (operating_mode != undefined_mode) | |
734 FATAL_ERROR (_("only one type of list may be specified")); | |
735 operating_mode = byte_mode; | |
736 spec_list_string = optarg; | |
737 break; | |
738 | |
739 case 'f': | |
740 /* Build the field list. */ | |
741 if (operating_mode != undefined_mode) | |
742 FATAL_ERROR (_("only one type of list may be specified")); | |
743 operating_mode = field_mode; | |
744 spec_list_string = optarg; | |
745 break; | |
746 | |
747 case 'd': | |
748 /* New delimiter. */ | |
749 /* Interpret -d '' to mean 'use the NUL byte as the delimiter.' */ | |
750 if (optarg[0] != '\0' && optarg[1] != '\0') | |
751 FATAL_ERROR (_("the delimiter must be a single character")); | |
752 delim = optarg[0]; | |
753 delim_specified = true; | |
754 break; | |
755 | |
756 case OUTPUT_DELIMITER_OPTION: | |
757 output_delimiter_specified = true; | |
758 /* Interpret --output-delimiter='' to mean | |
759 'use the NUL byte as the delimiter.' */ | |
760 output_delimiter_length = (optarg[0] == '\0' | |
761 ? 1 : strlen (optarg)); | |
762 output_delimiter_string = xstrdup (optarg); | |
763 break; | |
764 | |
765 case 'n': | |
766 break; | |
767 | |
768 case 's': | |
769 suppress_non_delimited = true; | |
770 break; | |
771 | |
772 case COMPLEMENT_OPTION: | |
773 complement = true; | |
774 break; | |
775 | |
776 case_GETOPT_HELP_CHAR; | |
777 | |
778 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); | |
779 | |
780 default: | |
781 usage (EXIT_FAILURE); | |
782 } | |
783 } | |
784 | |
785 if (operating_mode == undefined_mode) | |
786 FATAL_ERROR (_("you must specify a list of bytes, characters, or fields")); | |
787 | |
788 if (delim_specified && operating_mode != field_mode) | |
789 FATAL_ERROR (_("an input delimiter may be specified only\ | |
790 when operating on fields")); | |
791 | |
792 if (suppress_non_delimited && operating_mode != field_mode) | |
793 FATAL_ERROR (_("suppressing non-delimited lines makes sense\n\ | |
794 \tonly when operating on fields")); | |
795 | |
796 if (! set_fields (spec_list_string)) | |
797 { | |
798 if (operating_mode == field_mode) | |
799 FATAL_ERROR (_("missing list of fields")); | |
800 else | |
801 FATAL_ERROR (_("missing list of positions")); | |
802 } | |
803 | |
804 if (!delim_specified) | |
805 delim = '\t'; | |
806 | |
807 if (output_delimiter_string == NULL) | |
808 { | |
809 static char dummy[2]; | |
810 dummy[0] = delim; | |
811 dummy[1] = '\0'; | |
812 output_delimiter_string = dummy; | |
813 output_delimiter_length = 1; | |
814 } | |
815 | |
816 if (optind == argc) | |
817 ok = cut_file ("-"); | |
818 else | |
819 for (ok = true; optind < argc; optind++) | |
820 ok &= cut_file (argv[optind]); | |
821 | |
822 | |
823 if (have_read_stdin && fclose (stdin) == EOF) | |
824 { | |
825 error (0, errno, "-"); | |
826 ok = false; | |
827 } | |
828 | |
829 return ok ? EXIT_SUCCESS : EXIT_FAILURE; | |
830 } |