Import csplit(1) from OpenBSD
Modified slightly to conform to the sbase style.
This commit is contained in:
		
							
								
								
									
										1
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Makefile
									
									
									
									
									
								
							@@ -42,6 +42,7 @@ SRC = \
 | 
			
		||||
	cols.c     \
 | 
			
		||||
	comm.c     \
 | 
			
		||||
	cp.c       \
 | 
			
		||||
	csplit.c   \
 | 
			
		||||
	cut.c      \
 | 
			
		||||
	date.c     \
 | 
			
		||||
	dirname.c  \
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								TODO
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								TODO
									
									
									
									
									
								
							@@ -6,7 +6,6 @@ such as vi and sh are also not listed here.
 | 
			
		||||
at
 | 
			
		||||
awk
 | 
			
		||||
bc
 | 
			
		||||
csplit
 | 
			
		||||
diff
 | 
			
		||||
ed
 | 
			
		||||
file
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										166
									
								
								csplit.1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								csplit.1
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,166 @@
 | 
			
		||||
.\"	$OpenBSD: csplit.1,v 1.11 2014/02/04 13:26:36 jmc Exp $
 | 
			
		||||
.\"
 | 
			
		||||
.\" Copyright (c) 2002 Tim J. Robbins.
 | 
			
		||||
.\" All rights reserved.
 | 
			
		||||
.\"
 | 
			
		||||
.\" Redistribution and use in source and binary forms, with or without
 | 
			
		||||
.\" modification, are permitted provided that the following conditions
 | 
			
		||||
.\" are met:
 | 
			
		||||
.\" 1. Redistributions of source code must retain the above copyright
 | 
			
		||||
.\"    notice, this list of conditions and the following disclaimer.
 | 
			
		||||
.\" 2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
.\"    notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
.\"    documentation and/or other materials provided with the distribution.
 | 
			
		||||
.\"
 | 
			
		||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 | 
			
		||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 | 
			
		||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
			
		||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
			
		||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
			
		||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
			
		||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
			
		||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
			
		||||
.\" SUCH DAMAGE.
 | 
			
		||||
.\"
 | 
			
		||||
.\" $FreeBSD: src/usr.bin/csplit/csplit.1,v 1.11 2005/01/25 22:29:51 tjr Exp $
 | 
			
		||||
.\"
 | 
			
		||||
.Dd $Mdocdate: February 4 2014 $
 | 
			
		||||
.Dt CSPLIT 1
 | 
			
		||||
.Os
 | 
			
		||||
.Sh NAME
 | 
			
		||||
.Nm csplit
 | 
			
		||||
.Nd split files based on context
 | 
			
		||||
.Sh SYNOPSIS
 | 
			
		||||
.Nm
 | 
			
		||||
.Op Fl ks
 | 
			
		||||
.Op Fl f Ar prefix
 | 
			
		||||
.Op Fl n Ar number
 | 
			
		||||
.Ar file args ...
 | 
			
		||||
.Sh DESCRIPTION
 | 
			
		||||
The
 | 
			
		||||
.Nm
 | 
			
		||||
utility splits
 | 
			
		||||
.Ar file
 | 
			
		||||
into pieces using the patterns
 | 
			
		||||
.Ar args .
 | 
			
		||||
If
 | 
			
		||||
.Ar file
 | 
			
		||||
is
 | 
			
		||||
a dash
 | 
			
		||||
.Pq Sq - ,
 | 
			
		||||
.Nm
 | 
			
		||||
reads from standard input.
 | 
			
		||||
.Pp
 | 
			
		||||
Files are created with a prefix of
 | 
			
		||||
.Dq xx
 | 
			
		||||
and two decimal digits.
 | 
			
		||||
The size of each file is written to standard output
 | 
			
		||||
as it is created.
 | 
			
		||||
If an error occurs whilst files are being created,
 | 
			
		||||
or a
 | 
			
		||||
.Dv HUP ,
 | 
			
		||||
.Dv INT ,
 | 
			
		||||
or
 | 
			
		||||
.Dv TERM
 | 
			
		||||
signal is received,
 | 
			
		||||
all files previously written are removed.
 | 
			
		||||
.Pp
 | 
			
		||||
The options are as follows:
 | 
			
		||||
.Bl -tag -width indent
 | 
			
		||||
.It Fl f Ar prefix
 | 
			
		||||
Create file names beginning with
 | 
			
		||||
.Ar prefix ,
 | 
			
		||||
instead of
 | 
			
		||||
.Dq xx .
 | 
			
		||||
.It Fl k
 | 
			
		||||
Do not remove previously created files if an error occurs or a
 | 
			
		||||
.Dv HUP ,
 | 
			
		||||
.Dv INT ,
 | 
			
		||||
or
 | 
			
		||||
.Dv TERM
 | 
			
		||||
signal is received.
 | 
			
		||||
.It Fl n Ar number
 | 
			
		||||
Create file names beginning with
 | 
			
		||||
.Ar number
 | 
			
		||||
of decimal digits after the prefix,
 | 
			
		||||
instead of 2.
 | 
			
		||||
.It Fl s
 | 
			
		||||
Do not write the size of each output file to standard output as it is
 | 
			
		||||
created.
 | 
			
		||||
.El
 | 
			
		||||
.Pp
 | 
			
		||||
The
 | 
			
		||||
.Ar args
 | 
			
		||||
operands may be a combination of the following patterns:
 | 
			
		||||
.Bl -tag -width indent
 | 
			
		||||
.It Xo
 | 
			
		||||
.Sm off
 | 
			
		||||
.No / Ar regexp No /
 | 
			
		||||
.Op Oo Cm + | - Oc Ar offset
 | 
			
		||||
.Sm on
 | 
			
		||||
.Xc
 | 
			
		||||
Create a file containing the input from the current line to (but not including)
 | 
			
		||||
the next line matching the given basic regular expression.
 | 
			
		||||
An optional
 | 
			
		||||
.Ar offset
 | 
			
		||||
from the line that matched may be specified.
 | 
			
		||||
.It Xo
 | 
			
		||||
.Sm off
 | 
			
		||||
.No % Ar regexp No %
 | 
			
		||||
.Op Oo Cm + | - Oc Ar offset
 | 
			
		||||
.Sm on
 | 
			
		||||
.Xc
 | 
			
		||||
Same as above but a file is not created for the output.
 | 
			
		||||
.It Ar line_no
 | 
			
		||||
Create containing the input from the current line to (but not including)
 | 
			
		||||
the specified line number.
 | 
			
		||||
.It { Ns Ar num Ns }
 | 
			
		||||
Repeat the previous pattern the specified number of times.
 | 
			
		||||
If it follows a line number pattern, a new file will be created for each
 | 
			
		||||
.Ar line_no
 | 
			
		||||
lines,
 | 
			
		||||
.Ar num
 | 
			
		||||
times.
 | 
			
		||||
The first line of the file is line number 1 for historic reasons.
 | 
			
		||||
.El
 | 
			
		||||
.Pp
 | 
			
		||||
After all the patterns have been processed, the remaining input data
 | 
			
		||||
(if there is any) will be written to a new file.
 | 
			
		||||
.Pp
 | 
			
		||||
Requesting to split at a line before the current line number or past the
 | 
			
		||||
end of the file will result in an error.
 | 
			
		||||
.Sh EXIT STATUS
 | 
			
		||||
.Ex -std csplit
 | 
			
		||||
.Sh EXAMPLES
 | 
			
		||||
Split the
 | 
			
		||||
.Xr mdoc 7
 | 
			
		||||
file
 | 
			
		||||
.Pa foo.1
 | 
			
		||||
into one file for each section (up to 21):
 | 
			
		||||
.Pp
 | 
			
		||||
.Dl "$ csplit -k foo.1 '%^\e.Sh%' '/^\e.Sh/' '{20}'"
 | 
			
		||||
.Pp
 | 
			
		||||
Split standard input after the first 99 lines and every 100 lines thereafter:
 | 
			
		||||
.Pp
 | 
			
		||||
.Dl "$ csplit -k - 100 '{19}'"
 | 
			
		||||
.Sh SEE ALSO
 | 
			
		||||
.Xr sed 1 ,
 | 
			
		||||
.Xr split 1 ,
 | 
			
		||||
.Xr re_format 7
 | 
			
		||||
.Sh STANDARDS
 | 
			
		||||
The
 | 
			
		||||
.Nm
 | 
			
		||||
utility is compliant with the
 | 
			
		||||
.St -p1003.1-2008
 | 
			
		||||
specification.
 | 
			
		||||
.Sh HISTORY
 | 
			
		||||
A
 | 
			
		||||
.Nm
 | 
			
		||||
command appeared in PWB
 | 
			
		||||
.Ux .
 | 
			
		||||
.Sh BUGS
 | 
			
		||||
Input lines are limited to
 | 
			
		||||
.Dv LINE_MAX
 | 
			
		||||
(2048) bytes in length.
 | 
			
		||||
							
								
								
									
										452
									
								
								csplit.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										452
									
								
								csplit.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,452 @@
 | 
			
		||||
/*	$OpenBSD: csplit.c,v 1.5 2014/05/20 01:25:23 guenther Exp $	*/
 | 
			
		||||
/*	$FreeBSD: src/usr.bin/csplit/csplit.c,v 1.9 2004/03/22 11:15:03 tjr Exp $	*/
 | 
			
		||||
 | 
			
		||||
/*-
 | 
			
		||||
 * Copyright (c) 2002 Tim J. Robbins.
 | 
			
		||||
 * All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 * 1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *    notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 * 2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *    notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *    documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 | 
			
		||||
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 | 
			
		||||
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
			
		||||
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
			
		||||
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
			
		||||
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
			
		||||
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
			
		||||
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
			
		||||
 * SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * csplit -- split files based on context
 | 
			
		||||
 *
 | 
			
		||||
 * This utility splits its input into numbered output files by line number
 | 
			
		||||
 * or by a regular expression. Regular expression matches have an optional
 | 
			
		||||
 * offset with them, allowing the split to occur a specified number of
 | 
			
		||||
 * lines before or after the match.
 | 
			
		||||
 *
 | 
			
		||||
 * To handle negative offsets, we stop reading when the match occurs and
 | 
			
		||||
 * store the offset that the file should have been split at, then use
 | 
			
		||||
 * this output file as input until all the "overflowed" lines have been read.
 | 
			
		||||
 * The file is then closed and truncated to the correct length.
 | 
			
		||||
 *
 | 
			
		||||
 * We assume that the output files can be seeked upon (ie. they cannot be
 | 
			
		||||
 * symlinks to named pipes or character devices), but make no such
 | 
			
		||||
 * assumption about the input.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <locale.h>
 | 
			
		||||
#include <regex.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
void	 cleanup(void);
 | 
			
		||||
void	 do_lineno(const char *);
 | 
			
		||||
void	 do_rexp(const char *);
 | 
			
		||||
char	*get_line(void);
 | 
			
		||||
void	 handlesig(int);
 | 
			
		||||
FILE	*newfile(void);
 | 
			
		||||
void	 toomuch(FILE *, long);
 | 
			
		||||
void	 usage(void);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Command line options
 | 
			
		||||
 */
 | 
			
		||||
const char *prefix;		/* File name prefix */
 | 
			
		||||
long	 sufflen;		/* Number of decimal digits for suffix */
 | 
			
		||||
int	 sflag;			/* Suppress output of file names */
 | 
			
		||||
int	 kflag;			/* Keep output if error occurs */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Other miscellaneous globals (XXX too many)
 | 
			
		||||
 */
 | 
			
		||||
long	 lineno;		/* Current line number in input file */
 | 
			
		||||
long	 reps;			/* Number of repetitions for this pattern */
 | 
			
		||||
long	 nfiles;		/* Number of files output so far */
 | 
			
		||||
long	 maxfiles;		/* Maximum number of files we can create */
 | 
			
		||||
char	 currfile[PATH_MAX];	/* Current output file */
 | 
			
		||||
const char *infn;		/* Name of the input file */
 | 
			
		||||
FILE	*infile;		/* Input file handle */
 | 
			
		||||
FILE	*overfile;		/* Overflow file for toomuch() */
 | 
			
		||||
off_t	 truncofs;		/* Offset this file should be truncated at */
 | 
			
		||||
int	 doclean;		/* Should cleanup() remove output? */
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	struct sigaction sa;
 | 
			
		||||
	long i;
 | 
			
		||||
	const char *expr;
 | 
			
		||||
	char *ep, *p;
 | 
			
		||||
	FILE *ofp;
 | 
			
		||||
 | 
			
		||||
	kflag = sflag = 0;
 | 
			
		||||
	prefix = "xx";
 | 
			
		||||
	sufflen = 2;
 | 
			
		||||
 | 
			
		||||
	ARGBEGIN {
 | 
			
		||||
	case 'f':
 | 
			
		||||
		prefix = EARGF(usage());
 | 
			
		||||
		break;
 | 
			
		||||
	case 'k':
 | 
			
		||||
		kflag = 1;
 | 
			
		||||
		break;
 | 
			
		||||
	case 'n':
 | 
			
		||||
		sufflen = estrtol(EARGF(usage()), 10);
 | 
			
		||||
		break;
 | 
			
		||||
	case 's':
 | 
			
		||||
		sflag = 1;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		usage();
 | 
			
		||||
	} ARGEND;
 | 
			
		||||
 | 
			
		||||
	if (sufflen + strlen(prefix) >= PATH_MAX)
 | 
			
		||||
		eprintf("name too long\n");
 | 
			
		||||
 | 
			
		||||
	if ((infn = *argv++) == NULL)
 | 
			
		||||
		usage();
 | 
			
		||||
	if (strcmp(infn, "-") == 0) {
 | 
			
		||||
		infile = stdin;
 | 
			
		||||
		infn = "stdin";
 | 
			
		||||
	} else if ((infile = fopen(infn, "r")) == NULL)
 | 
			
		||||
		eprintf("fopen %s:", infn);
 | 
			
		||||
 | 
			
		||||
	if (!kflag) {
 | 
			
		||||
		doclean = 1;
 | 
			
		||||
		atexit(cleanup);
 | 
			
		||||
		sa.sa_flags = 0;
 | 
			
		||||
		sa.sa_handler = handlesig;
 | 
			
		||||
		sigemptyset(&sa.sa_mask);
 | 
			
		||||
		sigaddset(&sa.sa_mask, SIGHUP);
 | 
			
		||||
		sigaddset(&sa.sa_mask, SIGINT);
 | 
			
		||||
		sigaddset(&sa.sa_mask, SIGTERM);
 | 
			
		||||
		sigaction(SIGHUP, &sa, NULL);
 | 
			
		||||
		sigaction(SIGINT, &sa, NULL);
 | 
			
		||||
		sigaction(SIGTERM, &sa, NULL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	lineno = 0;
 | 
			
		||||
	nfiles = 0;
 | 
			
		||||
	truncofs = 0;
 | 
			
		||||
	overfile = NULL;
 | 
			
		||||
 | 
			
		||||
	/* Ensure 10^sufflen < LONG_MAX. */
 | 
			
		||||
	for (maxfiles = 1, i = 0; i < sufflen; i++) {
 | 
			
		||||
		if (maxfiles > LONG_MAX / 10)
 | 
			
		||||
			eprintf("%ld: suffix too long (limit %ld)\n",
 | 
			
		||||
			    sufflen, i);
 | 
			
		||||
		maxfiles *= 10;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Create files based on supplied patterns. */
 | 
			
		||||
	while (nfiles < maxfiles - 1 && (expr = *argv++) != NULL) {
 | 
			
		||||
		/* Look ahead & see if this pattern has any repetitions. */
 | 
			
		||||
		if (*argv != NULL && **argv == '{') {
 | 
			
		||||
			errno = 0;
 | 
			
		||||
			reps = strtol(*argv + 1, &ep, 10);
 | 
			
		||||
			if (reps < 0 || *ep != '}' || errno != 0)
 | 
			
		||||
				eprintf("%s: bad repetition count\n", *argv + 1);
 | 
			
		||||
			argv++;
 | 
			
		||||
		} else
 | 
			
		||||
			reps = 0;
 | 
			
		||||
 | 
			
		||||
		if (*expr == '/' || *expr == '%') {
 | 
			
		||||
			do {
 | 
			
		||||
				do_rexp(expr);
 | 
			
		||||
			} while (reps-- != 0 && nfiles < maxfiles - 1);
 | 
			
		||||
		} else if (isdigit((unsigned char)*expr))
 | 
			
		||||
			do_lineno(expr);
 | 
			
		||||
		else
 | 
			
		||||
			eprintf("%s: unrecognised pattern\n", expr);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Copy the rest into a new file. */
 | 
			
		||||
	if (!feof(infile)) {
 | 
			
		||||
		ofp = newfile();
 | 
			
		||||
		while ((p = get_line()) != NULL && fputs(p, ofp) == 0)
 | 
			
		||||
			;
 | 
			
		||||
		if (!sflag)
 | 
			
		||||
			printf("%jd\n", (intmax_t)ftello(ofp));
 | 
			
		||||
		if (fclose(ofp) != 0)
 | 
			
		||||
			eprintf("fclose %s:", currfile);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	toomuch(NULL, 0);
 | 
			
		||||
	doclean = 0;
 | 
			
		||||
 | 
			
		||||
	return (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
usage(void)
 | 
			
		||||
{
 | 
			
		||||
	eprintf("usage: %s [-ks] [-f prefix] [-n number] file args ...\n", argv0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ARGSUSED */
 | 
			
		||||
void
 | 
			
		||||
handlesig(int sig)
 | 
			
		||||
{
 | 
			
		||||
	const char msg[] = "csplit: caught signal, cleaning up\n";
 | 
			
		||||
 | 
			
		||||
	write(STDERR_FILENO, msg, sizeof(msg) - 1);
 | 
			
		||||
	cleanup();
 | 
			
		||||
	_exit(2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Create a new output file. */
 | 
			
		||||
FILE *
 | 
			
		||||
newfile(void)
 | 
			
		||||
{
 | 
			
		||||
	FILE *fp;
 | 
			
		||||
 | 
			
		||||
	if ((size_t)snprintf(currfile, sizeof(currfile), "%s%0*ld", prefix,
 | 
			
		||||
	    (int)sufflen, nfiles) >= sizeof(currfile))
 | 
			
		||||
		eprintf("%s: name too long\n", currfile);
 | 
			
		||||
	if ((fp = fopen(currfile, "w+")) == NULL)
 | 
			
		||||
		eprintf("fopen %s:", currfile);
 | 
			
		||||
	nfiles++;
 | 
			
		||||
 | 
			
		||||
	return (fp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Remove partial output, called before exiting. */
 | 
			
		||||
void
 | 
			
		||||
cleanup(void)
 | 
			
		||||
{
 | 
			
		||||
	char fnbuf[PATH_MAX];
 | 
			
		||||
	long i;
 | 
			
		||||
 | 
			
		||||
	if (!doclean)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * NOTE: One cannot portably assume to be able to call snprintf() from
 | 
			
		||||
	 * inside a signal handler.  It is, however, safe to do on OpenBSD.
 | 
			
		||||
	 */
 | 
			
		||||
	for (i = 0; i < nfiles; i++) {
 | 
			
		||||
		snprintf(fnbuf, sizeof(fnbuf), "%s%0*ld", prefix,
 | 
			
		||||
		    (int)sufflen, i);
 | 
			
		||||
		unlink(fnbuf);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Read a line from the input into a static buffer. */
 | 
			
		||||
char *
 | 
			
		||||
get_line(void)
 | 
			
		||||
{
 | 
			
		||||
	static char lbuf[LINE_MAX];
 | 
			
		||||
	FILE *src;
 | 
			
		||||
 | 
			
		||||
	src = overfile != NULL ? overfile : infile;
 | 
			
		||||
 | 
			
		||||
again: if (fgets(lbuf, sizeof(lbuf), src) == NULL) {
 | 
			
		||||
		if (src == overfile) {
 | 
			
		||||
			src = infile;
 | 
			
		||||
			goto again;
 | 
			
		||||
		}
 | 
			
		||||
		return (NULL);
 | 
			
		||||
	}
 | 
			
		||||
	if (ferror(src))
 | 
			
		||||
		eprintf("%s: read error:", infn);
 | 
			
		||||
	lineno++;
 | 
			
		||||
 | 
			
		||||
	return (lbuf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Conceptually rewind the input (as obtained by get_line()) back `n' lines. */
 | 
			
		||||
void
 | 
			
		||||
toomuch(FILE *ofp, long n)
 | 
			
		||||
{
 | 
			
		||||
	char buf[BUFSIZ];
 | 
			
		||||
	size_t i, nread;
 | 
			
		||||
 | 
			
		||||
	if (overfile != NULL) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Truncate the previous file we overflowed into back to
 | 
			
		||||
		 * the correct length, close it.
 | 
			
		||||
		 */
 | 
			
		||||
		if (fflush(overfile) != 0)
 | 
			
		||||
			eprintf("overflow\n");
 | 
			
		||||
		if (ftruncate(fileno(overfile), truncofs) != 0)
 | 
			
		||||
			eprintf("overflow\n");
 | 
			
		||||
		if (fclose(overfile) != 0)
 | 
			
		||||
			eprintf("overflow\n");
 | 
			
		||||
		overfile = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (n == 0)
 | 
			
		||||
		/* Just tidying up */
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	lineno -= n;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Wind the overflow file backwards to `n' lines before the
 | 
			
		||||
	 * current one.
 | 
			
		||||
	 */
 | 
			
		||||
	do {
 | 
			
		||||
		if (ftello(ofp) < (off_t)sizeof(buf))
 | 
			
		||||
			rewind(ofp);
 | 
			
		||||
		else
 | 
			
		||||
			fseeko(ofp, -(off_t)sizeof(buf), SEEK_CUR);
 | 
			
		||||
		if (ferror(ofp))
 | 
			
		||||
			eprintf("%s: can't seek:", currfile);
 | 
			
		||||
		if ((nread = fread(buf, 1, sizeof(buf), ofp)) == 0)
 | 
			
		||||
			eprintf("can't read overflowed output\n");
 | 
			
		||||
		if (fseeko(ofp, -(off_t)nread, SEEK_CUR) != 0)
 | 
			
		||||
			eprintf("fseeko %s:", currfile);
 | 
			
		||||
		for (i = 1; i <= nread; i++)
 | 
			
		||||
			if (buf[nread - i] == '\n' && n-- == 0)
 | 
			
		||||
				break;
 | 
			
		||||
		if (ftello(ofp) == 0)
 | 
			
		||||
			break;
 | 
			
		||||
	} while (n > 0);
 | 
			
		||||
	if (fseeko(ofp, (off_t)(nread - i + 1), SEEK_CUR) != 0)
 | 
			
		||||
		eprintf("fseeko %s:", currfile);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * get_line() will read from here. Next call will truncate to
 | 
			
		||||
	 * truncofs in this file.
 | 
			
		||||
	 */
 | 
			
		||||
	overfile = ofp;
 | 
			
		||||
	truncofs = ftello(overfile);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle splits for /regexp/ and %regexp% patterns. */
 | 
			
		||||
void
 | 
			
		||||
do_rexp(const char *expr)
 | 
			
		||||
{
 | 
			
		||||
	regex_t cre;
 | 
			
		||||
	intmax_t nwritten;
 | 
			
		||||
	long ofs;
 | 
			
		||||
	int first;
 | 
			
		||||
	char *ecopy, *ep, *p, *pofs, *re;
 | 
			
		||||
	FILE *ofp;
 | 
			
		||||
 | 
			
		||||
	if ((ecopy = strdup(expr)) == NULL)
 | 
			
		||||
		eprintf("strdup:");
 | 
			
		||||
 | 
			
		||||
	re = ecopy + 1;
 | 
			
		||||
	if ((pofs = strrchr(ecopy, *expr)) == NULL || pofs[-1] == '\\')
 | 
			
		||||
		eprintf("%s: missing trailing %c\n", expr, *expr);
 | 
			
		||||
	*pofs++ = '\0';
 | 
			
		||||
 | 
			
		||||
	if (*pofs != '\0') {
 | 
			
		||||
		errno = 0;
 | 
			
		||||
		ofs = strtol(pofs, &ep, 10);
 | 
			
		||||
		if (*ep != '\0' || errno != 0)
 | 
			
		||||
			eprintf("%s: bad offset\n", pofs);
 | 
			
		||||
	} else
 | 
			
		||||
		ofs = 0;
 | 
			
		||||
 | 
			
		||||
	if (regcomp(&cre, re, REG_NOSUB) != 0)
 | 
			
		||||
		eprintf("%s: bad regular expression'n", re);
 | 
			
		||||
 | 
			
		||||
	if (*expr == '/')
 | 
			
		||||
		/* /regexp/: Save results to a file. */
 | 
			
		||||
		ofp = newfile();
 | 
			
		||||
	else {
 | 
			
		||||
		/* %regexp%: Make a temporary file for overflow. */
 | 
			
		||||
		if ((ofp = tmpfile()) == NULL)
 | 
			
		||||
			eprintf("tmpfile:");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Read and output lines until we get a match. */
 | 
			
		||||
	first = 1;
 | 
			
		||||
	while ((p = get_line()) != NULL) {
 | 
			
		||||
		if (fputs(p, ofp) != 0)
 | 
			
		||||
			break;
 | 
			
		||||
		if (!first && regexec(&cre, p, 0, NULL, 0) == 0)
 | 
			
		||||
			break;
 | 
			
		||||
		first = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (p == NULL)
 | 
			
		||||
		eprintf("%s: no match\n", re);
 | 
			
		||||
 | 
			
		||||
	if (ofs <= 0) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Negative (or zero) offset: throw back any lines we should
 | 
			
		||||
		 * not have read yet.
 | 
			
		||||
		  */
 | 
			
		||||
		if (p != NULL) {
 | 
			
		||||
			toomuch(ofp, -ofs + 1);
 | 
			
		||||
			nwritten = (intmax_t)truncofs;
 | 
			
		||||
		} else
 | 
			
		||||
			nwritten = (intmax_t)ftello(ofp);
 | 
			
		||||
	} else {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Positive offset: copy the requested number of lines
 | 
			
		||||
		 * after the match.
 | 
			
		||||
		 */
 | 
			
		||||
		while (--ofs > 0 && (p = get_line()) != NULL)
 | 
			
		||||
			fputs(p, ofp);
 | 
			
		||||
		toomuch(NULL, 0);
 | 
			
		||||
		nwritten = (intmax_t)ftello(ofp);
 | 
			
		||||
		if (fclose(ofp) != 0)
 | 
			
		||||
			eprintf("fclose %s:", currfile);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!sflag && *expr == '/')
 | 
			
		||||
		printf("%jd\n", nwritten);
 | 
			
		||||
 | 
			
		||||
	regfree(&cre);
 | 
			
		||||
	free(ecopy);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle splits based on line number. */
 | 
			
		||||
void
 | 
			
		||||
do_lineno(const char *expr)
 | 
			
		||||
{
 | 
			
		||||
	long lastline, tgtline;
 | 
			
		||||
	char *ep, *p;
 | 
			
		||||
	FILE *ofp;
 | 
			
		||||
 | 
			
		||||
	errno = 0;
 | 
			
		||||
	tgtline = strtol(expr, &ep, 10);
 | 
			
		||||
	if (tgtline <= 0 || errno != 0 || *ep != '\0')
 | 
			
		||||
		eprintf("%s: bad line number\n", expr);
 | 
			
		||||
	lastline = tgtline;
 | 
			
		||||
	if (lastline <= lineno)
 | 
			
		||||
		eprintf("%s: can't go backwards\n", expr);
 | 
			
		||||
 | 
			
		||||
	while (nfiles < maxfiles - 1) {
 | 
			
		||||
		ofp = newfile();
 | 
			
		||||
		while (lineno + 1 != lastline) {
 | 
			
		||||
			if ((p = get_line()) == NULL)
 | 
			
		||||
				eprintf("%ld: out of range\n", lastline);
 | 
			
		||||
			if (fputs(p, ofp) != 0)
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
		if (!sflag)
 | 
			
		||||
			printf("%jd\n", (intmax_t)ftello(ofp));
 | 
			
		||||
		if (fclose(ofp) != 0)
 | 
			
		||||
			eprintf("fclose %s:", currfile);
 | 
			
		||||
		if (reps-- == 0)
 | 
			
		||||
			break;
 | 
			
		||||
		lastline += tgtline;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user