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
56static	void	Usage();
57static	void	next_file_name();
58
59
60static	char	*progname;
61static	int	suffix_length = 2;
62
63int
64main(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
351static	void
352next_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
370static	void
371Usage()
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