split.c revision 1.19 1 /* $NetBSD: split.c,v 1.19 2003/07/10 21:30:16 bjh21 Exp $ */
2
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. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT("@(#) Copyright (c) 1987, 1993, 1994\n\
39 The Regents of the University of California. All rights reserved.\n");
40 #endif /* not lint */
41
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "@(#)split.c 8.3 (Berkeley) 4/25/94";
45 #endif
46 __RCSID("$NetBSD: split.c,v 1.19 2003/07/10 21:30:16 bjh21 Exp $");
47 #endif /* not lint */
48
49 #include <sys/param.h>
50
51 #include <ctype.h>
52 #include <err.h>
53 #include <errno.h>
54 #include <fcntl.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <unistd.h>
59
60 #define DEFLINE 1000 /* Default num lines per file. */
61
62 static int file_open; /* If a file open. */
63 static int ifd = STDIN_FILENO, ofd = -1; /* Input/output file descriptors. */
64 static char *fname; /* File name prefix. */
65 static size_t sfxlen = 2; /* suffix length. */
66
67 int main(int, char **);
68 static void newfile(void);
69 static void split1(off_t);
70 static void split2(off_t);
71 static void usage(void) __attribute__((__noreturn__));
72 static size_t bigwrite(int, void const *, size_t);
73
74 int
75 main(int argc, char *argv[])
76 {
77 int ch;
78 char *ep, *p;
79 char const *base;
80 off_t bytecnt = 0; /* Byte count to split on. */
81 off_t numlines = 0; /* Line count to split on. */
82
83 while ((ch = getopt(argc, argv, "0123456789b:l:a:")) != -1)
84 switch (ch) {
85 case '0': case '1': case '2': case '3': case '4':
86 case '5': case '6': case '7': case '8': case '9':
87 /*
88 * Undocumented kludge: split was originally designed
89 * to take a number after a dash.
90 */
91 if (numlines == 0) {
92 p = argv[optind - 1];
93 if (p[0] == '-' && p[1] == ch && !p[2])
94 p++;
95 else
96 p = argv[optind] + 1;
97 numlines = strtoull(p, &ep, 10);
98 if (numlines == 0 || *ep != '\0')
99 errx(1, "%s: illegal line count.", p);
100 }
101 break;
102 case 'b': /* Byte count. */
103 if (!isdigit((unsigned char)optarg[0]) ||
104 (bytecnt = strtoull(optarg, &ep, 10)) == 0 ||
105 (*ep != '\0' && *ep != 'k' && *ep != 'm'))
106 errx(1, "%s: illegal byte count.", optarg);
107 if (*ep == 'k')
108 bytecnt *= 1024;
109 else if (*ep == 'm')
110 bytecnt *= 1024 * 1024;
111 break;
112 case 'l': /* Line count. */
113 if (numlines != 0)
114 usage();
115 if (!isdigit((unsigned char)optarg[0]) ||
116 (numlines = strtoull(optarg, &ep, 10)) == 0 ||
117 *ep != '\0')
118 errx(1, "%s: illegal line count.", optarg);
119 break;
120 case 'a': /* Suffix length. */
121 if (!isdigit((unsigned char)optarg[0]) ||
122 (sfxlen = (size_t)strtoul(optarg, &ep, 10)) == 0 ||
123 *ep != '\0')
124 errx(1, "%s: illegal suffix length.", optarg);
125 break;
126 default:
127 usage();
128 }
129 argv += optind;
130 argc -= optind;
131
132 if (*argv != NULL) {
133 if (strcmp(*argv, "-") != 0 &&
134 (ifd = open(*argv, O_RDONLY, 0)) < 0)
135 err(1, "%s", *argv);
136 ++argv;
137 }
138
139
140 base = (*argv != NULL) ? *argv++ : "x";
141 if ((fname = malloc(strlen(base) + sfxlen + 1)) == NULL)
142 err(EXIT_FAILURE, NULL);
143 (void)strcpy(fname, base); /* File name prefix. */
144
145 if (*argv != NULL)
146 usage();
147
148 if (numlines == 0)
149 numlines = DEFLINE;
150 else if (bytecnt)
151 usage();
152
153 if (bytecnt)
154 split1(bytecnt);
155 else
156 split2(numlines);
157
158 return 0;
159 }
160
161 /*
162 * split1 --
163 * Split the input by bytes.
164 */
165 static void
166 split1(off_t bytecnt)
167 {
168 off_t bcnt;
169 ssize_t dist, len;
170 char *C;
171 char bfr[MAXBSIZE];
172
173 for (bcnt = 0;;)
174 switch (len = read(ifd, bfr, MAXBSIZE)) {
175 case 0:
176 exit(0);
177 /* NOTREACHED */
178 case -1:
179 err(1, "read");
180 /* NOTREACHED */
181 default:
182 if (!file_open) {
183 newfile();
184 file_open = 1;
185 }
186 if (bcnt + len >= bytecnt) {
187 /* LINTED: bytecnt - bcnt <= len */
188 dist = bytecnt - bcnt;
189 if (bigwrite(ofd, bfr, dist) != dist)
190 err(1, "write");
191 len -= dist;
192 for (C = bfr + dist; len >= bytecnt;
193 /* LINTED: bytecnt <= len */
194 len -= bytecnt, C += bytecnt) {
195 newfile();
196 /* LINTED: as above */
197 if (bigwrite(ofd,
198 C, bytecnt) != bytecnt)
199 err(1, "write");
200 }
201 if (len) {
202 newfile();
203 /* LINTED: len >= 0 */
204 if (bigwrite(ofd, C, len) != len)
205 err(1, "write");
206 } else
207 file_open = 0;
208 bcnt = len;
209 } else {
210 bcnt += len;
211 /* LINTED: len >= 0 */
212 if (bigwrite(ofd, bfr, len) != len)
213 err(1, "write");
214 }
215 }
216 }
217
218 /*
219 * split2 --
220 * Split the input by lines.
221 */
222 static void
223 split2(off_t numlines)
224 {
225 off_t lcnt;
226 size_t bcnt;
227 ssize_t len;
228 char *Ce, *Cs;
229 char bfr[MAXBSIZE];
230
231 for (lcnt = 0;;)
232 switch (len = read(ifd, bfr, MAXBSIZE)) {
233 case 0:
234 exit(0);
235 /* NOTREACHED */
236 case -1:
237 err(1, "read");
238 /* NOTREACHED */
239 default:
240 if (!file_open) {
241 newfile();
242 file_open = 1;
243 }
244 for (Cs = Ce = bfr; len--; Ce++)
245 if (*Ce == '\n' && ++lcnt == numlines) {
246 bcnt = Ce - Cs + 1;
247 if (bigwrite(ofd, Cs, bcnt) != bcnt)
248 err(1, "write");
249 lcnt = 0;
250 Cs = Ce + 1;
251 if (len)
252 newfile();
253 else
254 file_open = 0;
255 }
256 if (Cs < Ce) {
257 bcnt = Ce - Cs;
258 if (bigwrite(ofd, Cs, bcnt) != bcnt)
259 err(1, "write");
260 }
261 }
262 }
263
264 /*
265 * newfile --
266 * Open a new output file.
267 */
268 static void
269 newfile(void)
270 {
271 static int fnum;
272 static char *fpnt;
273 int quot, i;
274
275 if (ofd == -1) {
276 fpnt = fname + strlen(fname);
277 fpnt[sfxlen] = '\0';
278 } else if (close(ofd) != 0)
279 err(1, "%s", fname);
280
281 quot = fnum;
282 for (i = sfxlen - 1; i >= 0; i--) {
283 fpnt[i] = quot % 26 + 'a';
284 quot = quot / 26;
285 }
286 if (quot > 0)
287 errx(1, "too many files.");
288 ++fnum;
289 if ((ofd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE)) < 0)
290 err(1, "%s", fname);
291 }
292
293 static size_t
294 bigwrite(int fd, const void *buf, size_t len)
295 {
296 const char *ptr = buf;
297 size_t sofar = 0;
298 ssize_t w;
299
300 while (len != 0) {
301 if ((w = write(fd, ptr, len)) == -1)
302 return sofar;
303 len -= w;
304 ptr += w;
305 sofar += w;
306 }
307 return sofar;
308 }
309
310
311 static void
312 usage(void)
313 {
314 (void)fprintf(stderr,
315 "Usage: %s [-b byte_count] [-l line_count] [-a suffix_length] "
316 "[file [prefix]]\n", getprogname());
317 exit(1);
318 }
319