Initial public busybox upstream commit
[busybox4maemo] / 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 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
26 enum {
27         OPT_INITIAL     = 1 << 0,
28         OPT_TABS        = 1 << 1,
29         OPT_ALL         = 1 << 2,
30 };
31
32 static void xputchar(char c)
33 {
34         if (putchar(c) < 0)
35                 bb_error_msg_and_die(bb_msg_write_error);
36 }
37
38 #if ENABLE_EXPAND
39 static void expand(FILE *file, unsigned tab_size, unsigned opt)
40 {
41         char *line;
42         char *ptr;
43         int convert;
44         int pos;
45
46         /* Increment tab_size by 1 locally.*/
47         tab_size++;
48
49         while ((line = xmalloc_fgets(file)) != NULL) {
50                 convert = 1;
51                 pos = 0;
52                 ptr = line;
53                 while (*line) {
54                         pos++;
55                         if (*line == '\t' && convert) {
56                                 for (; pos < tab_size; pos++) {
57                                         xputchar(' ');
58                                 }
59                         } else {
60                                 if ((opt & OPT_INITIAL) && !isblank(*line)) {
61                                         convert = 0;
62                                 }
63                                 xputchar(*line);
64                         }
65                         if (pos == tab_size) {
66                                 pos = 0;
67                         }
68                         line++;
69                 }
70                 free(ptr);
71         }
72 }
73 #endif
74
75 #if ENABLE_UNEXPAND
76 static void unexpand(FILE *file, unsigned int tab_size, unsigned opt)
77 {
78         char *line;
79         char *ptr;
80         int convert;
81         int pos;
82         int i = 0;
83         int column = 0;
84
85         while ((line = xmalloc_fgets(file)) != NULL) {
86                 convert = 1;
87                 pos = 0;
88                 ptr = line;
89                 while (*line) {
90                         while ((*line == ' ' || *line == '\t') && convert) {
91                                 pos += (*line == ' ') ? 1 : tab_size;
92                                 line++;
93                                 column++;
94                                 if ((opt & OPT_ALL) && column == tab_size) {
95                                         column = 0;
96                                         goto put_tab;
97                                 }
98                         }
99                         if (pos) {
100                                 i = pos / tab_size;
101                                 if (i) {
102                                         for (; i > 0; i--) {
103  put_tab:
104                                                 xputchar('\t');
105                                         }
106                                 } else {
107                                         for (i = pos % tab_size; i > 0; i--) {
108                                                 xputchar(' ');
109                                         }
110                                 }
111                                 pos = 0;
112                         } else {
113                                 if (opt & OPT_INITIAL) {
114                                         convert = 0;
115                                 }
116                                 if (opt & OPT_ALL) {
117                                         column++;
118                                 }
119                                 xputchar(*line);
120                                 line++;
121                         }
122                 }
123                 free(ptr);
124         }
125 }
126 #endif
127
128 int expand_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
129 int expand_main(int argc ATTRIBUTE_UNUSED, char **argv)
130 {
131         /* Default 8 spaces for 1 tab */
132         const char *opt_t = "8";
133         FILE *file;
134         unsigned tab_size;
135         unsigned opt;
136         int exit_status = EXIT_SUCCESS;
137
138 #if ENABLE_FEATURE_EXPAND_LONG_OPTIONS
139         static const char expand_longopts[] ALIGN1 =
140                 /* name, has_arg, val */
141                 "initial\0"          No_argument       "i"
142                 "tabs\0"             Required_argument "t"
143         ;
144 #endif
145 #if ENABLE_FEATURE_UNEXPAND_LONG_OPTIONS
146         static const char unexpand_longopts[] ALIGN1 =
147                 /* name, has_arg, val */
148                 "first-only\0"       No_argument       "i"
149                 "tabs\0"             Required_argument "t"
150                 "all\0"              No_argument       "a"
151         ;
152 #endif
153
154         if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e')) {
155                 USE_FEATURE_EXPAND_LONG_OPTIONS(applet_long_options = expand_longopts);
156                 opt = getopt32(argv, "it:", &opt_t);
157         } else {
158                 USE_FEATURE_UNEXPAND_LONG_OPTIONS(applet_long_options = unexpand_longopts);
159                 /* -t NUM sets also -a */
160                 opt_complementary = "ta";
161                 opt = getopt32(argv, "ft:a", &opt_t);
162                 /* -f --first-only is the default */
163                 if (!(opt & OPT_ALL)) opt |= OPT_INITIAL;
164         }
165         tab_size = xatou_range(opt_t, 1, UINT_MAX);
166
167         argv += optind;
168
169         if (!*argv) {
170                 *--argv = (char*)bb_msg_standard_input;
171         }
172         do {
173                 file = fopen_or_warn_stdin(*argv);
174                 if (!file) {
175                         exit_status = EXIT_FAILURE;
176                         continue;
177                 }
178
179                 if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e'))
180                         USE_EXPAND(expand(file, tab_size, opt));
181                 else
182                         USE_UNEXPAND(unexpand(file, tab_size, opt));
183
184                 /* Check and close the file */
185                 if (fclose_if_not_stdin(file)) {
186                         bb_simple_perror_msg(*argv);
187                         exit_status = EXIT_FAILURE;
188                 }
189                 /* If stdin also clear EOF */
190                 if (file == stdin)
191                         clearerr(file);
192         } while (*++argv);
193
194         /* Now close stdin also */
195         /* (if we didn't read from it, it's a no-op) */
196         if (fclose(stdin))
197                 bb_perror_msg_and_die(bb_msg_standard_input);
198
199         fflush_stdout_and_exit(exit_status);
200 }