busybox/coreutils/expand.c
<<
>>
Prefs
   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
  24//usage:#define expand_trivial_usage
  25//usage:       "[-i] [-t N] [FILE]..."
  26//usage:#define expand_full_usage "\n\n"
  27//usage:       "Convert tabs to spaces, writing to stdout\n"
  28//usage:        IF_FEATURE_EXPAND_LONG_OPTIONS(
  29//usage:     "\n        -i,--initial    Don't convert tabs after non blanks"
  30//usage:     "\n        -t,--tabs=N     Tabstops every N chars"
  31//usage:        )
  32//usage:        IF_NOT_FEATURE_EXPAND_LONG_OPTIONS(
  33//usage:     "\n        -i      Don't convert tabs after non blanks"
  34//usage:     "\n        -t      Tabstops every N chars"
  35//usage:        )
  36
  37//usage:#define unexpand_trivial_usage
  38//usage:       "[-fa][-t N] [FILE]..."
  39//usage:#define unexpand_full_usage "\n\n"
  40//usage:       "Convert spaces to tabs, writing to stdout\n"
  41//usage:        IF_FEATURE_UNEXPAND_LONG_OPTIONS(
  42//usage:     "\n        -a,--all        Convert all blanks"
  43//usage:     "\n        -f,--first-only Convert only leading blanks"
  44//usage:     "\n        -t,--tabs=N     Tabstops every N chars"
  45//usage:        )
  46//usage:        IF_NOT_FEATURE_UNEXPAND_LONG_OPTIONS(
  47//usage:     "\n        -a      Convert all blanks"
  48//usage:     "\n        -f      Convert only leading blanks"
  49//usage:     "\n        -t N    Tabstops every N chars"
  50//usage:        )
  51
  52#include "libbb.h"
  53#include "unicode.h"
  54
  55enum {
  56        OPT_INITIAL     = 1 << 0,
  57        OPT_TABS        = 1 << 1,
  58        OPT_ALL         = 1 << 2,
  59};
  60
  61#if ENABLE_EXPAND
  62static void expand(FILE *file, unsigned tab_size, unsigned opt)
  63{
  64        char *line;
  65
  66        while ((line = xmalloc_fgets(file)) != NULL) {
  67                unsigned char c;
  68                char *ptr;
  69                char *ptr_strbeg;
  70
  71                ptr = ptr_strbeg = line;
  72                while ((c = *ptr) != '\0') {
  73                        if ((opt & OPT_INITIAL) && !isblank(c)) {
  74                                /* not space or tab */
  75                                break;
  76                        }
  77                        if (c == '\t') {
  78                                unsigned len;
  79                                *ptr = '\0';
  80# if ENABLE_UNICODE_SUPPORT
  81                                {
  82                                        uni_stat_t uni_stat;
  83                                        printable_string(&uni_stat, ptr_strbeg);
  84                                        len = uni_stat.unicode_width;
  85                                }
  86# else
  87                                len = ptr - ptr_strbeg;
  88# endif
  89                                len = tab_size - (len % tab_size);
  90                                /*while (ptr[1] == '\t') { ptr++; len += tab_size; } - can handle many tabs at once */
  91                                printf("%s%*s", ptr_strbeg, len, "");
  92                                ptr_strbeg = ptr + 1;
  93                        }
  94                        ptr++;
  95                }
  96                fputs(ptr_strbeg, stdout);
  97                free(line);
  98        }
  99}
 100#endif
 101
 102#if ENABLE_UNEXPAND
 103static void unexpand(FILE *file, unsigned tab_size, unsigned opt)
 104{
 105        char *line;
 106
 107        while ((line = xmalloc_fgets(file)) != NULL) {
 108                char *ptr = line;
 109                unsigned column = 0;
 110
 111                while (*ptr) {
 112                        unsigned n;
 113                        unsigned len = 0;
 114
 115                        while (*ptr == ' ') {
 116                                ptr++;
 117                                len++;
 118                        }
 119                        column += len;
 120                        if (*ptr == '\t') {
 121                                column += tab_size - (column % tab_size);
 122                                ptr++;
 123                                continue;
 124                        }
 125
 126                        n = column / tab_size;
 127                        if (n) {
 128                                len = column = column % tab_size;
 129                                while (n--)
 130                                        putchar('\t');
 131                        }
 132
 133                        if ((opt & OPT_INITIAL) && ptr != line) {
 134                                printf("%*s%s", len, "", ptr);
 135                                break;
 136                        }
 137                        n = strcspn(ptr, "\t ");
 138                        printf("%*s%.*s", len, "", n, ptr);
 139# if ENABLE_UNICODE_SUPPORT
 140                        {
 141                                char c;
 142                                uni_stat_t uni_stat;
 143                                c = ptr[n];
 144                                ptr[n] = '\0';
 145                                printable_string(&uni_stat, ptr);
 146                                len = uni_stat.unicode_width;
 147                                ptr[n] = c;
 148                        }
 149# else
 150                        len = n;
 151# endif
 152                        ptr += n;
 153                        column = (column + len) % tab_size;
 154                }
 155                free(line);
 156        }
 157}
 158#endif
 159
 160int expand_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 161int expand_main(int argc UNUSED_PARAM, char **argv)
 162{
 163        /* Default 8 spaces for 1 tab */
 164        const char *opt_t = "8";
 165        FILE *file;
 166        unsigned tab_size;
 167        unsigned opt;
 168        int exit_status = EXIT_SUCCESS;
 169
 170#if ENABLE_FEATURE_EXPAND_LONG_OPTIONS
 171        static const char expand_longopts[] ALIGN1 =
 172                /* name, has_arg, val */
 173                "initial\0"          No_argument       "i"
 174                "tabs\0"             Required_argument "t"
 175        ;
 176#endif
 177#if ENABLE_FEATURE_UNEXPAND_LONG_OPTIONS
 178        static const char unexpand_longopts[] ALIGN1 =
 179                /* name, has_arg, val */
 180                "first-only\0"       No_argument       "i"
 181                "tabs\0"             Required_argument "t"
 182                "all\0"              No_argument       "a"
 183        ;
 184#endif
 185        init_unicode();
 186
 187        if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e')) {
 188                IF_FEATURE_EXPAND_LONG_OPTIONS(applet_long_options = expand_longopts);
 189                opt = getopt32(argv, "it:", &opt_t);
 190        } else {
 191                IF_FEATURE_UNEXPAND_LONG_OPTIONS(applet_long_options = unexpand_longopts);
 192                /* -t NUM sets also -a */
 193                opt_complementary = "ta";
 194                opt = getopt32(argv, "ft:a", &opt_t);
 195                /* -f --first-only is the default */
 196                if (!(opt & OPT_ALL)) opt |= OPT_INITIAL;
 197        }
 198        tab_size = xatou_range(opt_t, 1, UINT_MAX);
 199
 200        argv += optind;
 201
 202        if (!*argv) {
 203                *--argv = (char*)bb_msg_standard_input;
 204        }
 205        do {
 206                file = fopen_or_warn_stdin(*argv);
 207                if (!file) {
 208                        exit_status = EXIT_FAILURE;
 209                        continue;
 210                }
 211
 212                if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e'))
 213                        IF_EXPAND(expand(file, tab_size, opt));
 214                else
 215                        IF_UNEXPAND(unexpand(file, tab_size, opt));
 216
 217                /* Check and close the file */
 218                if (fclose_if_not_stdin(file)) {
 219                        bb_simple_perror_msg(*argv);
 220                        exit_status = EXIT_FAILURE;
 221                }
 222                /* If stdin also clear EOF */
 223                if (file == stdin)
 224                        clearerr(file);
 225        } while (*++argv);
 226
 227        /* Now close stdin also */
 228        /* (if we didn't read from it, it's a no-op) */
 229        if (fclose(stdin))
 230                bb_perror_msg_and_die(bb_msg_standard_input);
 231
 232        fflush_stdout_and_exit(exit_status);
 233}
 234