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