blob: b874b6ad461b4545edae401dbedff2fa81b134b6 [file] [log] [blame]
Denis Vlasenko3952f202007-08-13 14:10:24 +00001/* 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 tarball for details.
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 */
Denis Vlasenko3952f202007-08-13 14:10:24 +000023#include "libbb.h"
Tomas Heinrichd2b1ba62010-01-04 16:21:31 +010024#include "unicode.h"
Denis Vlasenko3952f202007-08-13 14:10:24 +000025
26enum {
27 OPT_INITIAL = 1 << 0,
28 OPT_TABS = 1 << 1,
29 OPT_ALL = 1 << 2,
30};
31
Denis Vlasenko3952f202007-08-13 14:10:24 +000032#if ENABLE_EXPAND
Tomas Heinrichd2b1ba62010-01-04 16:21:31 +010033static void expand(FILE *file, unsigned tab_size, unsigned opt)
Denis Vlasenko3952f202007-08-13 14:10:24 +000034{
35 char *line;
Denis Vlasenko3952f202007-08-13 14:10:24 +000036
Denis Vlasenko3952f202007-08-13 14:10:24 +000037 while ((line = xmalloc_fgets(file)) != NULL) {
Denis Vlasenko3139ea72008-12-14 15:45:25 +000038 unsigned char c;
Tomas Heinrichd2b1ba62010-01-04 16:21:31 +010039 char *ptr;
40 char *ptr_strbeg;
Denis Vlasenko3139ea72008-12-14 15:45:25 +000041
Tomas Heinrichd2b1ba62010-01-04 16:21:31 +010042 ptr = ptr_strbeg = line;
Denis Vlasenko3139ea72008-12-14 15:45:25 +000043 while ((c = *ptr) != '\0') {
44 if ((opt & OPT_INITIAL) && !isblank(c)) {
Tomas Heinrichd2b1ba62010-01-04 16:21:31 +010045 /* not space or tab */
Denis Vlasenko3139ea72008-12-14 15:45:25 +000046 break;
Denis Vlasenko3952f202007-08-13 14:10:24 +000047 }
Denis Vlasenko3139ea72008-12-14 15:45:25 +000048 if (c == '\t') {
Tomas Heinrichd2b1ba62010-01-04 16:21:31 +010049 unsigned len;
50 *ptr = '\0';
Denys Vlasenko19158a82010-03-26 14:06:56 +010051# if ENABLE_UNICODE_SUPPORT
Tomas Heinrich1abc07d2010-03-26 09:40:47 +010052 {
53 uni_stat_t uni_stat;
54 printable_string(&uni_stat, ptr_strbeg);
55 len = uni_stat.unicode_width;
56 }
Tomas Heinrichd2b1ba62010-01-04 16:21:31 +010057# else
58 len = ptr - ptr_strbeg;
59# endif
60 len = tab_size - (len % tab_size);
61 /*while (ptr[1] == '\t') { ptr++; len += tab_size; } - can handle many tabs at once */
62 printf("%s%*s", ptr_strbeg, len, "");
63 ptr_strbeg = ptr + 1;
Denis Vlasenko3952f202007-08-13 14:10:24 +000064 }
Tomas Heinrichd2b1ba62010-01-04 16:21:31 +010065 ptr++;
Denis Vlasenko3952f202007-08-13 14:10:24 +000066 }
Tomas Heinrichd2b1ba62010-01-04 16:21:31 +010067 fputs(ptr_strbeg, stdout);
Denis Vlasenko3139ea72008-12-14 15:45:25 +000068 free(line);
Denis Vlasenko3952f202007-08-13 14:10:24 +000069 }
70}
71#endif
72
73#if ENABLE_UNEXPAND
Denis Vlasenko3139ea72008-12-14 15:45:25 +000074static void unexpand(FILE *file, unsigned tab_size, unsigned opt)
Denis Vlasenko3952f202007-08-13 14:10:24 +000075{
76 char *line;
Denis Vlasenko3952f202007-08-13 14:10:24 +000077
78 while ((line = xmalloc_fgets(file)) != NULL) {
Denis Vlasenkoe40e76f2008-12-26 14:56:03 +000079 char *ptr = line;
80 unsigned column = 0;
81
82 while (*ptr) {
83 unsigned n;
Tomas Heinrich968951f2010-03-26 09:46:07 +010084 unsigned len = 0;
Denis Vlasenkoe40e76f2008-12-26 14:56:03 +000085
86 while (*ptr == ' ') {
Denis Vlasenkoe40e76f2008-12-26 14:56:03 +000087 ptr++;
Tomas Heinrich968951f2010-03-26 09:46:07 +010088 len++;
Denis Vlasenko3952f202007-08-13 14:10:24 +000089 }
Tomas Heinrich968951f2010-03-26 09:46:07 +010090 column += len;
Denis Vlasenkoe40e76f2008-12-26 14:56:03 +000091 if (*ptr == '\t') {
92 column += tab_size - (column % tab_size);
93 ptr++;
94 continue;
Denis Vlasenko3952f202007-08-13 14:10:24 +000095 }
Denis Vlasenkoe40e76f2008-12-26 14:56:03 +000096
97 n = column / tab_size;
Tomas Heinrich968951f2010-03-26 09:46:07 +010098 if (n) {
99 len = column = column % tab_size;
100 while (n--)
101 putchar('\t');
102 }
Denis Vlasenkoe40e76f2008-12-26 14:56:03 +0000103
104 if ((opt & OPT_INITIAL) && ptr != line) {
Tomas Heinrich968951f2010-03-26 09:46:07 +0100105 printf("%*s%s", len, "", ptr);
Denis Vlasenkoe40e76f2008-12-26 14:56:03 +0000106 break;
107 }
108 n = strcspn(ptr, "\t ");
Tomas Heinrich968951f2010-03-26 09:46:07 +0100109 printf("%*s%.*s", len, "", n, ptr);
Denys Vlasenko19158a82010-03-26 14:06:56 +0100110# if ENABLE_UNICODE_SUPPORT
Tomas Heinrichd2b1ba62010-01-04 16:21:31 +0100111 {
112 char c;
Tomas Heinrich1abc07d2010-03-26 09:40:47 +0100113 uni_stat_t uni_stat;
Tomas Heinrichd2b1ba62010-01-04 16:21:31 +0100114 c = ptr[n];
115 ptr[n] = '\0';
Tomas Heinrich1abc07d2010-03-26 09:40:47 +0100116 printable_string(&uni_stat, ptr);
117 len = uni_stat.unicode_width;
Tomas Heinrichd2b1ba62010-01-04 16:21:31 +0100118 ptr[n] = c;
119 }
120# else
121 len = n;
122# endif
Denis Vlasenkoe40e76f2008-12-26 14:56:03 +0000123 ptr += n;
Tomas Heinrichd2b1ba62010-01-04 16:21:31 +0100124 column = (column + len) % tab_size;
Denis Vlasenko3952f202007-08-13 14:10:24 +0000125 }
Denis Vlasenkoe40e76f2008-12-26 14:56:03 +0000126 free(line);
Denis Vlasenko3952f202007-08-13 14:10:24 +0000127 }
128}
129#endif
130
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000131int expand_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000132int expand_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko3952f202007-08-13 14:10:24 +0000133{
134 /* Default 8 spaces for 1 tab */
135 const char *opt_t = "8";
136 FILE *file;
137 unsigned tab_size;
138 unsigned opt;
139 int exit_status = EXIT_SUCCESS;
140
141#if ENABLE_FEATURE_EXPAND_LONG_OPTIONS
142 static const char expand_longopts[] ALIGN1 =
143 /* name, has_arg, val */
144 "initial\0" No_argument "i"
145 "tabs\0" Required_argument "t"
146 ;
147#endif
148#if ENABLE_FEATURE_UNEXPAND_LONG_OPTIONS
149 static const char unexpand_longopts[] ALIGN1 =
150 /* name, has_arg, val */
151 "first-only\0" No_argument "i"
152 "tabs\0" Required_argument "t"
153 "all\0" No_argument "a"
154 ;
155#endif
Denys Vlasenko28055022010-01-04 20:49:58 +0100156 init_unicode();
Denis Vlasenko3952f202007-08-13 14:10:24 +0000157
158 if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e')) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000159 IF_FEATURE_EXPAND_LONG_OPTIONS(applet_long_options = expand_longopts);
Denis Vlasenkofe7cd642007-08-18 15:32:12 +0000160 opt = getopt32(argv, "it:", &opt_t);
Denis Vlasenko62a90cd2008-03-17 09:07:36 +0000161 } else {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000162 IF_FEATURE_UNEXPAND_LONG_OPTIONS(applet_long_options = unexpand_longopts);
Denis Vlasenko3952f202007-08-13 14:10:24 +0000163 /* -t NUM sets also -a */
164 opt_complementary = "ta";
Denis Vlasenkofe7cd642007-08-18 15:32:12 +0000165 opt = getopt32(argv, "ft:a", &opt_t);
Denis Vlasenko3952f202007-08-13 14:10:24 +0000166 /* -f --first-only is the default */
167 if (!(opt & OPT_ALL)) opt |= OPT_INITIAL;
168 }
169 tab_size = xatou_range(opt_t, 1, UINT_MAX);
170
171 argv += optind;
172
Denis Vlasenko3952f202007-08-13 14:10:24 +0000173 if (!*argv) {
174 *--argv = (char*)bb_msg_standard_input;
Denis Vlasenko3952f202007-08-13 14:10:24 +0000175 }
Denis Vlasenko3952f202007-08-13 14:10:24 +0000176 do {
Denis Vlasenko62a90cd2008-03-17 09:07:36 +0000177 file = fopen_or_warn_stdin(*argv);
178 if (!file) {
179 exit_status = EXIT_FAILURE;
180 continue;
Denis Vlasenko3952f202007-08-13 14:10:24 +0000181 }
182
183 if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e'))
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000184 IF_EXPAND(expand(file, tab_size, opt));
Denis Vlasenko62a90cd2008-03-17 09:07:36 +0000185 else
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000186 IF_UNEXPAND(unexpand(file, tab_size, opt));
Denis Vlasenko3952f202007-08-13 14:10:24 +0000187
188 /* Check and close the file */
Denis Vlasenko62a90cd2008-03-17 09:07:36 +0000189 if (fclose_if_not_stdin(file)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000190 bb_simple_perror_msg(*argv);
Denis Vlasenko3952f202007-08-13 14:10:24 +0000191 exit_status = EXIT_FAILURE;
192 }
193 /* If stdin also clear EOF */
Denis Vlasenko6a2f7f42007-08-16 10:35:17 +0000194 if (file == stdin)
Denis Vlasenko3952f202007-08-13 14:10:24 +0000195 clearerr(file);
196 } while (*++argv);
197
198 /* Now close stdin also */
199 /* (if we didn't read from it, it's a no-op) */
200 if (fclose(stdin))
201 bb_perror_msg_and_die(bb_msg_standard_input);
202
203 fflush_stdout_and_exit(exit_status);
204}