xref: /netbsd-src/usr.bin/csplit/csplit.c (revision ea1a131a0082dbcfa7d9afe29019e68d3b133412)
1*ea1a131aScheusov /*	$NetBSD: csplit.c,v 1.7 2017/07/30 23:02:53 cheusov Exp $	*/
21a6e50f2Schristos /*	$FreeBSD: src/usr.bin/csplit/csplit.c,v 1.9 2004/03/22 11:15:03 tjr Exp$	*/
31a6e50f2Schristos 
41a6e50f2Schristos /*-
51a6e50f2Schristos  * Copyright (c) 2002 Tim J. Robbins.
61a6e50f2Schristos  * All rights reserved.
71a6e50f2Schristos  *
81a6e50f2Schristos  * Redistribution and use in source and binary forms, with or without
91a6e50f2Schristos  * modification, are permitted provided that the following conditions
101a6e50f2Schristos  * are met:
111a6e50f2Schristos  * 1. Redistributions of source code must retain the above copyright
121a6e50f2Schristos  *    notice, this list of conditions and the following disclaimer.
131a6e50f2Schristos  * 2. Redistributions in binary form must reproduce the above copyright
141a6e50f2Schristos  *    notice, this list of conditions and the following disclaimer in the
151a6e50f2Schristos  *    documentation and/or other materials provided with the distribution.
161a6e50f2Schristos  *
171a6e50f2Schristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
181a6e50f2Schristos  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191a6e50f2Schristos  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201a6e50f2Schristos  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
211a6e50f2Schristos  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221a6e50f2Schristos  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231a6e50f2Schristos  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241a6e50f2Schristos  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251a6e50f2Schristos  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261a6e50f2Schristos  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271a6e50f2Schristos  * SUCH DAMAGE.
281a6e50f2Schristos  */
291a6e50f2Schristos 
301a6e50f2Schristos /*
311a6e50f2Schristos  * csplit -- split files based on context
321a6e50f2Schristos  *
331a6e50f2Schristos  * This utility splits its input into numbered output files by line number
341a6e50f2Schristos  * or by a regular expression. Regular expression matches have an optional
351a6e50f2Schristos  * offset with them, allowing the split to occur a specified number of
361a6e50f2Schristos  * lines before or after the match.
371a6e50f2Schristos  *
381a6e50f2Schristos  * To handle negative offsets, we stop reading when the match occurs and
391a6e50f2Schristos  * store the offset that the file should have been split at, then use
401a6e50f2Schristos  * this output file as input until all the "overflowed" lines have been read.
411a6e50f2Schristos  * The file is then closed and truncated to the correct length.
421a6e50f2Schristos  *
431a6e50f2Schristos  * We assume that the output files can be seeked upon (ie. they cannot be
441a6e50f2Schristos  * symlinks to named pipes or character devices), but make no such
451a6e50f2Schristos  * assumption about the input.
461a6e50f2Schristos  */
471a6e50f2Schristos 
481a6e50f2Schristos #include <sys/cdefs.h>
491a6e50f2Schristos #ifndef lint
50*ea1a131aScheusov __RCSID("$NetBSD: csplit.c,v 1.7 2017/07/30 23:02:53 cheusov Exp $");
511a6e50f2Schristos #endif
521a6e50f2Schristos 
531a6e50f2Schristos #include <sys/types.h>
541a6e50f2Schristos 
551a6e50f2Schristos #include <ctype.h>
561a6e50f2Schristos #include <err.h>
571a6e50f2Schristos #include <errno.h>
581a6e50f2Schristos #include <limits.h>
591a6e50f2Schristos #include <locale.h>
601a6e50f2Schristos #include <regex.h>
611a6e50f2Schristos #include <signal.h>
621a6e50f2Schristos #include <stdint.h>
631a6e50f2Schristos #include <stdio.h>
641a6e50f2Schristos #include <stdlib.h>
651a6e50f2Schristos #include <string.h>
661a6e50f2Schristos #include <unistd.h>
675d427989Slukem #include <util.h>
681a6e50f2Schristos 
691a6e50f2Schristos static void	 cleanup(void);
701a6e50f2Schristos static void	 do_lineno(const char *);
711a6e50f2Schristos static void	 do_rexp(const char *);
727027866aSroy static char	*get_line(void);
731a6e50f2Schristos static void	 handlesig(int);
741a6e50f2Schristos static FILE	*newfile(void);
751a6e50f2Schristos static void	 toomuch(FILE *, long);
768b0f9554Sperry static void	 usage(void) __dead;
771a6e50f2Schristos 
781a6e50f2Schristos /*
791a6e50f2Schristos  * Command line options
801a6e50f2Schristos  */
8131870c16Sjoerg static const char *prefix;		/* File name prefix */
8231870c16Sjoerg static long	 sufflen;		/* Number of decimal digits for suffix */
8331870c16Sjoerg static int	 sflag;			/* Suppress output of file names */
8431870c16Sjoerg static int	 kflag;			/* Keep output if error occurs */
851a6e50f2Schristos 
861a6e50f2Schristos /*
871a6e50f2Schristos  * Other miscellaneous globals (XXX too many)
881a6e50f2Schristos  */
8931870c16Sjoerg static long	 lineno;		/* Current line number in input file */
9031870c16Sjoerg static long	 reps;			/* Number of repetitions for this pattern */
9131870c16Sjoerg static long	 nfiles;		/* Number of files output so far */
9231870c16Sjoerg static long	 maxfiles;		/* Maximum number of files we can create */
9331870c16Sjoerg static char	 currfile[PATH_MAX];	/* Current output file */
9431870c16Sjoerg static const char *infn;		/* Name of the input file */
9531870c16Sjoerg static FILE	*infile;		/* Input file handle */
9631870c16Sjoerg static FILE	*overfile;		/* Overflow file for toomuch() */
9731870c16Sjoerg static off_t	 truncofs;		/* Offset this file should be truncated at */
9831870c16Sjoerg static int	 doclean;		/* Should cleanup() remove output? */
991a6e50f2Schristos 
1001a6e50f2Schristos int
main(int argc,char * argv[])1011a6e50f2Schristos main(int argc, char *argv[])
1021a6e50f2Schristos {
1031a6e50f2Schristos 	struct sigaction sa;
1041a6e50f2Schristos 	long i;
1051a6e50f2Schristos 	int ch;
1061a6e50f2Schristos 	const char *expr;
1071a6e50f2Schristos 	char *ep, *p;
1081a6e50f2Schristos 	FILE *ofp;
1091a6e50f2Schristos 
1101a6e50f2Schristos 	(void)setlocale(LC_ALL, "");
1111a6e50f2Schristos 
1121a6e50f2Schristos 	kflag = sflag = 0;
1131a6e50f2Schristos 	prefix = "xx";
1141a6e50f2Schristos 	sufflen = 2;
1151a6e50f2Schristos 	while ((ch = getopt(argc, argv, "ksf:n:")) > 0) {
1161a6e50f2Schristos 		switch (ch) {
1171a6e50f2Schristos 		case 'f':
1181a6e50f2Schristos 			prefix = optarg;
1191a6e50f2Schristos 			break;
1201a6e50f2Schristos 		case 'k':
1211a6e50f2Schristos 			kflag = 1;
1221a6e50f2Schristos 			break;
1231a6e50f2Schristos 		case 'n':
1241a6e50f2Schristos 			errno = 0;
1251a6e50f2Schristos 			sufflen = strtol(optarg, &ep, 10);
1261a6e50f2Schristos 			if (sufflen <= 0 || *ep != '\0' || errno != 0)
1271a6e50f2Schristos 				errx(1, "%s: bad suffix length", optarg);
1281a6e50f2Schristos 			break;
1291a6e50f2Schristos 		case 's':
1301a6e50f2Schristos 			sflag = 1;
1311a6e50f2Schristos 			break;
1321a6e50f2Schristos 		default:
1331a6e50f2Schristos 			usage();
1341a6e50f2Schristos 			/*NOTREACHED*/
1351a6e50f2Schristos 		}
1361a6e50f2Schristos 	}
1371a6e50f2Schristos 
1381a6e50f2Schristos 	if (sufflen + strlen(prefix) >= PATH_MAX)
1391a6e50f2Schristos 		errx(1, "name too long");
1401a6e50f2Schristos 
1411a6e50f2Schristos 	argc -= optind;
1421a6e50f2Schristos 	argv += optind;
1431a6e50f2Schristos 
1441a6e50f2Schristos 	if ((infn = *argv++) == NULL)
1451a6e50f2Schristos 		usage();
1461a6e50f2Schristos 	if (strcmp(infn, "-") == 0) {
1471a6e50f2Schristos 		infile = stdin;
1481a6e50f2Schristos 		infn = "stdin";
1491a6e50f2Schristos 	} else if ((infile = fopen(infn, "r")) == NULL)
1501a6e50f2Schristos 		err(1, "%s", infn);
1511a6e50f2Schristos 
1521a6e50f2Schristos 	if (!kflag) {
1531a6e50f2Schristos 		doclean = 1;
1541a6e50f2Schristos 		(void)atexit(cleanup);
1551a6e50f2Schristos 		sa.sa_flags = 0;
1561a6e50f2Schristos 		sa.sa_handler = handlesig;
1571a6e50f2Schristos 		(void)sigemptyset(&sa.sa_mask);
1581a6e50f2Schristos 		(void)sigaddset(&sa.sa_mask, SIGHUP);
1591a6e50f2Schristos 		(void)sigaddset(&sa.sa_mask, SIGINT);
1601a6e50f2Schristos 		(void)sigaddset(&sa.sa_mask, SIGTERM);
1611a6e50f2Schristos 		(void)sigaction(SIGHUP, &sa, NULL);
1621a6e50f2Schristos 		(void)sigaction(SIGINT, &sa, NULL);
1631a6e50f2Schristos 		(void)sigaction(SIGTERM, &sa, NULL);
1641a6e50f2Schristos 	}
1651a6e50f2Schristos 
1661a6e50f2Schristos 	lineno = 0;
1671a6e50f2Schristos 	nfiles = 0;
1681a6e50f2Schristos 	truncofs = 0;
1691a6e50f2Schristos 	overfile = NULL;
1701a6e50f2Schristos 
1711a6e50f2Schristos 	/* Ensure 10^sufflen < LONG_MAX. */
1721a6e50f2Schristos 	for (maxfiles = 1, i = 0; i < sufflen; i++) {
1731a6e50f2Schristos 		if (maxfiles > LONG_MAX / 10)
1741a6e50f2Schristos 			errx(1, "%ld: suffix too long (limit %ld)",
1751a6e50f2Schristos 			    sufflen, i);
1761a6e50f2Schristos 		maxfiles *= 10;
1771a6e50f2Schristos 	}
1781a6e50f2Schristos 
1791a6e50f2Schristos 	/* Create files based on supplied patterns. */
1801a6e50f2Schristos 	while (nfiles < maxfiles - 1 && (expr = *argv++) != NULL) {
1811a6e50f2Schristos 		/* Look ahead & see if this pattern has any repetitions. */
1821a6e50f2Schristos 		if (*argv != NULL && **argv == '{') {
1831a6e50f2Schristos 			errno = 0;
1841a6e50f2Schristos 			reps = strtol(*argv + 1, &ep, 10);
1851a6e50f2Schristos 			if (reps < 0 || *ep != '}' || errno != 0)
1861a6e50f2Schristos 				errx(1, "%s: bad repetition count", *argv + 1);
1871a6e50f2Schristos 			argv++;
1881a6e50f2Schristos 		} else
1891a6e50f2Schristos 			reps = 0;
1901a6e50f2Schristos 
1911a6e50f2Schristos 		if (*expr == '/' || *expr == '%') {
1921a6e50f2Schristos 			do
1931a6e50f2Schristos 				do_rexp(expr);
1941a6e50f2Schristos 			while (reps-- != 0 && nfiles < maxfiles - 1);
1951a6e50f2Schristos 		} else if (isdigit((unsigned char)*expr))
1961a6e50f2Schristos 			do_lineno(expr);
1971a6e50f2Schristos 		else
1981a6e50f2Schristos 			errx(1, "%s: unrecognised pattern", expr);
1991a6e50f2Schristos 	}
2001a6e50f2Schristos 
2011a6e50f2Schristos 	/* Copy the rest into a new file. */
2021a6e50f2Schristos 	if (!feof(infile)) {
2031a6e50f2Schristos 		ofp = newfile();
204*ea1a131aScheusov 		while ((p = get_line()) != NULL && fputs(p, ofp) != EOF)
2051a6e50f2Schristos 			;
2061a6e50f2Schristos 		if (!sflag)
2071a6e50f2Schristos 			(void)printf("%jd\n", (intmax_t)ftello(ofp));
2081a6e50f2Schristos 		if (fclose(ofp) != 0)
2091a6e50f2Schristos 			err(1, "%s", currfile);
2101a6e50f2Schristos 	}
2111a6e50f2Schristos 
2121a6e50f2Schristos 	toomuch(NULL, 0L);
2131a6e50f2Schristos 	doclean = 0;
2141a6e50f2Schristos 
2151a6e50f2Schristos 	return (0);
2161a6e50f2Schristos }
2171a6e50f2Schristos 
2181a6e50f2Schristos static void
usage(void)2191a6e50f2Schristos usage(void)
2201a6e50f2Schristos {
2211a6e50f2Schristos 
2221a6e50f2Schristos 	(void)fprintf(stderr,
2231a6e50f2Schristos "Usage: %s [-ks] [-f prefix] [-n number] file args ...\n", getprogname());
2241a6e50f2Schristos 	exit(1);
2251a6e50f2Schristos }
2261a6e50f2Schristos 
22731870c16Sjoerg __dead static void
handlesig(int sig)2281a6e50f2Schristos handlesig(int sig)
2291a6e50f2Schristos {
2301a6e50f2Schristos 	char msg[BUFSIZ];
2311a6e50f2Schristos 	size_t len;
2321a6e50f2Schristos 
2331a6e50f2Schristos 	len = snprintf(msg, sizeof(msg), "%s: Caught %s, cleaning up\n",
2341a6e50f2Schristos 	    getprogname(), strsignal(sig));
2351a6e50f2Schristos 	if (len < sizeof(msg))
2361a6e50f2Schristos 		(void)write(STDERR_FILENO, msg, len);
2371a6e50f2Schristos 	cleanup();
2385d427989Slukem 	(void)raise_default_signal(sig);
2391a6e50f2Schristos 	_exit(2);
2401a6e50f2Schristos }
2411a6e50f2Schristos 
2421a6e50f2Schristos /* Create a new output file. */
2431a6e50f2Schristos static FILE *
newfile(void)2441a6e50f2Schristos newfile(void)
2451a6e50f2Schristos {
2461a6e50f2Schristos 	FILE *fp;
2471a6e50f2Schristos 
2481a6e50f2Schristos 	if ((size_t)snprintf(currfile, sizeof(currfile), "%s%0*ld", prefix,
2491a6e50f2Schristos 	    (int)sufflen, nfiles) >= sizeof(currfile))
2501a6e50f2Schristos 		errx(1, "%s: %s", currfile, strerror(ENAMETOOLONG));
2511a6e50f2Schristos 	if ((fp = fopen(currfile, "w+")) == NULL)
2521a6e50f2Schristos 		err(1, "%s", currfile);
2531a6e50f2Schristos 	nfiles++;
2541a6e50f2Schristos 
2551a6e50f2Schristos 	return (fp);
2561a6e50f2Schristos }
2571a6e50f2Schristos 
2581a6e50f2Schristos /* Remove partial output, called before exiting. */
2591a6e50f2Schristos static void
cleanup(void)2601a6e50f2Schristos cleanup(void)
2611a6e50f2Schristos {
2621a6e50f2Schristos 	char fnbuf[PATH_MAX];
2631a6e50f2Schristos 	long i;
2641a6e50f2Schristos 
2651a6e50f2Schristos 	if (!doclean)
2661a6e50f2Schristos 		return;
2671a6e50f2Schristos 
2681a6e50f2Schristos 	/*
2691a6e50f2Schristos 	 * NOTE: One cannot portably assume to be able to call snprintf()
2701a6e50f2Schristos 	 * from inside a signal handler. It does, however, appear to be safe
2711a6e50f2Schristos 	 * to do on FreeBSD and NetBSD. The solution to this problem is worse
2721a6e50f2Schristos 	 * than the problem itself.
2731a6e50f2Schristos 	 */
2741a6e50f2Schristos 
2751a6e50f2Schristos 	for (i = 0; i < nfiles; i++) {
2761a6e50f2Schristos 		(void)snprintf(fnbuf, sizeof(fnbuf), "%s%0*ld", prefix,
2771a6e50f2Schristos 		    (int)sufflen, i);
2781a6e50f2Schristos 		(void)unlink(fnbuf);
2791a6e50f2Schristos 	}
2801a6e50f2Schristos }
2811a6e50f2Schristos 
2821a6e50f2Schristos /* Read a line from the input into a static buffer. */
2831a6e50f2Schristos static char *
get_line(void)2847027866aSroy get_line(void)
2851a6e50f2Schristos {
2861a6e50f2Schristos 	static char lbuf[LINE_MAX];
2871a6e50f2Schristos 	FILE *src;
2881a6e50f2Schristos 
2891a6e50f2Schristos 	src = overfile != NULL ? overfile : infile;
2901a6e50f2Schristos 
2911a6e50f2Schristos again: if (fgets(lbuf, sizeof(lbuf), src) == NULL) {
2921a6e50f2Schristos 		if (src == overfile) {
2931a6e50f2Schristos 			src = infile;
2941a6e50f2Schristos 			goto again;
2951a6e50f2Schristos 		}
2961a6e50f2Schristos 		return (NULL);
2971a6e50f2Schristos 	}
2981a6e50f2Schristos 	if (ferror(src))
2991a6e50f2Schristos 		err(1, "%s", infn);
3001a6e50f2Schristos 	lineno++;
3011a6e50f2Schristos 
3021a6e50f2Schristos 	return (lbuf);
3031a6e50f2Schristos }
3041a6e50f2Schristos 
3057027866aSroy /* Conceptually rewind the input (as obtained by get_line()) back `n' lines. */
3061a6e50f2Schristos static void
toomuch(FILE * ofp,long n)3071a6e50f2Schristos toomuch(FILE *ofp, long n)
3081a6e50f2Schristos {
3091a6e50f2Schristos 	char buf[BUFSIZ];
3101a6e50f2Schristos 	size_t i, nread;
3111a6e50f2Schristos 
3121a6e50f2Schristos 	if (overfile != NULL) {
3131a6e50f2Schristos 		/*
3141a6e50f2Schristos 		 * Truncate the previous file we overflowed into back to
3151a6e50f2Schristos 		 * the correct length, close it.
3161a6e50f2Schristos 		 */
3171a6e50f2Schristos 		if (fflush(overfile) != 0)
3181a6e50f2Schristos 			err(1, "overflow");
3191a6e50f2Schristos 		if (ftruncate(fileno(overfile), truncofs) != 0)
3201a6e50f2Schristos 			err(1, "overflow");
3211a6e50f2Schristos 		if (fclose(overfile) != 0)
3221a6e50f2Schristos 			err(1, "overflow");
3231a6e50f2Schristos 		overfile = NULL;
3241a6e50f2Schristos 	}
3251a6e50f2Schristos 
3261a6e50f2Schristos 	if (n == 0)
3271a6e50f2Schristos 		/* Just tidying up */
3281a6e50f2Schristos 		return;
3291a6e50f2Schristos 
3301a6e50f2Schristos 	lineno -= n;
3311a6e50f2Schristos 
3321a6e50f2Schristos 	/*
3331a6e50f2Schristos 	 * Wind the overflow file backwards to `n' lines before the
3341a6e50f2Schristos 	 * current one.
3351a6e50f2Schristos 	 */
3361a6e50f2Schristos 	do {
3371a6e50f2Schristos 		if (ftello(ofp) < (off_t)sizeof(buf))
3381a6e50f2Schristos 			rewind(ofp);
3391a6e50f2Schristos 		else
3401a6e50f2Schristos 			(void)fseeko(ofp, -(off_t)sizeof(buf), SEEK_CUR);
3411a6e50f2Schristos 		if (ferror(ofp))
3421a6e50f2Schristos 			errx(1, "%s: can't seek", currfile);
3431a6e50f2Schristos 		if ((nread = fread(buf, 1, sizeof(buf), ofp)) == 0)
3441a6e50f2Schristos 			errx(1, "can't read overflowed output");
3451a6e50f2Schristos 		if (fseeko(ofp, -(off_t)nread, SEEK_CUR) != 0)
3461a6e50f2Schristos 			err(1, "%s", currfile);
3471a6e50f2Schristos 		for (i = 1; i <= nread; i++)
3481a6e50f2Schristos 			if (buf[nread - i] == '\n' && n-- == 0)
3491a6e50f2Schristos 				break;
3501a6e50f2Schristos 		if (ftello(ofp) == 0)
3511a6e50f2Schristos 			break;
3521a6e50f2Schristos 	} while (n > 0);
3531a6e50f2Schristos 	if (fseeko(ofp, (off_t)nread - i + 1, SEEK_CUR) != 0)
3541a6e50f2Schristos 		err(1, "%s", currfile);
3551a6e50f2Schristos 
3561a6e50f2Schristos 	/*
3577027866aSroy 	 * get_line() will read from here. Next call will truncate to
3581a6e50f2Schristos 	 * truncofs in this file.
3591a6e50f2Schristos 	 */
3601a6e50f2Schristos 	overfile = ofp;
3611a6e50f2Schristos 	truncofs = ftello(overfile);
3621a6e50f2Schristos }
3631a6e50f2Schristos 
3641a6e50f2Schristos /* Handle splits for /regexp/ and %regexp% patterns. */
3651a6e50f2Schristos static void
do_rexp(const char * expr)3661a6e50f2Schristos do_rexp(const char *expr)
3671a6e50f2Schristos {
3681a6e50f2Schristos 	regex_t cre;
3691a6e50f2Schristos 	intmax_t nwritten;
3701a6e50f2Schristos 	long ofs;
3711a6e50f2Schristos 	int first;
3721a6e50f2Schristos 	char *ecopy, *ep, *p, *pofs, *re;
3731a6e50f2Schristos 	FILE *ofp;
3741a6e50f2Schristos 
3751a6e50f2Schristos 	if ((ecopy = strdup(expr)) == NULL)
3761a6e50f2Schristos 		err(1, "strdup");
3771a6e50f2Schristos 
3781a6e50f2Schristos 	re = ecopy + 1;
3791a6e50f2Schristos 	if ((pofs = strrchr(ecopy, *expr)) == NULL || pofs[-1] == '\\')
3801a6e50f2Schristos 		errx(1, "%s: missing trailing %c", expr, *expr);
3811a6e50f2Schristos 	*pofs++ = '\0';
3821a6e50f2Schristos 
3831a6e50f2Schristos 	if (*pofs != '\0') {
3841a6e50f2Schristos 		errno = 0;
3851a6e50f2Schristos 		ofs = strtol(pofs, &ep, 10);
3861a6e50f2Schristos 		if (*ep != '\0' || errno != 0)
3871a6e50f2Schristos 			errx(1, "%s: bad offset", pofs);
3881a6e50f2Schristos 	} else
3891a6e50f2Schristos 		ofs = 0;
3901a6e50f2Schristos 
3911a6e50f2Schristos 	if (regcomp(&cre, re, REG_BASIC|REG_NOSUB) != 0)
3921a6e50f2Schristos 		errx(1, "%s: bad regular expression", re);
3931a6e50f2Schristos 
3941a6e50f2Schristos 	if (*expr == '/')
3951a6e50f2Schristos 		/* /regexp/: Save results to a file. */
3961a6e50f2Schristos 		ofp = newfile();
3971a6e50f2Schristos 	else {
3981a6e50f2Schristos 		/* %regexp%: Make a temporary file for overflow. */
3991a6e50f2Schristos 		if ((ofp = tmpfile()) == NULL)
4001a6e50f2Schristos 			err(1, "tmpfile");
4011a6e50f2Schristos 	}
4021a6e50f2Schristos 
4031a6e50f2Schristos 	/* Read and output lines until we get a match. */
4041a6e50f2Schristos 	first = 1;
4057027866aSroy 	while ((p = get_line()) != NULL) {
406*ea1a131aScheusov 		if (fputs(p, ofp) == EOF)
4071a6e50f2Schristos 			break;
4081a6e50f2Schristos 		if (!first && regexec(&cre, p, 0, NULL, 0) == 0)
4091a6e50f2Schristos 			break;
4101a6e50f2Schristos 		first = 0;
4111a6e50f2Schristos 	}
4121a6e50f2Schristos 
4131a6e50f2Schristos 	if (p == NULL)
4141a6e50f2Schristos 		errx(1, "%s: no match", re);
4151a6e50f2Schristos 
4161a6e50f2Schristos 	if (ofs <= 0) {
4171a6e50f2Schristos 		/*
4181a6e50f2Schristos 		 * Negative (or zero) offset: throw back any lines we should
4191a6e50f2Schristos 		 * not have read yet.
4201a6e50f2Schristos 		  */
4211a6e50f2Schristos 		if (p != NULL) {
4221a6e50f2Schristos 			toomuch(ofp, -ofs + 1);
4231a6e50f2Schristos 			nwritten = (intmax_t)truncofs;
4241a6e50f2Schristos 		} else
4251a6e50f2Schristos 			nwritten = (intmax_t)ftello(ofp);
4261a6e50f2Schristos 	} else {
4271a6e50f2Schristos 		/*
4281a6e50f2Schristos 		 * Positive offset: copy the requested number of lines
4291a6e50f2Schristos 		 * after the match.
4301a6e50f2Schristos 		 */
4317027866aSroy 		while (--ofs > 0 && (p = get_line()) != NULL)
432*ea1a131aScheusov 			if (fputs(p, ofp) == EOF)
4331a6e50f2Schristos 				break;
4341a6e50f2Schristos 		toomuch(NULL, 0L);
4351a6e50f2Schristos 		nwritten = (intmax_t)ftello(ofp);
4361a6e50f2Schristos 		if (fclose(ofp) != 0)
4371a6e50f2Schristos 			err(1, "%s", currfile);
4381a6e50f2Schristos 	}
4391a6e50f2Schristos 
4401a6e50f2Schristos 	if (!sflag && *expr == '/')
4411a6e50f2Schristos 		(void)printf("%jd\n", nwritten);
4421a6e50f2Schristos 
4431a6e50f2Schristos 	regfree(&cre);
4441a6e50f2Schristos 	free(ecopy);
4451a6e50f2Schristos }
4461a6e50f2Schristos 
4471a6e50f2Schristos /* Handle splits based on line number. */
4481a6e50f2Schristos static void
do_lineno(const char * expr)4491a6e50f2Schristos do_lineno(const char *expr)
4501a6e50f2Schristos {
4511a6e50f2Schristos 	long lastline, tgtline;
4521a6e50f2Schristos 	char *ep, *p;
4531a6e50f2Schristos 	FILE *ofp;
4541a6e50f2Schristos 
4551a6e50f2Schristos 	errno = 0;
4561a6e50f2Schristos 	tgtline = strtol(expr, &ep, 10);
4571a6e50f2Schristos 	if (tgtline <= 0 || errno != 0 || *ep != '\0')
4581a6e50f2Schristos 		errx(1, "%s: bad line number", expr);
4591a6e50f2Schristos 	lastline = tgtline;
4601a6e50f2Schristos 	if (lastline <= lineno)
4611a6e50f2Schristos 		errx(1, "%s: can't go backwards", expr);
4621a6e50f2Schristos 
4631a6e50f2Schristos 	while (nfiles < maxfiles - 1) {
4641a6e50f2Schristos 		ofp = newfile();
4651a6e50f2Schristos 		while (lineno + 1 != lastline) {
4667027866aSroy 			if ((p = get_line()) == NULL)
4671a6e50f2Schristos 				errx(1, "%ld: out of range", lastline);
468*ea1a131aScheusov 			if (fputs(p, ofp) == EOF)
4691a6e50f2Schristos 				break;
4701a6e50f2Schristos 		}
4711a6e50f2Schristos 		if (!sflag)
4721a6e50f2Schristos 			(void)printf("%jd\n", (intmax_t)ftello(ofp));
4731a6e50f2Schristos 		if (fclose(ofp) != 0)
4741a6e50f2Schristos 			err(1, "%s", currfile);
4751a6e50f2Schristos 		if (reps-- == 0)
4761a6e50f2Schristos 			break;
4771a6e50f2Schristos 		lastline += tgtline;
4781a6e50f2Schristos 	}
4791a6e50f2Schristos }
480