users: new applet.
[platform/upstream/busybox.git] / coreutils / expand.c
1 /* expand - convert tabs to spaces
2  * unexpand - convert spaces to tabs
3  *
4  * Copyright (C) 89, 91, 1995-2006 Free Software Foundation, Inc.
5  *
6  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
7  *
8  * David MacKenzie <djm@gnu.ai.mit.edu>
9  *
10  * Options for expand:
11  * -t num  --tabs=NUM      Convert tabs to num spaces (default 8 spaces).
12  * -i      --initial       Only convert initial tabs on each line to spaces.
13  *
14  * Options for unexpand:
15  * -a      --all           Convert all blanks, instead of just initial blanks.
16  * -f      --first-only    Convert only leading sequences of blanks (default).
17  * -t num  --tabs=NUM      Have tabs num characters apart instead of 8.
18  *
19  *  Busybox version (C) 2007 by Tito Ragusa <farmatito@tiscali.it>
20  *
21  *  Caveat: this versions of expand and unexpand don't accept tab lists.
22  */
23
24 //usage:#define expand_trivial_usage
25 //usage:       "[-i] [-t N] [FILE]..."
26 //usage:#define expand_full_usage "\n\n"
27 //usage:       "Convert tabs to spaces, writing to stdout\n"
28 //usage:        IF_FEATURE_EXPAND_LONG_OPTIONS(
29 //usage:     "\n        -i,--initial    Don't convert tabs after non blanks"
30 //usage:     "\n        -t,--tabs=N     Tabstops every N chars"
31 //usage:        )
32 //usage:        IF_NOT_FEATURE_EXPAND_LONG_OPTIONS(
33 //usage:     "\n        -i      Don't convert tabs after non blanks"
34 //usage:     "\n        -t      Tabstops every N chars"
35 //usage:        )
36
37 //usage:#define unexpand_trivial_usage
38 //usage:       "[-fa][-t N] [FILE]..."
39 //usage:#define unexpand_full_usage "\n\n"
40 //usage:       "Convert spaces to tabs, writing to stdout\n"
41 //usage:        IF_FEATURE_UNEXPAND_LONG_OPTIONS(
42 //usage:     "\n        -a,--all        Convert all blanks"
43 //usage:     "\n        -f,--first-only Convert only leading blanks"
44 //usage:     "\n        -t,--tabs=N     Tabstops every N chars"
45 //usage:        )
46 //usage:        IF_NOT_FEATURE_UNEXPAND_LONG_OPTIONS(
47 //usage:     "\n        -a      Convert all blanks"
48 //usage:     "\n        -f      Convert only leading blanks"
49 //usage:     "\n        -t N    Tabstops every N chars"
50 //usage:        )
51
52 #include "libbb.h"
53 #include "unicode.h"
54
55 enum {
56         OPT_INITIAL     = 1 << 0,
57         OPT_TABS        = 1 << 1,
58         OPT_ALL         = 1 << 2,
59 };
60
61 #if ENABLE_EXPAND
62 static void expand(FILE *file, unsigned tab_size, unsigned opt)
63 {
64         char *line;
65
66         while ((line = xmalloc_fgets(file)) != NULL) {
67                 unsigned char c;
68                 char *ptr;
69                 char *ptr_strbeg;
70
71                 ptr = ptr_strbeg = line;
72                 while ((c = *ptr) != '\0') {
73                         if ((opt & OPT_INITIAL) && !isblank(c)) {
74                                 /* not space or tab */
75                                 break;
76                         }
77                         if (c == '\t') {
78                                 unsigned len;
79                                 *ptr = '\0';
80 # if ENABLE_UNICODE_SUPPORT
81                                 {
82                                         uni_stat_t uni_stat;
83                                         printable_string(&uni_stat, ptr_strbeg);
84                                         len = uni_stat.unicode_width;
85                                 }
86 # else
87                                 len = ptr - ptr_strbeg;
88 # endif
89                                 len = tab_size - (len % tab_size);
90                                 /*while (ptr[1] == '\t') { ptr++; len += tab_size; } - can handle many tabs at once */
91                                 printf("%s%*s", ptr_strbeg, len, "");
92                                 ptr_strbeg = ptr + 1;
93                         }
94                         ptr++;
95                 }
96                 fputs(ptr_strbeg, stdout);
97                 free(line);
98         }
99 }
100 #endif
101
102 #if ENABLE_UNEXPAND
103 static void unexpand(FILE *file, unsigned tab_size, unsigned opt)
104 {
105         char *line;
106
107         while ((line = xmalloc_fgets(file)) != NULL) {
108                 char *ptr = line;
109                 unsigned column = 0;
110
111                 while (*ptr) {
112                         unsigned n;
113                         unsigned len = 0;
114
115                         while (*ptr == ' ') {
116                                 ptr++;
117                                 len++;
118                         }
119                         column += len;
120                         if (*ptr == '\t') {
121                                 column += tab_size - (column % tab_size);
122                                 ptr++;
123                                 continue;
124                         }
125
126                         n = column / tab_size;
127                         if (n) {
128                                 len = column = column % tab_size;
129                                 while (n--)
130                                         putchar('\t');
131                         }
132
133                         if ((opt & OPT_INITIAL) && ptr != line) {
134                                 printf("%*s%s", len, "", ptr);
135                                 break;
136                         }
137                         n = strcspn(ptr, "\t ");
138                         printf("%*s%.*s", len, "", n, ptr);
139 # if ENABLE_UNICODE_SUPPORT
140                         {
141                                 char c;
142                                 uni_stat_t uni_stat;
143                                 c = ptr[n];
144                                 ptr[n] = '\0';
145                                 printable_string(&uni_stat, ptr);
146                                 len = uni_stat.unicode_width;
147                                 ptr[n] = c;
148                         }
149 # else
150                         len = n;
151 # endif
152                         ptr += n;
153                         column = (column + len) % tab_size;
154                 }
155                 free(line);
156         }
157 }
158 #endif
159
160 int expand_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
161 int expand_main(int argc UNUSED_PARAM, char **argv)
162 {
163         /* Default 8 spaces for 1 tab */
164         const char *opt_t = "8";
165         FILE *file;
166         unsigned tab_size;
167         unsigned opt;
168         int exit_status = EXIT_SUCCESS;
169
170 #if ENABLE_FEATURE_EXPAND_LONG_OPTIONS
171         static const char expand_longopts[] ALIGN1 =
172                 /* name, has_arg, val */
173                 "initial\0"          No_argument       "i"
174                 "tabs\0"             Required_argument "t"
175         ;
176 #endif
177 #if ENABLE_FEATURE_UNEXPAND_LONG_OPTIONS
178         static const char unexpand_longopts[] ALIGN1 =
179                 /* name, has_arg, val */
180                 "first-only\0"       No_argument       "i"
181                 "tabs\0"             Required_argument "t"
182                 "all\0"              No_argument       "a"
183         ;
184 #endif
185         init_unicode();
186
187         if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e')) {
188                 IF_FEATURE_EXPAND_LONG_OPTIONS(applet_long_options = expand_longopts);
189                 opt = getopt32(argv, "it:", &opt_t);
190         } else {
191                 IF_FEATURE_UNEXPAND_LONG_OPTIONS(applet_long_options = unexpand_longopts);
192                 /* -t NUM sets also -a */
193                 opt_complementary = "ta";
194                 opt = getopt32(argv, "ft:a", &opt_t);
195                 /* -f --first-only is the default */
196                 if (!(opt & OPT_ALL)) opt |= OPT_INITIAL;
197         }
198         tab_size = xatou_range(opt_t, 1, UINT_MAX);
199
200         argv += optind;
201
202         if (!*argv) {
203                 *--argv = (char*)bb_msg_standard_input;
204         }
205         do {
206                 file = fopen_or_warn_stdin(*argv);
207                 if (!file) {
208                         exit_status = EXIT_FAILURE;
209                         continue;
210                 }
211
212                 if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e'))
213                         IF_EXPAND(expand(file, tab_size, opt));
214                 else
215                         IF_UNEXPAND(unexpand(file, tab_size, opt));
216
217                 /* Check and close the file */
218                 if (fclose_if_not_stdin(file)) {
219                         bb_simple_perror_msg(*argv);
220                         exit_status = EXIT_FAILURE;
221                 }
222                 /* If stdin also clear EOF */
223                 if (file == stdin)
224                         clearerr(file);
225         } while (*++argv);
226
227         /* Now close stdin also */
228         /* (if we didn't read from it, it's a no-op) */
229         if (fclose(stdin))
230                 bb_perror_msg_and_die(bb_msg_standard_input);
231
232         fflush_stdout_and_exit(exit_status);
233 }