ln: explain why we use xstrdup
[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 #include "libbb.h"
24 #include "unicode.h"
25
26 enum {
27         OPT_INITIAL     = 1 << 0,
28         OPT_TABS        = 1 << 1,
29         OPT_ALL         = 1 << 2,
30 };
31
32 #if ENABLE_EXPAND
33 static void expand(FILE *file, unsigned tab_size, unsigned opt)
34 {
35         char *line;
36
37         while ((line = xmalloc_fgets(file)) != NULL) {
38                 unsigned char c;
39                 char *ptr;
40                 char *ptr_strbeg;
41
42                 ptr = ptr_strbeg = line;
43                 while ((c = *ptr) != '\0') {
44                         if ((opt & OPT_INITIAL) && !isblank(c)) {
45                                 /* not space or tab */
46                                 break;
47                         }
48                         if (c == '\t') {
49                                 unsigned len;
50                                 *ptr = '\0';
51 # if ENABLE_UNICODE_SUPPORT
52                                 {
53                                         uni_stat_t uni_stat;
54                                         printable_string(&uni_stat, ptr_strbeg);
55                                         len = uni_stat.unicode_width;
56                                 }
57 # 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;
64                         }
65                         ptr++;
66                 }
67                 fputs(ptr_strbeg, stdout);
68                 free(line);
69         }
70 }
71 #endif
72
73 #if ENABLE_UNEXPAND
74 static void unexpand(FILE *file, unsigned tab_size, unsigned opt)
75 {
76         char *line;
77
78         while ((line = xmalloc_fgets(file)) != NULL) {
79                 char *ptr = line;
80                 unsigned column = 0;
81
82                 while (*ptr) {
83                         unsigned n;
84                         unsigned len = 0;
85
86                         while (*ptr == ' ') {
87                                 ptr++;
88                                 len++;
89                         }
90                         column += len;
91                         if (*ptr == '\t') {
92                                 column += tab_size - (column % tab_size);
93                                 ptr++;
94                                 continue;
95                         }
96
97                         n = column / tab_size;
98                         if (n) {
99                                 len = column = column % tab_size;
100                                 while (n--)
101                                         putchar('\t');
102                         }
103
104                         if ((opt & OPT_INITIAL) && ptr != line) {
105                                 printf("%*s%s", len, "", ptr);
106                                 break;
107                         }
108                         n = strcspn(ptr, "\t ");
109                         printf("%*s%.*s", len, "", n, ptr);
110 # if ENABLE_UNICODE_SUPPORT
111                         {
112                                 char c;
113                                 uni_stat_t uni_stat;
114                                 c = ptr[n];
115                                 ptr[n] = '\0';
116                                 printable_string(&uni_stat, ptr);
117                                 len = uni_stat.unicode_width;
118                                 ptr[n] = c;
119                         }
120 # else
121                         len = n;
122 # endif
123                         ptr += n;
124                         column = (column + len) % tab_size;
125                 }
126                 free(line);
127         }
128 }
129 #endif
130
131 int expand_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
132 int expand_main(int argc UNUSED_PARAM, char **argv)
133 {
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
156         init_unicode();
157
158         if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e')) {
159                 IF_FEATURE_EXPAND_LONG_OPTIONS(applet_long_options = expand_longopts);
160                 opt = getopt32(argv, "it:", &opt_t);
161         } else {
162                 IF_FEATURE_UNEXPAND_LONG_OPTIONS(applet_long_options = unexpand_longopts);
163                 /* -t NUM sets also -a */
164                 opt_complementary = "ta";
165                 opt = getopt32(argv, "ft:a", &opt_t);
166                 /* -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
173         if (!*argv) {
174                 *--argv = (char*)bb_msg_standard_input;
175         }
176         do {
177                 file = fopen_or_warn_stdin(*argv);
178                 if (!file) {
179                         exit_status = EXIT_FAILURE;
180                         continue;
181                 }
182
183                 if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e'))
184                         IF_EXPAND(expand(file, tab_size, opt));
185                 else
186                         IF_UNEXPAND(unexpand(file, tab_size, opt));
187
188                 /* Check and close the file */
189                 if (fclose_if_not_stdin(file)) {
190                         bb_simple_perror_msg(*argv);
191                         exit_status = EXIT_FAILURE;
192                 }
193                 /* If stdin also clear EOF */
194                 if (file == stdin)
195                         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 }