home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / txtut122.zip / textutil / src / split.c < prev    next >
C/C++ Source or Header  |  1998-04-11  |  14KB  |  576 lines

  1. /* split.c -- split a file into pieces.
  2.    Copyright (C) 88, 91, 95, 1996 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software Foundation,
  16.    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
  17.  
  18. /* By tege@sics.se, with rms.
  19.  
  20.    To do:
  21.    * Implement -t CHAR or -t REGEX to specify break characters other
  22.      than newline. */
  23.  
  24. #include <config.h>
  25.  
  26. #include <stdio.h>
  27. #include <getopt.h>
  28. #include <sys/types.h>
  29.  
  30. #if HAVE_LIMITS_H
  31. # include <limits.h>
  32. #endif
  33.  
  34. #ifndef UINT_MAX
  35. # define UINT_MAX ((unsigned int) ~(unsigned int) 0)
  36. #endif
  37.  
  38. #ifndef INT_MAX
  39. # define INT_MAX ((int) (UINT_MAX >> 1))
  40. #endif
  41.  
  42. #include "system.h"
  43. #include "error.h"
  44. #include "xstrtol.h"
  45.  
  46. char *xmalloc ();
  47. int full_write ();
  48. int safe_read ();
  49.  
  50. /* The name this program was run with. */
  51. char *program_name;
  52.  
  53. /* Base name of output files.  */
  54. static char *outfile;
  55.  
  56. /* Pointer to the end of the prefix in OUTFILE.
  57.    Suffixes are inserted here.  */
  58. static char *outfile_mid;
  59.  
  60. /* Pointer to the end of OUTFILE. */
  61. static char *outfile_end;
  62.  
  63. /* Name of input file.  May be "-".  */
  64. static char *infile;
  65.  
  66. /* Descriptor on which input file is open.  */
  67. static int input_desc;
  68.  
  69. /* Descriptor on which output file is open.  */
  70. static int output_desc;
  71.  
  72. /* If nonzero, display usage information and exit.  */
  73. static int show_help;
  74.  
  75. /* If nonzero, print the version on standard output then exit.  */
  76. static int show_version;
  77.  
  78. /* If nonzero, print a diagnostic on standard error just before each
  79.    output file is opened. */
  80. static int verbose;
  81.  
  82. static struct option const longopts[] =
  83. {
  84.   {"bytes", required_argument, NULL, 'b'},
  85.   {"lines", required_argument, NULL, 'l'},
  86.   {"line-bytes", required_argument, NULL, 'C'},
  87.   {"verbose", no_argument, NULL, 2},
  88.   {"help", no_argument, &show_help, 1},
  89.   {"version", no_argument, &show_version, 1},
  90.   {NULL, 0, NULL, 0}
  91. };
  92.  
  93. static void
  94. usage (int status)
  95. {
  96.   if (status != 0)
  97.     fprintf (stderr, _("Try `%s --help' for more information.\n"),
  98.          program_name);
  99.   else
  100.     {
  101.       printf (_("\
  102. Usage: %s [OPTION] [INPUT [PREFIX]]\n\
  103. "),
  104.           program_name);
  105.     printf (_("\
  106. Output fixed-size pieces of INPUT to PREFIXaa, PREFIXab, ...; default\n\
  107. PREFIX is `x'.  With no INPUT, or when INPUT is -, read standard input.\n\
  108. \n\
  109.   -b, --bytes=SIZE        put SIZE bytes per output file\n\
  110.   -C, --line-bytes=SIZE   put at most SIZE bytes of lines per output file\n\
  111.   -l, --lines=NUMBER      put NUMBER lines per output file\n\
  112.   -NUMBER                 same as -l NUMBER\n\
  113.       --verbose           print a diagnostic to standard error just\n\
  114.                 before each output file is opened\n\
  115.       --help              display this help and exit\n\
  116.       --version           output version information and exit\n\
  117. \n\
  118. SIZE may have a multiplier suffix: b for 512, k for 1K, m for 1 Meg.\n\
  119. "));
  120.       puts (_("\nReport bugs to textutils-bugs@gnu.ai.mit.edu"));
  121.     }
  122.   exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
  123. }
  124.  
  125. /* Compute the next sequential output file name suffix and store it
  126.    into the string `outfile' at the position pointed to by `outfile_mid'.  */
  127.  
  128. static void
  129. next_file_name (void)
  130. {
  131.   int x;
  132.   char *ne;
  133.   unsigned int i;
  134.  
  135.   static int first_call = 1;
  136.  
  137.   /* Status for outfile name generation.  */
  138.   static unsigned outfile_count = 0;
  139.   static unsigned outfile_name_limit = 25 * 26;
  140.   static unsigned outfile_name_generation = 1;
  141.  
  142.   if (!first_call)
  143.     outfile_count++;
  144.   first_call = 0;
  145.   if (outfile_count < outfile_name_limit)
  146.     {
  147.       for (ne = outfile_end - 1; ; ne--)
  148.     {
  149.       x = *ne;
  150.       if (x != 'z')
  151.         break;
  152.       *ne = 'a';
  153.     }
  154.       *ne = x + 1;
  155.       return;
  156.     }
  157.  
  158.   outfile_count = 0;
  159.   outfile_name_limit *= 26;
  160.   outfile_name_generation++;
  161.   *outfile_mid++ = 'z';
  162.   for (i = 0; i <= outfile_name_generation; i++)
  163.     outfile_mid[i] = 'a';
  164.   outfile_end += 2;
  165. }
  166.  
  167. /* Write BYTES bytes at BP to an output file.
  168.    If NEW_FILE_FLAG is nonzero, open the next output file.
  169.    Otherwise add to the same output file already in use.  */
  170.  
  171. static void
  172. cwrite (int new_file_flag, const char *bp, int bytes)
  173. {
  174.   if (new_file_flag)
  175.     {
  176.       if (output_desc >= 0 && close (output_desc) < 0)
  177.     error (EXIT_FAILURE, errno, "%s", outfile);
  178.  
  179.       next_file_name ();
  180.       if (verbose)
  181.     fprintf (stderr, _("creating file `%s'\n"), outfile);
  182.       output_desc = open (outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
  183.       if (output_desc < 0)
  184.     error (EXIT_FAILURE, errno, "%s", outfile);
  185.     }
  186.   if (full_write (output_desc, bp, bytes) < 0)
  187.     error (EXIT_FAILURE, errno, "%s", outfile);
  188. }
  189.  
  190. /* Read NCHARS bytes from the input file into BUF.
  191.    Return the number of bytes successfully read.
  192.    If this is less than NCHARS, do not call `stdread' again.  */
  193.  
  194. static int
  195. stdread (char *buf, int nchars)
  196. {
  197.   int n_read;
  198.   int to_be_read = nchars;
  199.  
  200.   while (to_be_read)
  201.     {
  202.       n_read = safe_read (input_desc, buf, to_be_read);
  203.       if (n_read < 0)
  204.     return -1;
  205.       if (n_read == 0)
  206.     break;
  207.       to_be_read -= n_read;
  208.       buf += n_read;
  209.     }
  210.   return nchars - to_be_read;
  211. }
  212.  
  213. /* Split into pieces of exactly NCHARS bytes.
  214.    Use buffer BUF, whose size is BUFSIZE.  */
  215.  
  216. static void
  217. bytes_split (int nchars, char *buf, int bufsize)
  218. {
  219.   int n_read;
  220.   int new_file_flag = 1;
  221.   int to_read;
  222.   int to_write = nchars;
  223.   char *bp_out;
  224.  
  225.   do
  226.     {
  227.       n_read = stdread (buf, bufsize);
  228.       if (n_read < 0)
  229.         error (EXIT_FAILURE, errno, "%s", infile);
  230.       bp_out = buf;
  231.       to_read = n_read;
  232.       for (;;)
  233.     {
  234.       if (to_read < to_write)
  235.         {
  236.           if (to_read)    /* do not write 0 bytes! */
  237.         {
  238.           cwrite (new_file_flag, bp_out, to_read);
  239.           to_write -= to_read;
  240.           new_file_flag = 0;
  241.         }
  242.           break;
  243.         }
  244.       else
  245.         {
  246.           cwrite (new_file_flag, bp_out, to_write);
  247.           bp_out += to_write;
  248.           to_read -= to_write;
  249.           new_file_flag = 1;
  250.           to_write = nchars;
  251.         }
  252.     }
  253.     }
  254.   while (n_read == bufsize);
  255. }
  256.  
  257. /* Split into pieces of exactly NLINES lines.
  258.    Use buffer BUF, whose size is BUFSIZE.  */
  259.  
  260. static void
  261. lines_split (int nlines, char *buf, int bufsize)
  262. {
  263.   int n_read;
  264.   char *bp, *bp_out, *eob;
  265.   int new_file_flag = 1;
  266.   int n = 0;
  267.  
  268.   do
  269.     {
  270.       n_read = stdread (buf, bufsize);
  271.       if (n_read < 0)
  272.     error (EXIT_FAILURE, errno, "%s", infile);
  273.       bp = bp_out = buf;
  274.       eob = bp + n_read;
  275.       *eob = '\n';
  276.       for (;;)
  277.     {
  278.       while (*bp++ != '\n')
  279.         ;            /* this semicolon takes most of the time */
  280.       if (bp > eob)
  281.         {
  282.           if (eob != bp_out) /* do not write 0 bytes! */
  283.         {
  284.           cwrite (new_file_flag, bp_out, eob - bp_out);
  285.           new_file_flag = 0;
  286.         }
  287.           break;
  288.         }
  289.       else
  290.         if (++n >= nlines)
  291.           {
  292.         cwrite (new_file_flag, bp_out, bp - bp_out);
  293.         bp_out = bp;
  294.         new_file_flag = 1;
  295.         n = 0;
  296.           }
  297.     }
  298.     }
  299.   while (n_read == bufsize);
  300. }
  301.  
  302. /* Split into pieces that are as large as possible while still not more
  303.    than NCHARS bytes, and are split on line boundaries except
  304.    where lines longer than NCHARS bytes occur. */
  305.  
  306. static void
  307. line_bytes_split (int nchars)
  308. {
  309.   int n_read;
  310.   char *bp;
  311.   int eof = 0;
  312.   int n_buffered = 0;
  313.   char *buf = (char *) xmalloc (nchars);
  314.  
  315.   do
  316.     {
  317.       /* Fill up the full buffer size from the input file.  */
  318.  
  319.       n_read = stdread (buf + n_buffered, nchars - n_buffered);
  320.       if (n_read < 0)
  321.     error (EXIT_FAILURE, errno, "%s", infile);
  322.  
  323.       n_buffered += n_read;
  324.       if (n_buffered != nchars)
  325.     eof = 1;
  326.  
  327.       /* Find where to end this chunk.  */
  328.       bp = buf + n_buffered;
  329.       if (n_buffered == nchars)
  330.     {
  331.       while (bp > buf && bp[-1] != '\n')
  332.         bp--;
  333.     }
  334.  
  335.       /* If chunk has no newlines, use all the chunk.  */
  336.       if (bp == buf)
  337.     bp = buf + n_buffered;
  338.  
  339.       /* Output the chars as one output file.  */
  340.       cwrite (1, buf, bp - buf);
  341.  
  342.       /* Discard the chars we just output; move rest of chunk
  343.      down to be the start of the next chunk.  Source and
  344.      destination probably overlap.  */
  345.       n_buffered -= bp - buf;
  346.       if (n_buffered > 0)
  347.     memmove (buf, bp, n_buffered);
  348.     }
  349.   while (!eof);
  350.   free (buf);
  351. }
  352.  
  353. int
  354. main (int argc, char **argv)
  355. {
  356.   struct stat stat_buf;
  357.   int num;            /* numeric argument from command line */
  358.   enum
  359.     {
  360.       type_undef, type_bytes, type_byteslines, type_lines, type_digits
  361.     } split_type = type_undef;
  362.   int in_blk_size;        /* optimal block size of input file device */
  363.   char *buf;            /* file i/o buffer */
  364.   int accum = 0;
  365.   char *outbase;
  366.   int c;
  367.   int digits_optind = 0;
  368.  
  369.  
  370. #ifdef __EMX__
  371. _wildcard(&argc, &argv);
  372. #endif
  373.  
  374.  
  375.   program_name = argv[0];
  376. #ifndef __EMX__
  377.   setlocale (LC_ALL, "");
  378.   bindtextdomain (PACKAGE, LOCALEDIR);
  379.   textdomain (PACKAGE);
  380. #endif
  381.   /* Parse command line options.  */
  382.  
  383.   infile = "-";
  384.   outbase = "x";
  385.  
  386.   while (1)
  387.     {
  388.       /* This is the argv-index of the option we will read next.  */
  389.       int this_optind = optind ? optind : 1;
  390.       long int tmp_long;
  391.  
  392.       c = getopt_long (argc, argv, "0123456789vb:l:C:", longopts, (int *) 0);
  393.       if (c == EOF)
  394.     break;
  395.  
  396.       switch (c)
  397.     {
  398.     case 0:
  399.       break;
  400.  
  401.     case 'b':
  402.       if (split_type != type_undef)
  403.         {
  404.           error (0, 0, _("cannot split in more than one way"));
  405.           usage (EXIT_FAILURE);
  406.         }
  407.       split_type = type_bytes;
  408.       if (xstrtol (optarg, NULL, 10, &tmp_long, "bkm") != LONGINT_OK
  409.           || tmp_long < 0 || tmp_long > INT_MAX)
  410.         {
  411.           error (0, 0, _("%s: invalid number of bytes"), optarg);
  412.           usage (EXIT_FAILURE);
  413.         }
  414.       accum = (int) tmp_long;
  415.       break;
  416.  
  417.     case 'l':
  418.       if (split_type != type_undef)
  419.         {
  420.           error (0, 0, _("cannot split in more than one way"));
  421.           usage (EXIT_FAILURE);
  422.         }
  423.       split_type = type_lines;
  424.       if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
  425.           || tmp_long < 0 || tmp_long > INT_MAX)
  426.         {
  427.           error (0, 0, _("%s: invalid number of lines"), optarg);
  428.           usage (EXIT_FAILURE);
  429.         }
  430.       accum = (int) tmp_long;
  431.       break;
  432.  
  433.     case 'C':
  434.       if (split_type != type_undef)
  435.         {
  436.           error (0, 0, _("cannot split in more than one way"));
  437.           usage (EXIT_FAILURE);
  438.         }
  439.  
  440.       split_type = type_byteslines;
  441.       if (xstrtol (optarg, NULL, 10, &tmp_long, "bkm") != LONGINT_OK
  442.           || tmp_long < 0 ||  tmp_long > INT_MAX)
  443.         {
  444.           error (0, 0, _("%s: invalid number of bytes"), optarg);
  445.           usage (EXIT_FAILURE);
  446.         }
  447.       accum = (int) tmp_long;
  448.       break;
  449.  
  450.     case '0':
  451.     case '1':
  452.     case '2':
  453.     case '3':
  454.     case '4':
  455.     case '5':
  456.     case '6':
  457.     case '7':
  458.     case '8':
  459.     case '9':
  460.       if (split_type != type_undef && split_type != type_digits)
  461.         {
  462.           error (0, 0, _("cannot split in more than one way"));
  463.           usage (EXIT_FAILURE);
  464.         }
  465.       if (digits_optind != 0 && digits_optind != this_optind)
  466.         accum = 0;        /* More than one number given; ignore other. */
  467.       digits_optind = this_optind;
  468.       split_type = type_digits;
  469.       accum = accum * 10 + c - '0';
  470.       break;
  471.  
  472.     case 2:
  473.       verbose = 1;
  474.       break;
  475.  
  476.     default:
  477.       usage (EXIT_FAILURE);
  478.     }
  479.     }
  480.  
  481.   if (show_version)
  482.     {
  483.       printf ("split (%s) %s\n", GNU_PACKAGE, VERSION);
  484.       exit (EXIT_SUCCESS);
  485.     }
  486.  
  487.   if (show_help)
  488.     usage (0);
  489.  
  490.   /* Handle default case.  */
  491.   if (split_type == type_undef)
  492.     {
  493.       split_type = type_lines;
  494.       accum = 1000;
  495.     }
  496.  
  497.   if (accum < 1)
  498.     {
  499.       error (0, 0, _("invalid number"));
  500.       usage (EXIT_FAILURE);
  501.     }
  502.   num = accum;
  503.  
  504.   /* Get out the filename arguments.  */
  505.  
  506.   if (optind < argc)
  507.     infile = argv[optind++];
  508.  
  509.   if (optind < argc)
  510.     outbase = argv[optind++];
  511.  
  512.   if (optind < argc)
  513.     {
  514.       error (0, 0, _("too many arguments"));
  515.       usage (EXIT_FAILURE);
  516.     }
  517.  
  518.   /* Open the input file.  */
  519.   if (!strcmp (infile, "-"))
  520.     input_desc = 0;
  521.   else
  522.     {
  523.       input_desc = open (infile, O_RDONLY);
  524.       if (input_desc < 0)
  525.     error (EXIT_FAILURE, errno, "%s", infile);
  526.     }
  527.  
  528.   /* No output file is open now.  */
  529.   output_desc = -1;
  530.  
  531.   /* Copy the output file prefix so we can add suffixes to it.
  532.      26**29 is certainly enough output files!  */
  533.  
  534.   outfile = xmalloc (strlen (outbase) + 30);
  535.   strcpy (outfile, outbase);
  536.   outfile_mid = outfile + strlen (outfile);
  537.   outfile_end = outfile_mid + 2;
  538.   memset (outfile_mid, 0, 30);
  539.   outfile_mid[0] = 'a';
  540.   outfile_mid[1] = 'a' - 1;  /* first call to next_file_name makes it an 'a' */
  541.  
  542.   /* Get the optimal block size of input device and make a buffer.  */
  543.  
  544.   if (fstat (input_desc, &stat_buf) < 0)
  545.     error (EXIT_FAILURE, errno, "%s", infile);
  546.   in_blk_size = ST_BLKSIZE (stat_buf);
  547.  
  548.   buf = xmalloc (in_blk_size + 1);
  549.  
  550.   switch (split_type)
  551.     {
  552.     case type_digits:
  553.     case type_lines:
  554.       lines_split (num, buf, in_blk_size);
  555.       break;
  556.  
  557.     case type_bytes:
  558.       bytes_split (num, buf, in_blk_size);
  559.       break;
  560.  
  561.     case type_byteslines:
  562.       line_bytes_split (num);
  563.       break;
  564.  
  565.     default:
  566.       abort ();
  567.     }
  568.  
  569.   if (close (input_desc) < 0)
  570.     error (EXIT_FAILURE, errno, "%s", infile);
  571.   if (output_desc >= 0 && close (output_desc) < 0)
  572.     error (EXIT_FAILURE, errno, "%s", outfile);
  573.  
  574.   exit (EXIT_SUCCESS);
  575. }
  576.