14c9d73a5SHiten Pandya /*-
24c9d73a5SHiten Pandya * Copyright (c) 2002 Tim J. Robbins.
34c9d73a5SHiten Pandya * All rights reserved.
44c9d73a5SHiten Pandya *
54c9d73a5SHiten Pandya * Redistribution and use in source and binary forms, with or without
64c9d73a5SHiten Pandya * modification, are permitted provided that the following conditions
74c9d73a5SHiten Pandya * are met:
84c9d73a5SHiten Pandya * 1. Redistributions of source code must retain the above copyright
94c9d73a5SHiten Pandya * notice, this list of conditions and the following disclaimer.
104c9d73a5SHiten Pandya * 2. Redistributions in binary form must reproduce the above copyright
114c9d73a5SHiten Pandya * notice, this list of conditions and the following disclaimer in the
124c9d73a5SHiten Pandya * documentation and/or other materials provided with the distribution.
134c9d73a5SHiten Pandya *
144c9d73a5SHiten Pandya * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
154c9d73a5SHiten Pandya * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
164c9d73a5SHiten Pandya * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
174c9d73a5SHiten Pandya * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
184c9d73a5SHiten Pandya * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
194c9d73a5SHiten Pandya * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
204c9d73a5SHiten Pandya * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
214c9d73a5SHiten Pandya * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
224c9d73a5SHiten Pandya * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
234c9d73a5SHiten Pandya * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
244c9d73a5SHiten Pandya * SUCH DAMAGE.
254c9d73a5SHiten Pandya *
264c9d73a5SHiten Pandya * $FreeBSD: src/usr.bin/csplit/csplit.c,v 1.9 2004/03/22 11:15:03 tjr Exp $
274c9d73a5SHiten Pandya */
284c9d73a5SHiten Pandya
294c9d73a5SHiten Pandya /*
304c9d73a5SHiten Pandya * csplit -- split files based on context
314c9d73a5SHiten Pandya *
324c9d73a5SHiten Pandya * This utility splits its input into numbered output files by line number
334c9d73a5SHiten Pandya * or by a regular expression. Regular expression matches have an optional
344c9d73a5SHiten Pandya * offset with them, allowing the split to occur a specified number of
354c9d73a5SHiten Pandya * lines before or after the match.
364c9d73a5SHiten Pandya *
374c9d73a5SHiten Pandya * To handle negative offsets, we stop reading when the match occurs and
384c9d73a5SHiten Pandya * store the offset that the file should have been split at, then use
394c9d73a5SHiten Pandya * this output file as input until all the "overflowed" lines have been read.
404c9d73a5SHiten Pandya * The file is then closed and truncated to the correct length.
414c9d73a5SHiten Pandya *
424c9d73a5SHiten Pandya * We assume that the output files can be seeked upon (ie. they cannot be
434c9d73a5SHiten Pandya * symlinks to named pipes or character devices), but make no such
444c9d73a5SHiten Pandya * assumption about the input.
454c9d73a5SHiten Pandya */
464c9d73a5SHiten Pandya
474c9d73a5SHiten Pandya #include <sys/types.h>
484c9d73a5SHiten Pandya
494c9d73a5SHiten Pandya #include <ctype.h>
504c9d73a5SHiten Pandya #include <err.h>
514c9d73a5SHiten Pandya #include <errno.h>
524c9d73a5SHiten Pandya #include <limits.h>
534c9d73a5SHiten Pandya #include <locale.h>
544c9d73a5SHiten Pandya #include <regex.h>
554c9d73a5SHiten Pandya #include <signal.h>
564c9d73a5SHiten Pandya #include <stdint.h>
574c9d73a5SHiten Pandya #include <stdio.h>
584c9d73a5SHiten Pandya #include <stdlib.h>
594c9d73a5SHiten Pandya #include <string.h>
604c9d73a5SHiten Pandya #include <unistd.h>
614c9d73a5SHiten Pandya
629c475e1eSSascha Wildner static void cleanup(void);
639c475e1eSSascha Wildner static void do_lineno(const char *);
649c475e1eSSascha Wildner static void do_rexp(const char *);
659c475e1eSSascha Wildner static char *get_line(void);
66*6d08986dSSascha Wildner static void handlesig(int) __dead2;
679c475e1eSSascha Wildner static FILE *newfile(void);
689c475e1eSSascha Wildner static void toomuch(FILE *, long);
69*6d08986dSSascha Wildner static void usage(void) __dead2;
704c9d73a5SHiten Pandya
714c9d73a5SHiten Pandya /*
724c9d73a5SHiten Pandya * Command line options
734c9d73a5SHiten Pandya */
749c475e1eSSascha Wildner static const char *prefix; /* File name prefix */
759c475e1eSSascha Wildner static long sufflen; /* Number of decimal digits for suffix */
769c475e1eSSascha Wildner static int sflag; /* Suppress output of file names */
779c475e1eSSascha Wildner static int kflag; /* Keep output if error occurs */
784c9d73a5SHiten Pandya
794c9d73a5SHiten Pandya /*
804c9d73a5SHiten Pandya * Other miscellaneous globals (XXX too many)
814c9d73a5SHiten Pandya */
829c475e1eSSascha Wildner static long lineno; /* Current line number in input file */
839c475e1eSSascha Wildner static long reps; /* Number of repetitions for this pattern */
849c475e1eSSascha Wildner static long nfiles; /* Number of files output so far */
859c475e1eSSascha Wildner static long maxfiles; /* Maximum number of files we can create */
869c475e1eSSascha Wildner static char currfile[PATH_MAX]; /* Current output file */
879c475e1eSSascha Wildner static const char *infn; /* Name of the input file */
889c475e1eSSascha Wildner static FILE *infile; /* Input file handle */
899c475e1eSSascha Wildner static FILE *overfile; /* Overflow file for toomuch() */
909c475e1eSSascha Wildner static off_t truncofs; /* Offset this file should be truncated at */
919c475e1eSSascha Wildner static int doclean; /* Should cleanup() remove output? */
924c9d73a5SHiten Pandya
934c9d73a5SHiten Pandya int
main(int argc,char * argv[])944c9d73a5SHiten Pandya main(int argc, char *argv[])
954c9d73a5SHiten Pandya {
964c9d73a5SHiten Pandya struct sigaction sa;
974c9d73a5SHiten Pandya long i;
984c9d73a5SHiten Pandya int ch;
994c9d73a5SHiten Pandya const char *expr;
1004c9d73a5SHiten Pandya char *ep, *p;
1014c9d73a5SHiten Pandya FILE *ofp;
1024c9d73a5SHiten Pandya
1034c9d73a5SHiten Pandya setlocale(LC_ALL, "");
1044c9d73a5SHiten Pandya
1054c9d73a5SHiten Pandya kflag = sflag = 0;
1064c9d73a5SHiten Pandya prefix = "xx";
1074c9d73a5SHiten Pandya sufflen = 2;
1084c9d73a5SHiten Pandya while ((ch = getopt(argc, argv, "ksf:n:")) > 0) {
1094c9d73a5SHiten Pandya switch (ch) {
1104c9d73a5SHiten Pandya case 'f':
1114c9d73a5SHiten Pandya prefix = optarg;
1124c9d73a5SHiten Pandya break;
1134c9d73a5SHiten Pandya case 'k':
1144c9d73a5SHiten Pandya kflag = 1;
1154c9d73a5SHiten Pandya break;
1164c9d73a5SHiten Pandya case 'n':
1174c9d73a5SHiten Pandya errno = 0;
1184c9d73a5SHiten Pandya sufflen = strtol(optarg, &ep, 10);
1194c9d73a5SHiten Pandya if (sufflen <= 0 || *ep != '\0' || errno != 0)
1204c9d73a5SHiten Pandya errx(1, "%s: bad suffix length", optarg);
1214c9d73a5SHiten Pandya break;
1224c9d73a5SHiten Pandya case 's':
1234c9d73a5SHiten Pandya sflag = 1;
1244c9d73a5SHiten Pandya break;
1254c9d73a5SHiten Pandya default:
1264c9d73a5SHiten Pandya usage();
1274c9d73a5SHiten Pandya /*NOTREACHED*/
1284c9d73a5SHiten Pandya }
1294c9d73a5SHiten Pandya }
1304c9d73a5SHiten Pandya
1314c9d73a5SHiten Pandya if (sufflen + strlen(prefix) >= PATH_MAX)
1324c9d73a5SHiten Pandya errx(1, "name too long");
1334c9d73a5SHiten Pandya
1344c9d73a5SHiten Pandya argc -= optind;
1354c9d73a5SHiten Pandya argv += optind;
1364c9d73a5SHiten Pandya
1374c9d73a5SHiten Pandya if ((infn = *argv++) == NULL)
1384c9d73a5SHiten Pandya usage();
1394c9d73a5SHiten Pandya if (strcmp(infn, "-") == 0) {
1404c9d73a5SHiten Pandya infile = stdin;
1414c9d73a5SHiten Pandya infn = "stdin";
1424c9d73a5SHiten Pandya } else if ((infile = fopen(infn, "r")) == NULL)
1434c9d73a5SHiten Pandya err(1, "%s", infn);
1444c9d73a5SHiten Pandya
1454c9d73a5SHiten Pandya if (!kflag) {
1464c9d73a5SHiten Pandya doclean = 1;
1474c9d73a5SHiten Pandya atexit(cleanup);
1484c9d73a5SHiten Pandya sa.sa_flags = 0;
1494c9d73a5SHiten Pandya sa.sa_handler = handlesig;
1504c9d73a5SHiten Pandya sigemptyset(&sa.sa_mask);
1514c9d73a5SHiten Pandya sigaddset(&sa.sa_mask, SIGHUP);
1524c9d73a5SHiten Pandya sigaddset(&sa.sa_mask, SIGINT);
1534c9d73a5SHiten Pandya sigaddset(&sa.sa_mask, SIGTERM);
1544c9d73a5SHiten Pandya sigaction(SIGHUP, &sa, NULL);
1554c9d73a5SHiten Pandya sigaction(SIGINT, &sa, NULL);
1564c9d73a5SHiten Pandya sigaction(SIGTERM, &sa, NULL);
1574c9d73a5SHiten Pandya }
1584c9d73a5SHiten Pandya
1594c9d73a5SHiten Pandya lineno = 0;
1604c9d73a5SHiten Pandya nfiles = 0;
1614c9d73a5SHiten Pandya truncofs = 0;
1624c9d73a5SHiten Pandya overfile = NULL;
1634c9d73a5SHiten Pandya
1644c9d73a5SHiten Pandya /* Ensure 10^sufflen < LONG_MAX. */
1654c9d73a5SHiten Pandya for (maxfiles = 1, i = 0; i < sufflen; i++) {
1664c9d73a5SHiten Pandya if (maxfiles > LONG_MAX / 10)
1674c9d73a5SHiten Pandya errx(1, "%ld: suffix too long (limit %ld)",
1684c9d73a5SHiten Pandya sufflen, i);
1694c9d73a5SHiten Pandya maxfiles *= 10;
1704c9d73a5SHiten Pandya }
1714c9d73a5SHiten Pandya
1724c9d73a5SHiten Pandya /* Create files based on supplied patterns. */
1734c9d73a5SHiten Pandya while (nfiles < maxfiles - 1 && (expr = *argv++) != NULL) {
1744c9d73a5SHiten Pandya /* Look ahead & see if this pattern has any repetitions. */
1754c9d73a5SHiten Pandya if (*argv != NULL && **argv == '{') {
1764c9d73a5SHiten Pandya errno = 0;
1774c9d73a5SHiten Pandya reps = strtol(*argv + 1, &ep, 10);
1784c9d73a5SHiten Pandya if (reps < 0 || *ep != '}' || errno != 0)
1794c9d73a5SHiten Pandya errx(1, "%s: bad repetition count", *argv + 1);
1804c9d73a5SHiten Pandya argv++;
1814c9d73a5SHiten Pandya } else
1824c9d73a5SHiten Pandya reps = 0;
1834c9d73a5SHiten Pandya
1844c9d73a5SHiten Pandya if (*expr == '/' || *expr == '%') {
1854c9d73a5SHiten Pandya do
1864c9d73a5SHiten Pandya do_rexp(expr);
1874c9d73a5SHiten Pandya while (reps-- != 0 && nfiles < maxfiles - 1);
1884c9d73a5SHiten Pandya } else if (isdigit((unsigned char)*expr))
1894c9d73a5SHiten Pandya do_lineno(expr);
1904c9d73a5SHiten Pandya else
1914c9d73a5SHiten Pandya errx(1, "%s: unrecognised pattern", expr);
1924c9d73a5SHiten Pandya }
1934c9d73a5SHiten Pandya
1944c9d73a5SHiten Pandya /* Copy the rest into a new file. */
1954c9d73a5SHiten Pandya if (!feof(infile)) {
1964c9d73a5SHiten Pandya ofp = newfile();
197cae2835bSzrj while ((p = get_line()) != NULL && fputs(p, ofp) == 0)
1984c9d73a5SHiten Pandya ;
1994c9d73a5SHiten Pandya if (!sflag)
2004c9d73a5SHiten Pandya printf("%jd\n", (intmax_t)ftello(ofp));
2014c9d73a5SHiten Pandya if (fclose(ofp) != 0)
2024c9d73a5SHiten Pandya err(1, "%s", currfile);
2034c9d73a5SHiten Pandya }
2044c9d73a5SHiten Pandya
2054c9d73a5SHiten Pandya toomuch(NULL, 0);
2064c9d73a5SHiten Pandya doclean = 0;
2074c9d73a5SHiten Pandya
2084c9d73a5SHiten Pandya return (0);
2094c9d73a5SHiten Pandya }
2104c9d73a5SHiten Pandya
2119c475e1eSSascha Wildner static void
usage(void)2124c9d73a5SHiten Pandya usage(void)
2134c9d73a5SHiten Pandya {
2144c9d73a5SHiten Pandya
2154c9d73a5SHiten Pandya fprintf(stderr,
2164c9d73a5SHiten Pandya "usage: csplit [-ks] [-f prefix] [-n number] file args ...\n");
2174c9d73a5SHiten Pandya exit(1);
2184c9d73a5SHiten Pandya }
2194c9d73a5SHiten Pandya
2209c475e1eSSascha Wildner static void
handlesig(int sig __unused)2214c9d73a5SHiten Pandya handlesig(int sig __unused)
2224c9d73a5SHiten Pandya {
2234c9d73a5SHiten Pandya const char msg[] = "csplit: caught signal, cleaning up\n";
2244c9d73a5SHiten Pandya
2254c9d73a5SHiten Pandya write(STDERR_FILENO, msg, sizeof(msg) - 1);
2264c9d73a5SHiten Pandya cleanup();
2274c9d73a5SHiten Pandya _exit(2);
2284c9d73a5SHiten Pandya }
2294c9d73a5SHiten Pandya
2304c9d73a5SHiten Pandya /* Create a new output file. */
2319c475e1eSSascha Wildner static FILE *
newfile(void)2324c9d73a5SHiten Pandya newfile(void)
2334c9d73a5SHiten Pandya {
2344c9d73a5SHiten Pandya FILE *fp;
2354c9d73a5SHiten Pandya
2364c9d73a5SHiten Pandya if ((size_t)snprintf(currfile, sizeof(currfile), "%s%0*ld", prefix,
2374c9d73a5SHiten Pandya (int)sufflen, nfiles) >= sizeof(currfile))
2384c9d73a5SHiten Pandya errc(1, ENAMETOOLONG, NULL);
2394c9d73a5SHiten Pandya if ((fp = fopen(currfile, "w+")) == NULL)
2404c9d73a5SHiten Pandya err(1, "%s", currfile);
2414c9d73a5SHiten Pandya nfiles++;
2424c9d73a5SHiten Pandya
2434c9d73a5SHiten Pandya return (fp);
2444c9d73a5SHiten Pandya }
2454c9d73a5SHiten Pandya
2464c9d73a5SHiten Pandya /* Remove partial output, called before exiting. */
2479c475e1eSSascha Wildner static void
cleanup(void)2484c9d73a5SHiten Pandya cleanup(void)
2494c9d73a5SHiten Pandya {
2504c9d73a5SHiten Pandya char fnbuf[PATH_MAX];
2514c9d73a5SHiten Pandya long i;
2524c9d73a5SHiten Pandya
2534c9d73a5SHiten Pandya if (!doclean)
2544c9d73a5SHiten Pandya return;
2554c9d73a5SHiten Pandya
2564c9d73a5SHiten Pandya /*
2574c9d73a5SHiten Pandya * NOTE: One cannot portably assume to be able to call snprintf()
2584c9d73a5SHiten Pandya * from inside a signal handler. It does, however, appear to be safe
2594c9d73a5SHiten Pandya * to do on FreeBSD. The solution to this problem is worse than the
2604c9d73a5SHiten Pandya * problem itself.
2614c9d73a5SHiten Pandya */
2624c9d73a5SHiten Pandya
2634c9d73a5SHiten Pandya for (i = 0; i < nfiles; i++) {
2644c9d73a5SHiten Pandya snprintf(fnbuf, sizeof(fnbuf), "%s%0*ld", prefix,
2654c9d73a5SHiten Pandya (int)sufflen, i);
2664c9d73a5SHiten Pandya unlink(fnbuf);
2674c9d73a5SHiten Pandya }
2684c9d73a5SHiten Pandya }
2694c9d73a5SHiten Pandya
2704c9d73a5SHiten Pandya /* Read a line from the input into a static buffer. */
2719c475e1eSSascha Wildner static char *
get_line(void)272cae2835bSzrj get_line(void)
2734c9d73a5SHiten Pandya {
2744c9d73a5SHiten Pandya static char lbuf[LINE_MAX];
2754c9d73a5SHiten Pandya FILE *src;
2764c9d73a5SHiten Pandya
2774c9d73a5SHiten Pandya src = overfile != NULL ? overfile : infile;
2784c9d73a5SHiten Pandya
2794c9d73a5SHiten Pandya again: if (fgets(lbuf, sizeof(lbuf), src) == NULL) {
2804c9d73a5SHiten Pandya if (src == overfile) {
2814c9d73a5SHiten Pandya src = infile;
2824c9d73a5SHiten Pandya goto again;
2834c9d73a5SHiten Pandya }
2844c9d73a5SHiten Pandya return (NULL);
2854c9d73a5SHiten Pandya }
2864c9d73a5SHiten Pandya if (ferror(src))
2874c9d73a5SHiten Pandya err(1, "%s", infn);
2884c9d73a5SHiten Pandya lineno++;
2894c9d73a5SHiten Pandya
2904c9d73a5SHiten Pandya return (lbuf);
2914c9d73a5SHiten Pandya }
2924c9d73a5SHiten Pandya
293cae2835bSzrj /* Conceptually rewind the input (as obtained by get_line()) back `n' lines. */
2949c475e1eSSascha Wildner static void
toomuch(FILE * ofp,long n)2954c9d73a5SHiten Pandya toomuch(FILE *ofp, long n)
2964c9d73a5SHiten Pandya {
2974c9d73a5SHiten Pandya char buf[BUFSIZ];
2984c9d73a5SHiten Pandya size_t i, nread;
2994c9d73a5SHiten Pandya
3004c9d73a5SHiten Pandya if (overfile != NULL) {
3014c9d73a5SHiten Pandya /*
3024c9d73a5SHiten Pandya * Truncate the previous file we overflowed into back to
3034c9d73a5SHiten Pandya * the correct length, close it.
3044c9d73a5SHiten Pandya */
3054c9d73a5SHiten Pandya if (fflush(overfile) != 0)
3064c9d73a5SHiten Pandya err(1, "overflow");
3074c9d73a5SHiten Pandya if (ftruncate(fileno(overfile), truncofs) != 0)
3084c9d73a5SHiten Pandya err(1, "overflow");
3094c9d73a5SHiten Pandya if (fclose(overfile) != 0)
3104c9d73a5SHiten Pandya err(1, "overflow");
3114c9d73a5SHiten Pandya overfile = NULL;
3124c9d73a5SHiten Pandya }
3134c9d73a5SHiten Pandya
3144c9d73a5SHiten Pandya if (n == 0)
3154c9d73a5SHiten Pandya /* Just tidying up */
3164c9d73a5SHiten Pandya return;
3174c9d73a5SHiten Pandya
3184c9d73a5SHiten Pandya lineno -= n;
3194c9d73a5SHiten Pandya
3204c9d73a5SHiten Pandya /*
3214c9d73a5SHiten Pandya * Wind the overflow file backwards to `n' lines before the
3224c9d73a5SHiten Pandya * current one.
3234c9d73a5SHiten Pandya */
3244c9d73a5SHiten Pandya do {
3254c9d73a5SHiten Pandya if (ftello(ofp) < (off_t)sizeof(buf))
3264c9d73a5SHiten Pandya rewind(ofp);
3274c9d73a5SHiten Pandya else
3284c9d73a5SHiten Pandya fseeko(ofp, -(off_t)sizeof(buf), SEEK_CUR);
3294c9d73a5SHiten Pandya if (ferror(ofp))
3304c9d73a5SHiten Pandya errx(1, "%s: can't seek", currfile);
3314c9d73a5SHiten Pandya if ((nread = fread(buf, 1, sizeof(buf), ofp)) == 0)
3324c9d73a5SHiten Pandya errx(1, "can't read overflowed output");
3334c9d73a5SHiten Pandya if (fseeko(ofp, -(off_t)nread, SEEK_CUR) != 0)
3344c9d73a5SHiten Pandya err(1, "%s", currfile);
3354c9d73a5SHiten Pandya for (i = 1; i <= nread; i++)
3364c9d73a5SHiten Pandya if (buf[nread - i] == '\n' && n-- == 0)
3374c9d73a5SHiten Pandya break;
3384c9d73a5SHiten Pandya if (ftello(ofp) == 0)
3394c9d73a5SHiten Pandya break;
3404c9d73a5SHiten Pandya } while (n > 0);
3414c9d73a5SHiten Pandya if (fseeko(ofp, nread - i + 1, SEEK_CUR) != 0)
3424c9d73a5SHiten Pandya err(1, "%s", currfile);
3434c9d73a5SHiten Pandya
3444c9d73a5SHiten Pandya /*
345cae2835bSzrj * get_line() will read from here. Next call will truncate to
3464c9d73a5SHiten Pandya * truncofs in this file.
3474c9d73a5SHiten Pandya */
3484c9d73a5SHiten Pandya overfile = ofp;
3494c9d73a5SHiten Pandya truncofs = ftello(overfile);
3504c9d73a5SHiten Pandya }
3514c9d73a5SHiten Pandya
3524c9d73a5SHiten Pandya /* Handle splits for /regexp/ and %regexp% patterns. */
3539c475e1eSSascha Wildner static void
do_rexp(const char * expr)3544c9d73a5SHiten Pandya do_rexp(const char *expr)
3554c9d73a5SHiten Pandya {
3564c9d73a5SHiten Pandya regex_t cre;
3574c9d73a5SHiten Pandya intmax_t nwritten;
3584c9d73a5SHiten Pandya long ofs;
3594c9d73a5SHiten Pandya int first;
3604c9d73a5SHiten Pandya char *ecopy, *ep, *p, *pofs, *re;
3614c9d73a5SHiten Pandya FILE *ofp;
3624c9d73a5SHiten Pandya
3634c9d73a5SHiten Pandya if ((ecopy = strdup(expr)) == NULL)
3644c9d73a5SHiten Pandya err(1, "strdup");
3654c9d73a5SHiten Pandya
3664c9d73a5SHiten Pandya re = ecopy + 1;
3674c9d73a5SHiten Pandya if ((pofs = strrchr(ecopy, *expr)) == NULL || pofs[-1] == '\\')
3684c9d73a5SHiten Pandya errx(1, "%s: missing trailing %c", expr, *expr);
3694c9d73a5SHiten Pandya *pofs++ = '\0';
3704c9d73a5SHiten Pandya
3714c9d73a5SHiten Pandya if (*pofs != '\0') {
3724c9d73a5SHiten Pandya errno = 0;
3734c9d73a5SHiten Pandya ofs = strtol(pofs, &ep, 10);
3744c9d73a5SHiten Pandya if (*ep != '\0' || errno != 0)
3754c9d73a5SHiten Pandya errx(1, "%s: bad offset", pofs);
3764c9d73a5SHiten Pandya } else
3774c9d73a5SHiten Pandya ofs = 0;
3784c9d73a5SHiten Pandya
3794c9d73a5SHiten Pandya if (regcomp(&cre, re, REG_BASIC|REG_NOSUB) != 0)
3804c9d73a5SHiten Pandya errx(1, "%s: bad regular expression", re);
3814c9d73a5SHiten Pandya
3824c9d73a5SHiten Pandya if (*expr == '/')
3834c9d73a5SHiten Pandya /* /regexp/: Save results to a file. */
3844c9d73a5SHiten Pandya ofp = newfile();
3854c9d73a5SHiten Pandya else {
3864c9d73a5SHiten Pandya /* %regexp%: Make a temporary file for overflow. */
3874c9d73a5SHiten Pandya if ((ofp = tmpfile()) == NULL)
3884c9d73a5SHiten Pandya err(1, "tmpfile");
3894c9d73a5SHiten Pandya }
3904c9d73a5SHiten Pandya
3914c9d73a5SHiten Pandya /* Read and output lines until we get a match. */
3924c9d73a5SHiten Pandya first = 1;
393cae2835bSzrj while ((p = get_line()) != NULL) {
3944c9d73a5SHiten Pandya if (fputs(p, ofp) != 0)
3954c9d73a5SHiten Pandya break;
3964c9d73a5SHiten Pandya if (!first && regexec(&cre, p, 0, NULL, 0) == 0)
3974c9d73a5SHiten Pandya break;
3984c9d73a5SHiten Pandya first = 0;
3994c9d73a5SHiten Pandya }
4004c9d73a5SHiten Pandya
4014c9d73a5SHiten Pandya if (p == NULL)
4024c9d73a5SHiten Pandya errx(1, "%s: no match", re);
4034c9d73a5SHiten Pandya
4044c9d73a5SHiten Pandya if (ofs <= 0) {
4054c9d73a5SHiten Pandya /*
4064c9d73a5SHiten Pandya * Negative (or zero) offset: throw back any lines we should
4074c9d73a5SHiten Pandya * not have read yet.
4084c9d73a5SHiten Pandya */
4094c9d73a5SHiten Pandya if (p != NULL) {
4104c9d73a5SHiten Pandya toomuch(ofp, -ofs + 1);
4114c9d73a5SHiten Pandya nwritten = (intmax_t)truncofs;
4124c9d73a5SHiten Pandya } else
4134c9d73a5SHiten Pandya nwritten = (intmax_t)ftello(ofp);
4144c9d73a5SHiten Pandya } else {
4154c9d73a5SHiten Pandya /*
4164c9d73a5SHiten Pandya * Positive offset: copy the requested number of lines
4174c9d73a5SHiten Pandya * after the match.
4184c9d73a5SHiten Pandya */
419cae2835bSzrj while (--ofs > 0 && (p = get_line()) != NULL)
4204c9d73a5SHiten Pandya fputs(p, ofp);
4214c9d73a5SHiten Pandya toomuch(NULL, 0);
4224c9d73a5SHiten Pandya nwritten = (intmax_t)ftello(ofp);
4234c9d73a5SHiten Pandya if (fclose(ofp) != 0)
4244c9d73a5SHiten Pandya err(1, "%s", currfile);
4254c9d73a5SHiten Pandya }
4264c9d73a5SHiten Pandya
4274c9d73a5SHiten Pandya if (!sflag && *expr == '/')
4284c9d73a5SHiten Pandya printf("%jd\n", nwritten);
4294c9d73a5SHiten Pandya
4304c9d73a5SHiten Pandya regfree(&cre);
4314c9d73a5SHiten Pandya free(ecopy);
4324c9d73a5SHiten Pandya }
4334c9d73a5SHiten Pandya
4344c9d73a5SHiten Pandya /* Handle splits based on line number. */
4359c475e1eSSascha Wildner static void
do_lineno(const char * expr)4364c9d73a5SHiten Pandya do_lineno(const char *expr)
4374c9d73a5SHiten Pandya {
4384c9d73a5SHiten Pandya long lastline, tgtline;
4394c9d73a5SHiten Pandya char *ep, *p;
4404c9d73a5SHiten Pandya FILE *ofp;
4414c9d73a5SHiten Pandya
4424c9d73a5SHiten Pandya errno = 0;
4434c9d73a5SHiten Pandya tgtline = strtol(expr, &ep, 10);
4444c9d73a5SHiten Pandya if (tgtline <= 0 || errno != 0 || *ep != '\0')
4454c9d73a5SHiten Pandya errx(1, "%s: bad line number", expr);
4464c9d73a5SHiten Pandya lastline = tgtline;
4474c9d73a5SHiten Pandya if (lastline <= lineno)
4484c9d73a5SHiten Pandya errx(1, "%s: can't go backwards", expr);
4494c9d73a5SHiten Pandya
4504c9d73a5SHiten Pandya while (nfiles < maxfiles - 1) {
4514c9d73a5SHiten Pandya ofp = newfile();
4524c9d73a5SHiten Pandya while (lineno + 1 != lastline) {
453cae2835bSzrj if ((p = get_line()) == NULL)
4544c9d73a5SHiten Pandya errx(1, "%ld: out of range", lastline);
4554c9d73a5SHiten Pandya if (fputs(p, ofp) != 0)
4564c9d73a5SHiten Pandya break;
4574c9d73a5SHiten Pandya }
4584c9d73a5SHiten Pandya if (!sflag)
4594c9d73a5SHiten Pandya printf("%jd\n", (intmax_t)ftello(ofp));
4604c9d73a5SHiten Pandya if (fclose(ofp) != 0)
4614c9d73a5SHiten Pandya err(1, "%s", currfile);
4624c9d73a5SHiten Pandya if (reps-- == 0)
4634c9d73a5SHiten Pandya break;
4644c9d73a5SHiten Pandya lastline += tgtline;
4654c9d73a5SHiten Pandya }
4664c9d73a5SHiten Pandya }
467