sbase

suckless unix tools
git clone git://git.2f30.org/sbase.git
Log | Files | Refs | README | LICENSE

commit 430b453c4d51c13a72b2a338c31c6bbeb55c5e06
parent 6427386b022c125377fccf21b2d03493eea613bf
Author: Connor Lane Smith <cls@lubutu.com>
Date:   Sun, 29 May 2011 21:30:44 +0100

add nl
Diffstat:
Makefile | 8++++----
config.mk | 10++++++++--
nl.1 | 44++++++++++++++++++++++++++++++++++++++++++++
nl.c | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 125 insertions(+), 6 deletions(-)

diff --git a/Makefile b/Makefile @@ -5,7 +5,7 @@ LIB = util/afgets.o util/agetcwd.o util/concat.o util/enmasse.o util/eprintf.o \ util/recurse.o SRC = basename.c cat.c chmod.c chown.c date.c dirname.c echo.c false.c grep.c \ - head.c ln.c ls.c mkdir.c mkfifo.c pwd.c rm.c sleep.c tail.c tee.c \ + head.c ln.c ls.c mkdir.c mkfifo.c nl.c pwd.c rm.c sleep.c tail.c tee.c \ touch.c true.c wc.c OBJ = $(SRC:.c=.o) $(LIB) BIN = $(SRC:.c=) @@ -13,13 +13,13 @@ MAN = $(SRC:.c=.1) all: $(BIN) -$(OBJ): util.h +$(OBJ): util.h config.mk $(BIN): util.a cat.o grep.o tail.o: text.h .o: - @echo CC -o $@ - @$(CC) -o $@ $< util.a $(LDFLAGS) + @echo LD -o $@ + @$(LD) -o $@ $< util.a $(LDFLAGS) .c.o: @echo CC -c $< diff --git a/config.mk b/config.mk @@ -1,9 +1,15 @@ # sbase version VERSION = 0.0 -#CC = cc +#CC = gcc #CC = musl-gcc - +LD = $(CC) CPPFLAGS = -D_POSIX_C_SOURCE=200112L CFLAGS = -Os -ansi -Wall -pedantic $(CPPFLAGS) LDFLAGS = -s -static + +#CC = tcc +#LD = $(CC) +#CPPFLAGS = -D_POSIX_C_SOURCE=200112L +#CFLAGS = -Os -Wall $(CPPFLAGS) +#LDFLAGS = diff --git a/nl.1 b/nl.1 @@ -0,0 +1,44 @@ +.TH NL 1 sbase\-VERSION +.SH NAME +nl \- number lines +.SH SYNOPSIS +.B nl +.RB [ \-b +.IR mode ] +.RB [ \-i +.IR increment ] +.RB [ \-s +.IR separator ] +.RI [ file ...] +.SH DESCRIPTION +.B nl +reads each file in sequence and writes it to stdout with non-empty lines +numbered. If no file is given, nl reads from stdin. +.SH OPTIONS +.TP +.BI \-b " mode" +defines which lines will be numbered: +.RS +.TP +.B a +all lines. +.TP +.B n +no lines. +.TP +.BI p pattern +only lines which match +.IR pattern , +a regular expression as defined in +.IR regex (7). +.TP +.B t +only non-empty lines (default). +.RE +.TP +.BI \-i " increment" +defines the increment between numbered lines. +.TP +.BI \-s " separator" +defines the string used to separate line numbers and lines. By default this is +a tab. diff --git a/nl.c b/nl.c @@ -0,0 +1,69 @@ +/* See LICENSE file for copyright and license details. */ +#include <regex.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "text.h" +#include "util.h" + +static void nl(FILE *, const char *); + +static char mode = 't'; +static const char *sep = "\t"; +static long incr = 1; +static regex_t preg; + +int +main(int argc, char *argv[]) +{ + char c, *end; + FILE *fp; + + while((c = getopt(argc, argv, "b:i:s:")) != -1) + switch(c) { + case 'b': + mode = optarg[0]; + if(optarg[0] == 'p') + regcomp(&preg, &optarg[1], REG_NOSUB); + else if(!strchr("ant", optarg[0]) || optarg[1] != '\0') + eprintf("usage: %s [-b mode] [file...]\n", argv[0]); + break; + case 'i': + incr = strtol(optarg, &end, 0); + if(*end != '\0') + eprintf("%s: not a number\n", optarg); + break; + case 's': + sep = optarg; + break; + default: + exit(EXIT_FAILURE); + } + if(optind == argc) + nl(stdin, "<stdin>"); + else for(; optind < argc; optind++) { + if(!(fp = fopen(argv[optind], "r"))) + eprintf("fopen %s:", argv[optind]); + nl(fp, argv[optind]); + fclose(fp); + } + return EXIT_SUCCESS; +} + +void +nl(FILE *fp, const char *str) +{ + char *buf = NULL; + long n = 0; + size_t size = 0; + + while(afgets(&buf, &size, fp)) + if((mode == 'a') + || (mode == 'p' && !regexec(&preg, buf, 0, NULL, 0)) + || (mode == 't' && buf[0] != '\n')) + printf("%6ld%s%s", n += incr, sep, buf); + else + printf(" %s", buf); + free(buf); +}