blob: 3ca7e5c29454470be070c405d650c7d3d4f6aa92 [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 */
23
24#include "libbb.h"
25
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
Denis Vlasenko3139ea72008-12-14 15:45:25 +000033static void expand(FILE *file, int tab_size, unsigned opt)
Denis Vlasenko3952f202007-08-13 14:10:24 +000034{
35 char *line;
Denis Vlasenko3952f202007-08-13 14:10:24 +000036
Denis Vlasenko3139ea72008-12-14 15:45:25 +000037 tab_size = -tab_size;
Denis Vlasenko3952f202007-08-13 14:10:24 +000038
39 while ((line = xmalloc_fgets(file)) != NULL) {
Denis Vlasenko3139ea72008-12-14 15:45:25 +000040 int pos;
41 unsigned char c;
42 char *ptr = line;
43
44 goto start;
45 while ((c = *ptr) != '\0') {
46 if ((opt & OPT_INITIAL) && !isblank(c)) {
47 fputs(ptr, stdout);
48 break;
Denis Vlasenko3952f202007-08-13 14:10:24 +000049 }
Denis Vlasenko3139ea72008-12-14 15:45:25 +000050 ptr++;
51 if (c == '\t') {
52 c = ' ';
53 while (++pos < 0)
54 bb_putchar(c);
Denis Vlasenko3952f202007-08-13 14:10:24 +000055 }
Denis Vlasenko3139ea72008-12-14 15:45:25 +000056 bb_putchar(c);
57 if (++pos >= 0) {
58 start:
59 pos = tab_size;
60 }
Denis Vlasenko3952f202007-08-13 14:10:24 +000061 }
Denis Vlasenko3139ea72008-12-14 15:45:25 +000062 free(line);
Denis Vlasenko3952f202007-08-13 14:10:24 +000063 }
64}
65#endif
66
67#if ENABLE_UNEXPAND
Denis Vlasenko3139ea72008-12-14 15:45:25 +000068static void unexpand(FILE *file, unsigned tab_size, unsigned opt)
Denis Vlasenko3952f202007-08-13 14:10:24 +000069{
70 char *line;
71 char *ptr;
72 int convert;
73 int pos;
74 int i = 0;
Denis Vlasenko77ad97f2008-05-13 02:27:31 +000075 unsigned column = 0;
Denis Vlasenko3952f202007-08-13 14:10:24 +000076
77 while ((line = xmalloc_fgets(file)) != NULL) {
78 convert = 1;
79 pos = 0;
80 ptr = line;
81 while (*line) {
82 while ((*line == ' ' || *line == '\t') && convert) {
83 pos += (*line == ' ') ? 1 : tab_size;
84 line++;
85 column++;
86 if ((opt & OPT_ALL) && column == tab_size) {
87 column = 0;
88 goto put_tab;
89 }
90 }
91 if (pos) {
92 i = pos / tab_size;
93 if (i) {
94 for (; i > 0; i--) {
95 put_tab:
Denis Vlasenko3139ea72008-12-14 15:45:25 +000096 bb_putchar('\t');
Denis Vlasenko3952f202007-08-13 14:10:24 +000097 }
98 } else {
99 for (i = pos % tab_size; i > 0; i--) {
Denis Vlasenko3139ea72008-12-14 15:45:25 +0000100 bb_putchar(' ');
Denis Vlasenko3952f202007-08-13 14:10:24 +0000101 }
102 }
103 pos = 0;
104 } else {
105 if (opt & OPT_INITIAL) {
106 convert = 0;
107 }
108 if (opt & OPT_ALL) {
109 column++;
110 }
Denis Vlasenko3139ea72008-12-14 15:45:25 +0000111 bb_putchar(*line);
Denis Vlasenko3952f202007-08-13 14:10:24 +0000112 line++;
113 }
114 }
115 free(ptr);
116 }
117}
118#endif
119
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000120int expand_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000121int expand_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko3952f202007-08-13 14:10:24 +0000122{
123 /* Default 8 spaces for 1 tab */
124 const char *opt_t = "8";
125 FILE *file;
126 unsigned tab_size;
127 unsigned opt;
128 int exit_status = EXIT_SUCCESS;
129
130#if ENABLE_FEATURE_EXPAND_LONG_OPTIONS
131 static const char expand_longopts[] ALIGN1 =
132 /* name, has_arg, val */
133 "initial\0" No_argument "i"
134 "tabs\0" Required_argument "t"
135 ;
136#endif
137#if ENABLE_FEATURE_UNEXPAND_LONG_OPTIONS
138 static const char unexpand_longopts[] ALIGN1 =
139 /* name, has_arg, val */
140 "first-only\0" No_argument "i"
141 "tabs\0" Required_argument "t"
142 "all\0" No_argument "a"
143 ;
144#endif
145
146 if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e')) {
147 USE_FEATURE_EXPAND_LONG_OPTIONS(applet_long_options = expand_longopts);
Denis Vlasenkofe7cd642007-08-18 15:32:12 +0000148 opt = getopt32(argv, "it:", &opt_t);
Denis Vlasenko62a90cd2008-03-17 09:07:36 +0000149 } else {
Denis Vlasenko3952f202007-08-13 14:10:24 +0000150 USE_FEATURE_UNEXPAND_LONG_OPTIONS(applet_long_options = unexpand_longopts);
151 /* -t NUM sets also -a */
152 opt_complementary = "ta";
Denis Vlasenkofe7cd642007-08-18 15:32:12 +0000153 opt = getopt32(argv, "ft:a", &opt_t);
Denis Vlasenko3952f202007-08-13 14:10:24 +0000154 /* -f --first-only is the default */
155 if (!(opt & OPT_ALL)) opt |= OPT_INITIAL;
156 }
157 tab_size = xatou_range(opt_t, 1, UINT_MAX);
158
159 argv += optind;
160
Denis Vlasenko3952f202007-08-13 14:10:24 +0000161 if (!*argv) {
162 *--argv = (char*)bb_msg_standard_input;
Denis Vlasenko3952f202007-08-13 14:10:24 +0000163 }
Denis Vlasenko3952f202007-08-13 14:10:24 +0000164 do {
Denis Vlasenko62a90cd2008-03-17 09:07:36 +0000165 file = fopen_or_warn_stdin(*argv);
166 if (!file) {
167 exit_status = EXIT_FAILURE;
168 continue;
Denis Vlasenko3952f202007-08-13 14:10:24 +0000169 }
170
171 if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e'))
172 USE_EXPAND(expand(file, tab_size, opt));
Denis Vlasenko62a90cd2008-03-17 09:07:36 +0000173 else
Denis Vlasenko3952f202007-08-13 14:10:24 +0000174 USE_UNEXPAND(unexpand(file, tab_size, opt));
175
176 /* Check and close the file */
Denis Vlasenko62a90cd2008-03-17 09:07:36 +0000177 if (fclose_if_not_stdin(file)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000178 bb_simple_perror_msg(*argv);
Denis Vlasenko3952f202007-08-13 14:10:24 +0000179 exit_status = EXIT_FAILURE;
180 }
181 /* If stdin also clear EOF */
Denis Vlasenko6a2f7f42007-08-16 10:35:17 +0000182 if (file == stdin)
Denis Vlasenko3952f202007-08-13 14:10:24 +0000183 clearerr(file);
184 } while (*++argv);
185
186 /* Now close stdin also */
187 /* (if we didn't read from it, it's a no-op) */
188 if (fclose(stdin))
189 bb_perror_msg_and_die(bb_msg_standard_input);
190
191 fflush_stdout_and_exit(exit_status);
192}