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
main(int argc,char ** argv)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
next_file_name(char * name)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
Usage()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