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