home *** CD-ROM | disk | FTP | other *** search
/ PC-Online 1996 May / PCOnline_05_1996.bin / linux / source / a / txtutils / textutil.9 / textutil / textutils-1.9 / src / split.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-10-24  |  12.8 KB  |  587 lines

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