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