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 
7 USE_EXPAND(NEWTOY(expand, "t*", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
8 
9 config 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 beyound
22     that becoming one space.
23 */
24 
25 #define FOR_expand
26 #include "toys.h"
27 
28 GLOBALS(
29   struct arg_list *tabs;
30 
31   unsigned tabcount, *tab;
32 )
33 
do_expand(int fd,char * name)34 static 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         wchar_t blah;
51 
52         width = mbrtowc(&blah, toybuf+i, len-i, 0);
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
parse_tablist(unsigned * tablist)91 static int parse_tablist(unsigned *tablist)
92 {
93   struct arg_list *tabs;
94   int tabcount = 0;
95 
96   for (tabs = TT.tabs; 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 
expand_main(void)116 void 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