From a124089295bc8f501c15e4891b9a9c2d4aa15412 Mon Sep 17 00:00:00 2001 From: Dylan Lom Date: Mon, 22 Feb 2021 22:24:31 +1100 Subject: [PATCH] Rename line to lines * It can now extract multiple lines, I'm not super excited about the code, but it works at least * Add ADVANCE_PTR macro to util --- doc/line.1 | 17 ------------ doc/lines.1 | 26 ++++++++++++++++++ src/{line.c => lines.c} | 61 +++++++++++++++++++++++++++++++---------- src/util.h | 5 ++-- 4 files changed, 76 insertions(+), 33 deletions(-) delete mode 100644 doc/line.1 create mode 100644 doc/lines.1 rename src/{line.c => lines.c} (51%) diff --git a/doc/line.1 b/doc/line.1 deleted file mode 100644 index f608585..0000000 --- a/doc/line.1 +++ /dev/null @@ -1,17 +0,0 @@ -.TH LINE 1 -.SH NAME -line \- extract line from input -.SH SYNOPSIS -.B line -.IR ln -.SH DESCRIPTION -.B line -extracts the ln'th line from input provided over STDIN -.SH EXAMPLES -Get the 3rd line from the file line.1 -.PP -.nf -.RS -line 3 < line.1 -.RE -.fi diff --git a/doc/lines.1 b/doc/lines.1 new file mode 100644 index 0000000..77a0f60 --- /dev/null +++ b/doc/lines.1 @@ -0,0 +1,26 @@ +.TH LINES 1 +.SH NAME +line \- extract lines from input +.SH SYNOPSIS +.B line +.IR [LINES...] +.SH DESCRIPTION +.B line +extracts lines from stdin. If multiple lines are provided, they will be sorted +numerically. If the same line is specified twice, it will only be extracted +once. +.SH EXAMPLES +Get the 3rd line from the file line.1 +.PP +.nf +.RS +line 3 < line.1 +.RE +.fi +Get the 3rd, 4th and 5th lines from the file line.1 +.PP +.nf +.RS +line 3 5 4 < line.1 +.RE +.fi diff --git a/src/line.c b/src/lines.c similarity index 51% rename from src/line.c rename to src/lines.c index 5dc5246..65b288f 100644 --- a/src/line.c +++ b/src/lines.c @@ -26,40 +26,73 @@ const char *argv0; void usage() { - die("usage: %s ln", argv0); + die("usage: %s [LINE...]", argv0); +} + +int +getlineno(const char *s) +{ + int ln; + char *p; + ln = (int)strtol(s, &p, 10); + if (errno != 0 || *p != '\0' || ln <= 0) { + errno ? edie("strtol: ") : die("unable to parse line"); + } + return ln; +} + +/* Insert ln into lns, assuming it is lns_size long and 0 initialised */ +int * +insertln(int ln, int *lns, size_t lns_size) +{ + int *ip = lns; + int j; + + while (lns_size && *ip && *ip < ln) { + ADVANCE_PTR(ip, lns_size); + } + + while (lns_size) { + j = *ip; + *ip = ln; + ln = j; + ADVANCE_PTR(ip, lns_size); + } + + return lns; } -/* Print the ln'th line from fp to stdout */ void -line(FILE *fp, int ln) +printlines(FILE *fp, int *lns, size_t lns_size) { char c; int cln = 1; - while ((c = fgetc(fp)) != '\0') { - if (cln > ln) break; - if (cln == ln) putc(c, stdout); + while (lns_size && (c = fgetc(fp)) != '\0') { + if (cln > *lns) { ADVANCE_PTR(lns, lns_size); } + if (cln == *lns) putc(c, stdout); if (c == '\n') cln++; } } -/* Print a specified line from stdin */ +/* Print specified lines from stdin */ int main(int argc, char *argv[]) { + int *lns; SET_ARGV0(); if (argc < 1) usage(); - int ln; - char *p; - ln = (int)strtol(argv[0], &p, 10); - if (errno != 0 || *p != '\0') { - errno ? edie("strtol: ") : die("unable to parse ln"); + size_t lns_size = argc; + lns = ecalloc(lns_size, sizeof(int)); + + while (argc) { + lns = insertln(getlineno(argv[0]), lns, lns_size); + SHIFT_ARGS(); } - SHIFT_ARGS(); FILE *fp; fp = fdopen(STDIN_FILENO, "r"); - line(fp, ln); + printlines(fp, lns, lns_size); return 0; } diff --git a/src/util.h b/src/util.h index 1b185bc..ff61bc7 100644 --- a/src/util.h +++ b/src/util.h @@ -1,7 +1,8 @@ /* SEE COPYRIGHT NOTICE IN util.c */ -#define SHIFT_ARGS() argv++; argc--; -#define SET_ARGV0() argv0 = argv[0]; SHIFT_ARGS(); +#define ADVANCE_PTR(ptr, size) (ptr)++; (size)-- +#define SHIFT_ARGS() ADVANCE_PTR(argv, argc) +#define SET_ARGV0() argv0 = argv[0]; SHIFT_ARGS(); void die(const char *fmt, ...); void edie(const char *fmt, ...);