Standardize on the vi editing directives being on the first line.
[oweals/busybox.git] / coreutils / uudecode.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  *  Copyright 2003, Glenn McGrath <bug1@iinet.net.au>
4  *
5  *  Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
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
14
15 #include <stdio.h>
16 #include <errno.h>
17 #include <getopt.h> /* optind */
18 #include <string.h>
19 #include <stdlib.h>
20 #include "busybox.h"
21
22 static int read_stduu(FILE *src_stream, FILE *dst_stream)
23 {
24         char *line;
25
26         while ((line = bb_get_chomped_line_from_file(src_stream)) != NULL) {
27                 int length;
28                 char *line_ptr = line;
29
30                 if (strcmp(line, "end") == 0) {
31                         return(EXIT_SUCCESS);
32                 }
33                 length = ((*line_ptr - 0x20) & 0x3f)* 4 / 3;
34
35                 if (length <= 0) {
36                         /* Ignore the "`\n" line, why is it even in the encode file ? */
37                         continue;
38                 }
39                 if (length > 60) {
40                         bb_error_msg_and_die("Line too long");
41                 }
42
43                 line_ptr++;
44                 /* Tolerate an overly long line to acomadate a possible exta '`' */
45                 if (strlen(line_ptr) < (size_t)length) {
46                         bb_error_msg_and_die("Short file");
47                 }
48
49                 while (length > 0) {
50                         /* Merge four 6 bit chars to three 8 bit chars */
51                         fputc(((line_ptr[0] - 0x20) & 077) << 2 | ((line_ptr[1] - 0x20) & 077) >> 4, dst_stream);
52                         line_ptr++;
53                         length--;
54                         if (length == 0) {
55                                 break;
56                         }
57
58                         fputc(((line_ptr[0] - 0x20) & 077) << 4 | ((line_ptr[1] - 0x20) & 077) >> 2, dst_stream);
59                         line_ptr++;
60                         length--;
61                         if (length == 0) {
62                                 break;
63                         }
64
65                         fputc(((line_ptr[0] - 0x20) & 077) << 6 | ((line_ptr[1] - 0x20) & 077), dst_stream);
66                         line_ptr += 2;
67                         length -= 2;
68                 }
69                 free(line);
70         }
71         bb_error_msg_and_die("Short file");
72 }
73
74 static int read_base64(FILE *src_stream, FILE *dst_stream)
75 {
76         static const char base64_table[] =
77                 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n";
78         char term_count = 0;
79
80         while (1) {
81                 char translated[4];
82                 int count = 0;
83
84                 while (count < 4) {
85                         char *table_ptr;
86                         int ch;
87
88                         /* Get next _valid_ character */
89                         do {
90                                 ch = fgetc(src_stream);
91                                 if (ch == EOF) {
92                                         bb_error_msg_and_die("Short file");
93                                 }
94                         } while ((table_ptr = strchr(base64_table, ch)) == NULL);
95
96                         /* Convert encoded charcter to decimal */
97                         ch = table_ptr - base64_table;
98
99                         if (*table_ptr == '=') {
100                                 if (term_count == 0) {
101                                         translated[count] = 0;
102                                         break;
103                                 }
104                                 term_count++;
105                         }
106                         else if (*table_ptr == '\n') {
107                                 /* Check for terminating line */
108                                 if (term_count == 5) {
109                                         return(EXIT_SUCCESS);
110                                 }
111                                 term_count = 1;
112                                 continue;
113                         } else {
114                                 translated[count] = ch;
115                                 count++;
116                                 term_count = 0;
117                         }
118                 }
119
120                 /* Merge 6 bit chars to 8 bit */
121             fputc(translated[0] << 2 | translated[1] >> 4, dst_stream);
122                 if (count > 2) {
123                         fputc(translated[1] << 4 | translated[2] >> 2, dst_stream);
124                 }
125                 if (count > 3) {
126                         fputc(translated[2] << 6 | translated[3], dst_stream);
127                 }
128         }
129 }
130
131 int uudecode_main(int argc, char **argv)
132 {
133         int (*decode_fn_ptr) (FILE * src, FILE * dst);
134         FILE *src_stream;
135         char *outname = NULL;
136         char *line;
137         int opt;
138
139         opt = bb_getopt_ulflags(argc, argv, "o:", &outname);
140
141         if (optind == argc) {
142                 src_stream = stdin;
143         } else if (optind + 1 == argc) {
144                 src_stream = bb_xfopen(argv[optind], "r");
145         } else {
146                 bb_show_usage();
147         }
148
149         /* Search for the start of the encoding */
150         while ((line = bb_get_chomped_line_from_file(src_stream)) != NULL) {
151                 char *line_ptr = NULL;
152
153                 if (line == NULL) {
154                         break;
155                 } else if (strncmp(line, "begin-base64 ", 13) == 0) {
156                         line_ptr = line + 13;
157                         decode_fn_ptr = read_base64;
158                 } else if (strncmp(line, "begin ", 6) == 0) {
159                         line_ptr = line + 6;
160                         decode_fn_ptr = read_stduu;
161                 }
162
163                 if (line_ptr) {
164                         FILE *dst_stream;
165                         int mode;
166                         int ret;
167
168                         mode = strtoul(line_ptr, NULL, 8);
169                         if (outname == NULL) {
170                                 outname = strchr(line_ptr, ' ');
171                                 if ((outname == NULL) || (*outname == '\0')) {
172                                         break;
173                                 }
174                                 outname++;
175                         }
176                         if (strcmp(outname, "-") == 0) {
177                                 dst_stream = stdout;
178                         } else {
179                                 dst_stream = bb_xfopen(outname, "w");
180                                 chmod(outname, mode & (S_IRWXU | S_IRWXG | S_IRWXO));
181                         }
182                         free(line);
183                         ret = decode_fn_ptr(src_stream, dst_stream);
184                         bb_fclose_nonstdin(src_stream);
185                         return(ret);
186                 }
187                 free(line);
188         }
189         bb_error_msg_and_die("No `begin' line");
190 }