aboutsummaryrefslogtreecommitdiff
path: root/toys/posix/expand.c
blob: e15d30d38376af1784283e342b1e3a0681d5c060 (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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/* expand.c - expands tabs to space
 *
 * Copyright 2012 Jonathan Clairembault <jonathan at clairembault dot fr>
 *
 * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/expand.html

USE_EXPAND(NEWTOY(expand, "t*", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))

config EXPAND
  bool "expand"
  default y
  help
    usage: expand [-t TABLIST] [FILE...]

    Expand tabs to spaces according to tabstops.

    -t	TABLIST

    Specify tab stops, either a single number instead of the default 8,
    or a comma separated list of increasing numbers representing tabstop
    positions (absolute, not increments) with each additional tab beyond
    that becoming one space.
*/

#define FOR_expand
#include "toys.h"

GLOBALS(
  struct arg_list *t;

  unsigned tabcount, *tab;
)

static void do_expand(int fd, char *name)
{
  int i, len, x=0, stop = 0;

  for (;;) {
    len = readall(fd, toybuf, sizeof(toybuf));
    if (len<0) {
      perror_msg_raw(name);
      return;
    }
    if (!len) break;
    for (i=0; i<len; i++) {
      unsigned blah;
      int width = utf8towc(&blah, toybuf+i, len-i);
      char c;

      if (width > 1) {
        if (width != fwrite(toybuf+i, width, 1, stdout))
          perror_exit("stdout");
        i += width-1;
        x++;
        continue;
      } else if (width == -2) break;
      else if (width == -1) continue;
      c = toybuf[i];

      if (c != '\t') {
        if (EOF == putc(c, stdout)) perror_exit(0);

        if (c == '\b' && x) width = -1;
        if (c == '\n') {
          x = stop = 0;
          continue;
        }
      } else {
        if (TT.tabcount < 2) {
          width = TT.tabcount ? *TT.tab : 8;
          width -= x%width;
        } else while (stop < TT.tabcount) {
          if (TT.tab[stop] > x) {
            width = TT.tab[stop] - x;
            break;
          } else stop++;
        }
        xprintf("%*c", width, ' ');
      }
      x += width;
    }
  }
}

// Parse -t options to fill out unsigned array in tablist (if not NULL)
// return number of entries in tablist
static int parse_tablist(unsigned *tablist)
{
  struct arg_list *tabs;
  int tabcount = 0;

  for (tabs = TT.t; tabs; tabs = tabs->next) {
    char *s = tabs->arg;

    while (*s) {
      int count;
      unsigned x, *t = tablist ? tablist+tabcount : &x;

      if (tabcount >= sizeof(toybuf)/sizeof(unsigned)) break;
      if (sscanf(s, "%u%n", t, &count) != 1) break;
      if (tabcount++ && tablist && *(t-1) >= *t) break;
      s += count;
      if (*s==' ' || *s==',') s++;
      else break;
    }
    if (*s) error_exit("bad tablist");
  }

  return tabcount;
}

void expand_main(void)
{
  TT.tabcount = parse_tablist(NULL);

  // Determine size of tablist, allocate memory, fill out tablist
  if (TT.tabcount) {
    TT.tab = xmalloc(sizeof(unsigned)*TT.tabcount);
    parse_tablist(TT.tab);
  }

  loopfiles(toys.optargs, do_expand);
  if (CFG_TOYBOX_FREE) free(TT.tab);
}