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