.
authorJim Meyering <jim@meyering.net>
Wed, 22 Dec 1993 22:41:00 +0000 (22:41 +0000)
committerJim Meyering <jim@meyering.net>
Wed, 22 Dec 1993 22:41:00 +0000 (22:41 +0000)
src/cut.c

index da32597..f6f6bfa 100644 (file)
--- a/src/cut.c
+++ b/src/cut.c
 #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
   {
@@ -143,9 +156,10 @@ char *program_name;
 
 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;
@@ -153,10 +167,6 @@ 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;
 
@@ -175,128 +185,164 @@ static struct option const longopts[] =
   {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 (;;)
     {
@@ -310,8 +356,6 @@ set_fields (fieldstr)
 
          if (value)
            {
-             if (value >= line_size)
-               enlarge_line (value);
              initial = value;
              value = 0;
            }
@@ -331,7 +375,7 @@ set_fields (fieldstr)
                {
                  /* `n-'.  From `initial' to end of line. */
                  eol_range_start = initial;
-                 fields_selected++;
+                 field_found = 1;
                }
              else
                {
@@ -339,9 +383,6 @@ set_fields (fieldstr)
                  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)
                    {
@@ -355,27 +396,25 @@ set_fields (fieldstr)
                             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;
                }
@@ -383,35 +422,210 @@ set_fields (fieldstr)
          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.
@@ -455,207 +669,103 @@ cut_file (file)
   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);
 }