busybox/coreutils/fold.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/* fold -- wrap each input line to fit in specified width.
   3
   4   Written by David MacKenzie, djm@gnu.ai.mit.edu.
   5   Copyright (C) 91, 1995-2002 Free Software Foundation, Inc.
   6
   7   Modified for busybox based on coreutils v 5.0
   8   Copyright (C) 2003 Glenn McGrath
   9
  10   Licensed under GPLv2 or later, see file LICENSE in this source tree.
  11*/
  12
  13//usage:#define fold_trivial_usage
  14//usage:       "[-bs] [-w WIDTH] [FILE]..."
  15//usage:#define fold_full_usage "\n\n"
  16//usage:       "Wrap input lines in each FILE (or stdin), writing to stdout\n"
  17//usage:     "\n        -b      Count bytes rather than columns"
  18//usage:     "\n        -s      Break at spaces"
  19//usage:     "\n        -w      Use WIDTH columns instead of 80"
  20
  21#include "libbb.h"
  22#include "unicode.h"
  23
  24/* This is a NOEXEC applet. Be very careful! */
  25
  26/* Must match getopt32 call */
  27#define FLAG_COUNT_BYTES        1
  28#define FLAG_BREAK_SPACES       2
  29#define FLAG_WIDTH              4
  30
  31/* Assuming the current column is COLUMN, return the column that
  32   printing C will move the cursor to.
  33   The first column is 0. */
  34static int adjust_column(unsigned column, char c)
  35{
  36        if (option_mask32 & FLAG_COUNT_BYTES)
  37                return ++column;
  38
  39        if (c == '\t')
  40                return column + 8 - column % 8;
  41
  42        if (c == '\b') {
  43                if ((int)--column < 0)
  44                        column = 0;
  45        }
  46        else if (c == '\r')
  47                column = 0;
  48        else { /* just a printable char */
  49                if (unicode_status != UNICODE_ON /* every byte is a new char */
  50                 || (c & 0xc0) != 0x80 /* it isn't a 2nd+ byte of a Unicode char */
  51                ) {
  52                        column++;
  53                }
  54        }
  55        return column;
  56}
  57
  58/* Note that this function can write NULs, unlike fputs etc. */
  59static void write2stdout(const void *buf, unsigned size)
  60{
  61        fwrite(buf, 1, size, stdout);
  62}
  63
  64int fold_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  65int fold_main(int argc UNUSED_PARAM, char **argv)
  66{
  67        char *line_out = NULL;
  68        const char *w_opt = "80";
  69        unsigned width;
  70        smallint exitcode = EXIT_SUCCESS;
  71
  72        init_unicode();
  73
  74        if (ENABLE_INCLUDE_SUSv2) {
  75                /* Turn any numeric options into -w options.  */
  76                int i;
  77                for (i = 1; argv[i]; i++) {
  78                        const char *a = argv[i];
  79                        if (*a == '-') {
  80                                a++;
  81                                if (*a == '-' && !a[1]) /* "--" */
  82                                        break;
  83                                if (isdigit(*a))
  84                                        argv[i] = xasprintf("-w%s", a);
  85                        }
  86                }
  87        }
  88
  89        getopt32(argv, "bsw:", &w_opt);
  90        width = xatou_range(w_opt, 1, 10000);
  91
  92        argv += optind;
  93        if (!*argv)
  94                *--argv = (char*)"-";
  95
  96        do {
  97                FILE *istream = fopen_or_warn_stdin(*argv);
  98                int c;
  99                unsigned column = 0;     /* Screen column where next char will go */
 100                unsigned offset_out = 0; /* Index in 'line_out' for next char */
 101
 102                if (istream == NULL) {
 103                        exitcode = EXIT_FAILURE;
 104                        continue;
 105                }
 106
 107                while ((c = getc(istream)) != EOF) {
 108                        /* We grow line_out in chunks of 0x1000 bytes */
 109                        if ((offset_out & 0xfff) == 0) {
 110                                line_out = xrealloc(line_out, offset_out + 0x1000);
 111                        }
 112 rescan:
 113                        line_out[offset_out] = c;
 114                        if (c == '\n') {
 115                                write2stdout(line_out, offset_out + 1);
 116                                column = offset_out = 0;
 117                                continue;
 118                        }
 119                        column = adjust_column(column, c);
 120                        if (column <= width || offset_out == 0) {
 121                                /* offset_out == 0 case happens
 122                                 * with small width (say, 1) and tabs.
 123                                 * The very first tab already goes to column 8,
 124                                 * but we must not wrap it */
 125                                offset_out++;
 126                                continue;
 127                        }
 128
 129                        /* This character would make the line too long.
 130                         * Print the line plus a newline, and make this character
 131                         * start the next line */
 132                        if (option_mask32 & FLAG_BREAK_SPACES) {
 133                                unsigned i;
 134                                unsigned logical_end;
 135
 136                                /* Look for the last blank. */
 137                                for (logical_end = offset_out - 1; (int)logical_end >= 0; logical_end--) {
 138                                        if (!isblank(line_out[logical_end]))
 139                                                continue;
 140
 141                                        /* Found a space or tab.
 142                                         * Output up to and including it, and start a new line */
 143                                        logical_end++;
 144                                        /*line_out[logical_end] = '\n'; - NO! this nukes one buffered character */
 145                                        write2stdout(line_out, logical_end);
 146                                        putchar('\n');
 147                                        /* Move the remainder to the beginning of the next line.
 148                                         * The areas being copied here might overlap. */
 149                                        memmove(line_out, line_out + logical_end, offset_out - logical_end);
 150                                        offset_out -= logical_end;
 151                                        for (column = i = 0; i < offset_out; i++) {
 152                                                column = adjust_column(column, line_out[i]);
 153                                        }
 154                                        goto rescan;
 155                                }
 156                                /* No blank found, wrap will split the overlong word */
 157                        }
 158                        /* Output what we accumulated up to now, and start a new line */
 159                        line_out[offset_out] = '\n';
 160                        write2stdout(line_out, offset_out + 1);
 161                        column = offset_out = 0;
 162                        goto rescan;
 163                } /* while (not EOF) */
 164
 165                if (offset_out) {
 166                        write2stdout(line_out, offset_out);
 167                }
 168
 169                if (fclose_if_not_stdin(istream)) {
 170                        bb_simple_perror_msg(*argv);
 171                        exitcode = EXIT_FAILURE;
 172                }
 173        } while (*++argv);
 174
 175        fflush_stdout_and_exit(exitcode);
 176}
 177