1 /* $NetBSD: split.c,v 1.17 2003/06/29 22:57:23 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.17 2003/06/29 22:57:23 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 off_t bytecnt = 0; /* Byte count to split on. */ 80 off_t numlines = 0; /* Line count to split on. */ 81 size_t namelen; 82 long name_max; 83 84 while ((ch = getopt(argc, argv, "0123456789b:l:a:")) != -1) 85 switch (ch) { 86 case '0': case '1': case '2': case '3': case '4': 87 case '5': case '6': case '7': case '8': case '9': 88 /* 89 * Undocumented kludge: split was originally designed 90 * to take a number after a dash. 91 */ 92 if (numlines == 0) { 93 p = argv[optind - 1]; 94 if (p[0] == '-' && p[1] == ch && !p[2]) 95 p++; 96 else 97 p = argv[optind] + 1; 98 numlines = strtoull(p, &ep, 10); 99 if (numlines == 0 || *ep != '\0') 100 errx(1, "%s: illegal line count.", p); 101 } 102 break; 103 case 'b': /* Byte count. */ 104 if (!isdigit((unsigned char)optarg[0]) || 105 (bytecnt = strtoull(optarg, &ep, 10)) == 0 || 106 (*ep != '\0' && *ep != 'k' && *ep != 'm')) 107 errx(1, "%s: illegal byte count.", optarg); 108 if (*ep == 'k') 109 bytecnt *= 1024; 110 else if (*ep == 'm') 111 bytecnt *= 1024 * 1024; 112 break; 113 case 'l': /* Line count. */ 114 if (numlines != 0) 115 usage(); 116 if (!isdigit((unsigned char)optarg[0]) || 117 (numlines = strtoull(optarg, &ep, 10)) == 0 || 118 *ep != '\0') 119 errx(1, "%s: illegal line count.", optarg); 120 break; 121 case 'a': /* Suffix length. */ 122 if (!isdigit((unsigned char)optarg[0]) || 123 (sfxlen = (size_t)strtoul(optarg, &ep, 10)) == 0 || 124 *ep != '\0') 125 errx(1, "%s: illegal suffix length.", optarg); 126 break; 127 default: 128 usage(); 129 } 130 argv += optind; 131 argc -= optind; 132 133 if (*argv != NULL) { 134 if (strcmp(*argv, "-") != 0 && 135 (ifd = open(*argv, O_RDONLY, 0)) < 0) 136 err(1, "%s", *argv); 137 ++argv; 138 } 139 140 errno = 0; 141 if ((name_max = pathconf(".", _PC_NAME_MAX)) == -1 && 142 errno != 0) 143 err(EXIT_FAILURE, "pathconf"); 144 if (*argv != NULL) { 145 namelen = strlen(*argv) + sfxlen; 146 if (name_max != -1 && namelen > name_max) 147 errx(EXIT_FAILURE, "Output file name too long"); 148 if ((fname = malloc(namelen + 1)) == NULL) 149 err(EXIT_FAILURE, NULL); 150 (void)strcpy(fname, *argv++); /* File name prefix. */ 151 } else { 152 if (name_max != -1 && 1 + sfxlen > name_max) 153 errx(EXIT_FAILURE, "Output file name too long"); 154 if ((fname = malloc(sfxlen + 2)) == NULL) 155 err(EXIT_FAILURE, NULL); 156 fname[0] = '\0'; 157 } 158 159 if (*argv != NULL) 160 usage(); 161 162 if (numlines == 0) 163 numlines = DEFLINE; 164 else if (bytecnt) 165 usage(); 166 167 if (bytecnt) 168 split1(bytecnt); 169 else 170 split2(numlines); 171 172 return 0; 173 } 174 175 /* 176 * split1 -- 177 * Split the input by bytes. 178 */ 179 static void 180 split1(off_t bytecnt) 181 { 182 off_t bcnt; 183 ssize_t dist, len; 184 char *C; 185 char bfr[MAXBSIZE]; 186 187 for (bcnt = 0;;) 188 switch (len = read(ifd, bfr, MAXBSIZE)) { 189 case 0: 190 exit(0); 191 /* NOTREACHED */ 192 case -1: 193 err(1, "read"); 194 /* NOTREACHED */ 195 default: 196 if (!file_open) { 197 newfile(); 198 file_open = 1; 199 } 200 if (bcnt + len >= bytecnt) { 201 /* LINTED: bytecnt - bcnt <= len */ 202 dist = bytecnt - bcnt; 203 if (bigwrite(ofd, bfr, dist) != dist) 204 err(1, "write"); 205 len -= dist; 206 for (C = bfr + dist; len >= bytecnt; 207 /* LINTED: bytecnt <= len */ 208 len -= bytecnt, C += bytecnt) { 209 newfile(); 210 /* LINTED: as above */ 211 if (bigwrite(ofd, 212 C, bytecnt) != bytecnt) 213 err(1, "write"); 214 } 215 if (len) { 216 newfile(); 217 /* LINTED: len >= 0 */ 218 if (bigwrite(ofd, C, len) != len) 219 err(1, "write"); 220 } else 221 file_open = 0; 222 bcnt = len; 223 } else { 224 bcnt += len; 225 /* LINTED: len >= 0 */ 226 if (bigwrite(ofd, bfr, len) != len) 227 err(1, "write"); 228 } 229 } 230 } 231 232 /* 233 * split2 -- 234 * Split the input by lines. 235 */ 236 static void 237 split2(off_t numlines) 238 { 239 off_t lcnt; 240 size_t bcnt; 241 ssize_t len; 242 char *Ce, *Cs; 243 char bfr[MAXBSIZE]; 244 245 for (lcnt = 0;;) 246 switch (len = read(ifd, bfr, MAXBSIZE)) { 247 case 0: 248 exit(0); 249 /* NOTREACHED */ 250 case -1: 251 err(1, "read"); 252 /* NOTREACHED */ 253 default: 254 if (!file_open) { 255 newfile(); 256 file_open = 1; 257 } 258 for (Cs = Ce = bfr; len--; Ce++) 259 if (*Ce == '\n' && ++lcnt == numlines) { 260 bcnt = Ce - Cs + 1; 261 if (bigwrite(ofd, Cs, bcnt) != bcnt) 262 err(1, "write"); 263 lcnt = 0; 264 Cs = Ce + 1; 265 if (len) 266 newfile(); 267 else 268 file_open = 0; 269 } 270 if (Cs < Ce) { 271 bcnt = Ce - Cs; 272 if (bigwrite(ofd, Cs, bcnt) != bcnt) 273 err(1, "write"); 274 } 275 } 276 } 277 278 /* 279 * newfile -- 280 * Open a new output file. 281 */ 282 static void 283 newfile(void) 284 { 285 static int fnum; 286 static int defname; 287 static char *fpnt; 288 int quot, i; 289 290 if (ofd == -1) { 291 if (fname[0] == '\0') { 292 fname[0] = 'x'; 293 fpnt = fname + 1; 294 defname = 1; 295 } else { 296 fpnt = fname + strlen(fname); 297 defname = 0; 298 } 299 } else { 300 if (close(ofd) != 0) 301 err(1, "%s", fname); 302 } 303 /* 304 * Hack to increase max files; original code wandered through 305 * magic characters. Maximum files is 3 * 26 * 26 == 2028 306 */ 307 fpnt[sfxlen] = '\0'; 308 quot = fnum; 309 for (i = sfxlen - 1; i >= 0; i--) { 310 fpnt[i] = quot % 26 + 'a'; 311 quot = quot / 26; 312 } 313 if (quot > 0) { 314 if (!defname || fname[0] == 'z') 315 errx(1, "too many files."); 316 ++fname[0]; 317 fnum = 0; 318 } 319 ++fnum; 320 if ((ofd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE)) < 0) 321 err(1, "%s", fname); 322 } 323 324 static size_t 325 bigwrite(int fd, const void *buf, size_t len) 326 { 327 const char *ptr = buf; 328 size_t sofar = 0; 329 ssize_t w; 330 331 while (len != 0) { 332 if ((w = write(fd, ptr, len)) == -1) 333 return sofar; 334 len -= w; 335 ptr += w; 336 sofar += w; 337 } 338 return sofar; 339 } 340 341 342 static void 343 usage(void) 344 { 345 (void)fprintf(stderr, 346 "Usage: %s [-b byte_count] [-l line_count] [-a suffix_length] " 347 "[file [prefix]]\n", getprogname()); 348 exit(1); 349 } 350