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