]> Zhao Yanbai Git Server - minix.git/commitdiff
Importing usr.bin/csplit 78/1078/1
authorThomas Cort <tcort@minix3.org>
Sun, 27 Oct 2013 17:31:12 +0000 (13:31 -0400)
committerThomas Cort <tcort@minix3.org>
Sun, 27 Oct 2013 20:55:12 +0000 (16:55 -0400)
No Minix-specific changes needed.

Change-Id: Id5d5deb380208ba9fccf404eab5128f8d892c255

distrib/sets/lists/minix/mi
releasetools/nbsd_ports
usr.bin/Makefile
usr.bin/csplit/Makefile [new file with mode: 0644]
usr.bin/csplit/csplit.1 [new file with mode: 0644]
usr.bin/csplit/csplit.c [new file with mode: 0644]

index bbc19b4c440e55e0dd0d84b7f66bea72725389e0..78de0b77c6807a3b89729e88954e69106336897c 100644 (file)
 ./usr/bin/crc                          minix-sys
 ./usr/bin/cron                         minix-sys
 ./usr/bin/crontab                      minix-sys
+./usr/bin/csplit                       minix-sys
 ./usr/bin/ctags                                minix-sys
 ./usr/bin/cut                          minix-sys
 ./usr/bin/datasizes                    minix-sys
 ./usr/man/man1/cpp.1                   minix-sys       gcccmds
 ./usr/man/man1/crc.1                   minix-sys
 ./usr/man/man1/crontab.1               minix-sys
+./usr/man/man1/csplit.1                        minix-sys
 ./usr/man/man1/ctags.1                 minix-sys
 ./usr/man/man1/cut.1                   minix-sys
 ./usr/man/man1/date.1                  minix-sys
index 4d026042b1ab81657866655f282a0204a55dc32b..87563d3a2945cc8d1c639c1dea9204d4d2286887 100644 (file)
 2012/10/17 12:00:00,usr.bin/col
 2013/10/16 12:00:00,usr.bin/column
 2012/10/17 12:00:00,usr.bin/comm
+2012/10/17 12:00:00,usr.bin/csplit
 2012/10/17 12:00:00,usr.bin/ctags
 2013/10/14 12:00:00,usr.bin/cut
 2012/10/17 12:00:00,usr.bin/dirname
index a058a0597b07d0431d4661ccfa36b097038af508..c87c85e10748d44c701862a27a01ac52c53faf18 100644 (file)
@@ -7,7 +7,7 @@ SUBDIR= asa \
        banner basename bdes \
        bzip2 bzip2recover \
        cal chpass cksum \
-       col column comm ctags cut \
+       col column comm csplit ctags cut \
        dirname du \
        env expand \
        finger from \
diff --git a/usr.bin/csplit/Makefile b/usr.bin/csplit/Makefile
new file mode 100644 (file)
index 0000000..38fff8c
--- /dev/null
@@ -0,0 +1,8 @@
+#      $NetBSD: Makefile,v 1.3 2009/04/14 22:15:19 lukem Exp $
+
+PROG=  csplit
+
+DPADD+=        ${LIBUTIL}
+LDADD+=        -lutil
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/csplit/csplit.1 b/usr.bin/csplit/csplit.1
new file mode 100644 (file)
index 0000000..49a85c3
--- /dev/null
@@ -0,0 +1,163 @@
+.\"    $NetBSD: csplit.1,v 1.4 2009/03/12 12:35:51 joerg 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 January 4, 2009
+.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 -offset indent -width 10n
+.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 -offset indent -width 10n
+.It / Ns Ar regexp Ns / Ns Oo Oo Cm + Ns | Ns Cm - Oc Ns Ar offset Oc
+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 % Ns Ar regexp Ns % Ns Oo Oo Cm + Ns | Ns Cm - Oc Ns Ar offset Oc
+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 Brq Ar num
+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.
+.Pp
+.Ex -std
+.Sh ENVIRONMENT
+The
+.Ev LANG , LC_ALL , LC_COLLATE ,
+and
+.Ev LC_CTYPE
+environment variables affect the execution of
+.Nm
+as described in
+.Xr environ 7 .
+.Sh EXAMPLES
+Split the
+.Xr mdoc 7
+file
+.Pa foo.1
+into one file for each section (up to 20):
+.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 conforms to
+.St -p1003.1-2004 .
+.Sh HISTORY
+A
+.Nm
+command appeared in PWB UNIX.
+.Sh BUGS
+Input lines are limited to
+.Dv LINE_MAX
+(2048) bytes in length.
diff --git a/usr.bin/csplit/csplit.c b/usr.bin/csplit/csplit.c
new file mode 100644 (file)
index 0000000..278a6bc
--- /dev/null
@@ -0,0 +1,479 @@
+/*     $NetBSD: csplit.c,v 1.6 2011/08/31 13:35:46 joerg 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/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: csplit.c,v 1.6 2011/08/31 13:35:46 joerg Exp $");
+#endif
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <err.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>
+
+static void     cleanup(void);
+static void     do_lineno(const char *);
+static void     do_rexp(const char *);
+static char    *get_line(void);
+static void     handlesig(int);
+static FILE    *newfile(void);
+static void     toomuch(FILE *, long);
+static void     usage(void) __dead;
+
+/*
+ * Command line options
+ */
+static const char *prefix;             /* File name prefix */
+static long     sufflen;               /* Number of decimal digits for suffix */
+static int      sflag;                 /* Suppress output of file names */
+static int      kflag;                 /* Keep output if error occurs */
+
+/*
+ * Other miscellaneous globals (XXX too many)
+ */
+static long     lineno;                /* Current line number in input file */
+static long     reps;                  /* Number of repetitions for this pattern */
+static long     nfiles;                /* Number of files output so far */
+static long     maxfiles;              /* Maximum number of files we can create */
+static char     currfile[PATH_MAX];    /* Current output file */
+static const char *infn;               /* Name of the input file */
+static FILE    *infile;                /* Input file handle */
+static FILE    *overfile;              /* Overflow file for toomuch() */
+static off_t    truncofs;              /* Offset this file should be truncated at */
+static int      doclean;               /* Should cleanup() remove output? */
+
+int
+main(int argc, char *argv[])
+{
+       struct sigaction sa;
+       long i;
+       int ch;
+       const char *expr;
+       char *ep, *p;
+       FILE *ofp;
+
+       (void)setlocale(LC_ALL, "");
+
+       kflag = sflag = 0;
+       prefix = "xx";
+       sufflen = 2;
+       while ((ch = getopt(argc, argv, "ksf:n:")) > 0) {
+               switch (ch) {
+               case 'f':
+                       prefix = optarg;
+                       break;
+               case 'k':
+                       kflag = 1;
+                       break;
+               case 'n':
+                       errno = 0;
+                       sufflen = strtol(optarg, &ep, 10);
+                       if (sufflen <= 0 || *ep != '\0' || errno != 0)
+                               errx(1, "%s: bad suffix length", optarg);
+                       break;
+               case 's':
+                       sflag = 1;
+                       break;
+               default:
+                       usage();
+                       /*NOTREACHED*/
+               }
+       }
+
+       if (sufflen + strlen(prefix) >= PATH_MAX)
+               errx(1, "name too long");
+
+       argc -= optind;
+       argv += optind;
+
+       if ((infn = *argv++) == NULL)
+               usage();
+       if (strcmp(infn, "-") == 0) {
+               infile = stdin;
+               infn = "stdin";
+       } else if ((infile = fopen(infn, "r")) == NULL)
+               err(1, "%s", infn);
+
+       if (!kflag) {
+               doclean = 1;
+               (void)atexit(cleanup);
+               sa.sa_flags = 0;
+               sa.sa_handler = handlesig;
+               (void)sigemptyset(&sa.sa_mask);
+               (void)sigaddset(&sa.sa_mask, SIGHUP);
+               (void)sigaddset(&sa.sa_mask, SIGINT);
+               (void)sigaddset(&sa.sa_mask, SIGTERM);
+               (void)sigaction(SIGHUP, &sa, NULL);
+               (void)sigaction(SIGINT, &sa, NULL);
+               (void)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)
+                       errx(1, "%ld: suffix too long (limit %ld)",
+                           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)
+                               errx(1, "%s: bad repetition count", *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
+                       errx(1, "%s: unrecognised pattern", expr);
+       }
+
+       /* Copy the rest into a new file. */
+       if (!feof(infile)) {
+               ofp = newfile();
+               while ((p = get_line()) != NULL && fputs(p, ofp) == 0)
+                       ;
+               if (!sflag)
+                       (void)printf("%jd\n", (intmax_t)ftello(ofp));
+               if (fclose(ofp) != 0)
+                       err(1, "%s", currfile);
+       }
+
+       toomuch(NULL, 0L);
+       doclean = 0;
+
+       return (0);
+}
+
+static void
+usage(void)
+{
+
+       (void)fprintf(stderr,
+"Usage: %s [-ks] [-f prefix] [-n number] file args ...\n", getprogname());
+       exit(1);
+}
+
+__dead static void
+handlesig(int sig)
+{
+       char msg[BUFSIZ];
+       size_t len;
+
+       len = snprintf(msg, sizeof(msg), "%s: Caught %s, cleaning up\n",
+           getprogname(), strsignal(sig));
+       if (len < sizeof(msg))
+               (void)write(STDERR_FILENO, msg, len);
+       cleanup();
+       (void)raise_default_signal(sig);
+       _exit(2);
+}
+
+/* Create a new output file. */
+static FILE *
+newfile(void)
+{
+       FILE *fp;
+
+       if ((size_t)snprintf(currfile, sizeof(currfile), "%s%0*ld", prefix,
+           (int)sufflen, nfiles) >= sizeof(currfile))
+               errx(1, "%s: %s", currfile, strerror(ENAMETOOLONG));
+       if ((fp = fopen(currfile, "w+")) == NULL)
+               err(1, "%s", currfile);
+       nfiles++;
+
+       return (fp);
+}
+
+/* Remove partial output, called before exiting. */
+static 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 does, however, appear to be safe
+        * to do on FreeBSD and NetBSD. The solution to this problem is worse
+        * than the problem itself.
+        */
+
+       for (i = 0; i < nfiles; i++) {
+               (void)snprintf(fnbuf, sizeof(fnbuf), "%s%0*ld", prefix,
+                   (int)sufflen, i);
+               (void)unlink(fnbuf);
+       }
+}
+
+/* Read a line from the input into a static buffer. */
+static 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))
+               err(1, "%s", infn);
+       lineno++;
+
+       return (lbuf);
+}
+
+/* Conceptually rewind the input (as obtained by get_line()) back `n' lines. */
+static 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)
+                       err(1, "overflow");
+               if (ftruncate(fileno(overfile), truncofs) != 0)
+                       err(1, "overflow");
+               if (fclose(overfile) != 0)
+                       err(1, "overflow");
+               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
+                       (void)fseeko(ofp, -(off_t)sizeof(buf), SEEK_CUR);
+               if (ferror(ofp))
+                       errx(1, "%s: can't seek", currfile);
+               if ((nread = fread(buf, 1, sizeof(buf), ofp)) == 0)
+                       errx(1, "can't read overflowed output");
+               if (fseeko(ofp, -(off_t)nread, SEEK_CUR) != 0)
+                       err(1, "%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)
+               err(1, "%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. */
+static 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)
+               err(1, "strdup");
+
+       re = ecopy + 1;
+       if ((pofs = strrchr(ecopy, *expr)) == NULL || pofs[-1] == '\\')
+               errx(1, "%s: missing trailing %c", expr, *expr);
+       *pofs++ = '\0';
+
+       if (*pofs != '\0') {
+               errno = 0;
+               ofs = strtol(pofs, &ep, 10);
+               if (*ep != '\0' || errno != 0)
+                       errx(1, "%s: bad offset", pofs);
+       } else
+               ofs = 0;
+
+       if (regcomp(&cre, re, REG_BASIC|REG_NOSUB) != 0)
+               errx(1, "%s: bad regular expression", re);
+
+       if (*expr == '/')
+               /* /regexp/: Save results to a file. */
+               ofp = newfile();
+       else {
+               /* %regexp%: Make a temporary file for overflow. */
+               if ((ofp = tmpfile()) == NULL)
+                       err(1, "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)
+               errx(1, "%s: no match", 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)
+                       if (fputs(p, ofp) != 0)
+                               break;
+               toomuch(NULL, 0L);
+               nwritten = (intmax_t)ftello(ofp);
+               if (fclose(ofp) != 0)
+                       err(1, "%s", currfile);
+       }
+
+       if (!sflag && *expr == '/')
+               (void)printf("%jd\n", nwritten);
+
+       regfree(&cre);
+       free(ecopy);
+}
+
+/* Handle splits based on line number. */
+static 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')
+               errx(1, "%s: bad line number", expr);
+       lastline = tgtline;
+       if (lastline <= lineno)
+               errx(1, "%s: can't go backwards", expr);
+
+       while (nfiles < maxfiles - 1) {
+               ofp = newfile();
+               while (lineno + 1 != lastline) {
+                       if ((p = get_line()) == NULL)
+                               errx(1, "%ld: out of range", lastline);
+                       if (fputs(p, ofp) != 0)
+                               break;
+               }
+               if (!sflag)
+                       (void)printf("%jd\n", (intmax_t)ftello(ofp));
+               if (fclose(ofp) != 0)
+                       err(1, "%s", currfile);
+               if (reps-- == 0)
+                       break;
+               lastline += tgtline;
+       }
+}