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