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