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#include "libbb.h" 24#include "unicode.h" 25 26enum { 27 OPT_INITIAL = 1 << 0, 28 OPT_TABS = 1 << 1, 29 OPT_ALL = 1 << 2, 30}; 31 32#if ENABLE_EXPAND 33static 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 74static 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 131int expand_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 132int 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} 205