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