change strange depend to libbb.h, but require busybox.h
[oweals/busybox.git] / coreutils / uudecode.c
1 /*
2  *  GPLv2
3  *  Copyright 2003, Glenn McGrath <bug1@iinet.net.au>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License version 2 as published
7  *  by the Free Software Foundation; either version 2 of the License.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU Library General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  *
18  *  Based on specification from
19  *  http://www.opengroup.org/onlinepubs/007904975/utilities/uuencode.html
20  *
21  *  Bugs: the spec doesnt mention anything about "`\n`\n" prior to the "end" line
22  */
23
24
25 #include <stdio.h>
26 #include <errno.h>
27 #include <getopt.h> /* optind */
28 #include <string.h>
29 #include <stdlib.h>
30 #include "busybox.h"
31
32 static int read_stduu(FILE *src_stream, FILE *dst_stream)
33 {
34         char *line;
35
36         while ((line = bb_get_chomped_line_from_file(src_stream)) != NULL) {
37                 int length;
38                 char *line_ptr = line;
39
40                 if (strcmp(line, "end") == 0) {
41                         return(EXIT_SUCCESS);
42                 }
43                 length = ((*line_ptr - 0x20) & 0x3f)* 4 / 3;
44
45                 if (length <= 0) {
46                         /* Ignore the "`\n" line, why is it even in the encode file ? */
47                         continue;
48                 }
49                 if (length > 60) {
50                         bb_error_msg_and_die("Line too long");
51                 }
52
53                 line_ptr++;
54                 /* Tolerate an overly long line to acomadate a possible exta '`' */
55                 if (strlen(line_ptr) < length) {
56                         bb_error_msg_and_die("Short file");
57                 }
58
59                 while (length > 0) {
60                         /* Merge four 6 bit chars to three 8 bit chars */
61                     fputc(((line_ptr[0] - 0x20) & 077) << 2 | ((line_ptr[1] - 0x20) & 077) >> 4, dst_stream);
62                         line_ptr++;
63                         length--;
64                         if (length == 0) {
65                                 break;
66                         }
67
68                         fputc(((line_ptr[0] - 0x20) & 077) << 4 | ((line_ptr[1] - 0x20) & 077) >> 2, dst_stream);
69                         line_ptr++;
70                         length--;
71                         if (length == 0) {
72                                 break;
73                         }
74
75                         fputc(((line_ptr[0] - 0x20) & 077) << 6 | ((line_ptr[1] - 0x20) & 077), dst_stream);
76                         line_ptr += 2;
77                         length -= 2;
78                 }
79                 free(line);
80         }
81         bb_error_msg_and_die("Short file");
82 }
83
84 static int read_base64(FILE *src_stream, FILE *dst_stream)
85 {
86         const char *base64_table =
87                 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n";
88         char term_count = 0;
89
90         while (1) {
91                 char translated[4];
92                 int count = 0;
93
94                 while (count < 4) {
95                         char *table_ptr;
96                         char ch;
97
98                         /* Get next _valid_ character */
99                         do {
100                                 ch = fgetc(src_stream);
101                                 if (ch == EOF) {
102                                         bb_error_msg_and_die("Short file");
103                                 }
104                         } while ((table_ptr = strchr(base64_table, ch)) == NULL);
105
106                         /* Convert encoded charcter to decimal */
107                         ch = table_ptr - base64_table;
108
109                         if (*table_ptr == '=') {
110                                 if (term_count == 0) {
111                                         translated[count] = 0;
112                                         break;
113                                 }
114                                 term_count++;
115                         }
116                         else if (*table_ptr == '\n') {
117                                 /* Check for terminating line */
118                                 if (term_count == 5) {
119                                         return(EXIT_SUCCESS);
120                                 }
121                                 term_count = 1;
122                                 continue;
123                         } else {
124                                 translated[count] = ch;
125                                 count++;
126                                 term_count = 0;
127                         }
128                 }
129
130                 /* Merge 6 bit chars to 8 bit */
131             fputc(translated[0] << 2 | translated[1] >> 4, dst_stream);
132                 if (count > 2) {
133                         fputc(translated[1] << 4 | translated[2] >> 2, dst_stream);
134                 }
135                 if (count > 3) {
136                         fputc(translated[2] << 6 | translated[3], dst_stream);
137                 }
138         }
139 }
140
141 extern int uudecode_main(int argc, char **argv)
142 {
143         int (*decode_fn_ptr) (FILE * src, FILE * dst);
144         FILE *src_stream;
145         char *outname = NULL;
146         char *line;
147         int opt;
148
149         opt = bb_getopt_ulflags(argc, argv, "o:", &outname);
150
151         if (optind == argc) {
152                 src_stream = stdin;
153         } else if (optind + 1 == argc) {
154                 src_stream = bb_xfopen(argv[optind], "r");
155         } else {
156                 bb_show_usage();
157         }
158
159         /* Search for the start of the encoding */
160         while ((line = bb_get_chomped_line_from_file(src_stream)) != NULL) {
161                 char *line_ptr = NULL;
162
163                 if (line == NULL) {
164                         break;
165                 } else if (strncmp(line, "begin-base64 ", 13) == 0) {
166                         line_ptr = line + 13;
167                         decode_fn_ptr = read_base64;
168                 } else if (strncmp(line, "begin ", 6) == 0) {
169                         line_ptr = line + 6;
170                         decode_fn_ptr = read_stduu;
171                 }
172
173                 if (line_ptr) {
174                         FILE *dst_stream;
175                         int mode;
176                         int ret;
177
178                         mode = strtoul(line_ptr, NULL, 8);
179                         if (outname == NULL) {
180                                 outname = strchr(line_ptr, ' ');
181                                 if ((outname == NULL) || (*outname == '\0')) {
182                                         break;
183                                 }
184                                 outname++;
185                         }
186                         if (strcmp(outname, "-") == 0) {
187                                 dst_stream = stdout;
188                         } else {
189                                 dst_stream = bb_xfopen(outname, "w");
190                                 chmod(outname, mode & (S_IRWXU | S_IRWXG | S_IRWXO));
191                         }
192                         free(line);
193                         ret = decode_fn_ptr(src_stream, dst_stream);
194                         bb_fclose_nonstdin(src_stream);
195                         return(ret);
196                 }
197                 free(line);
198         }
199         bb_error_msg_and_die("No `begin' line");
200 }