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