Home | History | Annotate | Download | only in posix
      1 /* nl.c - print line numbers
      2  *
      3  * Copyright 2013 CE Strake <strake888 (at) gmail.com>
      4  *
      5  * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/nl.html
      6  *
      7  * This implements a subset: only one logical page (-ip), no sections (-dfh).
      8  * todo: -lv
      9 
     10 USE_NL(NEWTOY(nl, "v#<1=1l#b:n:s:w#<0=6E", TOYFLAG_BIN))
     11 
     12 config NL
     13   bool "nl"
     14   default y
     15   help
     16     usage: nl [-E] [-l #] [-b MODE] [-n STYLE] [-s SEPARATOR] [-w WIDTH] [FILE...]
     17 
     18     Number lines of input.
     19 
     20     -E	Use extended regex syntax (when doing -b pREGEX)
     21     -b	which lines to number: a (all) t (non-empty, default) pREGEX (pattern)
     22     -l	Only count last of this many consecutive blank lines
     23     -n	number STYLE: ln (left justified) rn (right justified) rz (zero pad)
     24     -s	Separator to use between number and line (instead of TAB)
     25     -w	Width of line numbers (default 6)
     26 */
     27 
     28 #define FOR_nl
     29 #include "toys.h"
     30 
     31 GLOBALS(
     32   long w;
     33   char *s;
     34   char *n;
     35   char *b;
     36   long l;
     37   long v;
     38 
     39   // Count of consecutive blank lines for -l has to persist between files
     40   long lcount;
     41 )
     42 
     43 static void do_nl(int fd, char *name)
     44 {
     45   FILE *f = xfdopen(fd, "r");
     46   int w = TT.w, slen = strlen(TT.s);
     47 
     48   for (;;) {
     49     char *line = 0;
     50     size_t temp;
     51     int match = *TT.b != 'n';
     52 
     53     if (getline(&line, &temp, f) < 1) {
     54       if (ferror(f)) perror_msg_raw(name);
     55       break;
     56     }
     57 
     58     if (*TT.b == 'p') match = !regexec((void *)(toybuf+16), line, 0, 0, 0);
     59     if (TT.l || *TT.b == 't')
     60       if (*line == '\n') match = TT.l && ++TT.lcount >= TT.l;
     61     if (match) {
     62       TT.lcount = 0;
     63       printf(toybuf, w, TT.v++, TT.s);
     64     } else printf("%*c", (int)w+slen, ' ');
     65     xprintf("%s", line);
     66 
     67     free(line);
     68   }
     69 
     70   fclose(f);
     71 }
     72 
     73 void nl_main(void)
     74 {
     75   char *clip = "";
     76 
     77   if (!TT.s) TT.s = "\t";
     78 
     79   if (!TT.n || !strcmp(TT.n, "rn")); // default
     80   else if (!strcmp(TT.n, "ln")) clip = "-";
     81   else if (!strcmp(TT.n, "rz")) clip = "0";
     82   else error_exit("bad -n '%s'", TT.n);
     83 
     84   sprintf(toybuf, "%%%s%s", clip, "*ld%s");
     85 
     86   if (!TT.b) TT.b = "t";
     87   if (*TT.b == 'p' && TT.b[1])
     88     xregcomp((void *)(toybuf+16), TT.b+1,
     89       REG_NOSUB | (toys.optflags&FLAG_E)*REG_EXTENDED);
     90   else if (!strchr("atn", *TT.b)) error_exit("bad -b '%s'", TT.b);
     91 
     92   loopfiles (toys.optargs, do_nl);
     93 }
     94