123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- /* expand - convert tabs to spaces
- * unexpand - convert spaces to tabs
- *
- * Copyright (C) 89, 91, 1995-2006 Free Software Foundation, Inc.
- *
- * Licensed under GPLv2 or later, see file LICENSE in this source tree.
- *
- * David MacKenzie <djm@gnu.ai.mit.edu>
- *
- * Options for expand:
- * -t num --tabs NUM Convert tabs to num spaces (default 8 spaces).
- * -i --initial Only convert initial tabs on each line to spaces.
- *
- * Options for unexpand:
- * -a --all Convert all blanks, instead of just initial blanks.
- * -f --first-only Convert only leading sequences of blanks (default).
- * -t num --tabs NUM Have tabs num characters apart instead of 8.
- *
- * Busybox version (C) 2007 by Tito Ragusa <farmatito@tiscali.it>
- *
- * Caveat: this versions of expand and unexpand don't accept tab lists.
- */
- //config:config EXPAND
- //config: bool "expand (5.1 kb)"
- //config: default y
- //config: help
- //config: By default, convert all tabs to spaces.
- //config:
- //config:config UNEXPAND
- //config: bool "unexpand (5.3 kb)"
- //config: default y
- //config: help
- //config: By default, convert only leading sequences of blanks to tabs.
- //applet:IF_EXPAND(APPLET(expand, BB_DIR_USR_BIN, BB_SUID_DROP))
- // APPLET_ODDNAME:name main location suid_type help
- //applet:IF_UNEXPAND(APPLET_ODDNAME(unexpand, expand, BB_DIR_USR_BIN, BB_SUID_DROP, unexpand))
- //kbuild:lib-$(CONFIG_EXPAND) += expand.o
- //kbuild:lib-$(CONFIG_UNEXPAND) += expand.o
- //usage:#define expand_trivial_usage
- //usage: "[-i] [-t N] [FILE]..."
- //usage:#define expand_full_usage "\n\n"
- //usage: "Convert tabs to spaces, writing to stdout\n"
- //usage: "\n -i Don't convert tabs after non blanks"
- //usage: "\n -t Tabstops every N chars"
- //usage:#define unexpand_trivial_usage
- //usage: "[-fa][-t N] [FILE]..."
- //usage:#define unexpand_full_usage "\n\n"
- //usage: "Convert spaces to tabs, writing to stdout\n"
- //usage: "\n -a Convert all blanks"
- //usage: "\n -f Convert only leading blanks"
- //usage: "\n -t N Tabstops every N chars"
- #include "libbb.h"
- #include "unicode.h"
- enum {
- OPT_INITIAL = 1 << 0,
- OPT_TABS = 1 << 1,
- OPT_ALL = 1 << 2,
- };
- //FIXME: does not work properly with input containing NULs
- //coreutils 8.30 preserves NULs but treats them as chars of width zero:
- //AB<nul><tab>C will expand <tab> to 6 spaces, not 5.
- #if ENABLE_EXPAND
- static void expand(FILE *file, unsigned tab_size, unsigned opt)
- {
- for (;;) {
- char *line;
- char *ptr;
- char *ptr_strbeg;
- //commented-out code handles NULs, +90 bytes of code, not tested much
- // size_t linelen;
- // unsigned len = 0;
- // linelen = 1024 * 1024;
- // line = xmalloc_fgets_str_len(file, "\n", &linelen);
- line = xmalloc_fgets(file); //
- if (!line)
- break;
- ptr = ptr_strbeg = line;
- for (;;) {
- unsigned char c = *ptr;
- if (c == '\0') {
- // size_t rem = line + linelen - ptr;
- // if (rem > 0) {
- //# if ENABLE_UNICODE_SUPPORT
- // len += unicode_strwidth(ptr_strbeg);
- //# else
- // len += ptr - ptr_strbeg;
- //# endif
- // printf("%s%c", ptr_strbeg, '\0');
- // memmove(ptr, ptr + 1, rem + 1);
- // ptr_strbeg = ptr;
- // linelen--;
- // continue;
- // }
- break;
- }
- if ((opt & OPT_INITIAL) && !isblank(c)) {
- /* not space or tab */
- break;
- }
- if (c == '\t') {
- unsigned len = 0; //
- *ptr = '\0';
- # if ENABLE_UNICODE_SUPPORT
- len += unicode_strwidth(ptr_strbeg);
- # else
- len += ptr - ptr_strbeg;
- # endif
- len = tab_size - (len % tab_size);
- /*while (ptr[1] == '\t') { ptr++; len += tab_size; } - can handle many tabs at once */
- printf("%s%*s", ptr_strbeg, len, "");
- // len = 0;
- ptr_strbeg = ptr + 1;
- }
- ptr++;
- }
- fputs(ptr_strbeg, stdout);
- free(line);
- }
- }
- #endif
- #if ENABLE_UNEXPAND
- static void unexpand(FILE *file, unsigned tab_size, unsigned opt)
- {
- char *line;
- while ((line = xmalloc_fgets(file)) != NULL) {
- char *ptr = line;
- unsigned column = 0;
- while (*ptr) {
- unsigned n;
- unsigned len = 0;
- while (*ptr == ' ') {
- ptr++;
- len++;
- }
- column += len;
- if (*ptr == '\t') {
- column += tab_size - (column % tab_size);
- ptr++;
- continue;
- }
- n = column / tab_size;
- if (n) {
- len = column = column % tab_size;
- while (n--)
- putchar('\t');
- }
- if ((opt & OPT_INITIAL) && ptr != line) {
- printf("%*s%s", len, "", ptr);
- break;
- }
- n = strcspn(ptr, "\t ");
- printf("%*s%.*s", len, "", n, ptr);
- # if ENABLE_UNICODE_SUPPORT
- {
- char c = ptr[n];
- ptr[n] = '\0';
- len = unicode_strwidth(ptr);
- ptr[n] = c;
- }
- # else
- len = n;
- # endif
- ptr += n;
- column = (column + len) % tab_size;
- }
- free(line);
- }
- }
- #endif
- int expand_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
- int expand_main(int argc UNUSED_PARAM, char **argv)
- {
- /* Default 8 spaces for 1 tab */
- const char *opt_t = "8";
- FILE *file;
- unsigned tab_size;
- unsigned opt;
- int exit_status = EXIT_SUCCESS;
- init_unicode();
- if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e')) {
- opt = getopt32long(argv, "it:",
- "initial\0" No_argument "i"
- "tabs\0" Required_argument "t"
- , &opt_t
- );
- } else {
- opt = getopt32long(argv, "^"
- "ft:a"
- "\0"
- "ta" /* -t NUM sets -a */,
- "first-only\0" No_argument "i"
- "tabs\0" Required_argument "t"
- "all\0" No_argument "a"
- , &opt_t
- );
- /* -f --first-only is the default */
- if (!(opt & OPT_ALL)) opt |= OPT_INITIAL;
- }
- tab_size = xatou_range(opt_t, 1, UINT_MAX);
- argv += optind;
- if (!*argv) {
- *--argv = (char*)bb_msg_standard_input;
- }
- do {
- file = fopen_or_warn_stdin(*argv);
- if (!file) {
- exit_status = EXIT_FAILURE;
- continue;
- }
- if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e'))
- IF_EXPAND(expand(file, tab_size, opt));
- else
- IF_UNEXPAND(unexpand(file, tab_size, opt));
- /* Check and close the file */
- if (fclose_if_not_stdin(file)) {
- bb_simple_perror_msg(*argv);
- exit_status = EXIT_FAILURE;
- }
- /* If stdin also clear EOF */
- if (file == stdin)
- clearerr(file);
- } while (*++argv);
- /* Now close stdin also */
- /* (if we didn't read from it, it's a no-op) */
- if (fclose(stdin))
- bb_perror_msg_and_die(bb_msg_standard_input);
- fflush_stdout_and_exit(exit_status);
- }
|