1 /* split.c -- split a file into pieces.
2 Copyright (C) 1988, 1991 Free Software Foundation, Inc.
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)
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.
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. */
18 /* By tege@sics.se, with rms.
21 * Implement -t CHAR or -t REGEX to specify break characters other
26 #include <sys/types.h>
33 static int convint ();
34 static int isdigits ();
35 static int stdread ();
36 static void line_bytes_split ();
37 static void bytes_split ();
38 static void cwrite ();
39 static void lines_split ();
40 static void next_file_name ();
42 /* The name this program was run with. */
45 /* Base name of output files. */
48 /* Pointer to the end of the prefix in OUTFILE.
49 Suffixes are inserted here. */
50 static char *outfile_mid;
52 /* Pointer to the end of OUTFILE. */
53 static char *outfile_end;
55 /* Status for outfile name generation. */
56 static unsigned outfile_count = -1;
57 static unsigned outfile_name_limit = 25 * 26;
58 static unsigned outfile_name_generation = 1;
60 /* Name of input file. May be "-". */
63 /* Descriptor on which input file is open. */
64 static int input_desc;
66 /* Descriptor on which output file is open. */
67 static int output_desc;
69 /* If non-zero, display usage information and exit. */
72 /* If non-zero, print the version on standard error. */
73 static int flag_version;
75 static struct option const longopts[] =
77 {"bytes", required_argument, NULL, 'b'},
78 {"lines", required_argument, NULL, 'l'},
79 {"line-bytes", required_argument, NULL, 'C'},
80 {"help", no_argument, &flag_help, 1},
81 {"version", no_argument, &flag_version, 1},
90 fprintf (stderr, "%s: %s\n", program_name, reason);
92 Usage: %s [-lines] [-l lines] [-b bytes[bkm]] [-C bytes[bkm]]\n\
93 [--lines=lines] [--bytes=bytes[bkm]] [--line-bytes=bytes[bkm]]\n\
94 [--help] [--version] [infile [outfile-prefix]]\n",
104 struct stat stat_buf;
105 int num; /* numeric argument from command line */
108 type_undef, type_bytes, type_byteslines, type_lines, type_digits
109 } split_type = type_undef;
110 int in_blk_size; /* optimal block size of input file device */
111 char *buf; /* file i/o buffer */
115 int digits_optind = 0;
117 program_name = argv[0];
119 /* Parse command line options. */
126 /* This is the argv-index of the option we will read next. */
127 int this_optind = optind ? optind : 1;
129 c = getopt_long (argc, argv, "0123456789b:l:C:", longopts, (int *) 0);
139 if (split_type != type_undef)
140 usage ("cannot split in more than one way");
141 split_type = type_bytes;
142 if (convint (optarg, &accum) == -1)
143 usage ("invalid number of bytes");
147 if (split_type != type_undef)
148 usage ("cannot split in more than one way");
149 split_type = type_lines;
150 if (!isdigits (optarg))
151 usage ("invalid number of lines");
152 accum = atoi (optarg);
156 if (split_type != type_undef)
157 usage ("cannot split in more than one way");
158 split_type = type_byteslines;
159 if (convint (optarg, &accum) == -1)
160 usage ("invalid number of bytes");
173 if (split_type != type_undef && split_type != type_digits)
174 usage ("cannot split in more than one way");
175 if (digits_optind != 0 && digits_optind != this_optind)
176 accum = 0; /* More than one number given; ignore other. */
177 digits_optind = this_optind;
178 split_type = type_digits;
179 accum = accum * 10 + c - '0';
189 fprintf (stderr, "%s\n", version_string);
196 /* Handle default case. */
197 if (split_type == type_undef)
199 split_type = type_lines;
204 usage ("invalid number");
207 /* Get out the filename arguments. */
210 infile = argv[optind++];
213 outbase = argv[optind++];
216 usage ("too many arguments");
218 /* Open the input file. */
219 if (!strcmp (infile, "-"))
223 input_desc = open (infile, O_RDONLY);
225 error (1, errno, "%s", infile);
228 /* No output file is open now. */
231 /* Copy the output file prefix so we can add suffixes to it.
232 26**29 is certainly enough output files! */
234 outfile = xmalloc (strlen (outbase) + 30);
235 strcpy (outfile, outbase);
236 outfile_mid = outfile + strlen (outfile);
237 outfile_end = outfile_mid + 2;
238 bzero (outfile_mid, 30);
239 outfile_mid[0] = 'a';
240 outfile_mid[1] = 'a' - 1; /* first call to next_file_name makes it an 'a' */
242 /* Get the optimal block size of input device and make a buffer. */
244 if (fstat (input_desc, &stat_buf) < 0)
245 error (1, errno, "%s", infile);
246 in_blk_size = ST_BLKSIZE (stat_buf);
248 buf = xmalloc (in_blk_size + 1);
254 lines_split (num, buf, in_blk_size);
258 bytes_split (num, buf, in_blk_size);
261 case type_byteslines:
262 line_bytes_split (num);
269 if (close (input_desc) < 0)
270 error (1, errno, "%s", infile);
271 if (output_desc >= 0 && close (output_desc) < 0)
272 error (1, errno, "%s", outfile);
277 /* Return nonzero if the string STR is composed entirely of decimal digits. */
293 /* Put the value of the number in STR into *VAL.
294 STR can specify a positive integer, optionally ending in `k'
295 to mean kilo or `m' to mean mega.
296 Return 0 if STR is valid, -1 if not. */
304 int arglen = strlen (str);
308 switch (str[arglen - 1])
312 str[arglen - 1] = '\0';
316 str[arglen - 1] = '\0';
319 multiplier = 1048576;
320 str[arglen - 1] = '\0';
326 *val = atoi (str) * multiplier;
330 /* Split into pieces of exactly NCHARS bytes.
331 Use buffer BUF, whose size is BUFSIZE. */
334 bytes_split (nchars, buf, bufsize)
340 int new_file_flag = 1;
342 int to_write = nchars;
347 n_read = stdread (buf, bufsize);
349 error (1, errno, "%s", infile);
354 if (to_read < to_write)
356 if (to_read) /* do not write 0 bytes! */
358 cwrite (new_file_flag, bp_out, to_read);
366 cwrite (new_file_flag, bp_out, to_write);
374 while (n_read == bufsize);
377 /* Split into pieces of exactly NLINES lines.
378 Use buffer BUF, whose size is BUFSIZE. */
381 lines_split (nlines, buf, bufsize)
387 char *bp, *bp_out, *eob;
388 int new_file_flag = 1;
393 n_read = stdread (buf, bufsize);
395 error (1, errno, "%s", infile);
401 while (*bp++ != '\n')
402 ; /* this semicolon takes most of the time */
405 if (eob != bp_out) /* do not write 0 bytes! */
407 cwrite (new_file_flag, bp_out, eob - bp_out);
415 cwrite (new_file_flag, bp_out, bp - bp_out);
422 while (n_read == bufsize);
425 /* Split into pieces that are as large as possible while still not more
426 than NCHARS bytes, and are split on line boundaries except
427 where lines longer than NCHARS bytes occur. */
430 line_bytes_split (nchars)
437 char *buf = (char *) xmalloc (nchars);
441 /* Fill up the full buffer size from the input file. */
443 n_read = stdread (buf + n_buffered, nchars - n_buffered);
445 error (1, errno, "%s", infile);
447 n_buffered += n_read;
448 if (n_buffered != nchars)
451 /* Find where to end this chunk. */
452 bp = buf + n_buffered;
453 if (n_buffered == nchars)
455 while (bp > buf && bp[-1] != '\n')
459 /* If chunk has no newlines, use all the chunk. */
461 bp = buf + n_buffered;
463 /* Output the chars as one output file. */
464 cwrite (1, buf, bp - buf);
466 /* Discard the chars we just output; move rest of chunk
467 down to be the start of the next chunk. */
468 n_buffered -= bp - buf;
470 bcopy (bp, buf, n_buffered);
476 /* Write BYTES bytes at BP to an output file.
477 If NEW_FILE_FLAG is nonzero, open the next output file.
478 Otherwise add to the same output file already in use. */
481 cwrite (new_file_flag, bp, bytes)
488 if (output_desc >= 0 && close (output_desc) < 0)
489 error (1, errno, "%s", outfile);
492 output_desc = open (outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
494 error (1, errno, "%s", outfile);
496 if (write (output_desc, bp, bytes) < 0)
497 error (1, errno, "%s", outfile);
500 /* Read NCHARS bytes from the input file into BUF.
501 Return the number of bytes successfully read.
502 If this is less than NCHARS, do not call `stdread' again. */
505 stdread (buf, nchars)
510 int to_be_read = nchars;
514 n_read = read (input_desc, buf, to_be_read);
519 to_be_read -= n_read;
522 return nchars - to_be_read;
525 /* Compute the next sequential output file name suffix and store it
526 into the string `outfile' at the position pointed to by `outfile_mid'. */
535 if (outfile_count < outfile_name_limit)
537 for (ne = outfile_end - 1; ; ne--)
549 outfile_name_limit *= 26;
550 outfile_name_generation++;
551 *outfile_mid++ = 'z';
552 for (x = 0; x <= outfile_name_generation; x++)
553 outfile_mid[x] = 'a';