merge with 1.8d+
[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 #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 \f
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 \f
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 \f
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 \f
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 }