commit 787d99d896e06b2383da763df695b9e9496bdff5
Author: Quentin Rameau <[email protected]>
Date:   Wed Mar 18 17:58:09 2015 +0100

    nl: add -d -p -f -h options

diff --git a/LICENSE b/LICENSE
index 3633fda..f4c6cc8 100644
--- a/LICENSE
+++ b/LICENSE
@@ -56,3 +56,4 @@ Authors/contributors include:
 © 2014 Adria Garriga <[email protected]>
 © 2014 Greg Reagle <[email protected]>
 © 2015 Tai Chi Minh Ralph Eastwood <[email protected]>
+© 2015 Quentin Rameau <[email protected]>
diff --git a/README b/README
index 4c832ff..0d9c3a2 100644
--- a/README
+++ b/README
@@ -49,7 +49,7 @@ The following tools are implemented ('*' == finished, '#' == 
UTF-8 support,
 =*| mktemp          non-posix                    none
 =*| mv              yes                          none (-i)
 =*| nice            yes                          none
-=   nl              no                           -d, -f, -h, -p
+=*  nl              yes                          none
 =*| nohup           yes                          none
 #*| paste           yes                          none
 =*| printenv        non-posix                    none
diff --git a/nl.1 b/nl.1
index 3f9cd81..0817421 100644
--- a/nl.1
+++ b/nl.1
@@ -1,4 +1,4 @@
-.Dd February 20, 2015
+.Dd March 18, 2015
 .Dt NL 1
 .Os sbase
 .Sh NAME
@@ -6,7 +6,11 @@
 .Nd line numbering filter
 .Sh SYNOPSIS
 .Nm
+.Op Fl p
 .Op Fl b Ar type
+.Op Fl d Ar delim
+.Op Fl f Ar type
+.Op Fl h Ar type
 .Op Fl i Ar incr
 .Op Fl l Ar num
 .Op Fl n Ar format
@@ -25,8 +29,10 @@ is given
 reads from stdin.
 .Sh OPTIONS
 .Bl -tag -width Ds
+.It Fl p
+Do not reset number for logical pages
 .It Fl b Ar type
-Defines which lines will be numbered:
+Defines which lines will be numbered for body sections:
 .Bl -tag -width pstringXX
 .It a
 All lines.
@@ -40,6 +46,16 @@ Only lines which match
 a regular expression as defined in
 .Xr regex 7 .
 .El
+.It Fl d Ar delim
+Specify the two characters delimiter (default is "\\:"). If only one character 
is specified, the second remains ':'.
+.It Fl f Ar type
+Same as
+.Fl b
+except for footer sections.
+.It Fl h Ar type
+Same as
+.Fl b
+except for header sections.
 .It Fl i Ar incr
 Defines the increment between numbered lines.
 .It Fl l Ar num
@@ -76,3 +92,9 @@ The default is 6.
 .El
 .Sh SEE ALSO
 .Xr pr 1
+.Sh STANDARDS
+The
+.Nm
+utility is compliant with the
+.St -p1003.1-2008
+specification.
diff --git a/nl.c b/nl.c
index d756f60..46b3c0b 100644
--- a/nl.c
+++ b/nl.c
@@ -13,29 +13,64 @@
 #define FORMAT_RN "%*ld%s"
 #define FORMAT_RZ "%0*ld%s"
 
-static char        mode = 't';
-static int         blines = 1;
+static char        type[] = { 'n', 't', 'n' }; /* footer, body, header */
+static char        delim[] = { '\\', ':' };
 static const char *format = FORMAT_RN;
 static const char *sep = "\t";
 static int         width = 6;
+static int         pflag = 0;
 static size_t      startnum = 1;
 static size_t      incr = 1;
-static regex_t     preg;
+static size_t      blines = 1;
+static regex_t     preg[3];
+
+static int
+getsection(char *buf, int *section)
+{
+       int sectionchanged = 0;
+       int newsection = *section;
+
+       while (!strncmp(buf, delim, 2)) {
+               if (!sectionchanged) {
+                       sectionchanged = 1;
+                       newsection = 0;
+               } else {
+                       ++newsection;
+                       newsection %= 3;
+               }
+               buf += 2;
+       }
+
+       if (buf && buf[0] == '\n')
+               *section = newsection;
+       else
+               sectionchanged = 0;
+
+       return sectionchanged;
+}
 
 static void
 nl(const char *name, FILE *fp)
 {
        char *buf = NULL;
-       int donumber, bl = 1;
-       size_t size = 0;
+       int donumber, oldsection, section = 1, bl = 1;
+       size_t number = startnum, size = 0;
 
        while (getline(&buf, &size, fp) != -1) {
                donumber = 0;
+               oldsection = section;
+
+               if (getsection(buf, &section)) {
+                       if ((section >= oldsection) && !pflag)
+                               number = startnum;
+                       continue;
+               }
 
-               if ((mode == 't' && buf[0] != '\n')
-                   || (mode == 'p' && !regexec(&preg, buf, 0, NULL, 0))) {
+               if ((type[section] == 't' && buf[0] != '\n')
+                   || (type[section] == 'p' &&
+                       !regexec(&preg[section], buf, 0, NULL, 0))) {
                        donumber = 1;
-               } else if (mode == 'a') {
+               } else if (type[section] == 'a') {
                        if (buf[0] == '\n' && bl < blines) {
                                ++bl;
                        } else {
@@ -45,8 +80,8 @@ nl(const char *name, FILE *fp)
                }
 
                if (donumber) {
-                       printf(format, width, startnum, sep);
-                       startnum += incr;
+                       printf(format, width, number, sep);
+                       number += incr;
                } else {
                        printf("%*s", width, "");
                }
@@ -60,23 +95,46 @@ nl(const char *name, FILE *fp)
 static void
 usage(void)
 {
-       eprintf("usage: %s [-b type] [-i incr] [-l num] [-n format] [-s sep] 
[-v startnum] [-w width] [file]\n", argv0);
+       eprintf("usage: %s [-p] [-b type] [-d delim] [-f type] "
+           "[-h type] [-i incr] [-l num]\n[-n format] [-s sep] "
+           "[-v startnum] [-w width] [file]\n", argv0);
+}
+static char
+getlinetype(char *type, regex_t *preg)
+{
+       if (type[0] == 'p')
+               eregcomp(preg, &type[1], REG_NOSUB);
+       else if (!strchr("ant", type[0]))
+               usage();
+
+       return type[0];
 }
 
 int
 main(int argc, char *argv[])
 {
        FILE *fp;
-       char *r;
+       char *d;
 
        ARGBEGIN {
        case 'b':
-               r = EARGF(usage());
-               mode = r[0];
-               if (r[0] == 'p')
-                       eregcomp(&preg, &r[1], REG_NOSUB);
-               else if (!strchr("ant", mode))
+               type[1] = getlinetype(EARGF(usage()), &preg[1]);
+               break;
+       case 'd':
+               d = EARGF(usage());
+               if (strlen(d) > 2) {
                        usage();
+               } else if (d[0] != '\0') {
+                       delim[0] = d[0];
+                       if (d[1])
+                               delim[1] = d[1];
+               }
+               break;
+       case 'f':
+               type[0] = getlinetype(EARGF(usage()), &preg[0]);
+               break;
+       case 'h':
+               type[2] = getlinetype(EARGF(usage()), &preg[2]);
                break;
        case 'i':
                incr = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX));
@@ -95,6 +153,9 @@ main(int argc, char *argv[])
                else
                        eprintf("%s: bad format\n", format);
                break;
+       case 'p':
+               pflag = 1;
+               break;
        case 's':
                sep = EARGF(usage());
                break;

Reply via email to