4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
23 /* All Rights Reserved */
25 /* Copyright 2004 Sun Microsystems, Inc. All rights reserved. */
26 /* Use is subject to license terms. */
28 #pragma ident "%Z%%M% %I% %E% SMI"
31 * uudecode [-o outfile | -p] [input]
33 * create the specified file, decoding as you go.
42 #include <sys/types.h>
54 #define BUFSIZE 90 /* must be a multiple of 3 */
56 #define TABLE_SIZE 0x40
58 #define isvalid(octet) (octet <= 0x40)
61 * base64 decoding table
64 static char base64tab
[] = {
65 '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377',
66 '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377',
67 '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377',
68 '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377',
69 '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377',
70 '\377', '\377', '\377', 62, '\377', '\377', '\377', 63,
71 52, 53, 54, 55, 56, 57, 58, 59,
72 60, 61, '\377', '\377', '\377', '\377', '\377', '\377',
73 '\377', 0, 1, 2, 3, 4, 5, 6,
74 7, 8, 9, 10, 11, 12, 13, 14,
75 15, 16, 17, 18, 19, 20, 21, 22,
76 23, 24, 25, '\377', '\377', '\377', '\377', '\377',
77 '\377', 26, 27, 28, 29, 30, 31, 32,
78 33, 34, 35, 36, 37, 38, 39, 40,
79 41, 42, 43, 44, 45, 46, 47, 48,
80 49, 50, 51, '\377', '\377', '\377', '\377', '\377'
84 static char decode_table
[UCHAR_MAX
+ 1];
86 /* DEC is the basic 1 character decoding function (historical algorithm) */
87 #define DEC(c) decode_table[c]
89 /* true if the character is in the base64 encoding table */
90 #define validbase64(c) (('A' <= (c) && (c) <= 'Z') || \
91 ('a' <= (c) && (c) <= 'z') || \
92 ('0' <= (c) && (c) <= '9') || \
93 (c) == '+' || (c) == '/')
95 static void decode(FILE *, FILE *, int);
96 static int outdec(unsigned char *, unsigned char *, int);
97 static int outdec64(unsigned char *, unsigned char *, int);
99 /* from usr/src/cmd/chmod/common.c */
101 void errmsg(int severity
, int code
, char *format
, ...);
103 extern mode_t
newmode(char *ms
, mode_t new_mode
, mode_t umsk
,
104 char *file
, char *path
);
107 static char outfile
[PATH_MAX
];
108 static int mode_err
= 0; /* mode conversion error flag */
111 main(int argc
, char **argv
)
117 mode_t mode
, p_umask
;
126 (void) signal(SIGPIPE
, SIG_DFL
);
127 bzero(dest
, sizeof (dest
));
130 /* Set locale environment variables local definitions */
131 (void) setlocale(LC_ALL
, "");
132 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
133 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */
135 (void) textdomain(TEXT_DOMAIN
);
136 p_umask
= umask((mode_t
)0);
138 while ((c
= getopt(argc
, argv
, "o:p")) != EOF
) {
142 (void) strncpy(outfile
, optarg
, sizeof (outfile
));
154 argv
= &argv
[optind
];
156 /* optional input arg */
158 if ((in
= fopen(*argv
, "r")) == NULL
) {
166 if (fstat(fileno(in
), &sbuf
) < 0) {
172 if ((argc
> 0) || errflag
|| (oflag
&& pipeout
)) {
173 (void) fprintf(stderr
,
174 gettext("Usage: %s [-o outfile | -p] [infile]\n"), prog
);
178 /* search for header line */
180 if (fgets(buf
, sizeof (buf
), in
) == NULL
) {
181 /* suppress message if we printed a mode error */
183 (void) fprintf(stderr
,
184 gettext("No begin line\n"));
188 * the check for begin-base64 obviously needs to come
189 * first, since both algorithms' begin strings start
190 * with 'begin'. Also verify that there is a valid
191 * octal or symbolic file mode.
193 if (strncmp(buf
, "begin-base64", 12) == 0) {
196 if ((sscanf(buf
+ 13, "%1023s %1023s",
197 modebits
, dest
) == 2) &&
198 ((sscanf(modebits
, "%lo", &mode
) == 1) ||
199 ((mode
= newmode(modebits
, 0, p_umask
,
200 "", "")) != 0) && mode_err
== 0))
202 } else if (strncmp(buf
, "begin", 5) == 0) {
205 if ((sscanf(buf
+ 6, "%1023s %1023s",
206 modebits
, dest
) == 2) &&
207 ((sscanf(modebits
, "%lo", &mode
) == 1) ||
208 ((mode
= newmode(modebits
, 0, p_umask
,
209 "", "")) != 0) && mode_err
== 0))
215 * Now that we know the type of encoding used, we can
216 * initialize the decode table.
218 if (base64flag
== 0) {
219 (void) memset(decode_table
, 0xFF, sizeof (decode_table
));
220 for (i
= 0; i
<= TABLE_SIZE
; i
++)
221 decode_table
[(unsigned char)i
+ 0x20] =
222 (unsigned char)i
& 0x3F;
224 (void) memcpy(decode_table
, base64tab
, sizeof (base64tab
));
227 * Filename specified on the command line with -o
228 * overrides filename in the encoded file.
230 if (outfile
[0] != '\0')
231 (void) strncpy(dest
, outfile
, sizeof (dest
));
234 (dest
[0] == '-' && dest
[1] == '\0' && outfile
[0] == '\0')) {
236 bzero(outfile
, sizeof (outfile
));
237 bzero(dest
, sizeof (dest
));
239 /* handle ~user/file format */
240 if (dest
[0] == '~') {
245 sl
= strchr(dest
, '/');
247 (void) fprintf(stderr
,
248 gettext("Illegal ~user\n"));
252 user
= getpwnam(dest
+1);
254 (void) fprintf(stderr
,
255 gettext("No such user as %s\n"), dest
);
258 (void) strncpy(dnbuf
, user
->pw_dir
, sizeof (dnbuf
));
259 (void) strlcat(dnbuf
, "/", sizeof (dnbuf
));
260 (void) strlcat(dnbuf
, sl
, sizeof (dnbuf
));
261 (void) strncpy(dest
, dnbuf
, sizeof (dest
));
264 /* if not using stdout, create file */
265 if (dest
[0] != '\0') {
266 if ((out
= fopen(dest
, "w")) == NULL
) {
270 (void) chmod(dest
, mode
& 0777);
273 decode(in
, out
, base64flag
);
275 if (fclose(out
) == EOF
) {
284 * copy from in to out, decoding as you go along.
288 decode(FILE *in
, FILE *out
, int base64
)
290 char inbuf
[120], *ibp
, *iptr
;
291 unsigned char outbuf
[BUFSIZE
], *obp
, *optr
;
292 int n
, octets
, warned
, endseen
, numbase64chars
;
293 unsigned char chr
[4], curchr
, ch
;
296 if (! base64
) { /* use historical algorithm */
298 for (line
= 1; ; line
++) {
299 /* for each input line */
300 if (fgets(inbuf
, sizeof (inbuf
), in
) == NULL
) {
301 (void) fprintf(stderr
,
302 gettext("No end line\n"));
306 /* Is line == 'end\n'? */
307 if (strcmp(inbuf
, "end\n") == 0) {
319 * Note that uuencode/uudecode uses only the portable
320 * character set for encoded data and the portable
321 * character set characters must be represented in
322 * a single byte. We use this knowledge to reuse
323 * buffer space while decoding.
326 obp
= (unsigned char *) &inbuf
[0];
329 if ((ch
= outdec((unsigned char *)obp
,
330 (unsigned char *)ibp
, octets
))
332 /* invalid characters where detected */
335 (void) fprintf(stderr
,
336 gettext("Invalid character"
338 " %lld\n"), ch
, line
);
347 * Only write out uncorrupted lines
350 (void) fwrite(inbuf
, n
, 1, out
);
353 } else { /* use base64 algorithm */
354 endseen
= numbase64chars
= 0;
356 while ((fgets(inbuf
, sizeof (inbuf
), in
)) != NULL
) {
357 /* process an input line */
359 while ((curchr
= *(iptr
++)) != NULL
) {
361 if (curchr
== '=') /* if end */
364 if (validbase64(curchr
))
365 chr
[numbase64chars
++] = curchr
;
367 * if we've gathered 4 base64 octets
368 * we need to decode and output them
370 if (numbase64chars
== 4) {
372 if (optr
- outbuf
> BUFSIZE
- 3) {
373 (void) fwrite(outbuf
,
375 (size_t)(optr
- outbuf
),
383 octets
= outdec64(optr
, chr
, 4);
389 * handle any remaining base64 octets at end
391 if (endseen
&& numbase64chars
> 0) {
392 octets
= outdec64(optr
, chr
, numbase64chars
);
398 * if we have generated any additional output
399 * in the buffer, write it out
401 if (optr
!= outbuf
) {
403 (void) fwrite(outbuf
, (size_t)(optr
- outbuf
),
412 (void) fprintf(stderr
, gettext("No end line\n"));
419 * historical algorithm
421 * output a group of 3 bytes (4 input characters).
422 * the input chars are pointed to by p, they are to
423 * be output to file f. n is used to tell us not to
424 * output all of them at the end of the file.
428 outdec(unsigned char *out
, unsigned char *in
, int n
)
430 unsigned char b0
= DEC(*(in
++));
431 unsigned char b1
= DEC(*(in
++));
432 unsigned char b2
= DEC(*(in
++));
433 unsigned char b3
= DEC(*in
);
442 *(out
++) = (b0
<< 2) | (b1
>> 4);
449 *(out
++) = (b1
<< 4) | (b2
>> 2);
455 *out
= (b2
<< 6) | b3
;
458 return (0x20); /* a know good value */
464 * Takes a pointer to the current position in the output buffer,
465 * a pointer to the (up to) 4 byte base64 input buffer and a
466 * count of the number of valid input bytes.
468 * Return the number of bytes placed in the output buffer
471 outdec64(unsigned char *out
, unsigned char *chr
, int num
)
474 unsigned char char1
, char2
, char3
, char4
;
475 unsigned char *outptr
= out
;
480 case 1: /* these are impossible */
483 case 2: /* 2 base64 bytes == 1 decoded byte */
484 char1
= base64tab
[chr
[0]] & 0xFF;
485 char2
= base64tab
[chr
[1]] & 0xFF;
486 *(outptr
++) = ((char1
<< 2) & 0xFC) |
487 ((char2
>> 4) & 0x03);
490 case 3: /* 3 base64 bytes == 2 decoded bytes */
491 char1
= base64tab
[chr
[0]] & 0xFF;
492 char2
= base64tab
[chr
[1]] & 0xFF;
493 char3
= base64tab
[chr
[2]] & 0xFF;
494 *(outptr
++) = ((char1
<< 2) & 0xFC) |
495 ((char2
>> 4) & 0x03);
496 *(outptr
++) = ((char2
<< 4) & 0xF0) |
497 ((char3
>> 2) & 0x0F);
500 case 4: /* 4 base64 bytes == 3 decoded bytes */
501 char1
= base64tab
[chr
[0]] & 0xFF;
502 char2
= base64tab
[chr
[1]] & 0xFF;
503 char3
= base64tab
[chr
[2]] & 0xFF;
504 char4
= base64tab
[chr
[3]] & 0xFF;
505 *(outptr
++) = ((char1
<< 2) & 0xFC) |
506 ((char2
>> 4) & 0x03);
507 *(outptr
++) = ((char2
<< 4) & 0xF0) |
508 ((char3
>> 2) & 0x0F);
509 *(outptr
++) = ((char3
<< 6) & 0xC0) |
518 * error message routine called by newmode.
520 * The severity and code are ignored here. If this routine gets
521 * called, we set a global flag (which can be tested after return
522 * from here) which tells us whether or not a valid mode has been
523 * parsed or if we printed an error message.
528 errmsg(int severity
, int code
, char *format
, ...)
532 va_start(ap
, format
);
534 (void) fprintf(stderr
, "uudecode: ");
535 (void) fprintf(stderr
, format
, ap
);