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