]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/split/split.c
Merge branch 'releng/11.3' into releng-CDN/11.3
[FreeBSD/FreeBSD.git] / usr.bin / split / split.c
1 /*
2  * Copyright (c) 1987, 1993, 1994
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 4. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #ifndef lint
34 static const char copyright[] =
35 "@(#) Copyright (c) 1987, 1993, 1994\n\
36         The Regents of the University of California.  All rights reserved.\n";
37 #endif
38
39 #ifndef lint
40 static const char sccsid[] = "@(#)split.c       8.2 (Berkeley) 4/16/94";
41 #endif
42
43 #include <sys/param.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46
47 #include <ctype.h>
48 #include <err.h>
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <inttypes.h>
52 #include <libutil.h>
53 #include <limits.h>
54 #include <locale.h>
55 #include <stdbool.h>
56 #include <stdint.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <unistd.h>
61 #include <regex.h>
62 #include <sysexits.h>
63
64 #define DEFLINE 1000                    /* Default num lines per file. */
65
66 static off_t     bytecnt;               /* Byte count to split on. */
67 static off_t     chunks = 0;            /* Chunks count to split into. */
68 static long      numlines;              /* Line count to split on. */
69 static int       file_open;             /* If a file open. */
70 static int       ifd = -1, ofd = -1;    /* Input/output file descriptors. */
71 static char      bfr[MAXBSIZE];         /* I/O buffer. */
72 static char      fname[MAXPATHLEN];     /* File name prefix. */
73 static regex_t   rgx;
74 static int       pflag;
75 static bool      dflag;
76 static long      sufflen = 2;           /* File name suffix length. */
77
78 static void newfile(void);
79 static void split1(void);
80 static void split2(void);
81 static void split3(void);
82 static void usage(void);
83
84 int
85 main(int argc, char **argv)
86 {
87         int ch;
88         int error;
89         char *ep, *p;
90
91         setlocale(LC_ALL, "");
92
93         dflag = false;
94         while ((ch = getopt(argc, argv, "0123456789a:b:dl:n:p:")) != -1)
95                 switch (ch) {
96                 case '0': case '1': case '2': case '3': case '4':
97                 case '5': case '6': case '7': case '8': case '9':
98                         /*
99                          * Undocumented kludge: split was originally designed
100                          * to take a number after a dash.
101                          */
102                         if (numlines == 0) {
103                                 p = argv[optind - 1];
104                                 if (p[0] == '-' && p[1] == ch && !p[2])
105                                         numlines = strtol(++p, &ep, 10);
106                                 else
107                                         numlines =
108                                             strtol(argv[optind] + 1, &ep, 10);
109                                 if (numlines <= 0 || *ep)
110                                         errx(EX_USAGE,
111                                             "%s: illegal line count", optarg);
112                         }
113                         break;
114                 case 'a':               /* Suffix length */
115                         if ((sufflen = strtol(optarg, &ep, 10)) <= 0 || *ep)
116                                 errx(EX_USAGE,
117                                     "%s: illegal suffix length", optarg);
118                         break;
119                 case 'b':               /* Byte count. */
120                         errno = 0;
121                         error = expand_number(optarg, &bytecnt);
122                         if (error == -1)
123                                 errx(EX_USAGE, "%s: offset too large", optarg);
124                         break;
125                 case 'd':               /* Decimal suffix */
126                         dflag = true;
127                         break;
128                 case 'l':               /* Line count. */
129                         if (numlines != 0)
130                                 usage();
131                         if ((numlines = strtol(optarg, &ep, 10)) <= 0 || *ep)
132                                 errx(EX_USAGE,
133                                     "%s: illegal line count", optarg);
134                         break;
135                 case 'n':               /* Chunks. */
136                         if (!isdigit((unsigned char)optarg[0]) ||
137                             (chunks = (size_t)strtoul(optarg, &ep, 10)) == 0 ||
138                             *ep != '\0') {
139                                 errx(EX_USAGE, "%s: illegal number of chunks",
140                                      optarg);
141                         }
142                         break;
143
144                 case 'p':               /* pattern matching. */
145                         if (regcomp(&rgx, optarg, REG_EXTENDED|REG_NOSUB) != 0)
146                                 errx(EX_USAGE, "%s: illegal regexp", optarg);
147                         pflag = 1;
148                         break;
149                 default:
150                         usage();
151                 }
152         argv += optind;
153         argc -= optind;
154
155         if (*argv != NULL) {                    /* Input file. */
156                 if (strcmp(*argv, "-") == 0)
157                         ifd = STDIN_FILENO;
158                 else if ((ifd = open(*argv, O_RDONLY, 0)) < 0)
159                         err(EX_NOINPUT, "%s", *argv);
160                 ++argv;
161         }
162         if (*argv != NULL)                      /* File name prefix. */
163                 if (strlcpy(fname, *argv++, sizeof(fname)) >= sizeof(fname))
164                         errx(EX_USAGE, "file name prefix is too long");
165         if (*argv != NULL)
166                 usage();
167
168         if (strlen(fname) + (unsigned long)sufflen >= sizeof(fname))
169                 errx(EX_USAGE, "suffix is too long");
170         if (pflag && (numlines != 0 || bytecnt != 0 || chunks != 0))
171                 usage();
172
173         if (numlines == 0)
174                 numlines = DEFLINE;
175         else if (bytecnt != 0 || chunks != 0)
176                 usage();
177
178         if (bytecnt && chunks)
179                 usage();
180
181         if (ifd == -1)                          /* Stdin by default. */
182                 ifd = 0;
183
184         if (bytecnt) {
185                 split1();
186                 exit (0);
187         } else if (chunks) {
188                 split3();
189                 exit (0);
190         }
191         split2();
192         if (pflag)
193                 regfree(&rgx);
194         exit(0);
195 }
196
197 /*
198  * split1 --
199  *      Split the input by bytes.
200  */
201 static void
202 split1(void)
203 {
204         off_t bcnt;
205         char *C;
206         ssize_t dist, len;
207         int nfiles;
208
209         nfiles = 0;
210
211         for (bcnt = 0;;)
212                 switch ((len = read(ifd, bfr, MAXBSIZE))) {
213                 case 0:
214                         exit(0);
215                 case -1:
216                         err(EX_IOERR, "read");
217                         /* NOTREACHED */
218                 default:
219                         if (!file_open) {
220                                 if (!chunks || (nfiles < chunks)) {
221                                         newfile();
222                                         nfiles++;
223                                 }
224                         }
225                         if (bcnt + len >= bytecnt) {
226                                 dist = bytecnt - bcnt;
227                                 if (write(ofd, bfr, dist) != dist)
228                                         err(EX_IOERR, "write");
229                                 len -= dist;
230                                 for (C = bfr + dist; len >= bytecnt;
231                                     len -= bytecnt, C += bytecnt) {
232                                         if (!chunks || (nfiles < chunks)) {
233                                         newfile();
234                                                 nfiles++;
235                                         }
236                                         if (write(ofd,
237                                             C, bytecnt) != bytecnt)
238                                                 err(EX_IOERR, "write");
239                                 }
240                                 if (len != 0) {
241                                         if (!chunks || (nfiles < chunks)) {
242                                         newfile();
243                                                 nfiles++;
244                                         }
245                                         if (write(ofd, C, len) != len)
246                                                 err(EX_IOERR, "write");
247                                 } else
248                                         file_open = 0;
249                                 bcnt = len;
250                         } else {
251                                 bcnt += len;
252                                 if (write(ofd, bfr, len) != len)
253                                         err(EX_IOERR, "write");
254                         }
255                 }
256 }
257
258 /*
259  * split2 --
260  *      Split the input by lines.
261  */
262 static void
263 split2(void)
264 {
265         long lcnt = 0;
266         FILE *infp;
267
268         /* Stick a stream on top of input file descriptor */
269         if ((infp = fdopen(ifd, "r")) == NULL)
270                 err(EX_NOINPUT, "fdopen");
271
272         /* Process input one line at a time */
273         while (fgets(bfr, sizeof(bfr), infp) != NULL) {
274                 const int len = strlen(bfr);
275
276                 /* If line is too long to deal with, just write it out */
277                 if (bfr[len - 1] != '\n')
278                         goto writeit;
279
280                 /* Check if we need to start a new file */
281                 if (pflag) {
282                         regmatch_t pmatch;
283
284                         pmatch.rm_so = 0;
285                         pmatch.rm_eo = len - 1;
286                         if (regexec(&rgx, bfr, 0, &pmatch, REG_STARTEND) == 0)
287                                 newfile();
288                 } else if (lcnt++ == numlines) {
289                         newfile();
290                         lcnt = 1;
291                 }
292
293 writeit:
294                 /* Open output file if needed */
295                 if (!file_open)
296                         newfile();
297
298                 /* Write out line */
299                 if (write(ofd, bfr, len) != len)
300                         err(EX_IOERR, "write");
301         }
302
303         /* EOF or error? */
304         if (ferror(infp))
305                 err(EX_IOERR, "read");
306         else
307                 exit(0);
308 }
309
310 /*
311  * split3 --
312  *      Split the input into specified number of chunks
313  */
314 static void
315 split3(void)
316 {
317         struct stat sb;
318
319         if (fstat(ifd, &sb) == -1) {
320                 err(1, "stat");
321                 /* NOTREACHED */
322         }
323
324         if (chunks > sb.st_size) {
325                 errx(1, "can't split into more than %d files",
326                     (int)sb.st_size);
327                 /* NOTREACHED */
328         }
329
330         bytecnt = sb.st_size / chunks;
331         split1();
332 }
333
334
335 /*
336  * newfile --
337  *      Open a new output file.
338  */
339 static void
340 newfile(void)
341 {
342         long i, maxfiles, tfnum;
343         static long fnum;
344         static char *fpnt;
345         char beg, end;
346         int pattlen;
347
348         if (ofd == -1) {
349                 if (fname[0] == '\0') {
350                         fname[0] = 'x';
351                         fpnt = fname + 1;
352                 } else {
353                         fpnt = fname + strlen(fname);
354                 }
355                 ofd = fileno(stdout);
356         }
357
358         if (dflag) {
359                 beg = '0';
360                 end = '9';
361         }
362         else {
363                 beg = 'a';
364                 end = 'z';
365         }
366         pattlen = end - beg + 1;
367
368         /* maxfiles = pattlen^sufflen, but don't use libm. */
369         for (maxfiles = 1, i = 0; i < sufflen; i++)
370                 if (LONG_MAX / pattlen < maxfiles)
371                         errx(EX_USAGE, "suffix is too long (max %ld)", i);
372                 else
373                         maxfiles *= pattlen;
374
375         if (fnum == maxfiles)
376                 errx(EX_DATAERR, "too many files");
377
378         /* Generate suffix of sufflen letters */
379         tfnum = fnum;
380         i = sufflen - 1;
381         do {
382                 fpnt[i] = tfnum % pattlen + beg;
383                 tfnum /= pattlen;
384         } while (i-- > 0);
385         fpnt[sufflen] = '\0';
386
387         ++fnum;
388         if (!freopen(fname, "w", stdout))
389                 err(EX_IOERR, "%s", fname);
390         file_open = 1;
391 }
392
393 static void
394 usage(void)
395 {
396         (void)fprintf(stderr,
397 "usage: split [-l line_count] [-a suffix_length] [file [prefix]]\n"
398 "       split -b byte_count[K|k|M|m|G|g] [-a suffix_length] [file [prefix]]\n"
399 "       split -n chunk_count [-a suffix_length] [file [prefix]]\n"
400 "       split -p pattern [-a suffix_length] [file [prefix]]\n");
401         exit(EX_USAGE);
402 }