/* crypto/des/des.c */
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
 * All rights reserved.
 *
 * This package is an SSL implementation written
 * by Eric Young (eay@cryptsoft.com).
 * The implementation was written so as to conform with Netscapes SSL.
 *
 * This library is free for commercial and non-commercial use as long as
 * the following conditions are aheared to.  The following conditions
 * apply to all code found in this distribution, be it the RC4, RSA,
 * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
 * included with this distribution is covered by the same copyright terms
 * except that the holder is Tim Hudson (tjh@cryptsoft.com).
 *
 * Copyright remains Eric Young's, and as such any Copyright notices in
 * the code are not to be removed.
 * If this package is used in a product, Eric Young should be given attribution
 * as the author of the parts of the library used.
 * This can be in the form of a textual message at program startup or
 * in documentation (online or textual) provided with the package.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    "This product includes cryptographic software written by
 *     Eric Young (eay@cryptsoft.com)"
 *    The word 'cryptographic' can be left out if the rouines from the library
 *    being used are not cryptographic related :-).
 * 4. If you include any Windows specific code (or a derivative thereof) from
 *    the apps directory (application code) you must include an acknowledgement:
 *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
 *
 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * The licence and distribution terms for any publically available version or
 * derivative of this code cannot be changed.  i.e. this code cannot simply be
 * copied and put under another distribution licence
 * [including the GNU Public Licence.]
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/opensslconf.h>
#ifndef OPENSSL_SYS_MSDOS
# ifndef OPENSSL_SYS_VMS
#  include OPENSSL_UNISTD
# else                          /* OPENSSL_SYS_VMS */
#  ifdef __DECC
#   include <unistd.h>
#  else                         /* not __DECC */
#   include <math.h>
#  endif                        /* __DECC */
# endif                         /* OPENSSL_SYS_VMS */
#else                           /* OPENSSL_SYS_MSDOS */
# include <io.h>
#endif

#include <time.h>
#include "des_ver.h"

#ifdef OPENSSL_SYS_VMS
# include <types.h>
# include <stat.h>
#else
# ifndef _IRIX
#  include <sys/types.h>
# endif
# include <sys/stat.h>
#endif
#include <openssl/des.h>
#include <openssl/rand.h>
#include <openssl/ui_compat.h>

void usage(void);
void doencryption(void);
int uufwrite(unsigned char *data, int size, unsigned int num, FILE *fp);
void uufwriteEnd(FILE *fp);
int uufread(unsigned char *out, int size, unsigned int num, FILE *fp);
int uuencode(unsigned char *in, int num, unsigned char *out);
int uudecode(unsigned char *in, int num, unsigned char *out);
void DES_3cbc_encrypt(DES_cblock *input, DES_cblock *output, long length,
                      DES_key_schedule sk1, DES_key_schedule sk2,
                      DES_cblock *ivec1, DES_cblock *ivec2, int enc);
#ifdef OPENSSL_SYS_VMS
# define EXIT(a) exit(a&0x10000000L)
#else
# define EXIT(a) exit(a)
#endif

#define BUFSIZE (8*1024)
#define VERIFY  1
#define KEYSIZ  8
#define KEYSIZB 1024            /* should hit tty line limit first :-) */
char key[KEYSIZB + 1];
int do_encrypt, longk = 0;
FILE *DES_IN, *DES_OUT, *CKSUM_OUT;
char uuname[200];
unsigned char uubuf[50];
int uubufnum = 0;
#define INUUBUFN        (45*100)
#define OUTUUBUF        (65*100)
unsigned char b[OUTUUBUF];
unsigned char bb[300];
DES_cblock cksum = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

char cksumname[200] = "";

int vflag, cflag, eflag, dflag, kflag, bflag, fflag, sflag, uflag, flag3,
    hflag, error;

int main(int argc, char **argv)
{
    int i;
    struct stat ins, outs;
    char *p;
    char *in = NULL, *out = NULL;

    vflag = cflag = eflag = dflag = kflag = hflag = bflag = fflag = sflag =
        uflag = flag3 = 0;
    error = 0;
    memset(key, 0, sizeof(key));

    for (i = 1; i < argc; i++) {
        p = argv[i];
        if ((p[0] == '-') && (p[1] != '\0')) {
            p++;
            while (*p) {
                switch (*(p++)) {
                case '3':
                    flag3 = 1;
                    longk = 1;
                    break;
                case 'c':
                    cflag = 1;
                    strncpy(cksumname, p, 200);
                    cksumname[sizeof(cksumname) - 1] = '\0';
                    p += strlen(cksumname);
                    break;
                case 'C':
                    cflag = 1;
                    longk = 1;
                    strncpy(cksumname, p, 200);
                    cksumname[sizeof(cksumname) - 1] = '\0';
                    p += strlen(cksumname);
                    break;
                case 'e':
                    eflag = 1;
                    break;
                case 'v':
                    vflag = 1;
                    break;
                case 'E':
                    eflag = 1;
                    longk = 1;
                    break;
                case 'd':
                    dflag = 1;
                    break;
                case 'D':
                    dflag = 1;
                    longk = 1;
                    break;
                case 'b':
                    bflag = 1;
                    break;
                case 'f':
                    fflag = 1;
                    break;
                case 's':
                    sflag = 1;
                    break;
                case 'u':
                    uflag = 1;
                    strncpy(uuname, p, 200);
                    uuname[sizeof(uuname) - 1] = '\0';
                    p += strlen(uuname);
                    break;
                case 'h':
                    hflag = 1;
                    break;
                case 'k':
                    kflag = 1;
                    if ((i + 1) == argc) {
                        fputs("must have a key with the -k option\n", stderr);
                        error = 1;
                    } else {
                        int j;

                        i++;
                        strncpy(key, argv[i], KEYSIZB);
                        for (j = strlen(argv[i]) - 1; j >= 0; j--)
                            argv[i][j] = '\0';
                    }
                    break;
                default:
                    fprintf(stderr, "'%c' unknown flag\n", p[-1]);
                    error = 1;
                    break;
                }
            }
        } else {
            if (in == NULL)
                in = argv[i];
            else if (out == NULL)
                out = argv[i];
            else
                error = 1;
        }
    }
    if (error)
        usage();
    /*-
     * We either
     * do checksum or
     * do encrypt or
     * do decrypt or
     * do decrypt then ckecksum or
     * do checksum then encrypt
     */
    if (((eflag + dflag) == 1) || cflag) {
        if (eflag)
            do_encrypt = DES_ENCRYPT;
        if (dflag)
            do_encrypt = DES_DECRYPT;
    } else {
        if (vflag) {
#ifndef _Windows
            fprintf(stderr, "des(1) built with %s\n", libdes_version);
#endif
            EXIT(1);
        } else
            usage();
    }

#ifndef _Windows
    if (vflag)
        fprintf(stderr, "des(1) built with %s\n", libdes_version);
#endif
    if ((in != NULL) && (out != NULL) &&
#ifndef OPENSSL_SYS_MSDOS
        (stat(in, &ins) != -1) &&
        (stat(out, &outs) != -1) &&
        (ins.st_dev == outs.st_dev) && (ins.st_ino == outs.st_ino))
#else                           /* OPENSSL_SYS_MSDOS */
        (strcmp(in, out) == 0))
#endif
    {
        fputs("input and output file are the same\n", stderr);
        EXIT(3);
    }

    if (!kflag)
        if (des_read_pw_string
            (key, KEYSIZB + 1, "Enter key:", eflag ? VERIFY : 0)) {
            fputs("password error\n", stderr);
            EXIT(2);
        }

    if (in == NULL)
        DES_IN = stdin;
    else if ((DES_IN = fopen(in, "r")) == NULL) {
        perror("opening input file");
        EXIT(4);
    }

    CKSUM_OUT = stdout;
    if (out == NULL) {
        DES_OUT = stdout;
        CKSUM_OUT = stderr;
    } else if ((DES_OUT = fopen(out, "w")) == NULL) {
        perror("opening output file");
        EXIT(5);
    }
#ifdef OPENSSL_SYS_MSDOS
    /* This should set the file to binary mode. */
    {
# include <fcntl.h>
        if (!(uflag && dflag))
            setmode(fileno(DES_IN), O_BINARY);
        if (!(uflag && eflag))
            setmode(fileno(DES_OUT), O_BINARY);
    }
#endif

    doencryption();
    fclose(DES_IN);
    fclose(DES_OUT);
    EXIT(0);
}

void usage(void)
{
    char **u;
    static const char *Usage[] = {
        "des <options> [input-file [output-file]]",
        "options:",
        "-v         : des(1) version number",
        "-e         : encrypt using SunOS compatible user key to DES key conversion.",
        "-E         : encrypt ",
        "-d         : decrypt using SunOS compatible user key to DES key conversion.",
        "-D         : decrypt ",
        "-c[ckname] : generate a cbc_cksum using SunOS compatible user key to",
        "             DES key conversion and output to ckname (stdout default,",
        "             stderr if data being output on stdout).  The checksum is",
        "             generated before encryption and after decryption if used",
        "             in conjunction with -[eEdD].",
        "-C[ckname] : generate a cbc_cksum as for -c but compatible with -[ED].",
        "-k key     : use key 'key'",
        "-h         : the key that is entered will be a hexadecimal number",
        "             that is used directly as the des key",
        "-u[uuname] : input file is uudecoded if -[dD] or output uuencoded data if -[eE]",
        "             (uuname is the filename to put in the uuencode header).",
        "-b         : encrypt using DES in ecb encryption mode, the default is cbc mode.",
        "-3         : encrypt using triple DES encryption.  This uses 2 keys",
        "             generated from the input key.  If the input key is less",
        "             than 8 characters long, this is equivalent to normal",
        "             encryption.  Default is triple cbc, -b makes it triple ecb.",
        NULL
    };
    for (u = (char **)Usage; *u; u++) {
        fputs(*u, stderr);
        fputc('\n', stderr);
    }

    EXIT(1);
}

void doencryption(void)
{
#ifdef _LIBC
    extern unsigned long time();
#endif

    register int i;
    DES_key_schedule ks, ks2;
    DES_cblock iv, iv2;
    char *p;
    int num = 0, j, k, l, rem, ll, len, last, ex = 0;
    DES_cblock kk, k2;
    FILE *O;
    int Exit = 0;
#ifndef OPENSSL_SYS_MSDOS
    static unsigned char buf[BUFSIZE + 8], obuf[BUFSIZE + 8];
#else
    static unsigned char *buf = NULL, *obuf = NULL;

    if (buf == NULL) {
        if (((buf = OPENSSL_malloc(BUFSIZE + 8)) == NULL) ||
            ((obuf = OPENSSL_malloc(BUFSIZE + 8)) == NULL)) {
            fputs("Not enough memory\n", stderr);
            Exit = 10;
            goto problems;
        }
    }
#endif

    if (hflag) {
        j = (flag3 ? 16 : 8);
        p = key;
        for (i = 0; i < j; i++) {
            k = 0;
            if ((*p <= '9') && (*p >= '0'))
                k = (*p - '0') << 4;
            else if ((*p <= 'f') && (*p >= 'a'))
                k = (*p - 'a' + 10) << 4;
            else if ((*p <= 'F') && (*p >= 'A'))
                k = (*p - 'A' + 10) << 4;
            else {
                fputs("Bad hex key\n", stderr);
                Exit = 9;
                goto problems;
            }
            p++;
            if ((*p <= '9') && (*p >= '0'))
                k |= (*p - '0');
            else if ((*p <= 'f') && (*p >= 'a'))
                k |= (*p - 'a' + 10);
            else if ((*p <= 'F') && (*p >= 'A'))
                k |= (*p - 'A' + 10);
            else {
                fputs("Bad hex key\n", stderr);
                Exit = 9;
                goto problems;
            }
            p++;
            if (i < 8)
                kk[i] = k;
            else
                k2[i - 8] = k;
        }
        DES_set_key_unchecked(&k2, &ks2);
        OPENSSL_cleanse(k2, sizeof(k2));
    } else if (longk || flag3) {
        if (flag3) {
            DES_string_to_2keys(key, &kk, &k2);
            DES_set_key_unchecked(&k2, &ks2);
            OPENSSL_cleanse(k2, sizeof(k2));
        } else
            DES_string_to_key(key, &kk);
    } else
        for (i = 0; i < KEYSIZ; i++) {
            l = 0;
            k = key[i];
            for (j = 0; j < 8; j++) {
                if (k & 1)
                    l++;
                k >>= 1;
            }
            if (l & 1)
                kk[i] = key[i] & 0x7f;
            else
                kk[i] = key[i] | 0x80;
        }

    DES_set_key_unchecked(&kk, &ks);
    OPENSSL_cleanse(key, sizeof(key));
    OPENSSL_cleanse(kk, sizeof(kk));
    /* woops - A bug that does not showup under unix :-( */
    memset(iv, 0, sizeof(iv));
    memset(iv2, 0, sizeof(iv2));

    l = 1;
    rem = 0;
    /* first read */
    if (eflag || (!dflag && cflag)) {
        for (;;) {
            num = l = fread(&(buf[rem]), 1, BUFSIZE, DES_IN);
            l += rem;
            num += rem;
            if (l < 0) {
                perror("read error");
                Exit = 6;
                goto problems;
            }

            rem = l % 8;
            len = l - rem;
            if (feof(DES_IN)) {
                for (i = 7 - rem; i > 0; i--)
                    RAND_pseudo_bytes(buf + l++, 1);
                buf[l++] = rem;
                ex = 1;
                len += rem;
            } else
                l -= rem;

            if (cflag) {
                DES_cbc_cksum(buf, &cksum, (long)len, &ks, &cksum);
                if (!eflag) {
                    if (feof(DES_IN))
                        break;
                    else
                        continue;
                }
            }

            if (bflag && !flag3)
                for (i = 0; i < l; i += 8)
                    DES_ecb_encrypt((DES_cblock *)&(buf[i]),
                                    (DES_cblock *)&(obuf[i]),
                                    &ks, do_encrypt);
            else if (flag3 && bflag)
                for (i = 0; i < l; i += 8)
                    DES_ecb2_encrypt((DES_cblock *)&(buf[i]),
                                     (DES_cblock *)&(obuf[i]),
                                     &ks, &ks2, do_encrypt);
            else if (flag3 && !bflag) {
                char tmpbuf[8];

                if (rem)
                    memcpy(tmpbuf, &(buf[l]), (unsigned int)rem);
                DES_3cbc_encrypt((DES_cblock *)buf, (DES_cblock *)obuf,
                                 (long)l, ks, ks2, &iv, &iv2, do_encrypt);
                if (rem)
                    memcpy(&(buf[l]), tmpbuf, (unsigned int)rem);
            } else {
                DES_cbc_encrypt(buf, obuf, (long)l, &ks, &iv, do_encrypt);
                if (l >= 8)
                    memcpy(iv, &(obuf[l - 8]), 8);
            }
            if (rem)
                memcpy(buf, &(buf[l]), (unsigned int)rem);

            i = 0;
            while (i < l) {
                if (uflag)
                    j = uufwrite(obuf, 1, (unsigned int)l - i, DES_OUT);
                else
                    j = fwrite(obuf, 1, (unsigned int)l - i, DES_OUT);
                if (j == -1) {
                    perror("Write error");
                    Exit = 7;
                    goto problems;
                }
                i += j;
            }
            if (feof(DES_IN)) {
                if (uflag)
                    uufwriteEnd(DES_OUT);
                break;
            }
        }
    } else {                    /* decrypt */

        ex = 1;
        for (;;) {
            if (ex) {
                if (uflag)
                    l = uufread(buf, 1, BUFSIZE, DES_IN);
                else
                    l = fread(buf, 1, BUFSIZE, DES_IN);
                ex = 0;
                rem = l % 8;
                l -= rem;
            }
            if (l < 0) {
                perror("read error");
                Exit = 6;
                goto problems;
            }

            if (bflag && !flag3)
                for (i = 0; i < l; i += 8)
                    DES_ecb_encrypt((DES_cblock *)&(buf[i]),
                                    (DES_cblock *)&(obuf[i]),
                                    &ks, do_encrypt);
            else if (flag3 && bflag)
                for (i = 0; i < l; i += 8)
                    DES_ecb2_encrypt((DES_cblock *)&(buf[i]),
                                     (DES_cblock *)&(obuf[i]),
                                     &ks, &ks2, do_encrypt);
            else if (flag3 && !bflag) {
                DES_3cbc_encrypt((DES_cblock *)buf, (DES_cblock *)obuf,
                                 (long)l, ks, ks2, &iv, &iv2, do_encrypt);
            } else {
                DES_cbc_encrypt(buf, obuf, (long)l, &ks, &iv, do_encrypt);
                if (l >= 8)
                    memcpy(iv, &(buf[l - 8]), 8);
            }

            if (uflag)
                ll = uufread(&(buf[rem]), 1, BUFSIZE, DES_IN);
            else
                ll = fread(&(buf[rem]), 1, BUFSIZE, DES_IN);
            ll += rem;
            rem = ll % 8;
            ll -= rem;
            if (feof(DES_IN) && (ll == 0)) {
                last = obuf[l - 1];

                if ((last > 7) || (last < 0)) {
                    fputs("The file was not decrypted correctly.\n", stderr);
                    Exit = 8;
                    last = 0;
                }
                l = l - 8 + last;
            }
            i = 0;
            if (cflag)
                DES_cbc_cksum(obuf,
                              (DES_cblock *)cksum, (long)l / 8 * 8, &ks,
                              (DES_cblock *)cksum);
            while (i != l) {
                j = fwrite(obuf, 1, (unsigned int)l - i, DES_OUT);
                if (j == -1) {
                    perror("Write error");
                    Exit = 7;
                    goto problems;
                }
                i += j;
            }
            l = ll;
            if ((l == 0) && feof(DES_IN))
                break;
        }
    }
    if (cflag) {
        l = 0;
        if (cksumname[0] != '\0') {
            if ((O = fopen(cksumname, "w")) != NULL) {
                CKSUM_OUT = O;
                l = 1;
            }
        }
        for (i = 0; i < 8; i++)
            fprintf(CKSUM_OUT, "%02X", cksum[i]);
        fprintf(CKSUM_OUT, "\n");
        if (l)
            fclose(CKSUM_OUT);
    }
 problems:
    OPENSSL_cleanse(buf, sizeof(buf));
    OPENSSL_cleanse(obuf, sizeof(obuf));
    OPENSSL_cleanse(&ks, sizeof(ks));
    OPENSSL_cleanse(&ks2, sizeof(ks2));
    OPENSSL_cleanse(iv, sizeof(iv));
    OPENSSL_cleanse(iv2, sizeof(iv2));
    OPENSSL_cleanse(kk, sizeof(kk));
    OPENSSL_cleanse(k2, sizeof(k2));
    OPENSSL_cleanse(uubuf, sizeof(uubuf));
    OPENSSL_cleanse(b, sizeof(b));
    OPENSSL_cleanse(bb, sizeof(bb));
    OPENSSL_cleanse(cksum, sizeof(cksum));
    if (Exit)
        EXIT(Exit);
}

/*    We ignore this parameter but it should be > ~50 I believe    */
int uufwrite(unsigned char *data, int size, unsigned int num, FILE *fp)
{
    int i, j, left, rem, ret = num;
    static int start = 1;

    if (start) {
        fprintf(fp, "begin 600 %s\n",
                (uuname[0] == '\0') ? "text.d" : uuname);
        start = 0;
    }

    if (uubufnum) {
        if (uubufnum + num < 45) {
            memcpy(&(uubuf[uubufnum]), data, (unsigned int)num);
            uubufnum += num;
            return (num);
        } else {
            i = 45 - uubufnum;
            memcpy(&(uubuf[uubufnum]), data, (unsigned int)i);
            j = uuencode((unsigned char *)uubuf, 45, b);
            fwrite(b, 1, (unsigned int)j, fp);
            uubufnum = 0;
            data += i;
            num -= i;
        }
    }

    for (i = 0; i < (((int)num) - INUUBUFN); i += INUUBUFN) {
        j = uuencode(&(data[i]), INUUBUFN, b);
        fwrite(b, 1, (unsigned int)j, fp);
    }
    rem = (num - i) % 45;
    left = (num - i - rem);
    if (left) {
        j = uuencode(&(data[i]), left, b);
        fwrite(b, 1, (unsigned int)j, fp);
        i += left;
    }
    if (i != num) {
        memcpy(uubuf, &(data[i]), (unsigned int)rem);
        uubufnum = rem;
    }
    return (ret);
}

void uufwriteEnd(FILE *fp)
{
    int j;
    static const char *end = " \nend\n";

    if (uubufnum != 0) {
        uubuf[uubufnum] = '\0';
        uubuf[uubufnum + 1] = '\0';
        uubuf[uubufnum + 2] = '\0';
        j = uuencode(uubuf, uubufnum, b);
        fwrite(b, 1, (unsigned int)j, fp);
    }
    fwrite(end, 1, strlen(end), fp);
}

/*
 * int size: should always be > ~ 60; I actually ignore this parameter :-)
 */
int uufread(unsigned char *out, int size, unsigned int num, FILE *fp)
{
    int i, j, tot;
    static int done = 0;
    static int valid = 0;
    static int start = 1;

    if (start) {
        for (;;) {
            b[0] = '\0';
            fgets((char *)b, 300, fp);
            if (b[0] == '\0') {
                fprintf(stderr, "no 'begin' found in uuencoded input\n");
                return (-1);
            }
            if (strncmp((char *)b, "begin ", 6) == 0)
                break;
        }
        start = 0;
    }
    if (done)
        return (0);
    tot = 0;
    if (valid) {
        memcpy(out, bb, (unsigned int)valid);
        tot = valid;
        valid = 0;
    }
    for (;;) {
        b[0] = '\0';
        fgets((char *)b, 300, fp);
        if (b[0] == '\0')
            break;
        i = strlen((char *)b);
        if ((b[0] == 'e') && (b[1] == 'n') && (b[2] == 'd')) {
            done = 1;
            while (!feof(fp)) {
                fgets((char *)b, 300, fp);
            }
            break;
        }
        i = uudecode(b, i, bb);
        if (i < 0)
            break;
        if ((i + tot + 8) > num) {
            /* num to copy to make it a multiple of 8 */
            j = (num / 8 * 8) - tot - 8;
            memcpy(&(out[tot]), bb, (unsigned int)j);
            tot += j;
            memcpy(bb, &(bb[j]), (unsigned int)i - j);
            valid = i - j;
            break;
        }
        memcpy(&(out[tot]), bb, (unsigned int)i);
        tot += i;
    }
    return (tot);
}

#define ccc2l(c,l)      (l =((DES_LONG)(*((c)++)))<<16, \
                         l|=((DES_LONG)(*((c)++)))<< 8, \
                         l|=((DES_LONG)(*((c)++))))

#define l2ccc(l,c)      (*((c)++)=(unsigned char)(((l)>>16)&0xff), \
                    *((c)++)=(unsigned char)(((l)>> 8)&0xff), \
                    *((c)++)=(unsigned char)(((l)    )&0xff))

int uuencode(unsigned char *in, int num, unsigned char *out)
{
    int j, i, n, tot = 0;
    DES_LONG l;
    register unsigned char *p;
    p = out;

    for (j = 0; j < num; j += 45) {
        if (j + 45 > num)
            i = (num - j);
        else
            i = 45;
        *(p++) = i + ' ';
        for (n = 0; n < i; n += 3) {
            ccc2l(in, l);
            *(p++) = ((l >> 18) & 0x3f) + ' ';
            *(p++) = ((l >> 12) & 0x3f) + ' ';
            *(p++) = ((l >> 6) & 0x3f) + ' ';
            *(p++) = ((l) & 0x3f) + ' ';
            tot += 4;
        }
        *(p++) = '\n';
        tot += 2;
    }
    *p = '\0';
    l = 0;
    return (tot);
}

int uudecode(unsigned char *in, int num, unsigned char *out)
{
    int j, i, k;
    unsigned int n = 0, space = 0;
    DES_LONG l;
    DES_LONG w, x, y, z;
    unsigned int blank = (unsigned int)'\n' - ' ';

    for (j = 0; j < num;) {
        n = *(in++) - ' ';
        if (n == blank) {
            n = 0;
            in--;
        }
        if (n > 60) {
            fprintf(stderr, "uuencoded line length too long\n");
            return (-1);
        }
        j++;

        for (i = 0; i < n; j += 4, i += 3) {
            /*
             * the following is for cases where spaces are removed from
             * lines.
             */
            if (space) {
                w = x = y = z = 0;
            } else {
                w = *(in++) - ' ';
                x = *(in++) - ' ';
                y = *(in++) - ' ';
                z = *(in++) - ' ';
            }
            if ((w > 63) || (x > 63) || (y > 63) || (z > 63)) {
                k = 0;
                if (w == blank)
                    k = 1;
                if (x == blank)
                    k = 2;
                if (y == blank)
                    k = 3;
                if (z == blank)
                    k = 4;
                space = 1;
                switch (k) {
                case 1:
                    w = 0;
                    in--;
                case 2:
                    x = 0;
                    in--;
                case 3:
                    y = 0;
                    in--;
                case 4:
                    z = 0;
                    in--;
                    break;
                case 0:
                    space = 0;
                    fprintf(stderr, "bad uuencoded data values\n");
                    w = x = y = z = 0;
                    return (-1);
                    break;
                }
            }
            l = (w << 18) | (x << 12) | (y << 6) | (z);
            l2ccc(l, out);
        }
        if (*(in++) != '\n') {
            fprintf(stderr, "missing nl in uuencoded line\n");
            w = x = y = z = 0;
            return (-1);
        }
        j++;
    }
    *out = '\0';
    w = x = y = z = 0;
    return (n);
}
