1 /* expand - convert tabs to spaces
2 * unexpand - convert spaces to tabs
4 * Copyright (C) 89, 91, 1995-2006 Free Software Foundation, Inc.
6 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 * David MacKenzie <djm@gnu.ai.mit.edu>
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.
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.
19 * Busybox version (C) 2007 by Tito Ragusa <farmatito@tiscali.it>
21 * Caveat: this versions of expand and unexpand don't accept tab lists.
23 //config:config EXPAND
24 //config: bool "expand (5.3 kb)"
27 //config: By default, convert all tabs to spaces.
29 //config:config UNEXPAND
30 //config: bool "unexpand (5.5 kb)"
33 //config: By default, convert only leading sequences of blanks to tabs.
35 //applet:IF_EXPAND(APPLET(expand, BB_DIR_USR_BIN, BB_SUID_DROP))
36 // APPLET_ODDNAME:name main location suid_type help
37 //applet:IF_UNEXPAND(APPLET_ODDNAME(unexpand, expand, BB_DIR_USR_BIN, BB_SUID_DROP, unexpand))
39 //kbuild:lib-$(CONFIG_EXPAND) += expand.o
40 //kbuild:lib-$(CONFIG_UNEXPAND) += expand.o
42 //usage:#define expand_trivial_usage
43 //usage: "[-i] [-t N] [FILE]..."
44 //usage:#define expand_full_usage "\n\n"
45 //usage: "Convert tabs to spaces, writing to stdout\n"
46 //usage: "\n -i Don't convert tabs after non blanks"
47 //usage: "\n -t Tabstops every N chars"
49 //usage:#define unexpand_trivial_usage
50 //usage: "[-fa][-t N] [FILE]..."
51 //usage:#define unexpand_full_usage "\n\n"
52 //usage: "Convert spaces to tabs, writing to stdout\n"
53 //usage: "\n -a Convert all blanks"
54 //usage: "\n -f Convert only leading blanks"
55 //usage: "\n -t N Tabstops every N chars"
66 //FIXME: does not work properly with input containing NULs
67 //coreutils 8.30 preserves NULs but treats them as chars of width zero:
68 //AB<nul><tab>C will expand <tab> to 6 spaces, not 5.
71 static void expand(FILE *file
, unsigned tab_size
, unsigned opt
)
78 //commented-out code handles NULs, +90 bytes of code, not tested much
82 // linelen = 1024 * 1024;
83 // line = xmalloc_fgets_str_len(file, "\n", &linelen);
84 line
= xmalloc_fgets(file
); //
87 ptr
= ptr_strbeg
= line
;
89 unsigned char c
= *ptr
;
91 // size_t rem = line + linelen - ptr;
93 //# if ENABLE_UNICODE_SUPPORT
94 // len += unicode_strwidth(ptr_strbeg);
96 // len += ptr - ptr_strbeg;
98 // printf("%s%c", ptr_strbeg, '\0');
99 // memmove(ptr, ptr + 1, rem + 1);
106 if ((opt
& OPT_INITIAL
) && !isblank(c
)) {
107 /* not space or tab */
113 # if ENABLE_UNICODE_SUPPORT
114 len
+= unicode_strwidth(ptr_strbeg
);
116 len
+= ptr
- ptr_strbeg
;
118 len
= tab_size
- (len
% tab_size
);
119 /*while (ptr[1] == '\t') { ptr++; len += tab_size; } - can handle many tabs at once */
120 printf("%s%*s", ptr_strbeg
, len
, "");
122 ptr_strbeg
= ptr
+ 1;
126 fputs_stdout(ptr_strbeg
);
133 static void unexpand(FILE *file
, unsigned tab_size
, unsigned opt
)
137 while ((line
= xmalloc_fgets(file
)) != NULL
) {
145 while (*ptr
== ' ') {
151 column
+= tab_size
- (column
% tab_size
);
156 n
= column
/ tab_size
;
158 len
= column
= column
% tab_size
;
163 if (!(opt
& OPT_ALL
) && ptr
!= line
) {
164 printf("%*s%s", len
, "", ptr
);
167 n
= strcspn(ptr
, "\t ");
168 printf("%*s%.*s", len
, "", n
, ptr
);
169 # if ENABLE_UNICODE_SUPPORT
173 len
= unicode_strwidth(ptr
);
180 column
= (column
+ len
) % tab_size
;
187 int expand_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
188 int expand_main(int argc UNUSED_PARAM
, char **argv
)
190 /* Default 8 spaces for 1 tab */
191 const char *opt_t
= "8";
195 exitcode_t exit_status
= EXIT_SUCCESS
;
199 if (ENABLE_EXPAND
&& (!ENABLE_UNEXPAND
|| applet_name
[0] == 'e')) {
200 opt
= getopt32long(argv
, "it:",
201 "initial\0" No_argument
"i"
202 "tabs\0" Required_argument
"t"
206 opt
= getopt32long(argv
, "^"
209 "ta" /* -t NUM sets -a */,
210 "first-only\0" No_argument
"f"
211 "tabs\0" Required_argument
"t"
212 "all\0" No_argument
"a"
215 /* -t implies -a, but an explicit -f overrides */
216 if (opt
& OPT_INITIAL
) opt
&= ~OPT_ALL
;
218 tab_size
= xatou_range(opt_t
, 1, UINT_MAX
);
223 *--argv
= (char*)bb_msg_standard_input
;
226 file
= fopen_or_warn_stdin(*argv
);
228 exit_status
= EXIT_FAILURE
;
232 if (ENABLE_EXPAND
&& (!ENABLE_UNEXPAND
|| applet_name
[0] == 'e'))
233 IF_EXPAND(expand(file
, tab_size
, opt
));
235 IF_UNEXPAND(unexpand(file
, tab_size
, opt
));
237 /* Check and close the file */
238 if (fclose_if_not_stdin(file
)) {
239 bb_simple_perror_msg(*argv
);
240 exit_status
= EXIT_FAILURE
;
242 /* If stdin also clear EOF */
247 /* Now close stdin also */
248 /* (if we didn't read from it, it's a no-op) */
250 bb_simple_perror_msg_and_die(bb_msg_standard_input
);
252 fflush_stdout_and_exit(exit_status
);