1/* nl.c - print line numbers 2 * 3 * Copyright 2013 CE Strake <strake888@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 10USE_NL(NEWTOY(nl, "v#<1=1l#b:n:s:w#<0=6E", TOYFLAG_BIN)) 11 12config 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 31GLOBALS( 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 43void 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("%s", 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 73void 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