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