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