nl.c - sbase - suckless unix tools
 (HTM) git clone git://git.suckless.org/sbase
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       nl.c (4369B)
       ---
            1 /* See LICENSE file for copyright and license details. */
            2 #include <limits.h>
            3 #include <stdint.h>
            4 #include <stdio.h>
            5 #include <stdlib.h>
            6 #include <string.h>
            7 
            8 #include "text.h"
            9 #include "utf.h"
           10 #include "util.h"
           11 
           12 static size_t   startnum = 1;
           13 static size_t   incr = 1;
           14 static size_t   blines = 1;
           15 static size_t   delimlen = 2;
           16 static size_t   seplen = 1;
           17 static int      width = 6;
           18 static int      pflag = 0;
           19 static char     type[] = { 'n', 't', 'n' }; /* footer, body, header */
           20 static char    *delim = "\\:";
           21 static char     format[6] = "%*ld";
           22 static char    *sep = "\t";
           23 static regex_t  preg[3];
           24 
           25 static int
           26 getsection(struct line *l, int *section)
           27 {
           28         size_t i;
           29         int sectionchanged = 0, newsection = *section;
           30 
           31         for (i = 0; (l->len - i) >= delimlen &&
           32              !memcmp(l->data + i, delim, delimlen); i += delimlen) {
           33                 if (!sectionchanged) {
           34                         sectionchanged = 1;
           35                         newsection = 0;
           36                 } else {
           37                         newsection = (newsection + 1) % 3;
           38                 }
           39         }
           40 
           41         if (!(l->len - i) || l->data[i] == '\n')
           42                 *section = newsection;
           43         else
           44                 sectionchanged = 0;
           45 
           46         return sectionchanged;
           47 }
           48 
           49 static void
           50 nl(const char *fname, FILE *fp)
           51 {
           52         static struct line line;
           53         static size_t size;
           54         size_t number = startnum, bl = 1;
           55         ssize_t len;
           56         int donumber, oldsection, section = 1;
           57 
           58         while ((len = getline(&line.data, &size, fp)) > 0) {
           59                 line.len = len;
           60                 donumber = 0;
           61                 oldsection = section;
           62 
           63                 if (getsection(&line, &section)) {
           64                         if ((section >= oldsection) && !pflag)
           65                                 number = startnum;
           66                         continue;
           67                 }
           68 
           69                 switch (type[section]) {
           70                 case 't':
           71                         if (line.data[0] != '\n')
           72                                 donumber = 1;
           73                         break;
           74                 case 'p':
           75                         if (!regexec(preg + section, line.data, 0, NULL, 0))
           76                                 donumber = 1;
           77                         break;
           78                 case 'a':
           79                         if (line.data[0] == '\n' && bl < blines) {
           80                                 ++bl;
           81                         } else {
           82                                 donumber = 1;
           83                                 bl = 1;
           84                         }
           85                 }
           86 
           87                 if (donumber) {
           88                         printf(format, width, number);
           89                         fwrite(sep, 1, seplen, stdout);
           90                         number += incr;
           91                 }
           92                 fwrite(line.data, 1, line.len, stdout);
           93         }
           94         free(line.data);
           95         if (ferror(fp))
           96                 eprintf("getline %s:", fname);
           97 }
           98 
           99 static void
          100 usage(void)
          101 {
          102         eprintf("usage: %s [-p] [-b type] [-d delim] [-f type]\n"
          103                 "       [-h type] [-i num] [-l num] [-n format]\n"
          104                 "       [-s sep] [-v num] [-w num] [file]\n", argv0);
          105 }
          106 
          107 static char
          108 getlinetype(char *type, regex_t *preg)
          109 {
          110         if (type[0] == 'p')
          111                 eregcomp(preg, type + 1, REG_NOSUB);
          112         else if (!type[0] || !strchr("ant", type[0]))
          113                 usage();
          114 
          115         return type[0];
          116 }
          117 
          118 int
          119 main(int argc, char *argv[])
          120 {
          121         FILE *fp = NULL;
          122         size_t s;
          123         int ret = 0;
          124         char *d, *formattype, *formatblit;
          125 
          126         ARGBEGIN {
          127         case 'd':
          128                 switch (utflen((d = EARGF(usage())))) {
          129                 case 0:
          130                         eprintf("empty logical page delimiter\n");
          131                 case 1:
          132                         s = strlen(d);
          133                         delim = emalloc(s + 1 + 1);
          134                         estrlcpy(delim, d, s + 1 + 1);
          135                         estrlcat(delim, ":", s + 1 + 1);
          136                         delimlen = s + 1;
          137                         break;
          138                 default:
          139                         delim = d;
          140                         delimlen = strlen(delim);
          141                         break;
          142                 }
          143                 break;
          144         case 'f':
          145                 type[0] = getlinetype(EARGF(usage()), preg);
          146                 break;
          147         case 'b':
          148                 type[1] = getlinetype(EARGF(usage()), preg + 1);
          149                 break;
          150         case 'h':
          151                 type[2] = getlinetype(EARGF(usage()), preg + 2);
          152                 break;
          153         case 'i':
          154                 incr = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX));
          155                 break;
          156         case 'l':
          157                 blines = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX));
          158                 break;
          159         case 'n':
          160                 formattype = EARGF(usage());
          161                 estrlcpy(format, "%", sizeof(format));
          162 
          163                 if (!strcmp(formattype, "ln")) {
          164                         formatblit = "-";
          165                 } else if (!strcmp(formattype, "rn")) {
          166                         formatblit = "";
          167                 } else if (!strcmp(formattype, "rz")) {
          168                         formatblit = "0";
          169                 } else {
          170                         eprintf("%s: bad format\n", formattype);
          171                 }
          172 
          173                 estrlcat(format, formatblit, sizeof(format));
          174                 estrlcat(format, "*ld", sizeof(format));
          175                 break;
          176         case 'p':
          177                 pflag = 1;
          178                 break;
          179         case 's':
          180                 sep = EARGF(usage());
          181                 seplen = unescape(sep);
          182                 break;
          183         case 'v':
          184                 startnum = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX));
          185                 break;
          186         case 'w':
          187                 width = estrtonum(EARGF(usage()), 1, INT_MAX);
          188                 break;
          189         default:
          190                 usage();
          191         } ARGEND
          192 
          193         if (argc > 1)
          194                 usage();
          195 
          196         if (!argc) {
          197                 nl("<stdin>", stdin);
          198         } else {
          199                 if (!strcmp(argv[0], "-")) {
          200                         argv[0] = "<stdin>";
          201                         fp = stdin;
          202                 } else if (!(fp = fopen(argv[0], "r"))) {
          203                         eprintf("fopen %s:", argv[0]);
          204                 }
          205                 nl(argv[0], fp);
          206         }
          207 
          208         ret |= fp && fp != stdin && fshut(fp, argv[0]);
          209         ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
          210 
          211         return ret;
          212 }