xref: /titanic_52/usr/src/cmd/csplit/csplit.c (revision 23a1ccea6aac035f084a7a4cdc968687d1b02daf)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*23a1cceaSRoger A. Faulkner  * Common Development and Distribution License (the "License").
6*23a1cceaSRoger A. Faulkner  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate 
227c478bd9Sstevel@tonic-gate /*
23*23a1cceaSRoger A. Faulkner  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
26*23a1cceaSRoger A. Faulkner /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27*23a1cceaSRoger A. Faulkner /*	  All Rights Reserved  	*/
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate /*
307c478bd9Sstevel@tonic-gate  * csplit - Context or line file splitter
317c478bd9Sstevel@tonic-gate  * Compile: cc -O -s -o csplit csplit.c
327c478bd9Sstevel@tonic-gate  */
337c478bd9Sstevel@tonic-gate 
347c478bd9Sstevel@tonic-gate #include <stdio.h>
357c478bd9Sstevel@tonic-gate #include <stdlib.h>
367c478bd9Sstevel@tonic-gate #include <unistd.h>
377c478bd9Sstevel@tonic-gate #include <string.h>
387c478bd9Sstevel@tonic-gate #include <ctype.h>
397c478bd9Sstevel@tonic-gate #include <errno.h>
407c478bd9Sstevel@tonic-gate #include <limits.h>
417c478bd9Sstevel@tonic-gate #include <regexpr.h>
427c478bd9Sstevel@tonic-gate #include <signal.h>
437c478bd9Sstevel@tonic-gate #include <locale.h>
447c478bd9Sstevel@tonic-gate #include <libintl.h>
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate #define	LAST	0LL
477c478bd9Sstevel@tonic-gate #define	ERR	-1
487c478bd9Sstevel@tonic-gate #define	FALSE	0
497c478bd9Sstevel@tonic-gate #define	TRUE	1
507c478bd9Sstevel@tonic-gate #define	EXPMODE	2
517c478bd9Sstevel@tonic-gate #define	LINMODE	3
527c478bd9Sstevel@tonic-gate #define	LINSIZ	LINE_MAX	/* POSIX.2 - read lines LINE_MAX long */
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate 	/* Globals */
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate char linbuf[LINSIZ];		/* Input line buffer */
577c478bd9Sstevel@tonic-gate char *expbuf;
587c478bd9Sstevel@tonic-gate char tmpbuf[BUFSIZ];		/* Temporary buffer for stdin */
597c478bd9Sstevel@tonic-gate char file[8192] = "xx";		/* File name buffer */
607c478bd9Sstevel@tonic-gate char *targ;			/* Arg ptr for error messages */
617c478bd9Sstevel@tonic-gate char *sptr;
627c478bd9Sstevel@tonic-gate FILE *infile, *outfile;		/* I/O file streams */
637c478bd9Sstevel@tonic-gate int silent, keep, create;	/* Flags: -s(ilent), -k(eep), (create) */
647c478bd9Sstevel@tonic-gate int errflg;
657c478bd9Sstevel@tonic-gate int fiwidth = 2;		/* file index width (output file names) */
667c478bd9Sstevel@tonic-gate extern int optind;
677c478bd9Sstevel@tonic-gate extern char *optarg;
687c478bd9Sstevel@tonic-gate offset_t offset;		/* Regular expression offset value */
697c478bd9Sstevel@tonic-gate offset_t curline;		/* Current line in input file */
707c478bd9Sstevel@tonic-gate 
717c478bd9Sstevel@tonic-gate /*
727c478bd9Sstevel@tonic-gate  * These defines are needed for regexp handling(see regexp(7))
737c478bd9Sstevel@tonic-gate  */
747c478bd9Sstevel@tonic-gate #define	PERROR(x)	fatal("%s: Illegal Regular Expression\n", targ);
757c478bd9Sstevel@tonic-gate 
767c478bd9Sstevel@tonic-gate static int asc_to_ll(char *, long long *);
777c478bd9Sstevel@tonic-gate static void closefile(void);
787c478bd9Sstevel@tonic-gate static void fatal(char *, char *);
797c478bd9Sstevel@tonic-gate static offset_t findline(char *, offset_t);
807c478bd9Sstevel@tonic-gate static void flush(void);
817c478bd9Sstevel@tonic-gate static FILE *getfile(void);
82*23a1cceaSRoger A. Faulkner static char *getaline(int);
837c478bd9Sstevel@tonic-gate static void line_arg(char *);
847c478bd9Sstevel@tonic-gate static void num_arg(char *, int);
857c478bd9Sstevel@tonic-gate static void re_arg(char *);
867c478bd9Sstevel@tonic-gate static void sig(int);
877c478bd9Sstevel@tonic-gate static void to_line(offset_t);
887c478bd9Sstevel@tonic-gate static void usage(void);
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate int
917c478bd9Sstevel@tonic-gate main(int argc, char **argv)
927c478bd9Sstevel@tonic-gate {
937c478bd9Sstevel@tonic-gate 	int ch, mode;
947c478bd9Sstevel@tonic-gate 	char *ptr;
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
977c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)		/* Should be defined by cc -D */
987c478bd9Sstevel@tonic-gate #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it weren't */
997c478bd9Sstevel@tonic-gate #endif
1007c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate 	while ((ch = getopt(argc, argv, "skf:n:")) != EOF) {
1037c478bd9Sstevel@tonic-gate 		switch (ch) {
1047c478bd9Sstevel@tonic-gate 			case 'f':
1057c478bd9Sstevel@tonic-gate 				(void) strcpy(file, optarg);
1067c478bd9Sstevel@tonic-gate 				if ((ptr = strrchr(optarg, '/')) == NULL)
1077c478bd9Sstevel@tonic-gate 					ptr = optarg;
1087c478bd9Sstevel@tonic-gate 				else
1097c478bd9Sstevel@tonic-gate 					ptr++;
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate 				break;
1127c478bd9Sstevel@tonic-gate 			case 'n':		/* POSIX.2 */
1137c478bd9Sstevel@tonic-gate 				for (ptr = optarg; *ptr != NULL; ptr++)
1147c478bd9Sstevel@tonic-gate 					if (!isdigit((int)*ptr))
1157c478bd9Sstevel@tonic-gate 						fatal("-n num\n", NULL);
1167c478bd9Sstevel@tonic-gate 				fiwidth = atoi(optarg);
1177c478bd9Sstevel@tonic-gate 				break;
1187c478bd9Sstevel@tonic-gate 			case 'k':
1197c478bd9Sstevel@tonic-gate 				keep++;
1207c478bd9Sstevel@tonic-gate 				break;
1217c478bd9Sstevel@tonic-gate 			case 's':
1227c478bd9Sstevel@tonic-gate 				silent++;
1237c478bd9Sstevel@tonic-gate 				break;
1247c478bd9Sstevel@tonic-gate 			case '?':
1257c478bd9Sstevel@tonic-gate 				errflg++;
1267c478bd9Sstevel@tonic-gate 		}
1277c478bd9Sstevel@tonic-gate 	}
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate 	argv = &argv[optind];
1307c478bd9Sstevel@tonic-gate 	argc -= optind;
1317c478bd9Sstevel@tonic-gate 	if (argc <= 1 || errflg)
1327c478bd9Sstevel@tonic-gate 		usage();
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate 	if (strcmp(*argv, "-") == 0) {
1357c478bd9Sstevel@tonic-gate 		infile = tmpfile();
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate 		while (fread(tmpbuf, 1, BUFSIZ, stdin) != 0) {
1387c478bd9Sstevel@tonic-gate 			if (fwrite(tmpbuf, 1, BUFSIZ, infile) == 0)
1397c478bd9Sstevel@tonic-gate 				if (errno == ENOSPC) {
1407c478bd9Sstevel@tonic-gate 					(void) fprintf(stderr, "csplit: ");
1417c478bd9Sstevel@tonic-gate 					(void) fprintf(stderr, gettext(
1427c478bd9Sstevel@tonic-gate 					    "No space left on device\n"));
1437c478bd9Sstevel@tonic-gate 					exit(1);
1447c478bd9Sstevel@tonic-gate 				} else {
1457c478bd9Sstevel@tonic-gate 					(void) fprintf(stderr, "csplit: ");
1467c478bd9Sstevel@tonic-gate 					(void) fprintf(stderr, gettext(
1477c478bd9Sstevel@tonic-gate 					    "Bad write to temporary "
1487c478bd9Sstevel@tonic-gate 					    "file\n"));
1497c478bd9Sstevel@tonic-gate 					exit(1);
1507c478bd9Sstevel@tonic-gate 				}
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate 	/* clear the buffer to get correct size when writing buffer */
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate 			(void) memset(tmpbuf, '\0', sizeof (tmpbuf));
1557c478bd9Sstevel@tonic-gate 		}
1567c478bd9Sstevel@tonic-gate 		rewind(infile);
1577c478bd9Sstevel@tonic-gate 	} else if ((infile = fopen(*argv, "r")) == NULL)
1587c478bd9Sstevel@tonic-gate 		fatal("Cannot open %s\n", *argv);
1597c478bd9Sstevel@tonic-gate 	++argv;
1607c478bd9Sstevel@tonic-gate 	curline = (offset_t)1;
1617c478bd9Sstevel@tonic-gate 	(void) signal(SIGINT, sig);
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate 	/*
1647c478bd9Sstevel@tonic-gate 	 * The following for loop handles the different argument types.
1657c478bd9Sstevel@tonic-gate 	 * A switch is performed on the first character of the argument
1667c478bd9Sstevel@tonic-gate 	 * and each case calls the appropriate argument handling routine.
1677c478bd9Sstevel@tonic-gate 	 */
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate 	for (; *argv; ++argv) {
1707c478bd9Sstevel@tonic-gate 		targ = *argv;
1717c478bd9Sstevel@tonic-gate 		switch (**argv) {
1727c478bd9Sstevel@tonic-gate 		case '/':
1737c478bd9Sstevel@tonic-gate 			mode = EXPMODE;
1747c478bd9Sstevel@tonic-gate 			create = TRUE;
1757c478bd9Sstevel@tonic-gate 			re_arg(*argv);
1767c478bd9Sstevel@tonic-gate 			break;
1777c478bd9Sstevel@tonic-gate 		case '%':
1787c478bd9Sstevel@tonic-gate 			mode = EXPMODE;
1797c478bd9Sstevel@tonic-gate 			create = FALSE;
1807c478bd9Sstevel@tonic-gate 			re_arg(*argv);
1817c478bd9Sstevel@tonic-gate 			break;
1827c478bd9Sstevel@tonic-gate 		case '{':
1837c478bd9Sstevel@tonic-gate 			num_arg(*argv, mode);
1847c478bd9Sstevel@tonic-gate 			mode = FALSE;
1857c478bd9Sstevel@tonic-gate 			break;
1867c478bd9Sstevel@tonic-gate 		default:
1877c478bd9Sstevel@tonic-gate 			mode = LINMODE;
1887c478bd9Sstevel@tonic-gate 			create = TRUE;
1897c478bd9Sstevel@tonic-gate 			line_arg(*argv);
1907c478bd9Sstevel@tonic-gate 			break;
1917c478bd9Sstevel@tonic-gate 		}
1927c478bd9Sstevel@tonic-gate 	}
1937c478bd9Sstevel@tonic-gate 	create = TRUE;
1947c478bd9Sstevel@tonic-gate 	to_line(LAST);
1957c478bd9Sstevel@tonic-gate 	return (0);
1967c478bd9Sstevel@tonic-gate }
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate /*
1997c478bd9Sstevel@tonic-gate  * asc_to_ll takes an ascii argument(str) and converts it to a long long(plc)
2007c478bd9Sstevel@tonic-gate  * It returns ERR if an illegal character.  The reason that asc_to_ll
2017c478bd9Sstevel@tonic-gate  * does not return an answer(long long) is that any value for the long
2027c478bd9Sstevel@tonic-gate  * long is legal, and this version of asc_to_ll detects error strings.
2037c478bd9Sstevel@tonic-gate  */
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate static int
2067c478bd9Sstevel@tonic-gate asc_to_ll(char *str, long long *plc)
2077c478bd9Sstevel@tonic-gate {
2087c478bd9Sstevel@tonic-gate 	int f;
2097c478bd9Sstevel@tonic-gate 	*plc = 0;
2107c478bd9Sstevel@tonic-gate 	f = 0;
2117c478bd9Sstevel@tonic-gate 	for (; ; str++) {
2127c478bd9Sstevel@tonic-gate 		switch (*str) {
2137c478bd9Sstevel@tonic-gate 		case ' ':
2147c478bd9Sstevel@tonic-gate 		case '\t':
2157c478bd9Sstevel@tonic-gate 			continue;
2167c478bd9Sstevel@tonic-gate 		case '-':
2177c478bd9Sstevel@tonic-gate 			f++;
2187c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
2197c478bd9Sstevel@tonic-gate 		case '+':
2207c478bd9Sstevel@tonic-gate 			str++;
2217c478bd9Sstevel@tonic-gate 		}
2227c478bd9Sstevel@tonic-gate 		break;
2237c478bd9Sstevel@tonic-gate 	}
2247c478bd9Sstevel@tonic-gate 	for (; *str != NULL; str++)
2257c478bd9Sstevel@tonic-gate 		if (*str >= '0' && *str <= '9')
2267c478bd9Sstevel@tonic-gate 			*plc = *plc * 10 + *str - '0';
2277c478bd9Sstevel@tonic-gate 		else
2287c478bd9Sstevel@tonic-gate 			return (ERR);
2297c478bd9Sstevel@tonic-gate 	if (f)
2307c478bd9Sstevel@tonic-gate 		*plc = -(*plc);
2317c478bd9Sstevel@tonic-gate 	return (TRUE);	/* not error */
2327c478bd9Sstevel@tonic-gate }
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate /*
2357c478bd9Sstevel@tonic-gate  * Closefile prints the byte count of the file created,(via fseeko
2367c478bd9Sstevel@tonic-gate  * and ftello), if the create flag is on and the silent flag is not on.
2377c478bd9Sstevel@tonic-gate  * If the create flag is on closefile then closes the file(fclose).
2387c478bd9Sstevel@tonic-gate  */
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate static void
2417c478bd9Sstevel@tonic-gate closefile()
2427c478bd9Sstevel@tonic-gate {
2437c478bd9Sstevel@tonic-gate 	if (!silent && create) {
2447c478bd9Sstevel@tonic-gate 		(void) fseeko(outfile, (offset_t)0, SEEK_END);
2457c478bd9Sstevel@tonic-gate 		(void) fprintf(stdout, "%lld\n", (offset_t)ftello(outfile));
2467c478bd9Sstevel@tonic-gate 	}
2477c478bd9Sstevel@tonic-gate 	if (create)
2487c478bd9Sstevel@tonic-gate 		(void) fclose(outfile);
2497c478bd9Sstevel@tonic-gate }
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate /*
2527c478bd9Sstevel@tonic-gate  * Fatal handles error messages and cleanup.
2537c478bd9Sstevel@tonic-gate  * Because "arg" can be the global file, and the cleanup processing
2547c478bd9Sstevel@tonic-gate  * uses the global file, the error message is printed first.  If the
2557c478bd9Sstevel@tonic-gate  * "keep" flag is not set, fatal unlinks all created files.  If the
2567c478bd9Sstevel@tonic-gate  * "keep" flag is set, fatal closes the current file(if there is one).
2577c478bd9Sstevel@tonic-gate  * Fatal exits with a value of 1.
2587c478bd9Sstevel@tonic-gate  */
2597c478bd9Sstevel@tonic-gate 
2607c478bd9Sstevel@tonic-gate static void
2617c478bd9Sstevel@tonic-gate fatal(char *string, char *arg)
2627c478bd9Sstevel@tonic-gate {
2637c478bd9Sstevel@tonic-gate 	char *fls;
2647c478bd9Sstevel@tonic-gate 	int num;
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, "csplit: ");
2677c478bd9Sstevel@tonic-gate 
2687c478bd9Sstevel@tonic-gate 	/* gettext dynamically replaces string */
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext(string), arg);
2717c478bd9Sstevel@tonic-gate 	if (!keep) {
2727c478bd9Sstevel@tonic-gate 		if (outfile) {
2737c478bd9Sstevel@tonic-gate 			(void) fclose(outfile);
2747c478bd9Sstevel@tonic-gate 			for (fls = file; *fls != '\0'; fls++)
2757c478bd9Sstevel@tonic-gate 				continue;
2767c478bd9Sstevel@tonic-gate 			fls -= fiwidth;
2777c478bd9Sstevel@tonic-gate 			for (num = atoi(fls); num >= 0; num--) {
2787c478bd9Sstevel@tonic-gate 				(void) sprintf(fls, "%.*d", fiwidth, num);
2797c478bd9Sstevel@tonic-gate 				(void) unlink(file);
2807c478bd9Sstevel@tonic-gate 			}
2817c478bd9Sstevel@tonic-gate 		}
2827c478bd9Sstevel@tonic-gate 	} else
2837c478bd9Sstevel@tonic-gate 		if (outfile)
2847c478bd9Sstevel@tonic-gate 			closefile();
2857c478bd9Sstevel@tonic-gate 	exit(1);
2867c478bd9Sstevel@tonic-gate }
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate /*
2897c478bd9Sstevel@tonic-gate  * Findline returns the line number referenced by the current argument.
2907c478bd9Sstevel@tonic-gate  * Its arguments are a pointer to the compiled regular expression(expr),
2917c478bd9Sstevel@tonic-gate  * and an offset(oset).  The variable lncnt is used to count the number
2927c478bd9Sstevel@tonic-gate  * of lines searched.  First the current stream location is saved via
293*23a1cceaSRoger A. Faulkner  * ftello(), and getaline is called so that R.E. searching starts at the
2947c478bd9Sstevel@tonic-gate  * line after the previously referenced line.  The while loop checks
2957c478bd9Sstevel@tonic-gate  * that there are more lines(error if none), bumps the line count, and
2967c478bd9Sstevel@tonic-gate  * checks for the R.E. on each line.  If the R.E. matches on one of the
2977c478bd9Sstevel@tonic-gate  * lines the old stream location is restored, and the line number
2987c478bd9Sstevel@tonic-gate  * referenced by the R.E. and the offset is returned.
2997c478bd9Sstevel@tonic-gate  */
3007c478bd9Sstevel@tonic-gate 
3017c478bd9Sstevel@tonic-gate static offset_t
3027c478bd9Sstevel@tonic-gate findline(char *expr, offset_t oset)
3037c478bd9Sstevel@tonic-gate {
3047c478bd9Sstevel@tonic-gate 	static int benhere = 0;
3057c478bd9Sstevel@tonic-gate 	offset_t lncnt = 0, saveloc;
3067c478bd9Sstevel@tonic-gate 
3077c478bd9Sstevel@tonic-gate 	saveloc = ftello(infile);
3087c478bd9Sstevel@tonic-gate 	if (curline != (offset_t)1 || benhere)	/* If first line, first time, */
309*23a1cceaSRoger A. Faulkner 		(void) getaline(FALSE);		/* then don't skip */
3107c478bd9Sstevel@tonic-gate 	else
3117c478bd9Sstevel@tonic-gate 		lncnt--;
3127c478bd9Sstevel@tonic-gate 	benhere = 1;
313*23a1cceaSRoger A. Faulkner 	while (getaline(FALSE) != NULL) {
3147c478bd9Sstevel@tonic-gate 		lncnt++;
3157c478bd9Sstevel@tonic-gate 		if ((sptr = strrchr(linbuf, '\n')) != NULL)
3167c478bd9Sstevel@tonic-gate 			*sptr = '\0';
3177c478bd9Sstevel@tonic-gate 		if (step(linbuf, expr)) {
3187c478bd9Sstevel@tonic-gate 			(void) fseeko(infile, (offset_t)saveloc, SEEK_SET);
3197c478bd9Sstevel@tonic-gate 			return (curline+lncnt+oset);
3207c478bd9Sstevel@tonic-gate 		}
3217c478bd9Sstevel@tonic-gate 	}
3227c478bd9Sstevel@tonic-gate 	(void) fseeko(infile, (offset_t)saveloc, SEEK_SET);
3237c478bd9Sstevel@tonic-gate 	return (curline+lncnt+oset+2);
3247c478bd9Sstevel@tonic-gate }
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate /*
3277c478bd9Sstevel@tonic-gate  * Flush uses fputs to put lines on the output file stream(outfile)
3287c478bd9Sstevel@tonic-gate  * Since fputs does its own buffering, flush doesn't need to.
3297c478bd9Sstevel@tonic-gate  * Flush does nothing if the create flag is not set.
3307c478bd9Sstevel@tonic-gate  */
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate static void
3337c478bd9Sstevel@tonic-gate flush()
3347c478bd9Sstevel@tonic-gate {
3357c478bd9Sstevel@tonic-gate 	if (create)
3367c478bd9Sstevel@tonic-gate 		(void) fputs(linbuf, outfile);
3377c478bd9Sstevel@tonic-gate }
3387c478bd9Sstevel@tonic-gate 
3397c478bd9Sstevel@tonic-gate /*
3407c478bd9Sstevel@tonic-gate  * Getfile does nothing if the create flag is not set.  If the create
3417c478bd9Sstevel@tonic-gate  * flag is set, getfile positions the file pointer(fptr) at the end of
3427c478bd9Sstevel@tonic-gate  * the file name prefix on the first call(fptr=0).  The file counter is
3437c478bd9Sstevel@tonic-gate  * stored in the file name and incremented.  If the subsequent fopen
3447c478bd9Sstevel@tonic-gate  * fails, the file name is copied to tfile for the error message, the
3457c478bd9Sstevel@tonic-gate  * previous file name is restored for cleanup, and fatal is called.  If
3467c478bd9Sstevel@tonic-gate  * the fopen succeeds, the stream(opfil) is returned.
3477c478bd9Sstevel@tonic-gate  */
3487c478bd9Sstevel@tonic-gate 
3497c478bd9Sstevel@tonic-gate FILE *
3507c478bd9Sstevel@tonic-gate getfile()
3517c478bd9Sstevel@tonic-gate {
3527c478bd9Sstevel@tonic-gate 	static char *fptr;
3537c478bd9Sstevel@tonic-gate 	static int ctr;
3547c478bd9Sstevel@tonic-gate 	FILE *opfil;
3557c478bd9Sstevel@tonic-gate 	char tfile[15];
3567c478bd9Sstevel@tonic-gate 	char *delim;
3577c478bd9Sstevel@tonic-gate 	char savedelim;
3587c478bd9Sstevel@tonic-gate 
3597c478bd9Sstevel@tonic-gate 	if (create) {
3607c478bd9Sstevel@tonic-gate 		if (fptr == 0)
361*23a1cceaSRoger A. Faulkner 			for (fptr = file; *fptr != NULL; fptr++)
362*23a1cceaSRoger A. Faulkner 				continue;
3637c478bd9Sstevel@tonic-gate 		(void) sprintf(fptr, "%.*d", fiwidth, ctr++);
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate 		/* check for suffix length overflow */
3667c478bd9Sstevel@tonic-gate 		if (strlen(fptr) > fiwidth) {
3677c478bd9Sstevel@tonic-gate 			fatal("Suffix longer than %ld chars; increase -n\n",
3687c478bd9Sstevel@tonic-gate 			    (char *)fiwidth);
3697c478bd9Sstevel@tonic-gate 		}
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate 		/* check for filename length overflow */
3727c478bd9Sstevel@tonic-gate 
3737c478bd9Sstevel@tonic-gate 		delim = strrchr(file, '/');
3747c478bd9Sstevel@tonic-gate 		if (delim == (char *)NULL) {
3757c478bd9Sstevel@tonic-gate 			if (strlen(file) > pathconf(".", _PC_NAME_MAX)) {
3767c478bd9Sstevel@tonic-gate 				fatal("Name too long: %s\n", file);
3777c478bd9Sstevel@tonic-gate 			}
3787c478bd9Sstevel@tonic-gate 		} else {
3797c478bd9Sstevel@tonic-gate 			/* truncate file at pathname delim to do pathconf */
3807c478bd9Sstevel@tonic-gate 			savedelim = *delim;
3817c478bd9Sstevel@tonic-gate 			*delim = '\0';
3827c478bd9Sstevel@tonic-gate 			/*
3837c478bd9Sstevel@tonic-gate 			 * file: pppppppp\0fffff\0
3847c478bd9Sstevel@tonic-gate 			 * ..... ^ file
3857c478bd9Sstevel@tonic-gate 			 * ............. ^ delim
3867c478bd9Sstevel@tonic-gate 			 */
3877c478bd9Sstevel@tonic-gate 			if (strlen(delim + 1) > pathconf(file, _PC_NAME_MAX)) {
3887c478bd9Sstevel@tonic-gate 				fatal("Name too long: %s\n", delim + 1);
3897c478bd9Sstevel@tonic-gate 			}
3907c478bd9Sstevel@tonic-gate 			*delim = savedelim;
3917c478bd9Sstevel@tonic-gate 		}
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 		if ((opfil = fopen(file, "w")) == NULL) {
3947c478bd9Sstevel@tonic-gate 			(void) strcpy(tfile, file);
3957c478bd9Sstevel@tonic-gate 			(void) sprintf(fptr, "%.*d", fiwidth, (ctr-2));
3967c478bd9Sstevel@tonic-gate 			fatal("Cannot create %s\n", tfile);
3977c478bd9Sstevel@tonic-gate 		}
3987c478bd9Sstevel@tonic-gate 		return (opfil);
3997c478bd9Sstevel@tonic-gate 	}
4007c478bd9Sstevel@tonic-gate 	return (NULL);
4017c478bd9Sstevel@tonic-gate }
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate /*
4047c478bd9Sstevel@tonic-gate  * Getline gets a line via fgets from the input stream "infile".
4057c478bd9Sstevel@tonic-gate  * The line is put into linbuf and may not be larger than LINSIZ.
406*23a1cceaSRoger A. Faulkner  * If getaline is called with a non-zero value, the current line
4077c478bd9Sstevel@tonic-gate  * is bumped, otherwise it is not(for R.E. searching).
4087c478bd9Sstevel@tonic-gate  */
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate static char *
411*23a1cceaSRoger A. Faulkner getaline(int bumpcur)
4127c478bd9Sstevel@tonic-gate {
4137c478bd9Sstevel@tonic-gate 	char *ret;
4147c478bd9Sstevel@tonic-gate 	if (bumpcur)
4157c478bd9Sstevel@tonic-gate 		curline++;
4167c478bd9Sstevel@tonic-gate 	ret = fgets(linbuf, LINSIZ, infile);
4177c478bd9Sstevel@tonic-gate 	return (ret);
4187c478bd9Sstevel@tonic-gate }
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate /*
4217c478bd9Sstevel@tonic-gate  * Line_arg handles line number arguments.
4227c478bd9Sstevel@tonic-gate  * line_arg takes as its argument a pointer to a character string
4237c478bd9Sstevel@tonic-gate  * (assumed to be a line number).  If that character string can be
4247c478bd9Sstevel@tonic-gate  * converted to a number(long long), to_line is called with that number,
4257c478bd9Sstevel@tonic-gate  * otherwise error.
4267c478bd9Sstevel@tonic-gate  */
4277c478bd9Sstevel@tonic-gate 
4287c478bd9Sstevel@tonic-gate static void
4297c478bd9Sstevel@tonic-gate line_arg(char *line)
4307c478bd9Sstevel@tonic-gate {
4317c478bd9Sstevel@tonic-gate 	long long to;
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate 	if (asc_to_ll(line, &to) == ERR)
4347c478bd9Sstevel@tonic-gate 		fatal("%s: bad line number\n", line);
4357c478bd9Sstevel@tonic-gate 	to_line(to);
4367c478bd9Sstevel@tonic-gate }
4377c478bd9Sstevel@tonic-gate 
4387c478bd9Sstevel@tonic-gate /*
4397c478bd9Sstevel@tonic-gate  * Num_arg handles repeat arguments.
4407c478bd9Sstevel@tonic-gate  * Num_arg copies the numeric argument to "rep" (error if number is
4417c478bd9Sstevel@tonic-gate  * larger than 20 characters or } is left off).  Num_arg then converts
4427c478bd9Sstevel@tonic-gate  * the number and checks for validity.  Next num_arg checks the mode
4437c478bd9Sstevel@tonic-gate  * of the previous argument, and applys the argument the correct number
4447c478bd9Sstevel@tonic-gate  * of times. If the mode is not set properly its an error.
4457c478bd9Sstevel@tonic-gate  */
4467c478bd9Sstevel@tonic-gate 
4477c478bd9Sstevel@tonic-gate static void
4487c478bd9Sstevel@tonic-gate num_arg(char *arg, int md)
4497c478bd9Sstevel@tonic-gate {
4507c478bd9Sstevel@tonic-gate 	offset_t repeat, toline;
4517c478bd9Sstevel@tonic-gate 	char rep[21];
4527c478bd9Sstevel@tonic-gate 	char *ptr;
4537c478bd9Sstevel@tonic-gate 	int		len;
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate 	ptr = rep;
4567c478bd9Sstevel@tonic-gate 	for (++arg; *arg != '}'; arg += len) {
4577c478bd9Sstevel@tonic-gate 		if (*arg == NULL)
4587c478bd9Sstevel@tonic-gate 			fatal("%s: missing '}'\n", targ);
4597c478bd9Sstevel@tonic-gate 		if ((len = mblen(arg, MB_LEN_MAX)) <= 0)
4607c478bd9Sstevel@tonic-gate 			len = 1;
4617c478bd9Sstevel@tonic-gate 		if ((ptr + len) >= &rep[20])
4627c478bd9Sstevel@tonic-gate 			fatal("%s: Repeat count too large\n", targ);
4637c478bd9Sstevel@tonic-gate 		(void) memcpy(ptr, arg, len);
4647c478bd9Sstevel@tonic-gate 		ptr += len;
4657c478bd9Sstevel@tonic-gate 	}
4667c478bd9Sstevel@tonic-gate 	*ptr = NULL;
4677c478bd9Sstevel@tonic-gate 	if ((asc_to_ll(rep, &repeat) == ERR) || repeat < 0L)
4687c478bd9Sstevel@tonic-gate 		fatal("Illegal repeat count: %s\n", targ);
4697c478bd9Sstevel@tonic-gate 	if (md == LINMODE) {
4707c478bd9Sstevel@tonic-gate 		toline = offset = curline;
4717c478bd9Sstevel@tonic-gate 		for (; repeat > 0LL; repeat--) {
4727c478bd9Sstevel@tonic-gate 			toline += offset;
4737c478bd9Sstevel@tonic-gate 			to_line(toline);
4747c478bd9Sstevel@tonic-gate 		}
4757c478bd9Sstevel@tonic-gate 	} else	if (md == EXPMODE)
4767c478bd9Sstevel@tonic-gate 			for (; repeat > 0LL; repeat--)
4777c478bd9Sstevel@tonic-gate 				to_line(findline(expbuf, offset));
4787c478bd9Sstevel@tonic-gate 		else
4797c478bd9Sstevel@tonic-gate 			fatal("No operation for %s\n", targ);
4807c478bd9Sstevel@tonic-gate }
4817c478bd9Sstevel@tonic-gate 
4827c478bd9Sstevel@tonic-gate /*
4837c478bd9Sstevel@tonic-gate  * Re_arg handles regular expression arguments.
4847c478bd9Sstevel@tonic-gate  * Re_arg takes a csplit regular expression argument.  It checks for
4857c478bd9Sstevel@tonic-gate  * delimiter balance, computes any offset, and compiles the regular
4867c478bd9Sstevel@tonic-gate  * expression.  Findline is called with the compiled expression and
4877c478bd9Sstevel@tonic-gate  * offset, and returns the corresponding line number, which is used
4887c478bd9Sstevel@tonic-gate  * as input to the to_line function.
4897c478bd9Sstevel@tonic-gate  */
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate static void
4927c478bd9Sstevel@tonic-gate re_arg(char *string)
4937c478bd9Sstevel@tonic-gate {
4947c478bd9Sstevel@tonic-gate 	char *ptr;
4957c478bd9Sstevel@tonic-gate 	char ch;
4967c478bd9Sstevel@tonic-gate 	int		len;
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate 	ch = *string;
4997c478bd9Sstevel@tonic-gate 	ptr = string;
5007c478bd9Sstevel@tonic-gate 	ptr++;
5017c478bd9Sstevel@tonic-gate 	while (*ptr != ch) {
5027c478bd9Sstevel@tonic-gate 		if (*ptr == '\\')
5037c478bd9Sstevel@tonic-gate 			++ptr;
5047c478bd9Sstevel@tonic-gate 
5057c478bd9Sstevel@tonic-gate 		if (*ptr == NULL)
5067c478bd9Sstevel@tonic-gate 			fatal("%s: missing delimiter\n", targ);
5077c478bd9Sstevel@tonic-gate 
5087c478bd9Sstevel@tonic-gate 		if ((len = mblen(ptr, MB_LEN_MAX)) <= 0)
5097c478bd9Sstevel@tonic-gate 			len = 1;
5107c478bd9Sstevel@tonic-gate 		ptr += len;
5117c478bd9Sstevel@tonic-gate 	}
5127c478bd9Sstevel@tonic-gate 
5137c478bd9Sstevel@tonic-gate 	/*
5147c478bd9Sstevel@tonic-gate 	 * The line below was added because compile no longer supports
5157c478bd9Sstevel@tonic-gate 	 * the fourth argument being passed.  The fourth argument used
5167c478bd9Sstevel@tonic-gate 	 * to be '/' or '%'.
5177c478bd9Sstevel@tonic-gate 	 */
5187c478bd9Sstevel@tonic-gate 
5197c478bd9Sstevel@tonic-gate 	*ptr = NULL;
5207c478bd9Sstevel@tonic-gate 	if (asc_to_ll(++ptr, &offset) == ERR)
5217c478bd9Sstevel@tonic-gate 		fatal("%s: illegal offset\n", string);
5227c478bd9Sstevel@tonic-gate 
5237c478bd9Sstevel@tonic-gate 	/*
5247c478bd9Sstevel@tonic-gate 	 * The line below was added because INIT which did this for us
5257c478bd9Sstevel@tonic-gate 	 * was removed from compile in regexp.h
5267c478bd9Sstevel@tonic-gate 	 */
5277c478bd9Sstevel@tonic-gate 
5287c478bd9Sstevel@tonic-gate 	string++;
5297c478bd9Sstevel@tonic-gate 	expbuf = compile(string, (char *)0, (char *)0);
5307c478bd9Sstevel@tonic-gate 	if (regerrno)
5317c478bd9Sstevel@tonic-gate 		PERROR(regerrno);
5327c478bd9Sstevel@tonic-gate 	to_line(findline(expbuf, offset));
5337c478bd9Sstevel@tonic-gate }
5347c478bd9Sstevel@tonic-gate 
5357c478bd9Sstevel@tonic-gate /*
5367c478bd9Sstevel@tonic-gate  * Sig handles breaks.  When a break occurs the signal is reset,
5377c478bd9Sstevel@tonic-gate  * and fatal is called to clean up and print the argument which
5387c478bd9Sstevel@tonic-gate  * was being processed at the time the interrupt occured.
5397c478bd9Sstevel@tonic-gate  */
5407c478bd9Sstevel@tonic-gate 
5417c478bd9Sstevel@tonic-gate /* ARGSUSED */
5427c478bd9Sstevel@tonic-gate static void
5437c478bd9Sstevel@tonic-gate sig(int s)
5447c478bd9Sstevel@tonic-gate {
5457c478bd9Sstevel@tonic-gate 	(void) signal(SIGINT, sig);
5467c478bd9Sstevel@tonic-gate 	fatal("Interrupt - program aborted at arg '%s'\n", targ);
5477c478bd9Sstevel@tonic-gate }
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate /*
5507c478bd9Sstevel@tonic-gate  * To_line creates split files.
5517c478bd9Sstevel@tonic-gate  * To_line gets as its argument the line which the current argument
5527c478bd9Sstevel@tonic-gate  * referenced.  To_line calls getfile for a new output stream, which
5537c478bd9Sstevel@tonic-gate  * does nothing if create is False.  If to_line's argument is not LAST
5547c478bd9Sstevel@tonic-gate  * it checks that the current line is not greater than its argument.
5557c478bd9Sstevel@tonic-gate  * While the current line is less than the desired line to_line gets
5567c478bd9Sstevel@tonic-gate  * lines and flushes(error if EOF is reached).
5577c478bd9Sstevel@tonic-gate  * If to_line's argument is LAST, it checks for more lines, and gets
5587c478bd9Sstevel@tonic-gate  * and flushes lines till the end of file.
5597c478bd9Sstevel@tonic-gate  * Finally, to_line calls closefile to close the output stream.
5607c478bd9Sstevel@tonic-gate  */
5617c478bd9Sstevel@tonic-gate 
5627c478bd9Sstevel@tonic-gate static void
5637c478bd9Sstevel@tonic-gate to_line(offset_t ln)
5647c478bd9Sstevel@tonic-gate {
5657c478bd9Sstevel@tonic-gate 	outfile = getfile();
5667c478bd9Sstevel@tonic-gate 	if (ln != LAST) {
5677c478bd9Sstevel@tonic-gate 		if (curline > ln)
5687c478bd9Sstevel@tonic-gate 			fatal("%s - out of range\n", targ);
5697c478bd9Sstevel@tonic-gate 		while (curline < ln) {
570*23a1cceaSRoger A. Faulkner 			if (getaline(TRUE) == NULL)
5717c478bd9Sstevel@tonic-gate 				fatal("%s - out of range\n", targ);
5727c478bd9Sstevel@tonic-gate 			flush();
5737c478bd9Sstevel@tonic-gate 		}
5747c478bd9Sstevel@tonic-gate 	} else		/* last file */
575*23a1cceaSRoger A. Faulkner 		if (getaline(TRUE) != NULL) {
5767c478bd9Sstevel@tonic-gate 			flush();
5777c478bd9Sstevel@tonic-gate 			for (;;) {
578*23a1cceaSRoger A. Faulkner 				if (getaline(TRUE) == NULL)
5797c478bd9Sstevel@tonic-gate 					break;
5807c478bd9Sstevel@tonic-gate 				flush();
5817c478bd9Sstevel@tonic-gate 			}
5827c478bd9Sstevel@tonic-gate 		} else
5837c478bd9Sstevel@tonic-gate 			fatal("%s - out of range\n", targ);
5847c478bd9Sstevel@tonic-gate 	closefile();
5857c478bd9Sstevel@tonic-gate }
5867c478bd9Sstevel@tonic-gate 
5877c478bd9Sstevel@tonic-gate static void
5887c478bd9Sstevel@tonic-gate usage()
5897c478bd9Sstevel@tonic-gate {
5907c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext(
5917c478bd9Sstevel@tonic-gate 	    "usage: csplit [-ks] [-f prefix] [-n number] "
5927c478bd9Sstevel@tonic-gate 	    "file arg1 ...argn\n"));
5937c478bd9Sstevel@tonic-gate 	exit(1);
5947c478bd9Sstevel@tonic-gate }
595