unexpand.c - sbase - suckless unix tools
 (HTM) git clone git://git.suckless.org/sbase
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       unexpand.c (3318B)
       ---
            1 /* See LICENSE file for copyright and license details. */
            2 #include <stdint.h>
            3 #include <stdlib.h>
            4 #include <string.h>
            5 
            6 #include "utf.h"
            7 #include "util.h"
            8 
            9 static int     aflag      = 0;
           10 static size_t *tablist    = NULL;
           11 static size_t  tablistlen = 8;
           12 
           13 static size_t
           14 parselist(const char *s)
           15 {
           16         size_t i;
           17         char  *p, *tmp;
           18 
           19         tmp = estrdup(s);
           20         for (i = 0; (p = strsep(&tmp, " ,")); i++) {
           21                 if (*p == '\0')
           22                         eprintf("empty field in tablist\n");
           23                 tablist = ereallocarray(tablist, i + 1, sizeof(*tablist));
           24                 tablist[i] = estrtonum(p, 1, MIN(LLONG_MAX, SIZE_MAX));
           25                 if (i > 0 && tablist[i - 1] >= tablist[i])
           26                         eprintf("tablist must be ascending\n");
           27         }
           28         tablist = ereallocarray(tablist, i + 1, sizeof(*tablist));
           29 
           30         return i;
           31 }
           32 
           33 static void
           34 unexpandspan(size_t last, size_t col)
           35 {
           36         size_t off, i, j;
           37         Rune r;
           38 
           39         if (tablistlen == 1) {
           40                 i = 0;
           41                 off = last % tablist[i];
           42 
           43                 if ((col - last) + off >= tablist[i] && last < col)
           44                         last -= off;
           45 
           46                 r = '\t';
           47                 for (; last + tablist[i] <= col; last += tablist[i])
           48                         efputrune(&r, stdout, "<stdout>");
           49                 r = ' ';
           50                 for (; last < col; last++)
           51                         efputrune(&r, stdout, "<stdout>");
           52         } else {
           53                 for (i = 0; i < tablistlen; i++)
           54                         if (col < tablist[i])
           55                                 break;
           56                 for (j = 0; j < tablistlen; j++)
           57                         if (last < tablist[j])
           58                                 break;
           59                 r = '\t';
           60                 for (; j < i; j++) {
           61                         efputrune(&r, stdout, "<stdout>");
           62                         last = tablist[j];
           63                 }
           64                 r = ' ';
           65                 for (; last < col; last++)
           66                         efputrune(&r, stdout, "<stdout>");
           67         }
           68 }
           69 
           70 static void
           71 unexpand(const char *file, FILE *fp)
           72 {
           73         Rune r;
           74         size_t last = 0, col = 0, i;
           75         int bol = 1;
           76 
           77         while (efgetrune(&r, fp, file)) {
           78                 switch (r) {
           79                 case ' ':
           80                         if (!bol && !aflag)
           81                                 last++;
           82                         col++;
           83                         break;
           84                 case '\t':
           85                         if (tablistlen == 1) {
           86                                 if (!bol && !aflag)
           87                                         last += tablist[0] - col % tablist[0];
           88                                 col += tablist[0] - col % tablist[0];
           89                         } else {
           90                                 for (i = 0; i < tablistlen; i++)
           91                                         if (col < tablist[i])
           92                                                 break;
           93                                 if (!bol && !aflag)
           94                                         last = tablist[i];
           95                                 col = tablist[i];
           96                         }
           97                         break;
           98                 case '\b':
           99                         if (bol || aflag)
          100                                 unexpandspan(last, col);
          101                         col -= (col > 0);
          102                         last = col;
          103                         bol = 0;
          104                         break;
          105                 case '\n':
          106                         if (bol || aflag)
          107                                 unexpandspan(last, col);
          108                         last = col = 0;
          109                         bol = 1;
          110                         break;
          111                 default:
          112                         if (bol || aflag)
          113                                 unexpandspan(last, col);
          114                         last = ++col;
          115                         bol = 0;
          116                         break;
          117                 }
          118                 if ((r != ' ' && r != '\t') || (!aflag && !bol))
          119                         efputrune(&r, stdout, "<stdout>");
          120         }
          121         if (last < col && (bol || aflag))
          122                 unexpandspan(last, col);
          123 }
          124 
          125 static void
          126 usage(void)
          127 {
          128         eprintf("usage: %s [-a] [-t tablist] [file ...]\n", argv0);
          129 }
          130 
          131 int
          132 main(int argc, char *argv[])
          133 {
          134         FILE *fp;
          135         int ret = 0;
          136         char *tl = "8";
          137 
          138         ARGBEGIN {
          139         case 't':
          140                 tl = EARGF(usage());
          141                 if (!*tl)
          142                         eprintf("tablist cannot be empty\n");
          143                 /* Fallthrough: -t implies -a */
          144         case 'a':
          145                 aflag = 1;
          146                 break;
          147         default:
          148                 usage();
          149         } ARGEND
          150 
          151         tablistlen = parselist(tl);
          152 
          153         if (!argc) {
          154                 unexpand("<stdin>", stdin);
          155         } else {
          156                 for (; *argv; argc--, argv++) {
          157                         if (!strcmp(*argv, "-")) {
          158                                 *argv = "<stdin>";
          159                                 fp = stdin;
          160                         } else if (!(fp = fopen(*argv, "r"))) {
          161                                 weprintf("fopen %s:", *argv);
          162                                 ret = 1;
          163                                 continue;
          164                         }
          165                         unexpand(*argv, fp);
          166                         if (fp != stdin && fshut(fp, *argv))
          167                                 ret = 1;
          168                 }
          169         }
          170 
          171         ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
          172 
          173         return ret;
          174 }