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/* https://pubs.opengroup.org/onlinepubs/9699919799/utilities/uudecode.html
 160 * https://pubs.opengroup.org/onlinepubs/9699919799/utilities/uuencode.html
 161 * The above says that output file name specified in input file
 162 * or overridden by -o OUTFILE can be special "/dev/stdout" string.
 163 * This usually works "implicitly": many systems have /dev/stdout.
 164 * If ENABLE_DESKTOP, support that explicitly:
 165 */
 166                 && (!ENABLE_DESKTOP || strcmp(outname, "/dev/stdout") != 0)
 167                ) {
 168                        dst_stream = xfopen_for_write(outname);
 169                        fchmod(fileno(dst_stream), mode & (S_IRWXU | S_IRWXG | S_IRWXO));
 170                }
 171                free(line);
 172                decode_fn_ptr(src_stream, dst_stream, /*flags:*/ BASE64_FLAG_UU_STOP + BASE64_FLAG_NO_STOP_CHAR);
 173                /* fclose_if_not_stdin(src_stream); - redundant */
 174                return EXIT_SUCCESS;
 175        }
 176        bb_simple_error_msg_and_die("no 'begin' line");
 177}
 178#endif
 179
 180//config:config BASE32
 181//config:       bool "base32 (4.9 kb)"
 182//config:       default y
 183//config:       help
 184//config:       Base32 encode and decode
 185
 186//config:config BASE64
 187//config:       bool "base64 (4.9 kb)"
 188//config:       default y
 189//config:       help
 190//config:       Base64 encode and decode
 191
 192//usage:#define base32_trivial_usage
 193//usage:        "[-d] [-w COL] [FILE]"
 194//usage:#define base32_full_usage "\n\n"
 195//usage:       "Base32 encode or decode FILE to standard output\n"
 196//usage:     "\n        -d      Decode data"
 197//usage:     "\n        -w COL  Wrap lines at COL (default 76, 0 disables)"
 198////usage:     "\n      -i      When decoding, ignore non-alphabet characters"
 199
 200//usage:#define base64_trivial_usage
 201//usage:        "[-d] [-w COL] [FILE]"
 202//usage:#define base64_full_usage "\n\n"
 203//usage:       "Base64 encode or decode FILE to standard output\n"
 204//usage:     "\n        -d      Decode data"
 205//usage:     "\n        -w COL  Wrap lines at COL (default 76, 0 disables)"
 206///////:     "\n        -i      When decoding, ignore non-alphabet characters"
 207// -i is accepted but has no effect: currently, decode_base32/64() functions
 208// (called via read_base64()) skip invalid chars unconditionally.
 209
 210//                 APPLET_ODDNAME:name    main     location    suid_type     help
 211//applet:IF_BASE32(APPLET_ODDNAME(base32, baseNUM, BB_DIR_BIN, BB_SUID_DROP, base32))
 212//applet:IF_BASE64(APPLET_ODDNAME(base64, baseNUM, BB_DIR_BIN, BB_SUID_DROP, base64))
 213
 214//kbuild:lib-$(CONFIG_BASE64) += uudecode.o
 215//kbuild:lib-$(CONFIG_BASE32) += uudecode.o
 216
 217#if ENABLE_BASE32 || ENABLE_BASE64
 218
 219# if ENABLE_BASE32
 220static void bb_b32encode(char *p, const void *src, int length)
 221{
 222#define tbl bb_uuenc_tbl_base32
 223        const unsigned char *s = src;
 224
 225        /* Transform 5x8 bits to 8x5 bits */
 226        while (length > 0) {
 227                unsigned cur, next;
 228
 229                length--;
 230                cur = *s++;
 231                *p++ = tbl[cur >> 3];                   // xxxxx--- -------- -------- -------- --------
 232                cur &= 7;
 233
 234                next = 0;
 235                if (--length >= 0)
 236                        next = *s++;
 237                *p++ = tbl[(cur << 2) + (next >> 6)];   // -----xxx xx------ -------- -------- --------
 238                cur = next & 0x3f;
 239
 240                *p++ = tbl[cur >> 1];                   // -------- --xxxxx- -------- -------- --------
 241                cur &= 1;
 242
 243                next = 0;
 244                if (--length >= 0)
 245                        next = *s++;
 246                *p++ = tbl[(cur << 4) + (next >> 4)];   // -------- -------x xxxx---- -------- --------
 247                cur = next & 0xf;
 248
 249                next = 0;
 250                if (--length >= 0)
 251                        next = *s++;
 252                *p++ = tbl[(cur << 1) + (next >> 7)];   // -------- -------- ----xxxx x------- --------
 253                cur = next & 0x7f;
 254
 255                *p++ = tbl[cur >> 2];                   // -------- -------- -------- -xxxxx-- --------
 256                cur &= 3;
 257
 258                next = 0;
 259                if (--length >= 0)
 260                        next = *s++;
 261                *p++ = tbl[(cur << 3) + (next >> 5)];   // -------- -------- -------- ------xx xxx-----
 262                cur = next & 0x1f;
 263
 264                *p++ = tbl[cur];                        // -------- -------- -------- -------- ---xxxxx
 265        }
 266#undef tbl
 267        /* Zero-terminate */
 268        *p = '\0';
 269        /* Pad as necessary */
 270        length = ((-length) * 3) >> 1; /* -4 => 6 pad chars, -3 => 4, -2 => 3, -1 => 1 */
 271        while (length--) {
 272                *--p = '=';
 273        }
 274}
 275# else
 276void bb_b32encode(char *p, const void *src, int length); /* undefined */
 277# endif
 278
 279int baseNUM_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 280int baseNUM_main(int argc UNUSED_PARAM, char **argv)
 281{
 282        FILE *src_stream;
 283        unsigned opts;
 284        unsigned col = 76;
 285
 286        opts = getopt32(argv, "^" "diw:+" "\0" "?1"/* 1 arg max*/, &col);
 287        argv += optind;
 288
 289        if (!argv[0])
 290                *--argv = (char*)"-";
 291        src_stream = xfopen_stdin(argv[0]);
 292        if (opts & 1) {
 293                /* -d: decode */
 294                int flags = (unsigned char)EOF;
 295                if (ENABLE_BASE32 && (!ENABLE_BASE64 || applet_name[4] == '3'))
 296                        flags = ((unsigned char)EOF) | BASE64_32;
 297                read_base64(src_stream, stdout, flags);
 298        } else {
 299                enum {
 300                        SRC_BUF_SIZE = 3 * 5 * 32, /* this *MUST* be a multiple of 3 and 5 */
 301                        DST_BUF_SIZE = 8 * ((SRC_BUF_SIZE + 4) / 5), /* max growth on encode (base32 case) */
 302                };
 303                /* Use one buffer for both input and output:
 304                 * encoding reads input "left-to-right",
 305                 * it's safe to place source at the end of the buffer and
 306                 * overwrite it while encoding, just be careful to have a gap.
 307                 */
 308                char dst_buf[((DST_BUF_SIZE + /*gap:*/ 16) /*round up to 16:*/ | 0xf) + 1];
 309#define src_buf (dst_buf + sizeof(dst_buf) - SRC_BUF_SIZE)
 310                int src_fd, rem;
 311
 312                src_fd = fileno(src_stream);
 313                rem = 0;
 314                while (1) {
 315                        size_t size = full_read(src_fd, src_buf, SRC_BUF_SIZE);
 316                        if ((ssize_t)size < 0)
 317                                bb_simple_perror_msg_and_die(bb_msg_read_error);
 318                        if (size == 0) {
 319                                if (rem != 0) bb_putchar('\n');
 320                                break;
 321                        }
 322
 323                        /* Encode the buffer we just read in */
 324                        if (ENABLE_BASE32 && (!ENABLE_BASE64 || applet_name[4] == '3')) {
 325                                bb_b32encode(dst_buf, src_buf, size);
 326                                size = 8 * ((size + 4) / 5);
 327                        } else {
 328                                bb_uuencode(dst_buf, src_buf, size, bb_uuenc_tbl_base64);
 329                                size = 4 * ((size + 2) / 3);
 330                        }
 331
 332                        if (col == 0) {
 333                                fputs_stdout(dst_buf);
 334                        } else {
 335                                char *result = dst_buf;
 336                                if (rem == 0)
 337                                        rem = col;
 338                                while (1) {
 339                                        int out = size < rem ? size : rem;
 340                                        rem -= out;
 341                                        printf(rem != 0 ? "%.*s" : "%.*s\n", out, result);
 342                                        if (rem != 0)
 343                                                break;
 344                                        size -= out;
 345                                        if (size == 0)
 346                                                break;
 347                                        result += out;
 348                                        rem = col;
 349                                }
 350                        }
 351                }
 352#undef src_buf
 353        }
 354
 355        fflush_stdout_and_exit(EXIT_SUCCESS);
 356}
 357#endif
 358
 359/* Test script.
 360Put this into an empty dir with busybox binary, an run.
 361
 362#!/bin/sh
 363test -x busybox || { echo "No ./busybox?"; exit; }
 364ln -sf busybox uudecode
 365ln -sf busybox uuencode
 366>A_null
 367echo -n A >A
 368echo -n AB >AB
 369echo -n ABC >ABC
 370echo -n ABCD >ABCD
 371echo -n ABCDE >ABCDE
 372echo -n ABCDEF >ABCDEF
 373cat busybox >A_bbox
 374for f in A*; do
 375    echo uuencode $f
 376    ./uuencode    $f <$f >u_$f
 377    ./uuencode -m $f <$f >m_$f
 378done
 379mkdir unpk_u unpk_m 2>/dev/null
 380for f in u_*; do
 381    ./uudecode <$f -o unpk_u/${f:2}
 382    diff -a ${f:2} unpk_u/${f:2} >/dev/null 2>&1
 383    echo uudecode $f: $?
 384done
 385for f in m_*; do
 386    ./uudecode <$f -o unpk_m/${f:2}
 387    diff -a ${f:2} unpk_m/${f:2} >/dev/null 2>&1
 388    echo uudecode $f: $?
 389done
 390*/
 391