wanted: utility to extract lines by line-no.

Dave Decot decot at hpisod2.HP.COM
Sat Apr 9 09:54:14 AEST 1988


# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by decot at hpisod2 on Fri Apr  8 16:54:06 1988
#
# This archive contains:
#	lines.1	lines.c	
#

LANG=""; export LANG

echo x - lines.1
cat >lines.1 <<'@EOF'
.TH LINES 1
.SH NAME
lines \- print selected line ranges from files
.SH SYNOPSIS
\fBlines\fR \fIrange\fR ... [ \fIfilename\fR ... ]
.SH DESCRIPTION
.I Lines
is a filter which prints selected lines from its input.
Each \fIrange\fR argument specifies a group of contiguous lines to be
printed.
If one or more \fIfilename\fRs are supplied,
the same lines are selected from each named
file instead of the standard input.
In this case, each file is processed completely before moving on to the
next file.
.PP
Each printed line is preceded by its line number, a colon, and a tab.
If more than one \fIfilename\fR was specified,
each line will be preceded by
the name of the file from which it came,
unless the name is more than 6 characters long.
In this case, the name of each file is printed
once before the lines from that file are printed.
.PP
Each \fIrange\fR should be one of the following,
where \fIn\fR, \fIm\fR, and \fIk\fR are positive integers:
.TP
\fIn\fR
Print line \fIn\fR.
.TP
\fIm\fB\-\fIn\fR
Print lines \fIm\fR through \fIn\fR, inclusive.
If \fIm\fR is omitted, the range selected begins at the line after
the end of the previous range,
or at line 1 if there is no previous range.
If \fIn\fR is omitted, the range ends at the end of the file.
.TP
\fIm\fB+\fIk\fR
Print lines \fIm\fR through \fIm\fB+\fIk\fR, inclusive.
If \fIm\fR is omitted, the range selected begins at the line after
the end of the previous range,
or at line 1 if there is no previous range.
If \fIk\fR is omitted, the range ends at the end of the file.
.PP
If
.I lines
is reading from a pipe, instead of from a file,
sequential \fIrange\fRs must not specify lines
in any but their numerical order.
.SH AUTHOR
Dave Decot, Hewlett-Packard Company, {hplabs,ucbvax}!hpda!decot.
.SH "SEE ALSO"
cat(1), comm(1), cut(1), head(1), nl(1), paste(1), pr(1), rev(1)
.SH "BUGS"
Lines longer than BUFSIZ are considered to be broken into pieces of
that size or smaller.
@EOF

chmod 664 lines.1

echo x - lines.c
cat >lines.c <<'@EOF'
/*------------------------------------------------------------------------*\
 * lines.c - Version 0.0 Copyright (c) 1986, 1987, 1988 by Dave Decot     *
 *                       All rights reserved.                             *
 *									  *
 * FREEWARE: you may use this for anything as long as you don't charge    *
 *           someone else extra for using or having it.                   *
 *                                                                        *
 * This notice must be present in all copies you distribute.              *
\*------------------------------------------------------------------------*/

#include <stdio.h>
#include <ctype.h>

extern void exit(), perror();

#define MAXRANGES 200
long start[MAXRANGES];
long stop[MAXRANGES];

int rangecount = 0;
long laststop = 0;

char *prog, *filename = "standard input";
int filecount;

main(argc, argv)
int argc;
char *argv[];
{
    int i, plus, badrange = 0;
    char *p;

	prog = argv[0];
	argv++, argc--;

	for (rangecount = 0; rangecount < argc; rangecount++)
	{
	    plus = 0;

	    p = argv[rangecount];
	    while (isdigit(*p))
	       start[rangecount] = start[rangecount] * 10 + *p++ - '0';

	    if (p == argv[rangecount] && (*p == '+' || *p == '-'))
		if (laststop == EOF)
		    badrange++;
		else
		    start[rangecount] = laststop+1;

	    if (*p == '-' || *p == '+')
	    {
		if (*p == '+')
		    plus++;

		if (*++p == '\0' || *p == ',')
		    laststop = stop[rangecount] = EOF;
	    }
	    else if (*p == '\0' || *p == ',')
		laststop = stop[rangecount] = start[rangecount];

	    if (stop[rangecount] == 0)
	    {
	        while (isdigit(*p))
		    stop[rangecount] = stop[rangecount] * 10 + *p++ - '0';

		if (*p != '\0' && *p != ',')
		    break;

		if (plus)
		   stop[rangecount] += start[rangecount];

	        laststop = stop[rangecount];

	        if (laststop < start[rangecount])
	        {
		   fprintf(stderr, "%s: range %d-%d goes backward\n",
					prog, start[rangecount], laststop);
		   exit(2);
		}
	    }

	    if (*p != ',' && *p != '\0')
		break;

	    if (start[rangecount] == 0 || stop[rangecount] == 0)
	    {
	       fprintf(stderr, "%s: 0 is not a line number\n", prog);
	       exit(1);
	    }

	    if (badrange)
	    {
	       fprintf(stderr, "%s: can't use two adjacent + or - signs\n",
	       			prog);
	       exit(1);
	    }

	    if (*p == ',')
	    {
	       (argv--)[rangecount] = p + 1;
	       argc++;
	       continue;
	    }
	}

	filecount = argc - rangecount;

	if (argc - rangecount == 0)
	   process(stdin);

	for (i = rangecount; i < argc; i++)
	    process(fopen(filename = argv[i], "r"));

	return 0;
}

process(fp)
FILE *fp;
{
    long line = 0;
    int range;
    char buf[BUFSIZ];
    extern int errno;

    if (rangecount == 0)
    {
	fprintf(stderr, "Usage:  %s range ... [file ...]\n", prog);
	fprintf(stderr, "\twhere range is one of:\n");
	fprintf(stderr, "\t m\tline m\n");
	fprintf(stderr, "\t m-n\tline m thru line n\n");
	fprintf(stderr, "\t m-  m+\tline m to end of file\n");
	fprintf(stderr, "\t -n\tthru line n\n");
	fprintf(stderr, "\t -   +\tthru end of file\n");
	fprintf(stderr, "\t m+k\tline m thru line m+k\n");
	exit(-1);
    }

    if (fp == NULL)
    {
	perror(filename);
	return;
    }

    if (filecount > 1 && strlen(filename) > 6)
	printf("%s:\n", filename);

    for (range = 0; range < rangecount; range++)
    {
	if (line > start[range])
	{
	    line = errno = 0;
	    rewind(fp);
	    if (errno != 0)
	    {
		fprintf(stderr, "%s: can't rewind %s\n", prog, filename);
		return;
	    }
	}

	while (line < start[range] && fgets(buf, BUFSIZ, fp) != NULL)
	   line++;

	if (line < start[range])
	{
	    fprintf(stderr, "%s: only %ld lines in %s\n", prog, line, filename);
	    return;
	}

	while (stop[range] == EOF || line <= stop[range])
	{
	    if (filecount > 1 && strlen(filename) <= 6)
		printf("%s:\t", filename);
	    printf("%ld:\t", line);
	    fputs(buf, stdout);

	    if (line == stop[range] || fgets(buf, BUFSIZ, fp) == NULL)
		break;

	    line++;
	}

	if (stop[range] != EOF && line < stop[range])
	{
	    fprintf(stderr, "%s: only %ld lines in %s\n",
	    		    prog,    line,     filename);
	    return;
	}
    }
}
@EOF

chmod 664 lines.c

exit 0



More information about the Comp.unix.questions mailing list