summaryrefslogtreecommitdiff
path: root/toys/posix/nl.c
blob: b462cdddd36a7fa3935e6ec12ade7da1ca072904 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/* nl.c - print line numbers
 *
 * Copyright 2013 CE Strake <strake888@gmail.com>
 *
 * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/nl.html
 *
 * This implements a subset: only one logical page (-ip), no sections (-dfh).
 * todo: -lv

USE_NL(NEWTOY(nl, "v#<1=1l#b:n:s:w#<0=6E", TOYFLAG_BIN))

config NL
  bool "nl"
  default y
  help
    usage: nl [-E] [-l #] [-b MODE] [-n STYLE] [-s SEPARATOR] [-w WIDTH] [FILE...]

    Number lines of input.

    -E	Use extended regex syntax (when doing -b pREGEX)
    -b	which lines to number: a (all) t (non-empty, default) pREGEX (pattern)
    -l	Only count last of this many consecutive blank lines
    -n	number STYLE: ln (left justified) rn (right justified) rz (zero pad)
    -s	Separator to use between number and line (instead of TAB)
    -w	Width of line numbers (default 6)
*/

#define FOR_nl
#include "toys.h"
#include "lib/xregcomp.h"

GLOBALS(
  long w;
  char *s;
  char *n;
  char *b;
  long l;
  long v;

  // Count of consecutive blank lines for -l has to persist between files
  long lcount;
)

void do_nl(int fd, char *name)
{
  FILE *f = xfdopen(fd, "r");
  int w = TT.w, slen = strlen(TT.s);

  for (;;) {
    char *line = 0;
    size_t temp;
    int match = *TT.b != 'n';

    if (getline(&line, &temp, f) < 1) {
      if (ferror(f)) perror_msg("%s", name);
      break;
    }

    if (*TT.b == 'p') match = !regexec((void *)(toybuf+16), line, 0, 0, 0);
    if (TT.l || *TT.b == 't')
      if (*line == '\n') match = TT.l && ++TT.lcount >= TT.l;
    if (match) {
      TT.lcount = 0;
      printf(toybuf, w, TT.v++, TT.s);
    } else printf("%*c", (int)w+slen, ' ');
    xprintf("%s", line);

    free(line);
  }

  fclose(f);
}

void nl_main(void)
{
  char *clip = "";

  if (!TT.s) TT.s = "\t";

  if (!TT.n || !strcmp(TT.n, "rn")); // default
  else if (!strcmp(TT.n, "ln")) clip = "-";
  else if (!strcmp(TT.n, "rz")) clip = "0";
  else error_exit("bad -n '%s'", TT.n);

  sprintf(toybuf, "%%%s%s", clip, "*ld%s");

  if (!TT.b) TT.b = "t";
  if (*TT.b == 'p' && TT.b[1])
    xregcomp((void *)(toybuf+16), TT.b+1,
      REG_NOSUB | (toys.optflags&FLAG_E)*REG_EXTENDED);
  else if (!strchr("atn", *TT.b)) error_exit("bad -b '%s'", TT.b);

  loopfiles (toys.optargs, do_nl);
}