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