(usage): Say that
[platform/upstream/coreutils.git] / src / split.c
index e96d5ad..a02e64a 100644 (file)
@@ -1,5 +1,5 @@
 /* split.c -- split a file into pieces.
-   Copyright (C) 1988, 1991, 1995 Free Software Foundation, Inc.
+   Copyright (C) 88, 91, 1995-2001 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -12,8 +12,8 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 \f
 /* By tege@sics.se, with rms.
 
 #include <stdio.h>
 #include <getopt.h>
 #include <sys/types.h>
+
 #include "system.h"
-#include "version.h"
+#include "closeout.h"
 #include "error.h"
+#include "full-write.h"
+#include "safe-read.h"
+#include "xstrtol.h"
+
+/* The official name of this program (e.g., no `g' prefix).  */
+#define PROGRAM_NAME "split"
 
-char *xmalloc ();
-int full_write ();
-int safe_read ();
+#define AUTHORS N_ ("Torbjorn Granlund and Richard M. Stallman")
 
 /* The name this program was run with. */
 char *program_name;
@@ -56,28 +61,24 @@ static int input_desc;
 /* Descriptor on which output file is open.  */
 static int output_desc;
 
-/* If non-zero, display usage information and exit.  */
-static int show_help;
-
-/* If non-zero, print the version on standard output then exit.  */
-static int show_version;
+/* If nonzero, print a diagnostic on standard error just before each
+   output file is opened. */
+static int verbose;
 
 static struct option const longopts[] =
 {
   {"bytes", required_argument, NULL, 'b'},
   {"lines", required_argument, NULL, 'l'},
   {"line-bytes", required_argument, NULL, 'C'},
-  {"help", no_argument, &show_help, 1},
-  {"version", no_argument, &show_version, 1},
+  {"verbose", no_argument, NULL, 2},
+  {GETOPT_HELP_OPTION_DECL},
+  {GETOPT_VERSION_OPTION_DECL},
   {NULL, 0, NULL, 0}
 };
 
-static void
-usage (int status, char *reason)
+void
+usage (int status)
 {
-  if (reason != NULL)
-    fprintf (stderr, "%s: %s\n", program_name, reason);
-
   if (status != 0)
     fprintf (stderr, _("Try `%s --help' for more information.\n"),
             program_name);
@@ -91,67 +92,21 @@ Usage: %s [OPTION] [INPUT [PREFIX]]\n\
 Output fixed-size pieces of INPUT to PREFIXaa, PREFIXab, ...; default\n\
 PREFIX is `x'.  With no INPUT, or when INPUT is -, read standard input.\n\
 \n\
-  -C, --line-bytes=SIZE   put at most SIZE bytes of lines per output file\n\
+Mandatory arguments to long options are mandatory for short options too.\n\
   -b, --bytes=SIZE        put SIZE bytes per output file\n\
+  -C, --line-bytes=SIZE   put at most SIZE bytes of lines per output file\n\
   -l, --lines=NUMBER      put NUMBER lines per output file\n\
   -NUMBER                 same as -l NUMBER\n\
+      --verbose           print a diagnostic to standard error just\n\
+                            before each output file is opened\n\
       --help              display this help and exit\n\
       --version           output version information and exit\n\
 \n\
 SIZE may have a multiplier suffix: b for 512, k for 1K, m for 1 Meg.\n\
 "));
+      puts (_("\nReport bugs to <bug-textutils@gnu.org>."));
     }
-  exit (status);
-}
-
-/* Return nonzero if the string STR is composed entirely of decimal digits.  */
-
-static int
-isdigits (char *str)
-{
-  do
-    {
-      if (!ISDIGIT (*str))
-       return 0;
-      str++;
-    }
-  while (*str);
-  return 1;
-}
-
-/* Put the value of the number in STR into *VAL.
-   STR can specify a positive integer, optionally ending in `k'
-   to mean kilo or `m' to mean mega.
-   Return 0 if STR is valid, -1 if not. */
-
-static int
-convint (char *str, int *val)
-{
-  int multiplier = 1;
-  int arglen = strlen (str);
-
-  if (arglen > 1)
-    {
-      switch (str[arglen - 1])
-       {
-       case 'b':
-         multiplier = 512;
-         str[arglen - 1] = '\0';
-         break;
-       case 'k':
-         multiplier = 1024;
-         str[arglen - 1] = '\0';
-         break;
-       case 'm':
-         multiplier = 1048576;
-         str[arglen - 1] = '\0';
-         break;
-       }
-    }
-  if (!isdigits (str))
-    return -1;
-  *val = atoi (str) * multiplier;
-  return 0;
+  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
 }
 
 /* Compute the next sequential output file name suffix and store it
@@ -160,40 +115,30 @@ convint (char *str, int *val)
 static void
 next_file_name (void)
 {
-  int x;
-  char *ne;
-  unsigned int i;
+  static unsigned n_digits = 2;
+  char *p;
 
-  static int first_call = 1;
+  /* Change any suffix of `z's to `a's.  */
+  for (p = outfile_end - 1; *p == 'z'; p--)
+    {
+      *p = 'a';
+    }
 
-  /* Status for outfile name generation.  */
-  static unsigned outfile_count = 0;
-  static unsigned outfile_name_limit = 25 * 26;
-  static unsigned outfile_name_generation = 1;
+  /* Increment the rightmost non-`z' character that was present before the
+     above z/a substitutions.  There is guaranteed to be such a character.  */
+  ++(*p);
 
-  if (!first_call)
-    outfile_count++;
-  first_call = 0;
-  if (outfile_count < outfile_name_limit)
+  /* If the result of that increment operation yielded a `z' and there
+     are only `z's to the left of it, then append two more `a' characters
+     to the end and add 1 (-1 + 2) to the number of digits (we're taking
+     out this `z' and adding two `a's).  */
+  if (*p == 'z' && p == outfile_mid)
     {
-      for (ne = outfile_end - 1; ; ne--)
-       {
-         x = *ne;
-         if (x != 'z')
-           break;
-         *ne = 'a';
-       }
-      *ne = x + 1;
-      return;
+      ++n_digits;
+      ++outfile_mid;
+      *outfile_end++ = 'a';
+      *outfile_end++ = 'a';
     }
-
-  outfile_count = 0;
-  outfile_name_limit *= 26;
-  outfile_name_generation++;
-  *outfile_mid++ = 'z';
-  for (i = 0; i <= outfile_name_generation; i++)
-    outfile_mid[i] = 'a';
-  outfile_end += 2;
 }
 
 /* Write BYTES bytes at BP to an output file.
@@ -201,20 +146,23 @@ next_file_name (void)
    Otherwise add to the same output file already in use.  */
 
 static void
-cwrite (int new_file_flag, char *bp, int bytes)
+cwrite (int new_file_flag, const char *bp, int bytes)
 {
   if (new_file_flag)
     {
       if (output_desc >= 0 && close (output_desc) < 0)
-       error (1, errno, "%s", outfile);
+       error (EXIT_FAILURE, errno, "%s", outfile);
 
       next_file_name ();
-      output_desc = open (outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+      if (verbose)
+       fprintf (stderr, _("creating file `%s'\n"), outfile);
+      output_desc = open (outfile,
+                         O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
       if (output_desc < 0)
-       error (1, errno, "%s", outfile);
+       error (EXIT_FAILURE, errno, "%s", outfile);
     }
-  if (full_write (output_desc, bp, bytes) < 0)
-    error (1, errno, "%s", outfile);
+  if (full_write (output_desc, bp, bytes) != bytes)
+    error (EXIT_FAILURE, errno, "%s", outfile);
 }
 
 /* Read NCHARS bytes from the input file into BUF.
@@ -256,7 +204,7 @@ bytes_split (int nchars, char *buf, int bufsize)
     {
       n_read = stdread (buf, bufsize);
       if (n_read < 0)
-        error (1, errno, "%s", infile);
+        error (EXIT_FAILURE, errno, "%s", infile);
       bp_out = buf;
       to_read = n_read;
       for (;;)
@@ -299,7 +247,7 @@ lines_split (int nlines, char *buf, int bufsize)
     {
       n_read = stdread (buf, bufsize);
       if (n_read < 0)
-       error (1, errno, "%s", infile);
+       error (EXIT_FAILURE, errno, "%s", infile);
       bp = bp_out = buf;
       eob = bp + n_read;
       *eob = '\n';
@@ -348,7 +296,7 @@ line_bytes_split (int nchars)
 
       n_read = stdread (buf + n_buffered, nchars - n_buffered);
       if (n_read < 0)
-       error (1, errno, "%s", infile);
+       error (EXIT_FAILURE, errno, "%s", infile);
 
       n_buffered += n_read;
       if (n_buffered != nchars)
@@ -380,7 +328,7 @@ line_bytes_split (int nchars)
   free (buf);
 }
 
-void
+int
 main (int argc, char **argv)
 {
   struct stat stat_buf;
@@ -397,6 +345,11 @@ main (int argc, char **argv)
   int digits_optind = 0;
 
   program_name = argv[0];
+  setlocale (LC_ALL, "");
+  bindtextdomain (PACKAGE, LOCALEDIR);
+  textdomain (PACKAGE);
+
+  atexit (close_stdout);
 
   /* Parse command line options.  */
 
@@ -407,8 +360,9 @@ main (int argc, char **argv)
     {
       /* This is the argv-index of the option we will read next.  */
       int this_optind = optind ? optind : 1;
+      long int tmp_long;
 
-      c = getopt_long (argc, argv, "0123456789b:l:C:", longopts, (int *) 0);
+      c = getopt_long (argc, argv, "0123456789vb:l:C:", longopts, (int *) 0);
       if (c == EOF)
        break;
 
@@ -419,30 +373,51 @@ main (int argc, char **argv)
 
        case 'b':
          if (split_type != type_undef)
-           usage (2, _("cannot split in more than one way"));
+           {
+             error (0, 0, _("cannot split in more than one way"));
+             usage (EXIT_FAILURE);
+           }
          split_type = type_bytes;
-         /* FIXME: use xstrtoul */
-         if (convint (optarg, &accum) == -1)
-           usage (2, _("invalid number of bytes"));
+         if (xstrtol (optarg, NULL, 10, &tmp_long, "bkm") != LONGINT_OK
+             || tmp_long < 0 || tmp_long > INT_MAX)
+           {
+             error (0, 0, _("%s: invalid number of bytes"), optarg);
+             usage (EXIT_FAILURE);
+           }
+         accum = (int) tmp_long;
          break;
 
        case 'l':
          if (split_type != type_undef)
-           usage (2, _("cannot split in more than one way"));
+           {
+             error (0, 0, _("cannot split in more than one way"));
+             usage (EXIT_FAILURE);
+           }
          split_type = type_lines;
-         if (!isdigits (optarg))
-           usage (2, _("invalid number of lines"));
-         /* FIXME: use xstrtoul */
-         accum = atoi (optarg);
+         if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
+             || tmp_long < 0 || tmp_long > INT_MAX)
+           {
+             error (0, 0, _("%s: invalid number of lines"), optarg);
+             usage (EXIT_FAILURE);
+           }
+         accum = (int) tmp_long;
          break;
 
        case 'C':
          if (split_type != type_undef)
-           usage (2, _("cannot split in more than one way"));
+           {
+             error (0, 0, _("cannot split in more than one way"));
+             usage (EXIT_FAILURE);
+           }
+
          split_type = type_byteslines;
-         /* FIXME: use xstrtoul */
-         if (convint (optarg, &accum) == -1)
-           usage (2, _("invalid number of bytes"));
+         if (xstrtol (optarg, NULL, 10, &tmp_long, "bkm") != LONGINT_OK
+             || tmp_long < 0 ||  tmp_long > INT_MAX)
+           {
+             error (0, 0, _("%s: invalid number of bytes"), optarg);
+             usage (EXIT_FAILURE);
+           }
+         accum = (int) tmp_long;
          break;
 
        case '0':
@@ -456,7 +431,10 @@ main (int argc, char **argv)
        case '8':
        case '9':
          if (split_type != type_undef && split_type != type_digits)
-           usage (2, _("cannot split in more than one way"));
+           {
+             error (0, 0, _("cannot split in more than one way"));
+             usage (EXIT_FAILURE);
+           }
          if (digits_optind != 0 && digits_optind != this_optind)
            accum = 0;          /* More than one number given; ignore other. */
          digits_optind = this_optind;
@@ -464,20 +442,19 @@ main (int argc, char **argv)
          accum = accum * 10 + c - '0';
          break;
 
+       case 2:
+         verbose = 1;
+         break;
+
+       case_GETOPT_HELP_CHAR;
+
+       case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
        default:
-         usage (2, (char *)0);
+         usage (EXIT_FAILURE);
        }
     }
 
-  if (show_version)
-    {
-      printf ("split - %s\n", version_string);
-      exit (0);
-    }
-
-  if (show_help)
-    usage (0, (char *)0);
-
   /* Handle default case.  */
   if (split_type == type_undef)
     {
@@ -486,7 +463,10 @@ main (int argc, char **argv)
     }
 
   if (accum < 1)
-    usage (2, _("invalid number"));
+    {
+      error (0, 0, _("invalid number"));
+      usage (EXIT_FAILURE);
+    }
   num = accum;
 
   /* Get out the filename arguments.  */
@@ -498,17 +478,22 @@ main (int argc, char **argv)
     outbase = argv[optind++];
 
   if (optind < argc)
-    usage (2, _("too many arguments"));
+    {
+      error (0, 0, _("too many arguments"));
+      usage (EXIT_FAILURE);
+    }
 
   /* Open the input file.  */
-  if (!strcmp (infile, "-"))
+  if (STREQ (infile, "-"))
     input_desc = 0;
   else
     {
       input_desc = open (infile, O_RDONLY);
       if (input_desc < 0)
-       error (1, errno, "%s", infile);
+       error (EXIT_FAILURE, errno, "%s", infile);
     }
+  /* Binary I/O is safer when bytecounts are used.  */
+  SET_BINARY (input_desc);
 
   /* No output file is open now.  */
   output_desc = -1;
@@ -527,7 +512,7 @@ main (int argc, char **argv)
   /* Get the optimal block size of input device and make a buffer.  */
 
   if (fstat (input_desc, &stat_buf) < 0)
-    error (1, errno, "%s", infile);
+    error (EXIT_FAILURE, errno, "%s", infile);
   in_blk_size = ST_BLKSIZE (stat_buf);
 
   buf = xmalloc (in_blk_size + 1);
@@ -552,9 +537,9 @@ main (int argc, char **argv)
     }
 
   if (close (input_desc) < 0)
-    error (1, errno, "%s", infile);
+    error (EXIT_FAILURE, errno, "%s", infile);
   if (output_desc >= 0 && close (output_desc) < 0)
-    error (1, errno, "%s", outfile);
+    error (EXIT_FAILURE, errno, "%s", outfile);
 
-  exit (0);
+  exit (EXIT_SUCCESS);
 }