xref: /illumos-gate/usr/src/cmd/split/split.c (revision 24da5b34f49324ed742a340010ed5bd3d4e06625)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 1999 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29 /*	  All Rights Reserved	*/
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #include <stdio.h>
34 #include <sys/types.h>
35 #include <sys/statvfs.h>
36 #include <locale.h>
37 #include <malloc.h>
38 #include <string.h>
39 #include <sys/param.h>
40 #include <stdlib.h>
41 #include <wchar.h>
42 #include <widec.h>
43 #include <ctype.h>
44 #include <errno.h>
45 
46 
47 #define	DEFAULT_LINES	1000
48 #define	ONE_K		1024
49 #define	ONE_M		ONE_K*ONE_K
50 #ifndef	TRUE
51 #define	TRUE		1
52 #define	FALSE		0
53 #endif
54 
55 
56 static	void	Usage();
57 static	void	next_file_name();
58 
59 
60 static	char	*progname;
61 static	int	suffix_length = 2;
62 
63 int
64 main(int argc, char **argv)
65 {
66 	long long	line_count = 0;
67 	long long	byte_count = 0;
68 	long long	out;
69 	char	*fname = NULL;
70 	char	head[MAXPATHLEN];
71 	char	*output_file_name;
72 	char	*tail;
73 	char	*last;
74 	FILE	*in_file = NULL;
75 	FILE	*out_file = (FILE *)NULL;
76 	int	i;
77 	int	c;
78 	wint_t	wc;
79 	int	output_file_open;
80 	struct	statvfs stbuf;
81 	int	opt;
82 	int	non_standard_line_count = FALSE;
83 
84 
85 	(void) setlocale(LC_ALL, "");
86 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
87 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
88 #endif
89 	(void) textdomain(TEXT_DOMAIN);
90 
91 	progname = argv[0];
92 
93 	/* check for explicit stdin "-" option */
94 	for (i = 1; i < argc; i++) {
95 		if (strcmp(argv[i], "-") == 0) {
96 			in_file = stdin;
97 			while (i < argc) {
98 				argv[i] = argv[i + 1];
99 
100 				/* a "-" before "--" is an error */
101 				if ((argv[i] != NULL) &&
102 				    (strcmp(argv[i], "--") == 0)) {
103 					Usage();
104 				}
105 				i++;
106 			}
107 			argc--;
108 		}
109 	}
110 
111 	/* check for non-standard "-line-count" option */
112 	for (i = 1; i < argc; i++) {
113 		if (strcmp(argv[i], "--") == 0)
114 			break;
115 
116 		if ((argv[i][0] == '-') && isdigit(argv[i][1])) {
117 			if (strlen(&argv[i][1]) !=
118 			    strspn(&argv[i][1], "0123456789")) {
119 				(void) fprintf(stderr, gettext(
120 				    "%s: Badly formed number\n"), progname);
121 				Usage();
122 			}
123 
124 			line_count = (long long) strtoll(&argv[i][1],
125 			    (char **)NULL, 10);
126 
127 			non_standard_line_count = TRUE;
128 			while (i < argc) {
129 				argv[i] = argv[i + 1];
130 				i++;
131 			}
132 			argc--;
133 		}
134 	}
135 
136 	/* get options */
137 	while ((opt = getopt(argc, argv, "a:b:l:")) != EOF) {
138 		switch (opt) {
139 		case 'a':
140 			if (strcmp(optarg, "--") == 0) {
141 				Usage();
142 			}
143 			suffix_length = (long long) strtoll(optarg,
144 					(char **)NULL, 10);
145 			if (suffix_length <= 0) {
146 				(void) fprintf(stderr, gettext(
147 				    "%s: Invalid \"-a %s\" option\n"),
148 				    progname, optarg);
149 				Usage();
150 			}
151 			break;
152 
153 		case 'b':
154 			if (strcmp(optarg, "--") == 0) {
155 				Usage();
156 			}
157 			byte_count = (long long) strtoll(optarg,
158 					(char **)NULL, 10);
159 			if (*(optarg + strspn(optarg, "0123456789")) == 'k')
160 				byte_count *= ONE_K;
161 			if (*(optarg + strspn(optarg, "0123456789")) == 'm')
162 				byte_count *= ONE_M;
163 			break;
164 
165 		case 'l':
166 			if (strcmp(optarg, "--") == 0) {
167 				Usage();
168 			}
169 			if (non_standard_line_count == TRUE) {
170 				Usage();
171 			}
172 			line_count = (long long) strtoll(optarg,
173 					(char **)NULL, 10);
174 			break;
175 
176 		default:
177 			Usage();
178 		}
179 	}
180 
181 	/* get input file */
182 	if ((in_file == NULL) && (optind < argc)) {
183 		if ((in_file = fopen(argv[optind++], "r")) == NULL) {
184 			(void) perror("split");
185 			return (1);
186 		}
187 	}
188 	if (in_file == NULL) {
189 		in_file = stdin;
190 	}
191 
192 	/* get output file name */
193 	if (optind < argc) {
194 		output_file_name = argv[optind];
195 		if ((tail = strrchr(output_file_name, '/')) == NULL) {
196 			tail = output_file_name;
197 			(void) getcwd(head, sizeof (head));
198 		} else {
199 			tail++;
200 			(void) strcpy(head, output_file_name);
201 			last = strrchr(head, '/');
202 			*++last = '\0';
203 		}
204 
205 		if (statvfs(head, &stbuf) < 0) {
206 			perror(head);
207 			return (1);
208 		}
209 
210 		if (strlen(tail) > (stbuf.f_namemax - suffix_length)) {
211 			(void) fprintf(stderr, gettext(
212 			    "%s: More than %d characters in file name\n"),
213 			    progname, stbuf.f_namemax - suffix_length);
214 			Usage();
215 		}
216 	} else
217 		output_file_name = "x";
218 
219 	/* check options */
220 	if (((int)strlen(output_file_name) + suffix_length) > FILENAME_MAX) {
221 		(void) fprintf(stderr, gettext(
222 		"%s: Output file name too long\n"), progname);
223 		return (1);
224 	}
225 
226 	if (line_count && byte_count) {
227 		    Usage();
228 	}
229 
230 	/* use default line count if none specified */
231 	if (line_count == 0) {
232 		line_count = DEFAULT_LINES;
233 	}
234 
235 	/*
236 	 * allocate buffer for the filenames we'll construct; it must be
237 	 * big enough to hold the name in 'output_file_name' + an n-char
238 	 * suffix + NULL terminator
239 	 */
240 	if ((fname = (char *)malloc(strlen(output_file_name) +
241 	    suffix_length + 1)) == NULL) {
242 		(void) perror("split");
243 		return (1);
244 	}
245 
246 	/* build first output file name */
247 	for (i = 0; output_file_name[i]; i++) {
248 		fname[i] = output_file_name[i];
249 	}
250 	while (i < (int)strlen(output_file_name) + suffix_length) {
251 		fname[i++] = 'a';
252 	}
253 	if (suffix_length)
254 		fname[i - 1] = 'a' - 1;
255 	fname[i] = '\0';
256 
257 	for (; ; ) {
258 	    output_file_open = FALSE;
259 	    if (byte_count) {
260 		for (out = 0; out < byte_count; out++) {
261 		    errno = 0;
262 		    c = getc(in_file);
263 		    if (c == EOF) {
264 			if (errno != 0) {
265 			    int lerrno = errno;
266 			    (void) fprintf(stderr, gettext(
267 				"%s: Read error at file offset %lld: %s, "
268 				"aborting split\n"),
269 				    progname, ftello(in_file),
270 				    strerror(lerrno));
271 			    if (output_file_open == TRUE)
272 				(void) fclose(out_file);
273 			    free(fname);
274 			    return (1);
275 			}
276 			if (output_file_open == TRUE)
277 			    (void) fclose(out_file);
278 			free(fname);
279 			return (0);
280 		    }
281 		    if (output_file_open == FALSE) {
282 			next_file_name(fname);
283 			if ((out_file = fopen(fname, "w")) == NULL) {
284 			    (void) perror("split");
285 			    free(fname);
286 			    return (1);
287 			}
288 			output_file_open = TRUE;
289 		    }
290 		    if (putc(c, out_file) == EOF) {
291 			perror("split");
292 			if (output_file_open == TRUE)
293 			    (void) fclose(out_file);
294 			free(fname);
295 			return (1);
296 		    }
297 		}
298 	    } else {
299 		for (out = 0; out < line_count; out++) {
300 		    do {
301 			errno = 0;
302 			wc = getwc(in_file);
303 			if (wc == WEOF) {
304 			    if (errno != 0) {
305 				if (errno == EILSEQ) {
306 				    (void) fprintf(stderr, gettext(
307 					"%s: Invalid multibyte sequence "
308 					"encountered at file offset %lld, "
309 					"aborting split\n"),
310 					    progname, ftello(in_file));
311 				} else {
312 				    (void) perror("split");
313 				}
314 				if (output_file_open == TRUE)
315 				    (void) fclose(out_file);
316 				free(fname);
317 				return (1);
318 			    }
319 			    if (output_file_open == TRUE)
320 				(void) fclose(out_file);
321 			    free(fname);
322 			    return (0);
323 			}
324 			if (output_file_open == FALSE) {
325 			    next_file_name(fname);
326 			    if ((out_file = fopen(fname, "w")) == NULL) {
327 				(void) perror("split");
328 				free(fname);
329 				return (1);
330 			    }
331 			    output_file_open = TRUE;
332 			}
333 			if (putwc(wc, out_file) == WEOF) {
334 			    (void) perror("split");
335 			    if (output_file_open == TRUE)
336 				(void) fclose(out_file);
337 			    free(fname);
338 			    return (1);
339 			}
340 		    } while (wc != '\n');
341 		}
342 	    }
343 	    if (output_file_open == TRUE)
344 		(void) fclose(out_file);
345 	}
346 
347 	/*NOTREACHED*/
348 }
349 
350 
351 static	void
352 next_file_name(char *name)
353 {
354 	int	i;
355 
356 	i = strlen(name) - 1;
357 
358 	while (i >= (int)(strlen(name) - suffix_length)) {
359 		if (++name[i] <= 'z')
360 			return;
361 		name[i--] = 'a';
362 	}
363 	(void) fprintf(stderr, gettext(
364 		"%s: Exhausted output file names, aborting split\n"),
365 		progname);
366 	exit(1);
367 }
368 
369 
370 static	void
371 Usage()
372 {
373 	(void) fprintf(stderr, gettext(
374 		"Usage: %s [-l #] [-a #] [file [name]]\n"
375 		"       %s [-b #[k|m]] [-a #] [file [name]]\n"
376 		"       %s [-#] [-a #] [file [name]]\n"),
377 		progname, progname, progname);
378 	exit(1);
379 }
380