busybox/coreutils/uudecode.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Copyright 2003, Glenn McGrath
   4 *
   5 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   6 *
   7 * Based on specification from
   8 * http://www.opengroup.org/onlinepubs/007904975/utilities/uuencode.html
   9 *
  10 * Bugs: the spec doesn't mention anything about "`\n`\n" prior to the
  11 * "end" line
  12 */
  13//config:config UUDECODE
  14//config:       bool "uudecode (5.8 kb)"
  15//config:       default y
  16//config:       help
  17//config:       uudecode is used to decode a uuencoded file.
  18
  19//applet:IF_UUDECODE(APPLET(uudecode, BB_DIR_USR_BIN, BB_SUID_DROP))
  20
  21//kbuild:lib-$(CONFIG_UUDECODE) += uudecode.o
  22
  23//usage:#define uudecode_trivial_usage
  24//usage:       "[-o OUTFILE] [INFILE]"
  25//usage:#define uudecode_full_usage "\n\n"
  26//usage:       "Uudecode a file\n"
  27//usage:       "Finds OUTFILE in uuencoded source unless -o is given"
  28//usage:
  29//usage:#define uudecode_example_usage
  30//usage:       "$ uudecode -o busybox busybox.uu\n"
  31//usage:       "$ ls -l busybox\n"
  32//usage:       "-rwxr-xr-x   1 ams      ams        245264 Jun  7 21:35 busybox\n"
  33
  34#include "libbb.h"
  35
  36#if ENABLE_UUDECODE
  37static void FAST_FUNC read_stduu(FILE *src_stream, FILE *dst_stream, int flags UNUSED_PARAM)
  38{
  39        char *line;
  40
  41        for (;;) {
  42                int encoded_len, str_len;
  43                char *line_ptr, *dst;
  44                size_t line_len;
  45
  46                line_len = 64 * 1024;
  47                line = xmalloc_fgets_str_len(src_stream, "\n", &line_len);
  48                if (!line)
  49                        break;
  50                /* Handle both Unix and MSDOS text.
  51                 * Note: space should not be trimmed, some encoders use it instead of "`"
  52                 * for padding of last incomplete 4-char block.
  53                 */
  54                str_len = line_len;
  55                while (--str_len >= 0
  56                 && (line[str_len] == '\n' || line[str_len] == '\r')
  57                ) {
  58                        line[str_len] = '\0';
  59                }
  60
  61                if (strcmp(line, "end") == 0) {
  62                        return; /* the only non-error exit */
  63                }
  64
  65                line_ptr = line;
  66                while (*line_ptr) {
  67                        *line_ptr = (*line_ptr - 0x20) & 0x3f;
  68                        line_ptr++;
  69                }
  70                str_len = line_ptr - line;
  71
  72                encoded_len = line[0] * 4 / 3;
  73                /* Check that line is not too short. (we tolerate
  74                 * overly _long_ line to accommodate possible extra "`").
  75                 * Empty line case is also caught here. */
  76                if (str_len <= encoded_len) {
  77                        break; /* go to bb_error_msg_and_die("short file"); */
  78                }
  79                if (encoded_len <= 0) {
  80                        /* Ignore the "`\n" line, why is it even in the encode file ? */
  81                        free(line);
  82                        continue;
  83                }
  84                if (encoded_len > 60) {
  85                        bb_simple_error_msg_and_die("line too long");
  86                }
  87
  88                dst = line;
  89                line_ptr = line + 1;
  90                do {
  91                        /* Merge four 6 bit chars to three 8 bit chars */
  92                        *dst++ = line_ptr[0] << 2 | line_ptr[1] >> 4;
  93                        encoded_len--;
  94                        if (encoded_len == 0) {
  95                                break;
  96                        }
  97
  98                        *dst++ = line_ptr[1] << 4 | line_ptr[2] >> 2;
  99                        encoded_len--;
 100                        if (encoded_len == 0) {
 101                                break;
 102                        }
 103
 104                        *dst++ = line_ptr[2] << 6 | line_ptr[3];
 105                        line_ptr += 4;
 106                        encoded_len -= 2;
 107                } while (encoded_len > 0);
 108                fwrite(line, 1, dst - line, dst_stream);
 109                free(line);
 110        }
 111        bb_simple_error_msg_and_die("short file");
 112}
 113
 114int uudecode_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 115int uudecode_main(int argc UNUSED_PARAM, char **argv)
 116{
 117        FILE *src_stream;
 118        char *outname = NULL;
 119        char *line;
 120
 121        getopt32(argv, "^" "o:" "\0" "?1"/* 1 arg max*/, &outname);
 122        argv += optind;
 123
 124        if (!argv[0])
 125                *--argv = (char*)"-";
 126        src_stream = xfopen_stdin(argv[0]);
 127
 128        /* Search for the start of the encoding */
 129        while ((line = xmalloc_fgetline(src_stream)) != NULL) {
 130                void FAST_FUNC (*decode_fn_ptr)(FILE *src, FILE *dst, int flags);
 131                char *line_ptr;
 132                FILE *dst_stream;
 133                int mode;
 134
 135                if (is_prefixed_with(line, "begin-base64 ")) {
 136                        line_ptr = line + 13;
 137                        decode_fn_ptr = read_base64;
 138                } else if (is_prefixed_with(line, "begin ")) {
 139                        line_ptr = line + 6;
 140                        decode_fn_ptr = read_stduu;
 141                } else {
 142                        free(line);
 143                        continue;
 144                }
 145
 146                /* begin line found. decode and exit */
 147                mode = bb_strtou(line_ptr, NULL, 8);
 148                if (outname == NULL) {
 149                        outname = strchr(line_ptr, ' ');
 150                        if (!outname)
 151                                break;
 152                        outname++;
 153                        trim(outname); /* remove trailing space (and '\r' for DOS text) */
 154                        if (!outname[0])
 155                                break;
 156                }
 157                dst_stream = stdout;
 158                if (NOT_LONE_DASH(outname)) {
 159                        dst_stream = xfopen_for_write(outname);
 160                        fchmod(fileno(dst_stream), mode & (S_IRWXU | S_IRWXG | S_IRWXO));
 161                }
 162                free(line);
 163                decode_fn_ptr(src_stream, dst_stream, /*flags:*/ BASE64_FLAG_UU_STOP + BASE64_FLAG_NO_STOP_CHAR);
 164                /* fclose_if_not_stdin(src_stream); - redundant */
 165                return EXIT_SUCCESS;
 166        }
 167        bb_simple_error_msg_and_die("no 'begin' line");
 168}
 169#endif
 170
 171//config:config BASE32
 172//config:       bool "base32 (4.9 kb)"
 173//config:       default y
 174//config:       help
 175//config:       Base32 encode and decode
 176
 177//config:config BASE64
 178//config:       bool "base64 (4.9 kb)"
 179//config:       default y
 180//config:       help
 181//config:       Base64 encode and decode
 182
 183//usage:#define base32_trivial_usage
 184//usage:        "[-d] [-w COL] [FILE]"
 185//usage:#define base32_full_usage "\n\n"
 186//usage:       "Base32 encode or decode FILE to standard output\n"
 187//usage:     "\n        -d      Decode data"
 188//usage:     "\n        -w COL  Wrap lines at COL (default 76, 0 disables)"
 189////usage:     "\n      -i      When decoding, ignore non-alphabet characters"
 190
 191//usage:#define base64_trivial_usage
 192//usage:        "[-d] [-w COL] [FILE]"
 193//usage:#define base64_full_usage "\n\n"
 194//usage:       "Base64 encode or decode FILE to standard output\n"
 195//usage:     "\n        -d      Decode data"
 196//usage:     "\n        -w COL  Wrap lines at COL (default 76, 0 disables)"
 197///////:     "\n        -i      When decoding, ignore non-alphabet characters"
 198// -i is accepted but has no effect: currently, decode_base32/64() functions
 199// (called via read_base64()) skip invalid chars unconditionally.
 200
 201//                 APPLET_ODDNAME:name    main     location    suid_type     help
 202//applet:IF_BASE32(APPLET_ODDNAME(base32, baseNUM, BB_DIR_BIN, BB_SUID_DROP, base32))
 203//applet:IF_BASE64(APPLET_ODDNAME(base64, baseNUM, BB_DIR_BIN, BB_SUID_DROP, base64))
 204
 205//kbuild:lib-$(CONFIG_BASE64) += uudecode.o
 206//kbuild:lib-$(CONFIG_BASE32) += uudecode.o
 207
 208#if ENABLE_BASE32 || ENABLE_BASE64
 209
 210# if ENABLE_BASE32
 211static void bb_b32encode(char *p, const void *src, int length)
 212{
 213#define tbl bb_uuenc_tbl_base32
 214        const unsigned char *s = src;
 215
 216        /* Transform 5x8 bits to 8x5 bits */
 217        while (length > 0) {
 218                unsigned cur, next;
 219
 220                length--;
 221                cur = *s++;
 222                *p++ = tbl[cur >> 3];                   // xxxxx--- -------- -------- -------- --------
 223                cur &= 7;
 224
 225                next = 0;
 226                if (--length >= 0)
 227                        next = *s++;
 228                *p++ = tbl[(cur << 2) + (next >> 6)];   // -----xxx xx------ -------- -------- --------
 229                cur = next & 0x3f;
 230
 231                *p++ = tbl[cur >> 1];                   // -------- --xxxxx- -------- -------- --------
 232                cur &= 1;
 233
 234                next = 0;
 235                if (--length >= 0)
 236                        next = *s++;
 237                *p++ = tbl[(cur << 4) + (next >> 4)];   // -------- -------x xxxx---- -------- --------
 238                cur = next & 0xf;
 239
 240                next = 0;
 241                if (--length >= 0)
 242                        next = *s++;
 243                *p++ = tbl[(cur << 1) + (next >> 7)];   // -------- -------- ----xxxx x------- --------
 244                cur = next & 0x7f;
 245
 246                *p++ = tbl[cur >> 2];                   // -------- -------- -------- -xxxxx-- --------
 247                cur &= 3;
 248
 249                next = 0;
 250                if (--length >= 0)
 251                        next = *s++;
 252                *p++ = tbl[(cur << 3) + (next >> 5)];   // -------- -------- -------- ------xx xxx-----
 253                cur = next & 0x1f;
 254
 255                *p++ = tbl[cur];                        // -------- -------- -------- -------- ---xxxxx
 256        }
 257#undef tbl
 258        /* Zero-terminate */
 259        *p = '\0';
 260        /* Pad as necessary */
 261        length = ((-length) * 3) >> 1; /* -4 => 6 pad chars, -3 => 4, -2 => 3, -1 => 1 */
 262        while (length--) {
 263                *--p = '=';
 264        }
 265}
 266# else
 267void bb_b32encode(char *p, const void *src, int length); /* undefined */
 268# endif
 269
 270int baseNUM_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 271int baseNUM_main(int argc UNUSED_PARAM, char **argv)
 272{
 273        FILE *src_stream;
 274        unsigned opts;
 275        unsigned col = 76;
 276
 277        opts = getopt32(argv, "^" "diw:+" "\0" "?1"/* 1 arg max*/, &col);
 278        argv += optind;
 279
 280        if (!argv[0])
 281                *--argv = (char*)"-";
 282        src_stream = xfopen_stdin(argv[0]);
 283        if (opts & 1) {
 284                /* -d: decode */
 285                int flags = (unsigned char)EOF;
 286                if (ENABLE_BASE32 && (!ENABLE_BASE64 || applet_name[4] == '3'))
 287                        flags = ((unsigned char)EOF) | BASE64_32;
 288                read_base64(src_stream, stdout, flags);
 289        } else {
 290                enum {
 291                        SRC_BUF_SIZE = 3 * 5 * 32, /* this *MUST* be a multiple of 3 and 5 */
 292                        DST_BUF_SIZE = 8 * ((SRC_BUF_SIZE + 4) / 5), /* max growth on encode (base32 case) */
 293                };
 294                /* Use one buffer for both input and output:
 295                 * encoding reads input "left-to-right",
 296                 * it's safe to place source at the end of the buffer and
 297                 * overwrite it while encoding, just be careful to have a gap.
 298                 */
 299                char dst_buf[((DST_BUF_SIZE + /*gap:*/ 16) /*round up to 16:*/ | 0xf) + 1];
 300#define src_buf (dst_buf + sizeof(dst_buf) - SRC_BUF_SIZE)
 301                int src_fd, rem;
 302
 303                src_fd = fileno(src_stream);
 304                rem = 0;
 305                while (1) {
 306                        size_t size = full_read(src_fd, src_buf, SRC_BUF_SIZE);
 307                        if ((ssize_t)size < 0)
 308                                bb_simple_perror_msg_and_die(bb_msg_read_error);
 309                        if (size == 0) {
 310                                if (rem != 0) bb_putchar('\n');
 311                                break;
 312                        }
 313
 314                        /* Encode the buffer we just read in */
 315                        if (ENABLE_BASE32 && (!ENABLE_BASE64 || applet_name[4] == '3')) {
 316                                bb_b32encode(dst_buf, src_buf, size);
 317                                size = 8 * ((size + 4) / 5);
 318                        } else {
 319                                bb_uuencode(dst_buf, src_buf, size, bb_uuenc_tbl_base64);
 320                                size = 4 * ((size + 2) / 3);
 321                        }
 322
 323                        if (col == 0) {
 324                                fputs_stdout(dst_buf);
 325                        } else {
 326                                char *result = dst_buf;
 327                                if (rem == 0)
 328                                        rem = col;
 329                                while (1) {
 330                                        int out = size < rem ? size : rem;
 331                                        rem -= out;
 332                                        printf(rem != 0 ? "%.*s" : "%.*s\n", out, result);
 333                                        if (rem != 0)
 334                                                break;
 335                                        size -= out;
 336                                        if (size == 0)
 337                                                break;
 338                                        result += out;
 339                                        rem = col;
 340                                }
 341                        }
 342                }
 343#undef src_buf
 344        }
 345
 346        fflush_stdout_and_exit(EXIT_SUCCESS);
 347}
 348#endif
 349
 350/* Test script.
 351Put this into an empty dir with busybox binary, an run.
 352
 353#!/bin/sh
 354test -x busybox || { echo "No ./busybox?"; exit; }
 355ln -sf busybox uudecode
 356ln -sf busybox uuencode
 357>A_null
 358echo -n A >A
 359echo -n AB >AB
 360echo -n ABC >ABC
 361echo -n ABCD >ABCD
 362echo -n ABCDE >ABCDE
 363echo -n ABCDEF >ABCDEF
 364cat busybox >A_bbox
 365for f in A*; do
 366    echo uuencode $f
 367    ./uuencode    $f <$f >u_$f
 368    ./uuencode -m $f <$f >m_$f
 369done
 370mkdir unpk_u unpk_m 2>/dev/null
 371for f in u_*; do
 372    ./uudecode <$f -o unpk_u/${f:2}
 373    diff -a ${f:2} unpk_u/${f:2} >/dev/null 2>&1
 374    echo uudecode $f: $?
 375done
 376for f in m_*; do
 377    ./uudecode <$f -o unpk_m/${f:2}
 378    diff -a ${f:2} unpk_m/${f:2} >/dev/null 2>&1
 379    echo uudecode $f: $?
 380done
 381*/
 382