#define _GNU_SOURCE
#include <stdio.h>
+
+/* FIXME */
+/* #define NDEBUG */
+#include <assert.h>
+
#include <getopt.h>
#include <sys/types.h>
#include "system.h"
} \
while (0)
+struct range_pair
+ {
+ int lo;
+ int hi;
+ };
+
+#define ADD_RANGE_PAIR(rp, low, high) \
+ do \
+ { \
+ if (n_rp >= n_rp_allocated) \
+ { \
+ n_rp_allocated *= 2; \
+ (rp) = (struct range_pair *) xrealloc ((rp), \
+ n_rp_allocated * sizeof (*(rp))); \
+ } \
+ rp[n_rp].lo = (low); \
+ rp[n_rp].hi = (high); \
+ ++n_rp; \
+ } \
+ while (0)
+
char *xmalloc ();
char *xrealloc ();
void error ();
-static int set_fields ();
-static int cut_file ();
-static void cut_stream ();
-static void cut_bytes ();
-static void cut_fields ();
-static void enlarge_line ();
-static void usage ();
-
-/* The number of elements allocated for the input line
- and the byte or field number.
- Enlarged as necessary. */
-static int line_size;
-
-/* Processed output buffer. */
-static char *outbuf;
-
-/* Where to save next char to output. */
-static char *outbufptr;
+/* FIXME: Comment. */
+static char *field_1_buffer;
-/* Raw line buffer for field mode. */
-static char *inbuf;
+/* FIXME: Comment. */
+static int field_1_bufsize;
-/* Where to save next input char. */
-static char *inbufptr;
+/* The largest field or byte index used as an endpoint of a closed
+ or degenerate range specification; this doesn't include the starting
+ index of right-open-ended ranges. For example, with either range spec
+ `2-5,9-', `2-3,5,9-' this variable would be set to 5. */
+static int max_range_endpoint;
-/* What can be done about a byte or field. */
-enum field_action
- {
- FIELD_OMIT,
- FIELD_OUTPUT
- };
+/* If nonzero, this is the index of the first field in a range that goes
+ to end of line. */
+static int eol_range_start;
/* In byte mode, which bytes to output.
- In field mode, which `delim'-separated fields to output.
+ In field mode, which DELIM-separated fields to output.
Both bytes and fields are numbered starting with 1,
- so the first element of `fields' is unused. */
-static enum field_action *fields;
+ so the zeroth element of this array is unused.
+ A field or byte K has been selected if
+ (K <= MAX_RANGE_ENDPOINT and PRINTABLE_FIELD[K])
+ || (EOL_RANGE_START > 0 && K >= EOL_RANGE_START). */
+static int *printable_field;
enum operating_mode
{
static enum operating_mode operating_mode;
-/* If nonzero,
- for field mode, do not output lines containing no delimeter characters. */
-static int delimited_lines_only;
+/* If non-zero do not output lines containing no delimeter characters.
+ Otherwise, all such lines are printed. This option is valid only
+ with field mode. */
+static int suppress_non_delimited;
/* The delimeter character for field mode. */
static unsigned char delim;
/* Nonzero if we have ever read standard input. */
static int have_read_stdin;
-/* If nonzero, this is the index of the first field in a range that goes
- to end of line. */
-static int eol_range_start;
-
/* If non-zero, display usage information and exit. */
static int show_help;
{0, 0, 0, 0}
};
-void
-main (argc, argv)
- int argc;
- char **argv;
+static void
+usage (status)
+ int status;
{
- int optc, exit_status = 0;
-
- program_name = argv[0];
-
- line_size = 512;
- operating_mode = undefined_mode;
- delimited_lines_only = 0;
- delim = '\0';
- have_read_stdin = 0;
-
- fields = (enum field_action *)
- xmalloc (line_size * sizeof (enum field_action));
- outbuf = (char *) xmalloc (line_size);
- inbuf = (char *) xmalloc (line_size);
-
- for (optc = 0; optc < line_size; optc++)
- fields[optc] = FIELD_OMIT;
-
- while ((optc = getopt_long (argc, argv, "b:c:d:f:ns", longopts, (int *) 0))
- != EOF)
+ if (status != 0)
+ fprintf (stderr, "Try `%s --help' for more information.\n",
+ program_name);
+ else
{
- switch (optc)
- {
- case 0:
- break;
-
- case 'b':
- case 'c':
- /* Build the byte list. */
- if (operating_mode != undefined_mode)
- FATAL_ERROR ("only one type of list may be specified");
- operating_mode = byte_mode;
- if (set_fields (optarg) == 0)
- FATAL_ERROR ("missing list of positions");
- break;
-
- case 'f':
- /* Build the field list. */
- if (operating_mode != undefined_mode)
- FATAL_ERROR ("only one type of list may be specified");
- operating_mode = field_mode;
- if (set_fields (optarg) == 0)
- FATAL_ERROR ("missing list of fields");
- break;
+ printf ("\
+Usage: %s [OPTION]... [FILE]...\n\
+",
+ program_name);
+ printf ("\
+\n\
+ -b, --bytes=LIST output only these bytes\n\
+ -c, --characters=LIST output only these characters\n\
+ -d, --delimiter=DELIM use DELIM instead of TAB for field delimiter\n\
+ -f, --fields=LIST output only these fields\n\
+ -n (ignored)\n\
+ -s, --only-delimited do not print lines not containing delimiters\n\
+ --help display this help and exit\n\
+ --version output version information and exit\n\
+\n\
+Use one, and only one of -b, -c or -f. Each LIST is made up of one\n\
+range, or many ranges separated by commas. Each range is one of:\n\
+\n\
+ N N'th byte, character or field, counted from 1\n\
+ N- from N'th byte, character or field, to end of line\n\
+ N-M from N'th to M'th (included) byte, character or field\n\
+ -M from first to M'th (included) byte, character or field\n\
+\n\
+With no FILE, or when FILE is -, read standard input.\n\
+");
+ }
+ exit (status);
+}
- case 'd':
- /* New delimiter. */
- if (optarg[0] == '\0')
- FATAL_ERROR ("missing delimiter argument");
- if (optarg[1] != '\0')
- FATAL_ERROR ("the delimiter must be a single character");
- delim = optarg[0];
- break;
+/* Begin ------------ from getline.c */
+/* Always add at least this many bytes when extending the buffer. */
+#define MIN_CHUNK 64
- case 'n':
- break;
+/* Read up to (and including) a newline or TERMINATOR from STREAM into
+ *LINEPTR (and null-terminate it). *LINEPTR is a pointer returned from
+ xmalloc (or NULL), pointing to *N characters of space. It is
+ xrealloc'd as necessary. Return the number of characters read (not
+ including the null terminator), or -1 on error or EOF. */
- case 's':
- delimited_lines_only++;
- break;
+int
+getstr (lineptr, n, stream, terminator)
+ char **lineptr;
+ int *n;
+ FILE *stream;
+ char terminator;
+{
+ int nchars_avail; /* Allocated but unused chars in *LINEPTR. */
+ char *read_pos; /* Where we're reading into *LINEPTR. */
- default:
- usage (2);
- }
- }
+ if (!lineptr || !n || !stream)
+ return -1;
- if (show_version)
+ if (!*lineptr)
{
- printf ("%s\n", version_string);
- exit (0);
+ *n = MIN_CHUNK;
+ *lineptr = xmalloc (*n);
+ if (!*lineptr)
+ return -1;
}
- if (show_help)
- usage (0);
+ nchars_avail = *n;
+ read_pos = *lineptr;
- if (operating_mode == undefined_mode)
- FATAL_ERROR ("you must specify a list of bytes, characters, or fields");
+ for (;;)
+ {
+ register int c = getc (stream);
- if ((delimited_lines_only || delim != '\0') && operating_mode != field_mode)
- FATAL_ERROR ("a delimiter may be specified only when operating on fields");
+ /* We always want at least one char left in the buffer, since we
+ always (unless we get an error while reading the first char)
+ NUL-terminate the line buffer. */
- if (delim == '\0')
- delim = '\t';
+ assert (*n - nchars_avail == read_pos - *lineptr);
+ if (nchars_avail < 1)
+ {
+ if (*n > MIN_CHUNK)
+ *n *= 2;
+ else
+ *n += MIN_CHUNK;
+
+ nchars_avail = *n + *lineptr - read_pos;
+ *lineptr = xrealloc (*lineptr, *n);
+ if (!*lineptr)
+ return -1;
+ read_pos = *n - nchars_avail + *lineptr;
+ assert (*n - nchars_avail == read_pos - *lineptr);
+ }
- if (optind == argc)
- exit_status |= cut_file ("-");
- else
- for (; optind < argc; optind++)
- exit_status |= cut_file (argv[optind]);
+ if (feof (stream) || ferror (stream))
+ {
+ /* Return partial line, if any. */
+ if (read_pos == *lineptr)
+ return -1;
+ else
+ break;
+ }
- if (have_read_stdin && fclose (stdin) == EOF)
- {
- error (0, errno, "-");
- exit_status = 1;
+ *read_pos++ = c;
+ nchars_avail--;
+
+ if (c == terminator || c == '\n')
+ /* Return the line. */
+ break;
}
- if (ferror (stdout) || fclose (stdout) == EOF)
- error (1, errno, "write error");
- exit (exit_status);
+ /* Done - NUL terminate and return the number of chars read. */
+ *read_pos = '\0';
+
+ return read_pos - *lineptr;
}
-/* Select for printing the positions in `fields' that are listed in
- byte or field specification FIELDSTR. FIELDSTR should be
- composed of one or more numbers or ranges of numbers, separated by
- blanks or commas. Incomplete ranges may be given: `-m' means
- `1-m'; `n-' means `n' through end of line or last field.
+static int
+print_kth (k)
+ int k;
+{
+ return ((eol_range_start > 0 && eol_range_start <= k)
+ || (k <= max_range_endpoint && printable_field[k]));
+}
- Return the number of fields selected. */
+/* Given the list of field or byte range specifications FIELDSTR, set
+ MAX_RANGE_ENDPOINT and allocate and initialize the PRINTABLE_FIELD
+ array. If there is a right-open-ended range, set EOL_RANGE_START
+ to its starting index. FIELDSTR should be composed of one or more
+ numbers or ranges of numbers, separated by blanks or commas.
+ Incomplete ranges may be given: `-m' means `1-m'; `n-' means `n'
+ through end of line or last field. Return non-zero if FIELDSTR
+ contains at least one field specification, zero otherwise. */
static int
set_fields (fieldstr)
char *fieldstr;
{
- int initial = 1; /* Value of first number in a range. */
- int dash_found = 0; /* Nonzero if a '-' is found in this field. */
- int value = 0; /* If nonzero, a number being accumulated. */
- int fields_selected = 0; /* Number of fields selected so far. */
+ int initial = 1; /* Value of first number in a range. */
+ int dash_found = 0; /* Nonzero if a '-' is found in this field. */
+ int value = 0; /* If nonzero, a number being accumulated. */
+ int field_found = 0; /* Non-zero if at least one field spec
+ has been processed. */
+
+ struct range_pair *rp;
+ unsigned int n_rp;
+ unsigned int n_rp_allocated;
+ int i;
+
+ n_rp = 0;
+ /* FIXME: use 1 only for testing. */
+ n_rp_allocated = 1;
+ rp = (struct range_pair *) xmalloc (n_rp_allocated * sizeof (*rp));
+
+ /* Collect and store in RP the range end points.
+ It also sets EOL_RANGE_START if appropriate. */
for (;;)
{
if (value)
{
- if (value >= line_size)
- enlarge_line (value);
initial = value;
value = 0;
}
{
/* `n-'. From `initial' to end of line. */
eol_range_start = initial;
- fields_selected++;
+ field_found = 1;
}
else
{
if (value < initial)
FATAL_ERROR ("invalid byte or field list");
- if (value >= line_size)
- enlarge_line (value);
-
/* Is there already a range going to end of line? */
if (eol_range_start != 0)
{
extend into the new range? */
if (value >= eol_range_start - 1)
{
- /* Yes. Simply move the end of line marker. */
- eol_range_start = initial;
+ /* Yes. Simply move the end of line marker. */
+ eol_range_start = initial;
}
else
{
/* No. A simple range, before and disjoint from
the range going to end of line. Fill it. */
- for (; initial <= value; initial++)
- fields[initial] = FIELD_OUTPUT;
+ ADD_RANGE_PAIR (rp, initial, value);
}
/* In any case, some fields were selected. */
- fields_selected++;
+ field_found = 1;
}
}
else
{
/* There is no range going to end of line. */
- for (; initial <= value; initial++)
- fields[initial] = FIELD_OUTPUT;
- fields_selected++;
+ ADD_RANGE_PAIR (rp, initial, value);
+ field_found = 1;
}
value = 0;
}
else if (value != 0)
{
/* A simple field number, not a range. */
- if (value >= line_size)
- enlarge_line (value);
-
- fields[value] = FIELD_OUTPUT;
+ ADD_RANGE_PAIR (rp, value, value);
value = 0;
- fields_selected++;
+ field_found = 1;
}
if (*fieldstr == '\0')
{
- /* If there was a range going to end of line, fill the
- array from the end of line point. */
- if (eol_range_start)
- for (initial = eol_range_start; initial < line_size; initial++)
- fields[initial] = FIELD_OUTPUT;
-
- return fields_selected;
+ break;
}
fieldstr++;
}
else if (ISDIGIT (*fieldstr))
{
+ /* FIXME: detect overflow? */
value = 10 * value + *fieldstr - '0';
fieldstr++;
}
else
FATAL_ERROR ("invalid byte or field list");
}
+
+ max_range_endpoint = 0;
+ for (i = 0; i < n_rp; i++)
+ {
+ if (rp[i].hi > max_range_endpoint)
+ max_range_endpoint = rp[i].hi;
+ }
+
+ /* Allocate an array large enough so that it may be indexed by
+ the field numbers corresponding to all finite ranges
+ (i.e. `2-6' or `-4', but not `5-') in FIELDSTR. */
+
+ printable_field = (int *) xmalloc ((max_range_endpoint + 1) * sizeof (int));
+ for (i = 1; i <= max_range_endpoint; i++)
+ printable_field[i] = 0;
+
+ /* Set the array entries corresponding to integers in the ranges of RP. */
+ for (i = 0; i < n_rp; i++)
+ {
+ int j;
+ for (j = rp[i].lo; j <= rp[i].hi; j++)
+ {
+ printable_field[j] = 1;
+ }
+ }
+
+ free (rp);
+
+ return field_found;
+}
+
+/* Print the file open for reading on stream STREAM
+ with the bytes marked `FIELD_OMIT' in `fields' removed from each line. */
+
+static void
+cut_bytes (stream)
+ FILE *stream;
+{
+ int n_bytes; /* Number of chars in the line so far. */
+ int printed_from_curr_line;
+
+ printed_from_curr_line = 0;
+ n_bytes = 0;
+ while (1)
+ {
+ register int c; /* Each character from the file. */
+
+ c = getc (stream);
+
+ if (c == '\n' || c == EOF)
+ {
+ if (printed_from_curr_line)
+ putchar ('\n');
+ if (c == EOF)
+ break;
+ printed_from_curr_line = 0;
+ n_bytes = 0;
+ }
+ else
+ {
+ ++n_bytes;
+ if (print_kth (n_bytes))
+ {
+ printed_from_curr_line = 1;
+ putchar (c);
+ }
+ }
+ /* WORKING */
+ }
+}
+
+/* Read from stream STREAM, printing to standard output any selected fields.
+ FIXME: comment. */
+
+static void
+cut_fields (FILE *stream)
+{
+ int c;
+ int field_idx;
+ int found_any_selected_field;
+ int first_field_special;
+
+ found_any_selected_field = 0;
+ field_idx = 1;
+
+ /* To support the semantics of the -s flag, we may have to buffer
+ all of the first field to determine whether it is `delimited.'
+ But that is unnecessary if all non-delimited lines must be printed
+ and the first field has been selected, or if non-delimited lines
+ must be suppressed and the first field has *not* been selected.
+ That is because a non-delimited line has exactly one field. */
+ first_field_special = (suppress_non_delimited ^ !print_kth (1));
+
+ while (1)
+ {
+ if (field_idx == 1 && first_field_special)
+ {
+ int len;
+
+ len = getstr (&field_1_buffer, &field_1_bufsize, stream, delim);
+ if (len < 0)
+ break;
+
+ assert (len != 0);
+
+ /* If the first field extends to the end of line (it is not
+ delimited) and we are printing all non-delimited lines,
+ print this one. */
+ if (field_1_buffer[len - 1] != delim)
+ {
+ if (suppress_non_delimited)
+ {
+ /* Empty. */
+ }
+ else
+ {
+ fwrite (field_1_buffer, sizeof (char), len, stdout);
+ /* Make sure the output line is newline terminated. */
+ if (field_1_buffer[len - 1] != '\n')
+ putchar ('\n');
+ }
+ continue;
+ }
+ if (print_kth (1))
+ {
+ /* Print the field, but not the trailing delimiter. */
+ fwrite (field_1_buffer, sizeof (char), len - 1, stdout);
+ found_any_selected_field = 1;
+ }
+ ++field_idx;
+ }
+
+ if (print_kth (field_idx))
+ {
+ if (found_any_selected_field)
+ putchar (delim);
+ found_any_selected_field = 1;
+
+ while ((c = getc (stream)) != delim && c != '\n' && c != EOF)
+ {
+ putchar (c);
+ }
+ }
+ else
+ {
+ while ((c = getc (stream)) != delim && c != '\n' && c != EOF)
+ {
+ /* Empty. */
+ }
+ }
+
+ if (c == '\n')
+ {
+ c = getc (stream);
+ if (c != EOF)
+ {
+ ungetc (c, stream);
+ c = '\n';
+ }
+ }
+
+ if (c == delim)
+ ++field_idx;
+ else if (c == '\n' || c == EOF)
+ {
+ if (found_any_selected_field)
+ putchar ('\n');
+ if (c == EOF)
+ break;
+ field_idx = 1;
+ found_any_selected_field = 0;
+ }
+ }
+}
+
+static void
+cut_stream (stream)
+ FILE *stream;
+{
+ if (operating_mode == byte_mode)
+ cut_bytes (stream);
+ else
+ cut_fields (stream);
}
/* Process file FILE to standard output.
return 0;
}
-static void
-cut_stream (stream)
- FILE *stream;
-{
- if (operating_mode == byte_mode)
- cut_bytes (stream);
- else
- cut_fields (stream);
-}
-
-/* Print the file open for reading on stream STREAM
- with the bytes marked `FIELD_OMIT' in `fields' removed from each line. */
-
-static void
-cut_bytes (stream)
- FILE *stream;
+void
+main (argc, argv)
+ int argc;
+ char **argv;
{
- register int c; /* Each character from the file. */
- int doneflag = 0; /* Nonzero if EOF reached. */
- int char_count; /* Number of chars in the line so far. */
-
- while (doneflag == 0)
- {
- /* Start processing a line. */
- outbufptr = outbuf;
- char_count = 0;
-
- do
- {
- c = getc (stream);
- if (c == EOF)
- {
- doneflag++;
- break;
- }
-
- /* If this character is to be sent, stow it in the outbuffer. */
-
- if (++char_count == line_size - 1)
- enlarge_line (char_count);
-
- if (fields[char_count] == FIELD_OUTPUT || c == '\n')
- *outbufptr++ = c;
- }
- while (c != '\n');
+ int optc, exit_status = 0;
- if (char_count)
- fwrite (outbuf, sizeof (char), outbufptr - outbuf, stdout);
- }
-}
+ program_name = argv[0];
-/* Print the file open for reading on stream STREAM
- with the fields marked `FIELD_OMIT' in `fields' removed from each line.
- All characters are initially stowed in the raw input buffer, until
- at least one field has been found. */
+ operating_mode = undefined_mode;
-static void
-cut_fields (stream)
- FILE *stream;
-{
- register int c; /* Each character from the file. */
- int last_c; /* The previous character. */
- int doneflag = 0; /* Nonzero if EOF reached. */
- int char_count; /* Number of chars in line before any delim. */
- int fieldfound; /* Nonzero if any fields to print found. */
- int curr_field; /* Current index in `fields'. */
+ /* By default, all non-delimited lines are printed. */
+ suppress_non_delimited = 0;
- c = EOF;
+ delim = '\0';
+ have_read_stdin = 0;
- while (doneflag == 0)
+ while ((optc = getopt_long (argc, argv, "b:c:d:f:ns", longopts, (int *) 0))
+ != EOF)
{
- char_count = 0;
- fieldfound = 0;
- curr_field = 1;
- outbufptr = outbuf;
- inbufptr = inbuf;
-
- do
+ switch (optc)
{
- last_c = c;
- c = getc (stream);
- if (c == EOF)
- {
- doneflag++;
- if (last_c == '\n' || last_c == EOF)
- break;
+ case 0:
+ break;
- /* The last character from the input stream is not a
- newline. Pretend that the input was NL terminated.
- But do that only if the file is not completely empty. */
- c = '\n';
- }
+ case 'b':
+ case 'c':
+ /* Build the byte list. */
+ if (operating_mode != undefined_mode)
+ FATAL_ERROR ("only one type of list may be specified");
+ operating_mode = byte_mode;
+ if (set_fields (optarg) == 0)
+ FATAL_ERROR ("missing list of positions");
+ break;
- if (fields[curr_field] == FIELD_OUTPUT && c != '\n')
- {
- /* Working on a field. It, and its terminating
- delimiter, go only into the processed buffer. */
- fieldfound = 1;
- if (outbufptr - outbuf == line_size - 2)
- enlarge_line (outbufptr - outbuf);
- *outbufptr++ = c;
- }
- else if (fieldfound == 0)
- {
- if (++char_count == line_size - 1)
- enlarge_line (char_count);
- *inbufptr++ = c;
- }
+ case 'f':
+ /* Build the field list. */
+ if (operating_mode != undefined_mode)
+ FATAL_ERROR ("only one type of list may be specified");
+ operating_mode = field_mode;
+ if (set_fields (optarg) == 0)
+ FATAL_ERROR ("missing list of fields");
+ break;
- if (c == delim)
- {
- ++curr_field;
- if (curr_field == line_size - 1)
- enlarge_line (curr_field);
- }
- }
- while (c != '\n' && !doneflag);
+ case 'd':
+ /* New delimiter. */
+ if (optarg[0] == '\0')
+ FATAL_ERROR ("missing delimiter argument");
+ if (optarg[1] != '\0')
+ FATAL_ERROR ("the delimiter must be a single character");
+ delim = optarg[0];
+ break;
- if (fieldfound)
- {
- /* Something was found. Print it. */
+ case 'n':
+ break;
- if ((unsigned char) outbufptr[-1] == delim && eol_range_start == 0)
- {
- /* Suppress the trailing delimiter unless there is a range
- extending to end of line. */
- --outbufptr;
- }
+ case 's':
+ suppress_non_delimited = 1;
+ break;
- fwrite (outbuf, sizeof (char), outbufptr - outbuf, stdout);
- if (c == '\n')
- putc (c, stdout);
+ default:
+ usage (2);
}
- else if (!delimited_lines_only && char_count)
- /* A line with some characters, no delimiters, and no
- suppression. Print it. */
- fwrite (inbuf, sizeof (char), inbufptr - inbuf, stdout);
}
-}
-
-/* Extend the buffers to accomodate at least NEW_SIZE characters. */
-static void
-enlarge_line (new_size)
- int new_size;
-{
- char *newp;
- int i;
-
- new_size += 256; /* Leave some room to grow. */
+ if (show_version)
+ {
+ printf ("%s\n", version_string);
+ exit (0);
+ }
- fields = (enum field_action *)
- xrealloc (fields, new_size * sizeof (enum field_action));
+ if (show_help)
+ usage (0);
- newp = (char *) xrealloc (outbuf, new_size);
- outbufptr += newp - outbuf;
- outbuf = newp;
+ if (operating_mode == undefined_mode)
+ FATAL_ERROR ("you must specify a list of bytes, characters, or fields");
- newp = (char *) xrealloc (inbuf, new_size);
- inbufptr += newp - inbuf;
- inbuf = newp;
+ /* FIXME: what is this? */
+ if ((suppress_non_delimited || delim != '\0') && operating_mode != field_mode)
+ FATAL_ERROR ("a delimiter may be specified only when operating on fields");
- for (i = line_size; i < new_size; i++)
- fields[i] = FIELD_OMIT;
- line_size = new_size;
-}
+ if (delim == '\0')
+ delim = '\t';
-static void
-usage (status)
- int status;
-{
- if (status != 0)
- fprintf (stderr, "Try `%s --help' for more information.\n",
- program_name);
+ if (optind == argc)
+ exit_status |= cut_file ("-");
else
+ for (; optind < argc; optind++)
+ exit_status |= cut_file (argv[optind]);
+
+ if (have_read_stdin && fclose (stdin) == EOF)
{
- printf ("\
-Usage: %s [OPTION]... [FILE]...\n\
-",
- program_name);
- printf ("\
-\n\
- -b, --bytes=LIST output only these bytes\n\
- -c, --characters=LIST output only these characters\n\
- -d, --delimiter=DELIM use DELIM instead of TAB for field delimiter\n\
- -f, --fields=LIST output only these fields\n\
- -n (ignored)\n\
- -s, --only-delimited do not print lines not containing delimiters\n\
- --help display this help and exit\n\
- --version output version information and exit\n\
-\n\
-Use one, and only one of -b, -c or -f. Each LIST is made up of one\n\
-range, or many ranges separated by commas. Each range is one of:\n\
-\n\
- N N'th byte, character or field, counted from 1\n\
- N- from N'th byte, character or field, to end of line\n\
- N-M from N'th to M'th (included) byte, character or field\n\
- -M from first to M'th (included) byte, character or field\n\
-\n\
-With no FILE, or when FILE is -, read standard input.\n\
-");
+ error (0, errno, "-");
+ exit_status = 1;
}
- exit (status);
+ if (ferror (stdout) || fclose (stdout) == EOF)
+ error (1, errno, "write error");
+
+ exit (exit_status);
}