xref: /third_party/toybox/toys/posix/expand.c (revision 0f66f451)
1/* expand.c - expands tabs to space
2 *
3 * Copyright 2012 Jonathan Clairembault <jonathan at clairembault dot fr>
4 *
5 * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/expand.html
6
7USE_EXPAND(NEWTOY(expand, "t*", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
8
9config EXPAND
10  bool "expand"
11  default y
12  help
13    usage: expand [-t TABLIST] [FILE...]
14
15    Expand tabs to spaces according to tabstops.
16
17    -t	TABLIST
18
19    Specify tab stops, either a single number instead of the default 8,
20    or a comma separated list of increasing numbers representing tabstop
21    positions (absolute, not increments) with each additional tab beyond
22    that becoming one space.
23*/
24
25#define FOR_expand
26#include "toys.h"
27
28GLOBALS(
29  struct arg_list *t;
30
31  unsigned tabcount, *tab;
32)
33
34static void do_expand(int fd, char *name)
35{
36  int i, len, x=0, stop = 0;
37
38  for (;;) {
39    len = readall(fd, toybuf, sizeof(toybuf));
40    if (len<0) {
41      perror_msg_raw(name);
42      return;
43    }
44    if (!len) break;
45    for (i=0; i<len; i++) {
46      int width = 1;
47      char c;
48
49      if (CFG_TOYBOX_I18N) {
50        unsigned blah;
51
52        width = utf8towc(&blah, toybuf+i, len-i);
53        if (width > 1) {
54          if (width != fwrite(toybuf+i, width, 1, stdout))
55            perror_exit("stdout");
56          i += width-1;
57          x++;
58          continue;
59        } else if (width == -2) break;
60        else if (width == -1) continue;
61      }
62      c = toybuf[i];
63
64      if (c != '\t') {
65        if (EOF == putc(c, stdout)) perror_exit(0);
66
67        if (c == '\b' && x) width = -1;
68        if (c == '\n') {
69          x = stop = 0;
70          continue;
71        }
72      } else {
73        if (TT.tabcount < 2) {
74          width = TT.tabcount ? *TT.tab : 8;
75          width -= x%width;
76        } else while (stop < TT.tabcount) {
77          if (TT.tab[stop] > x) {
78            width = TT.tab[stop] - x;
79            break;
80          } else stop++;
81        }
82        xprintf("%*c", width, ' ');
83      }
84      x += width;
85    }
86  }
87}
88
89// Parse -t options to fill out unsigned array in tablist (if not NULL)
90// return number of entries in tablist
91static int parse_tablist(unsigned *tablist)
92{
93  struct arg_list *tabs;
94  int tabcount = 0;
95
96  for (tabs = TT.t; tabs; tabs = tabs->next) {
97    char *s = tabs->arg;
98
99    while (*s) {
100      int count;
101      unsigned x, *t = tablist ? tablist+tabcount : &x;
102
103      if (tabcount >= sizeof(toybuf)/sizeof(unsigned)) break;
104      if (sscanf(s, "%u%n", t, &count) != 1) break;
105      if (tabcount++ && tablist && *(t-1) >= *t) break;
106      s += count;
107      if (*s==' ' || *s==',') s++;
108      else break;
109    }
110    if (*s) error_exit("bad tablist");
111  }
112
113  return tabcount;
114}
115
116void expand_main(void)
117{
118  TT.tabcount = parse_tablist(NULL);
119
120  // Determine size of tablist, allocate memory, fill out tablist
121  if (TT.tabcount) {
122    TT.tab = xmalloc(sizeof(unsigned)*TT.tabcount);
123    parse_tablist(TT.tab);
124  }
125
126  loopfiles(toys.optargs, do_expand);
127  if (CFG_TOYBOX_FREE) free(TT.tab);
128}
129