xref: /titanic_51/usr/src/cmd/csplit/csplit.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23*7c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
24*7c478bd9Sstevel@tonic-gate 
25*7c478bd9Sstevel@tonic-gate 
26*7c478bd9Sstevel@tonic-gate /*
27*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
28*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
29*7c478bd9Sstevel@tonic-gate  */
30*7c478bd9Sstevel@tonic-gate 
31*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
32*7c478bd9Sstevel@tonic-gate 
33*7c478bd9Sstevel@tonic-gate /*
34*7c478bd9Sstevel@tonic-gate  * csplit - Context or line file splitter
35*7c478bd9Sstevel@tonic-gate  * Compile: cc -O -s -o csplit csplit.c
36*7c478bd9Sstevel@tonic-gate  */
37*7c478bd9Sstevel@tonic-gate 
38*7c478bd9Sstevel@tonic-gate #include <stdio.h>
39*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
40*7c478bd9Sstevel@tonic-gate #include <unistd.h>
41*7c478bd9Sstevel@tonic-gate #include <string.h>
42*7c478bd9Sstevel@tonic-gate #include <ctype.h>
43*7c478bd9Sstevel@tonic-gate #include <errno.h>
44*7c478bd9Sstevel@tonic-gate #include <limits.h>
45*7c478bd9Sstevel@tonic-gate #include <regexpr.h>
46*7c478bd9Sstevel@tonic-gate #include <signal.h>
47*7c478bd9Sstevel@tonic-gate #include <locale.h>
48*7c478bd9Sstevel@tonic-gate #include <libintl.h>
49*7c478bd9Sstevel@tonic-gate 
50*7c478bd9Sstevel@tonic-gate #define	LAST	0LL
51*7c478bd9Sstevel@tonic-gate #define	ERR	-1
52*7c478bd9Sstevel@tonic-gate #define	FALSE	0
53*7c478bd9Sstevel@tonic-gate #define	TRUE	1
54*7c478bd9Sstevel@tonic-gate #define	EXPMODE	2
55*7c478bd9Sstevel@tonic-gate #define	LINMODE	3
56*7c478bd9Sstevel@tonic-gate #define	LINSIZ	LINE_MAX	/* POSIX.2 - read lines LINE_MAX long */
57*7c478bd9Sstevel@tonic-gate 
58*7c478bd9Sstevel@tonic-gate 	/* Globals */
59*7c478bd9Sstevel@tonic-gate 
60*7c478bd9Sstevel@tonic-gate char linbuf[LINSIZ];		/* Input line buffer */
61*7c478bd9Sstevel@tonic-gate char *expbuf;
62*7c478bd9Sstevel@tonic-gate char tmpbuf[BUFSIZ];		/* Temporary buffer for stdin */
63*7c478bd9Sstevel@tonic-gate char file[8192] = "xx";		/* File name buffer */
64*7c478bd9Sstevel@tonic-gate char *targ;			/* Arg ptr for error messages */
65*7c478bd9Sstevel@tonic-gate char *sptr;
66*7c478bd9Sstevel@tonic-gate FILE *infile, *outfile;		/* I/O file streams */
67*7c478bd9Sstevel@tonic-gate int silent, keep, create;	/* Flags: -s(ilent), -k(eep), (create) */
68*7c478bd9Sstevel@tonic-gate int errflg;
69*7c478bd9Sstevel@tonic-gate int fiwidth = 2;		/* file index width (output file names) */
70*7c478bd9Sstevel@tonic-gate extern int optind;
71*7c478bd9Sstevel@tonic-gate extern char *optarg;
72*7c478bd9Sstevel@tonic-gate offset_t offset;		/* Regular expression offset value */
73*7c478bd9Sstevel@tonic-gate offset_t curline;		/* Current line in input file */
74*7c478bd9Sstevel@tonic-gate 
75*7c478bd9Sstevel@tonic-gate /*
76*7c478bd9Sstevel@tonic-gate  * These defines are needed for regexp handling(see regexp(7))
77*7c478bd9Sstevel@tonic-gate  */
78*7c478bd9Sstevel@tonic-gate #define	PERROR(x)	fatal("%s: Illegal Regular Expression\n", targ);
79*7c478bd9Sstevel@tonic-gate 
80*7c478bd9Sstevel@tonic-gate static int asc_to_ll(char *, long long *);
81*7c478bd9Sstevel@tonic-gate static void closefile(void);
82*7c478bd9Sstevel@tonic-gate static void fatal(char *, char *);
83*7c478bd9Sstevel@tonic-gate static offset_t findline(char *, offset_t);
84*7c478bd9Sstevel@tonic-gate static void flush(void);
85*7c478bd9Sstevel@tonic-gate static FILE *getfile(void);
86*7c478bd9Sstevel@tonic-gate static char *getline(int);
87*7c478bd9Sstevel@tonic-gate static void line_arg(char *);
88*7c478bd9Sstevel@tonic-gate static void num_arg(char *, int);
89*7c478bd9Sstevel@tonic-gate static void re_arg(char *);
90*7c478bd9Sstevel@tonic-gate static void sig(int);
91*7c478bd9Sstevel@tonic-gate static void to_line(offset_t);
92*7c478bd9Sstevel@tonic-gate static void usage(void);
93*7c478bd9Sstevel@tonic-gate 
94*7c478bd9Sstevel@tonic-gate int
95*7c478bd9Sstevel@tonic-gate main(int argc, char **argv)
96*7c478bd9Sstevel@tonic-gate {
97*7c478bd9Sstevel@tonic-gate 	int ch, mode;
98*7c478bd9Sstevel@tonic-gate 	char *ptr;
99*7c478bd9Sstevel@tonic-gate 
100*7c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
101*7c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)		/* Should be defined by cc -D */
102*7c478bd9Sstevel@tonic-gate #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it weren't */
103*7c478bd9Sstevel@tonic-gate #endif
104*7c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
105*7c478bd9Sstevel@tonic-gate 
106*7c478bd9Sstevel@tonic-gate 	while ((ch = getopt(argc, argv, "skf:n:")) != EOF) {
107*7c478bd9Sstevel@tonic-gate 		switch (ch) {
108*7c478bd9Sstevel@tonic-gate 			case 'f':
109*7c478bd9Sstevel@tonic-gate 				(void) strcpy(file, optarg);
110*7c478bd9Sstevel@tonic-gate 				if ((ptr = strrchr(optarg, '/')) == NULL)
111*7c478bd9Sstevel@tonic-gate 					ptr = optarg;
112*7c478bd9Sstevel@tonic-gate 				else
113*7c478bd9Sstevel@tonic-gate 					ptr++;
114*7c478bd9Sstevel@tonic-gate 
115*7c478bd9Sstevel@tonic-gate 				break;
116*7c478bd9Sstevel@tonic-gate 			case 'n':		/* POSIX.2 */
117*7c478bd9Sstevel@tonic-gate 				for (ptr = optarg; *ptr != NULL; ptr++)
118*7c478bd9Sstevel@tonic-gate 					if (!isdigit((int)*ptr))
119*7c478bd9Sstevel@tonic-gate 						fatal("-n num\n", NULL);
120*7c478bd9Sstevel@tonic-gate 				fiwidth = atoi(optarg);
121*7c478bd9Sstevel@tonic-gate 				break;
122*7c478bd9Sstevel@tonic-gate 			case 'k':
123*7c478bd9Sstevel@tonic-gate 				keep++;
124*7c478bd9Sstevel@tonic-gate 				break;
125*7c478bd9Sstevel@tonic-gate 			case 's':
126*7c478bd9Sstevel@tonic-gate 				silent++;
127*7c478bd9Sstevel@tonic-gate 				break;
128*7c478bd9Sstevel@tonic-gate 			case '?':
129*7c478bd9Sstevel@tonic-gate 				errflg++;
130*7c478bd9Sstevel@tonic-gate 		}
131*7c478bd9Sstevel@tonic-gate 	}
132*7c478bd9Sstevel@tonic-gate 
133*7c478bd9Sstevel@tonic-gate 	argv = &argv[optind];
134*7c478bd9Sstevel@tonic-gate 	argc -= optind;
135*7c478bd9Sstevel@tonic-gate 	if (argc <= 1 || errflg)
136*7c478bd9Sstevel@tonic-gate 		usage();
137*7c478bd9Sstevel@tonic-gate 
138*7c478bd9Sstevel@tonic-gate 	if (strcmp(*argv, "-") == 0) {
139*7c478bd9Sstevel@tonic-gate 		infile = tmpfile();
140*7c478bd9Sstevel@tonic-gate 
141*7c478bd9Sstevel@tonic-gate 		while (fread(tmpbuf, 1, BUFSIZ, stdin) != 0) {
142*7c478bd9Sstevel@tonic-gate 			if (fwrite(tmpbuf, 1, BUFSIZ, infile) == 0)
143*7c478bd9Sstevel@tonic-gate 				if (errno == ENOSPC) {
144*7c478bd9Sstevel@tonic-gate 					(void) fprintf(stderr, "csplit: ");
145*7c478bd9Sstevel@tonic-gate 					(void) fprintf(stderr, gettext(
146*7c478bd9Sstevel@tonic-gate 						"No space left on device\n"));
147*7c478bd9Sstevel@tonic-gate 					exit(1);
148*7c478bd9Sstevel@tonic-gate 				} else {
149*7c478bd9Sstevel@tonic-gate 					(void) fprintf(stderr, "csplit: ");
150*7c478bd9Sstevel@tonic-gate 					(void) fprintf(stderr, gettext(
151*7c478bd9Sstevel@tonic-gate 						"Bad write to temporary "
152*7c478bd9Sstevel@tonic-gate 							"file\n"));
153*7c478bd9Sstevel@tonic-gate 					exit(1);
154*7c478bd9Sstevel@tonic-gate 				}
155*7c478bd9Sstevel@tonic-gate 
156*7c478bd9Sstevel@tonic-gate 	/* clear the buffer to get correct size when writing buffer */
157*7c478bd9Sstevel@tonic-gate 
158*7c478bd9Sstevel@tonic-gate 			(void) memset(tmpbuf, '\0', sizeof (tmpbuf));
159*7c478bd9Sstevel@tonic-gate 		}
160*7c478bd9Sstevel@tonic-gate 		rewind(infile);
161*7c478bd9Sstevel@tonic-gate 	} else if ((infile = fopen(*argv, "r")) == NULL)
162*7c478bd9Sstevel@tonic-gate 		fatal("Cannot open %s\n", *argv);
163*7c478bd9Sstevel@tonic-gate 	++argv;
164*7c478bd9Sstevel@tonic-gate 	curline = (offset_t)1;
165*7c478bd9Sstevel@tonic-gate 	(void) signal(SIGINT, sig);
166*7c478bd9Sstevel@tonic-gate 
167*7c478bd9Sstevel@tonic-gate 	/*
168*7c478bd9Sstevel@tonic-gate 	 * The following for loop handles the different argument types.
169*7c478bd9Sstevel@tonic-gate 	 * A switch is performed on the first character of the argument
170*7c478bd9Sstevel@tonic-gate 	 * and each case calls the appropriate argument handling routine.
171*7c478bd9Sstevel@tonic-gate 	 */
172*7c478bd9Sstevel@tonic-gate 
173*7c478bd9Sstevel@tonic-gate 	for (; *argv; ++argv) {
174*7c478bd9Sstevel@tonic-gate 		targ = *argv;
175*7c478bd9Sstevel@tonic-gate 		switch (**argv) {
176*7c478bd9Sstevel@tonic-gate 		case '/':
177*7c478bd9Sstevel@tonic-gate 			mode = EXPMODE;
178*7c478bd9Sstevel@tonic-gate 			create = TRUE;
179*7c478bd9Sstevel@tonic-gate 			re_arg(*argv);
180*7c478bd9Sstevel@tonic-gate 			break;
181*7c478bd9Sstevel@tonic-gate 		case '%':
182*7c478bd9Sstevel@tonic-gate 			mode = EXPMODE;
183*7c478bd9Sstevel@tonic-gate 			create = FALSE;
184*7c478bd9Sstevel@tonic-gate 			re_arg(*argv);
185*7c478bd9Sstevel@tonic-gate 			break;
186*7c478bd9Sstevel@tonic-gate 		case '{':
187*7c478bd9Sstevel@tonic-gate 			num_arg(*argv, mode);
188*7c478bd9Sstevel@tonic-gate 			mode = FALSE;
189*7c478bd9Sstevel@tonic-gate 			break;
190*7c478bd9Sstevel@tonic-gate 		default:
191*7c478bd9Sstevel@tonic-gate 			mode = LINMODE;
192*7c478bd9Sstevel@tonic-gate 			create = TRUE;
193*7c478bd9Sstevel@tonic-gate 			line_arg(*argv);
194*7c478bd9Sstevel@tonic-gate 			break;
195*7c478bd9Sstevel@tonic-gate 		}
196*7c478bd9Sstevel@tonic-gate 	}
197*7c478bd9Sstevel@tonic-gate 	create = TRUE;
198*7c478bd9Sstevel@tonic-gate 	to_line(LAST);
199*7c478bd9Sstevel@tonic-gate 	return (0);
200*7c478bd9Sstevel@tonic-gate }
201*7c478bd9Sstevel@tonic-gate 
202*7c478bd9Sstevel@tonic-gate /*
203*7c478bd9Sstevel@tonic-gate  * asc_to_ll takes an ascii argument(str) and converts it to a long long(plc)
204*7c478bd9Sstevel@tonic-gate  * It returns ERR if an illegal character.  The reason that asc_to_ll
205*7c478bd9Sstevel@tonic-gate  * does not return an answer(long long) is that any value for the long
206*7c478bd9Sstevel@tonic-gate  * long is legal, and this version of asc_to_ll detects error strings.
207*7c478bd9Sstevel@tonic-gate  */
208*7c478bd9Sstevel@tonic-gate 
209*7c478bd9Sstevel@tonic-gate static int
210*7c478bd9Sstevel@tonic-gate asc_to_ll(char *str, long long *plc)
211*7c478bd9Sstevel@tonic-gate {
212*7c478bd9Sstevel@tonic-gate 	int f;
213*7c478bd9Sstevel@tonic-gate 	*plc = 0;
214*7c478bd9Sstevel@tonic-gate 	f = 0;
215*7c478bd9Sstevel@tonic-gate 	for (; ; str++) {
216*7c478bd9Sstevel@tonic-gate 		switch (*str) {
217*7c478bd9Sstevel@tonic-gate 		case ' ':
218*7c478bd9Sstevel@tonic-gate 		case '\t':
219*7c478bd9Sstevel@tonic-gate 			continue;
220*7c478bd9Sstevel@tonic-gate 		case '-':
221*7c478bd9Sstevel@tonic-gate 			f++;
222*7c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
223*7c478bd9Sstevel@tonic-gate 		case '+':
224*7c478bd9Sstevel@tonic-gate 			str++;
225*7c478bd9Sstevel@tonic-gate 		}
226*7c478bd9Sstevel@tonic-gate 		break;
227*7c478bd9Sstevel@tonic-gate 	}
228*7c478bd9Sstevel@tonic-gate 	for (; *str != NULL; str++)
229*7c478bd9Sstevel@tonic-gate 		if (*str >= '0' && *str <= '9')
230*7c478bd9Sstevel@tonic-gate 			*plc = *plc * 10 + *str - '0';
231*7c478bd9Sstevel@tonic-gate 		else
232*7c478bd9Sstevel@tonic-gate 			return (ERR);
233*7c478bd9Sstevel@tonic-gate 	if (f)
234*7c478bd9Sstevel@tonic-gate 		*plc = -(*plc);
235*7c478bd9Sstevel@tonic-gate 	return (TRUE);	/* not error */
236*7c478bd9Sstevel@tonic-gate }
237*7c478bd9Sstevel@tonic-gate 
238*7c478bd9Sstevel@tonic-gate /*
239*7c478bd9Sstevel@tonic-gate  * Closefile prints the byte count of the file created,(via fseeko
240*7c478bd9Sstevel@tonic-gate  * and ftello), if the create flag is on and the silent flag is not on.
241*7c478bd9Sstevel@tonic-gate  * If the create flag is on closefile then closes the file(fclose).
242*7c478bd9Sstevel@tonic-gate  */
243*7c478bd9Sstevel@tonic-gate 
244*7c478bd9Sstevel@tonic-gate static void
245*7c478bd9Sstevel@tonic-gate closefile()
246*7c478bd9Sstevel@tonic-gate {
247*7c478bd9Sstevel@tonic-gate 	if (!silent && create) {
248*7c478bd9Sstevel@tonic-gate 		(void) fseeko(outfile, (offset_t)0, SEEK_END);
249*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stdout, "%lld\n", (offset_t)ftello(outfile));
250*7c478bd9Sstevel@tonic-gate 	}
251*7c478bd9Sstevel@tonic-gate 	if (create)
252*7c478bd9Sstevel@tonic-gate 		(void) fclose(outfile);
253*7c478bd9Sstevel@tonic-gate }
254*7c478bd9Sstevel@tonic-gate 
255*7c478bd9Sstevel@tonic-gate /*
256*7c478bd9Sstevel@tonic-gate  * Fatal handles error messages and cleanup.
257*7c478bd9Sstevel@tonic-gate  * Because "arg" can be the global file, and the cleanup processing
258*7c478bd9Sstevel@tonic-gate  * uses the global file, the error message is printed first.  If the
259*7c478bd9Sstevel@tonic-gate  * "keep" flag is not set, fatal unlinks all created files.  If the
260*7c478bd9Sstevel@tonic-gate  * "keep" flag is set, fatal closes the current file(if there is one).
261*7c478bd9Sstevel@tonic-gate  * Fatal exits with a value of 1.
262*7c478bd9Sstevel@tonic-gate  */
263*7c478bd9Sstevel@tonic-gate 
264*7c478bd9Sstevel@tonic-gate static void
265*7c478bd9Sstevel@tonic-gate fatal(char *string, char *arg)
266*7c478bd9Sstevel@tonic-gate {
267*7c478bd9Sstevel@tonic-gate 	char *fls;
268*7c478bd9Sstevel@tonic-gate 	int num;
269*7c478bd9Sstevel@tonic-gate 
270*7c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, "csplit: ");
271*7c478bd9Sstevel@tonic-gate 
272*7c478bd9Sstevel@tonic-gate 	/* gettext dynamically replaces string */
273*7c478bd9Sstevel@tonic-gate 
274*7c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext(string), arg);
275*7c478bd9Sstevel@tonic-gate 	if (!keep) {
276*7c478bd9Sstevel@tonic-gate 		if (outfile) {
277*7c478bd9Sstevel@tonic-gate 			(void) fclose(outfile);
278*7c478bd9Sstevel@tonic-gate 			for (fls = file; *fls != '\0'; fls++)
279*7c478bd9Sstevel@tonic-gate 				continue;
280*7c478bd9Sstevel@tonic-gate 			fls -= fiwidth;
281*7c478bd9Sstevel@tonic-gate 			for (num = atoi(fls); num >= 0; num--) {
282*7c478bd9Sstevel@tonic-gate 				(void) sprintf(fls, "%.*d", fiwidth, num);
283*7c478bd9Sstevel@tonic-gate 				(void) unlink(file);
284*7c478bd9Sstevel@tonic-gate 			}
285*7c478bd9Sstevel@tonic-gate 		}
286*7c478bd9Sstevel@tonic-gate 	} else
287*7c478bd9Sstevel@tonic-gate 		if (outfile)
288*7c478bd9Sstevel@tonic-gate 			closefile();
289*7c478bd9Sstevel@tonic-gate 	exit(1);
290*7c478bd9Sstevel@tonic-gate }
291*7c478bd9Sstevel@tonic-gate 
292*7c478bd9Sstevel@tonic-gate /*
293*7c478bd9Sstevel@tonic-gate  * Findline returns the line number referenced by the current argument.
294*7c478bd9Sstevel@tonic-gate  * Its arguments are a pointer to the compiled regular expression(expr),
295*7c478bd9Sstevel@tonic-gate  * and an offset(oset).  The variable lncnt is used to count the number
296*7c478bd9Sstevel@tonic-gate  * of lines searched.  First the current stream location is saved via
297*7c478bd9Sstevel@tonic-gate  * ftello(), and getline is called so that R.E. searching starts at the
298*7c478bd9Sstevel@tonic-gate  * line after the previously referenced line.  The while loop checks
299*7c478bd9Sstevel@tonic-gate  * that there are more lines(error if none), bumps the line count, and
300*7c478bd9Sstevel@tonic-gate  * checks for the R.E. on each line.  If the R.E. matches on one of the
301*7c478bd9Sstevel@tonic-gate  * lines the old stream location is restored, and the line number
302*7c478bd9Sstevel@tonic-gate  * referenced by the R.E. and the offset is returned.
303*7c478bd9Sstevel@tonic-gate  */
304*7c478bd9Sstevel@tonic-gate 
305*7c478bd9Sstevel@tonic-gate static offset_t
306*7c478bd9Sstevel@tonic-gate findline(char *expr, offset_t oset)
307*7c478bd9Sstevel@tonic-gate {
308*7c478bd9Sstevel@tonic-gate 	static int benhere = 0;
309*7c478bd9Sstevel@tonic-gate 	offset_t lncnt = 0, saveloc;
310*7c478bd9Sstevel@tonic-gate 
311*7c478bd9Sstevel@tonic-gate 	saveloc = ftello(infile);
312*7c478bd9Sstevel@tonic-gate 	if (curline != (offset_t)1 || benhere)	/* If first line, first time, */
313*7c478bd9Sstevel@tonic-gate 		(void) getline(FALSE);		/* then don't skip */
314*7c478bd9Sstevel@tonic-gate 	else
315*7c478bd9Sstevel@tonic-gate 		lncnt--;
316*7c478bd9Sstevel@tonic-gate 	benhere = 1;
317*7c478bd9Sstevel@tonic-gate 	while (getline(FALSE) != NULL) {
318*7c478bd9Sstevel@tonic-gate 		lncnt++;
319*7c478bd9Sstevel@tonic-gate 		if ((sptr = strrchr(linbuf, '\n')) != NULL)
320*7c478bd9Sstevel@tonic-gate 			*sptr = '\0';
321*7c478bd9Sstevel@tonic-gate 		if (step(linbuf, expr)) {
322*7c478bd9Sstevel@tonic-gate 			(void) fseeko(infile, (offset_t)saveloc, SEEK_SET);
323*7c478bd9Sstevel@tonic-gate 			return (curline+lncnt+oset);
324*7c478bd9Sstevel@tonic-gate 		}
325*7c478bd9Sstevel@tonic-gate 	}
326*7c478bd9Sstevel@tonic-gate 	(void) fseeko(infile, (offset_t)saveloc, SEEK_SET);
327*7c478bd9Sstevel@tonic-gate 	return (curline+lncnt+oset+2);
328*7c478bd9Sstevel@tonic-gate }
329*7c478bd9Sstevel@tonic-gate 
330*7c478bd9Sstevel@tonic-gate /*
331*7c478bd9Sstevel@tonic-gate  * Flush uses fputs to put lines on the output file stream(outfile)
332*7c478bd9Sstevel@tonic-gate  * Since fputs does its own buffering, flush doesn't need to.
333*7c478bd9Sstevel@tonic-gate  * Flush does nothing if the create flag is not set.
334*7c478bd9Sstevel@tonic-gate  */
335*7c478bd9Sstevel@tonic-gate 
336*7c478bd9Sstevel@tonic-gate static void
337*7c478bd9Sstevel@tonic-gate flush()
338*7c478bd9Sstevel@tonic-gate {
339*7c478bd9Sstevel@tonic-gate 	if (create)
340*7c478bd9Sstevel@tonic-gate 		(void) fputs(linbuf, outfile);
341*7c478bd9Sstevel@tonic-gate }
342*7c478bd9Sstevel@tonic-gate 
343*7c478bd9Sstevel@tonic-gate /*
344*7c478bd9Sstevel@tonic-gate  * Getfile does nothing if the create flag is not set.  If the create
345*7c478bd9Sstevel@tonic-gate  * flag is set, getfile positions the file pointer(fptr) at the end of
346*7c478bd9Sstevel@tonic-gate  * the file name prefix on the first call(fptr=0).  The file counter is
347*7c478bd9Sstevel@tonic-gate  * stored in the file name and incremented.  If the subsequent fopen
348*7c478bd9Sstevel@tonic-gate  * fails, the file name is copied to tfile for the error message, the
349*7c478bd9Sstevel@tonic-gate  * previous file name is restored for cleanup, and fatal is called.  If
350*7c478bd9Sstevel@tonic-gate  * the fopen succeeds, the stream(opfil) is returned.
351*7c478bd9Sstevel@tonic-gate  */
352*7c478bd9Sstevel@tonic-gate 
353*7c478bd9Sstevel@tonic-gate FILE *
354*7c478bd9Sstevel@tonic-gate getfile()
355*7c478bd9Sstevel@tonic-gate {
356*7c478bd9Sstevel@tonic-gate 	static char *fptr;
357*7c478bd9Sstevel@tonic-gate 	static int ctr;
358*7c478bd9Sstevel@tonic-gate 	FILE *opfil;
359*7c478bd9Sstevel@tonic-gate 	char tfile[15];
360*7c478bd9Sstevel@tonic-gate 	char *delim;
361*7c478bd9Sstevel@tonic-gate 	char savedelim;
362*7c478bd9Sstevel@tonic-gate 
363*7c478bd9Sstevel@tonic-gate 	if (create) {
364*7c478bd9Sstevel@tonic-gate 		if (fptr == 0)
365*7c478bd9Sstevel@tonic-gate 			for (fptr = file; *fptr != NULL; fptr++);
366*7c478bd9Sstevel@tonic-gate 		(void) sprintf(fptr, "%.*d", fiwidth, ctr++);
367*7c478bd9Sstevel@tonic-gate 
368*7c478bd9Sstevel@tonic-gate 		/* check for suffix length overflow */
369*7c478bd9Sstevel@tonic-gate 		if (strlen(fptr) > fiwidth) {
370*7c478bd9Sstevel@tonic-gate 			fatal("Suffix longer than %ld chars; increase -n\n",
371*7c478bd9Sstevel@tonic-gate 			    (char *)fiwidth);
372*7c478bd9Sstevel@tonic-gate 		}
373*7c478bd9Sstevel@tonic-gate 
374*7c478bd9Sstevel@tonic-gate 		/* check for filename length overflow */
375*7c478bd9Sstevel@tonic-gate 
376*7c478bd9Sstevel@tonic-gate 		delim = strrchr(file, '/');
377*7c478bd9Sstevel@tonic-gate 		if (delim == (char *)NULL) {
378*7c478bd9Sstevel@tonic-gate 			if (strlen(file) > pathconf(".", _PC_NAME_MAX)) {
379*7c478bd9Sstevel@tonic-gate 				fatal("Name too long: %s\n", file);
380*7c478bd9Sstevel@tonic-gate 			}
381*7c478bd9Sstevel@tonic-gate 		} else {
382*7c478bd9Sstevel@tonic-gate 			/* truncate file at pathname delim to do pathconf */
383*7c478bd9Sstevel@tonic-gate 			savedelim = *delim;
384*7c478bd9Sstevel@tonic-gate 			*delim = '\0';
385*7c478bd9Sstevel@tonic-gate 			/*
386*7c478bd9Sstevel@tonic-gate 			 * file: pppppppp\0fffff\0
387*7c478bd9Sstevel@tonic-gate 			 * ..... ^ file
388*7c478bd9Sstevel@tonic-gate 			 * ............. ^ delim
389*7c478bd9Sstevel@tonic-gate 			 */
390*7c478bd9Sstevel@tonic-gate 			if (strlen(delim + 1) > pathconf(file, _PC_NAME_MAX)) {
391*7c478bd9Sstevel@tonic-gate 				fatal("Name too long: %s\n", delim + 1);
392*7c478bd9Sstevel@tonic-gate 			}
393*7c478bd9Sstevel@tonic-gate 			*delim = savedelim;
394*7c478bd9Sstevel@tonic-gate 		}
395*7c478bd9Sstevel@tonic-gate 
396*7c478bd9Sstevel@tonic-gate 		if ((opfil = fopen(file, "w")) == NULL) {
397*7c478bd9Sstevel@tonic-gate 			(void) strcpy(tfile, file);
398*7c478bd9Sstevel@tonic-gate 			(void) sprintf(fptr, "%.*d", fiwidth, (ctr-2));
399*7c478bd9Sstevel@tonic-gate 			fatal("Cannot create %s\n", tfile);
400*7c478bd9Sstevel@tonic-gate 		}
401*7c478bd9Sstevel@tonic-gate 		return (opfil);
402*7c478bd9Sstevel@tonic-gate 	}
403*7c478bd9Sstevel@tonic-gate 	return (NULL);
404*7c478bd9Sstevel@tonic-gate }
405*7c478bd9Sstevel@tonic-gate 
406*7c478bd9Sstevel@tonic-gate /*
407*7c478bd9Sstevel@tonic-gate  * Getline gets a line via fgets from the input stream "infile".
408*7c478bd9Sstevel@tonic-gate  * The line is put into linbuf and may not be larger than LINSIZ.
409*7c478bd9Sstevel@tonic-gate  * If getline is called with a non-zero value, the current line
410*7c478bd9Sstevel@tonic-gate  * is bumped, otherwise it is not(for R.E. searching).
411*7c478bd9Sstevel@tonic-gate  */
412*7c478bd9Sstevel@tonic-gate 
413*7c478bd9Sstevel@tonic-gate static char *
414*7c478bd9Sstevel@tonic-gate getline(int bumpcur)
415*7c478bd9Sstevel@tonic-gate {
416*7c478bd9Sstevel@tonic-gate 	char *ret;
417*7c478bd9Sstevel@tonic-gate 	if (bumpcur)
418*7c478bd9Sstevel@tonic-gate 		curline++;
419*7c478bd9Sstevel@tonic-gate 	ret = fgets(linbuf, LINSIZ, infile);
420*7c478bd9Sstevel@tonic-gate 	return (ret);
421*7c478bd9Sstevel@tonic-gate }
422*7c478bd9Sstevel@tonic-gate 
423*7c478bd9Sstevel@tonic-gate /*
424*7c478bd9Sstevel@tonic-gate  * Line_arg handles line number arguments.
425*7c478bd9Sstevel@tonic-gate  * line_arg takes as its argument a pointer to a character string
426*7c478bd9Sstevel@tonic-gate  * (assumed to be a line number).  If that character string can be
427*7c478bd9Sstevel@tonic-gate  * converted to a number(long long), to_line is called with that number,
428*7c478bd9Sstevel@tonic-gate  * otherwise error.
429*7c478bd9Sstevel@tonic-gate  */
430*7c478bd9Sstevel@tonic-gate 
431*7c478bd9Sstevel@tonic-gate static void
432*7c478bd9Sstevel@tonic-gate line_arg(char *line)
433*7c478bd9Sstevel@tonic-gate {
434*7c478bd9Sstevel@tonic-gate 	long long to;
435*7c478bd9Sstevel@tonic-gate 
436*7c478bd9Sstevel@tonic-gate 	if (asc_to_ll(line, &to) == ERR)
437*7c478bd9Sstevel@tonic-gate 		fatal("%s: bad line number\n", line);
438*7c478bd9Sstevel@tonic-gate 	to_line(to);
439*7c478bd9Sstevel@tonic-gate }
440*7c478bd9Sstevel@tonic-gate 
441*7c478bd9Sstevel@tonic-gate /*
442*7c478bd9Sstevel@tonic-gate  * Num_arg handles repeat arguments.
443*7c478bd9Sstevel@tonic-gate  * Num_arg copies the numeric argument to "rep" (error if number is
444*7c478bd9Sstevel@tonic-gate  * larger than 20 characters or } is left off).  Num_arg then converts
445*7c478bd9Sstevel@tonic-gate  * the number and checks for validity.  Next num_arg checks the mode
446*7c478bd9Sstevel@tonic-gate  * of the previous argument, and applys the argument the correct number
447*7c478bd9Sstevel@tonic-gate  * of times. If the mode is not set properly its an error.
448*7c478bd9Sstevel@tonic-gate  */
449*7c478bd9Sstevel@tonic-gate 
450*7c478bd9Sstevel@tonic-gate static void
451*7c478bd9Sstevel@tonic-gate num_arg(char *arg, int md)
452*7c478bd9Sstevel@tonic-gate {
453*7c478bd9Sstevel@tonic-gate 	offset_t repeat, toline;
454*7c478bd9Sstevel@tonic-gate 	char rep[21];
455*7c478bd9Sstevel@tonic-gate 	char *ptr;
456*7c478bd9Sstevel@tonic-gate 	int		len;
457*7c478bd9Sstevel@tonic-gate 
458*7c478bd9Sstevel@tonic-gate 	ptr = rep;
459*7c478bd9Sstevel@tonic-gate 	for (++arg; *arg != '}'; arg += len) {
460*7c478bd9Sstevel@tonic-gate 		if (*arg == NULL)
461*7c478bd9Sstevel@tonic-gate 			fatal("%s: missing '}'\n", targ);
462*7c478bd9Sstevel@tonic-gate 		if ((len = mblen(arg, MB_LEN_MAX)) <= 0)
463*7c478bd9Sstevel@tonic-gate 			len = 1;
464*7c478bd9Sstevel@tonic-gate 		if ((ptr + len) >= &rep[20])
465*7c478bd9Sstevel@tonic-gate 			fatal("%s: Repeat count too large\n", targ);
466*7c478bd9Sstevel@tonic-gate 		(void) memcpy(ptr, arg, len);
467*7c478bd9Sstevel@tonic-gate 		ptr += len;
468*7c478bd9Sstevel@tonic-gate 	}
469*7c478bd9Sstevel@tonic-gate 	*ptr = NULL;
470*7c478bd9Sstevel@tonic-gate 	if ((asc_to_ll(rep, &repeat) == ERR) || repeat < 0L)
471*7c478bd9Sstevel@tonic-gate 		fatal("Illegal repeat count: %s\n", targ);
472*7c478bd9Sstevel@tonic-gate 	if (md == LINMODE) {
473*7c478bd9Sstevel@tonic-gate 		toline = offset = curline;
474*7c478bd9Sstevel@tonic-gate 		for (; repeat > 0LL; repeat--) {
475*7c478bd9Sstevel@tonic-gate 			toline += offset;
476*7c478bd9Sstevel@tonic-gate 			to_line(toline);
477*7c478bd9Sstevel@tonic-gate 		}
478*7c478bd9Sstevel@tonic-gate 	} else	if (md == EXPMODE)
479*7c478bd9Sstevel@tonic-gate 			for (; repeat > 0LL; repeat--)
480*7c478bd9Sstevel@tonic-gate 				to_line(findline(expbuf, offset));
481*7c478bd9Sstevel@tonic-gate 		else
482*7c478bd9Sstevel@tonic-gate 			fatal("No operation for %s\n", targ);
483*7c478bd9Sstevel@tonic-gate }
484*7c478bd9Sstevel@tonic-gate 
485*7c478bd9Sstevel@tonic-gate /*
486*7c478bd9Sstevel@tonic-gate  * Re_arg handles regular expression arguments.
487*7c478bd9Sstevel@tonic-gate  * Re_arg takes a csplit regular expression argument.  It checks for
488*7c478bd9Sstevel@tonic-gate  * delimiter balance, computes any offset, and compiles the regular
489*7c478bd9Sstevel@tonic-gate  * expression.  Findline is called with the compiled expression and
490*7c478bd9Sstevel@tonic-gate  * offset, and returns the corresponding line number, which is used
491*7c478bd9Sstevel@tonic-gate  * as input to the to_line function.
492*7c478bd9Sstevel@tonic-gate  */
493*7c478bd9Sstevel@tonic-gate 
494*7c478bd9Sstevel@tonic-gate static void
495*7c478bd9Sstevel@tonic-gate re_arg(char *string)
496*7c478bd9Sstevel@tonic-gate {
497*7c478bd9Sstevel@tonic-gate 	char *ptr;
498*7c478bd9Sstevel@tonic-gate 	char ch;
499*7c478bd9Sstevel@tonic-gate 	int		len;
500*7c478bd9Sstevel@tonic-gate 
501*7c478bd9Sstevel@tonic-gate 	ch = *string;
502*7c478bd9Sstevel@tonic-gate 	ptr = string;
503*7c478bd9Sstevel@tonic-gate 	ptr++;
504*7c478bd9Sstevel@tonic-gate 	while (*ptr != ch) {
505*7c478bd9Sstevel@tonic-gate 		if (*ptr == '\\')
506*7c478bd9Sstevel@tonic-gate 			++ptr;
507*7c478bd9Sstevel@tonic-gate 
508*7c478bd9Sstevel@tonic-gate 		if (*ptr == NULL)
509*7c478bd9Sstevel@tonic-gate 			fatal("%s: missing delimiter\n", targ);
510*7c478bd9Sstevel@tonic-gate 
511*7c478bd9Sstevel@tonic-gate 		if ((len = mblen(ptr, MB_LEN_MAX)) <= 0)
512*7c478bd9Sstevel@tonic-gate 			len = 1;
513*7c478bd9Sstevel@tonic-gate 		ptr += len;
514*7c478bd9Sstevel@tonic-gate 	}
515*7c478bd9Sstevel@tonic-gate 
516*7c478bd9Sstevel@tonic-gate 	/*
517*7c478bd9Sstevel@tonic-gate 	 * The line below was added because compile no longer supports
518*7c478bd9Sstevel@tonic-gate 	 * the fourth argument being passed.  The fourth argument used
519*7c478bd9Sstevel@tonic-gate 	 * to be '/' or '%'.
520*7c478bd9Sstevel@tonic-gate 	 */
521*7c478bd9Sstevel@tonic-gate 
522*7c478bd9Sstevel@tonic-gate 	*ptr = NULL;
523*7c478bd9Sstevel@tonic-gate 	if (asc_to_ll(++ptr, &offset) == ERR)
524*7c478bd9Sstevel@tonic-gate 		fatal("%s: illegal offset\n", string);
525*7c478bd9Sstevel@tonic-gate 
526*7c478bd9Sstevel@tonic-gate 	/*
527*7c478bd9Sstevel@tonic-gate 	 * The line below was added because INIT which did this for us
528*7c478bd9Sstevel@tonic-gate 	 * was removed from compile in regexp.h
529*7c478bd9Sstevel@tonic-gate 	 */
530*7c478bd9Sstevel@tonic-gate 
531*7c478bd9Sstevel@tonic-gate 	string++;
532*7c478bd9Sstevel@tonic-gate 	expbuf = compile(string, (char *)0, (char *)0);
533*7c478bd9Sstevel@tonic-gate 	if (regerrno)
534*7c478bd9Sstevel@tonic-gate 		PERROR(regerrno);
535*7c478bd9Sstevel@tonic-gate 	to_line(findline(expbuf, offset));
536*7c478bd9Sstevel@tonic-gate }
537*7c478bd9Sstevel@tonic-gate 
538*7c478bd9Sstevel@tonic-gate /*
539*7c478bd9Sstevel@tonic-gate  * Sig handles breaks.  When a break occurs the signal is reset,
540*7c478bd9Sstevel@tonic-gate  * and fatal is called to clean up and print the argument which
541*7c478bd9Sstevel@tonic-gate  * was being processed at the time the interrupt occured.
542*7c478bd9Sstevel@tonic-gate  */
543*7c478bd9Sstevel@tonic-gate 
544*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
545*7c478bd9Sstevel@tonic-gate static void
546*7c478bd9Sstevel@tonic-gate sig(int s)
547*7c478bd9Sstevel@tonic-gate {
548*7c478bd9Sstevel@tonic-gate 	(void) signal(SIGINT, sig);
549*7c478bd9Sstevel@tonic-gate 	fatal("Interrupt - program aborted at arg '%s'\n", targ);
550*7c478bd9Sstevel@tonic-gate }
551*7c478bd9Sstevel@tonic-gate 
552*7c478bd9Sstevel@tonic-gate /*
553*7c478bd9Sstevel@tonic-gate  * To_line creates split files.
554*7c478bd9Sstevel@tonic-gate  * To_line gets as its argument the line which the current argument
555*7c478bd9Sstevel@tonic-gate  * referenced.  To_line calls getfile for a new output stream, which
556*7c478bd9Sstevel@tonic-gate  * does nothing if create is False.  If to_line's argument is not LAST
557*7c478bd9Sstevel@tonic-gate  * it checks that the current line is not greater than its argument.
558*7c478bd9Sstevel@tonic-gate  * While the current line is less than the desired line to_line gets
559*7c478bd9Sstevel@tonic-gate  * lines and flushes(error if EOF is reached).
560*7c478bd9Sstevel@tonic-gate  * If to_line's argument is LAST, it checks for more lines, and gets
561*7c478bd9Sstevel@tonic-gate  * and flushes lines till the end of file.
562*7c478bd9Sstevel@tonic-gate  * Finally, to_line calls closefile to close the output stream.
563*7c478bd9Sstevel@tonic-gate  */
564*7c478bd9Sstevel@tonic-gate 
565*7c478bd9Sstevel@tonic-gate static void
566*7c478bd9Sstevel@tonic-gate to_line(offset_t ln)
567*7c478bd9Sstevel@tonic-gate {
568*7c478bd9Sstevel@tonic-gate 	outfile = getfile();
569*7c478bd9Sstevel@tonic-gate 	if (ln != LAST) {
570*7c478bd9Sstevel@tonic-gate 		if (curline > ln)
571*7c478bd9Sstevel@tonic-gate 			fatal("%s - out of range\n", targ);
572*7c478bd9Sstevel@tonic-gate 		while (curline < ln) {
573*7c478bd9Sstevel@tonic-gate 			if (getline(TRUE) == NULL)
574*7c478bd9Sstevel@tonic-gate 				fatal("%s - out of range\n", targ);
575*7c478bd9Sstevel@tonic-gate 			flush();
576*7c478bd9Sstevel@tonic-gate 		}
577*7c478bd9Sstevel@tonic-gate 	} else		/* last file */
578*7c478bd9Sstevel@tonic-gate 		if (getline(TRUE) != NULL) {
579*7c478bd9Sstevel@tonic-gate 			flush();
580*7c478bd9Sstevel@tonic-gate 			for (;;) {
581*7c478bd9Sstevel@tonic-gate 				if (getline(TRUE) == NULL)
582*7c478bd9Sstevel@tonic-gate 					break;
583*7c478bd9Sstevel@tonic-gate 				flush();
584*7c478bd9Sstevel@tonic-gate 			}
585*7c478bd9Sstevel@tonic-gate 		} else
586*7c478bd9Sstevel@tonic-gate 			fatal("%s - out of range\n", targ);
587*7c478bd9Sstevel@tonic-gate 	closefile();
588*7c478bd9Sstevel@tonic-gate }
589*7c478bd9Sstevel@tonic-gate 
590*7c478bd9Sstevel@tonic-gate static void
591*7c478bd9Sstevel@tonic-gate usage()
592*7c478bd9Sstevel@tonic-gate {
593*7c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext(
594*7c478bd9Sstevel@tonic-gate 		"usage: csplit [-ks] [-f prefix] [-n number] "
595*7c478bd9Sstevel@tonic-gate 			"file arg1 ...argn\n"));
596*7c478bd9Sstevel@tonic-gate 	exit(1);
597*7c478bd9Sstevel@tonic-gate }
598