lineedit: do not hang on error, but return error indicator.
[oweals/busybox.git] / coreutils / uudecode.c
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 #include "libbb.h"
14
15 #if ENABLE_UUDECODE
16 static void read_stduu(FILE *src_stream, FILE *dst_stream)
17 {
18         char *line;
19
20         while ((line = xmalloc_fgetline(src_stream)) != NULL) {
21                 int encoded_len, str_len;
22                 char *line_ptr, *dst;
23
24                 if (strcmp(line, "end") == 0) {
25                         return; /* the only non-error exit */
26                 }
27
28                 line_ptr = line;
29                 while (*line_ptr) {
30                         *line_ptr = (*line_ptr - 0x20) & 0x3f;
31                         line_ptr++;
32                 }
33                 str_len = line_ptr - line;
34
35                 encoded_len = line[0] * 4 / 3;
36                 /* Check that line is not too short. (we tolerate
37                  * overly _long_ line to accomodate possible extra '`').
38                  * Empty line case is also caught here. */
39                 if (str_len <= encoded_len) {
40                         break; /* go to bb_error_msg_and_die("short file"); */
41                 }
42                 if (encoded_len <= 0) {
43                         /* Ignore the "`\n" line, why is it even in the encode file ? */
44                         free(line);
45                         continue;
46                 }
47                 if (encoded_len > 60) {
48                         bb_error_msg_and_die("line too long");
49                 }
50
51                 dst = line;
52                 line_ptr = line + 1;
53                 do {
54                         /* Merge four 6 bit chars to three 8 bit chars */
55                         *dst++ = line_ptr[0] << 2 | line_ptr[1] >> 4;
56                         encoded_len--;
57                         if (encoded_len == 0) {
58                                 break;
59                         }
60
61                         *dst++ = line_ptr[1] << 4 | line_ptr[2] >> 2;
62                         encoded_len--;
63                         if (encoded_len == 0) {
64                                 break;
65                         }
66
67                         *dst++ = line_ptr[2] << 6 | line_ptr[3];
68                         line_ptr += 4;
69                         encoded_len -= 2;
70                 } while (encoded_len > 0);
71                 fwrite(line, 1, dst - line, dst_stream);
72                 free(line);
73         }
74         bb_error_msg_and_die("short file");
75 }
76 #endif
77
78 static void read_base64(FILE *src_stream, FILE *dst_stream)
79 {
80         int term_count = 0;
81
82         while (1) {
83                 unsigned char translated[4];
84                 int count = 0;
85
86                 /* Process one group of 4 chars */
87                 while (count < 4) {
88                         char *table_ptr;
89                         int ch;
90
91                         /* Get next _valid_ character.
92                          * bb_uuenc_tbl_base64[] contains this string:
93                          *  0         1         2         3         4         5         6
94                          *  012345678901234567890123456789012345678901234567890123456789012345
95                          * "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n"
96                          */
97                         do {
98                                 ch = fgetc(src_stream);
99                                 if (ch == EOF) {
100                                         if (ENABLE_BASE64
101                                          && (!ENABLE_UUDECODE || applet_name[0] == 'b')
102                                          && count == 0
103                                         ) {
104                                                 return;
105                                         }
106                                         bb_error_msg_and_die("short file");
107                                 }
108                                 table_ptr = strchr(bb_uuenc_tbl_base64, ch);
109                         } while (!table_ptr);
110
111                         /* Convert encoded character to decimal */
112                         ch = table_ptr - bb_uuenc_tbl_base64;
113
114                         if (ch == 65 /* '\n' */) {
115                                 /* Terminating "====" line? */
116                                 if (term_count == 4)
117                                         return; /* yes */
118                                 term_count = 0;
119                                 continue;
120                         }
121                         /* ch is 64 is char was '=', otherwise 0..63 */
122                         translated[count] = ch & 63; /* 64 -> 0 */
123                         if (ch == 64) {
124                                 term_count++;
125                                 break;
126                         }
127                         count++;
128                 }
129
130                 /* Merge 6 bit chars to 8 bit.
131                  * count can be < 4 when we decode the tail:
132                  * "eQ==" -> "y", not "y NUL NUL"
133                  */
134                 if (count > 1)
135                         fputc(translated[0] << 2 | translated[1] >> 4, dst_stream);
136                 if (count > 2)
137                         fputc(translated[1] << 4 | translated[2] >> 2, dst_stream);
138                 if (count > 3)
139                         fputc(translated[2] << 6 | translated[3], dst_stream);
140         } /* while (1) */
141 }
142
143 #if ENABLE_UUDECODE
144 int uudecode_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
145 int uudecode_main(int argc UNUSED_PARAM, char **argv)
146 {
147         FILE *src_stream;
148         char *outname = NULL;
149         char *line;
150
151         opt_complementary = "?1"; /* 1 argument max */
152         getopt32(argv, "o:", &outname);
153         argv += optind;
154
155         if (!argv[0])
156                 *--argv = (char*)"-";
157         src_stream = xfopen_stdin(argv[0]);
158
159         /* Search for the start of the encoding */
160         while ((line = xmalloc_fgetline(src_stream)) != NULL) {
161                 void (*decode_fn_ptr)(FILE *src, FILE *dst);
162                 char *line_ptr;
163                 FILE *dst_stream;
164                 int mode;
165
166                 if (strncmp(line, "begin-base64 ", 13) == 0) {
167                         line_ptr = line + 13;
168                         decode_fn_ptr = read_base64;
169                 } else if (strncmp(line, "begin ", 6) == 0) {
170                         line_ptr = line + 6;
171                         decode_fn_ptr = read_stduu;
172                 } else {
173                         free(line);
174                         continue;
175                 }
176
177                 /* begin line found. decode and exit */
178                 mode = bb_strtou(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                 dst_stream = stdout;
187                 if (NOT_LONE_DASH(outname)) {
188                         dst_stream = xfopen_for_write(outname);
189                         fchmod(fileno(dst_stream), mode & (S_IRWXU | S_IRWXG | S_IRWXO));
190                 }
191                 free(line);
192                 decode_fn_ptr(src_stream, dst_stream);
193                 /* fclose_if_not_stdin(src_stream); - redundant */
194                 return EXIT_SUCCESS;
195         }
196         bb_error_msg_and_die("no 'begin' line");
197 }
198 #endif
199
200 //applet:IF_BASE64(APPLET(base64, _BB_DIR_BIN, _BB_SUID_DROP))
201
202 //kbuild:lib-$(CONFIG_BASE64) += uudecode.o
203
204 //config:config BASE64
205 //config:       bool "base64"
206 //config:       default y
207 //config:       help
208 //config:         Base64 encode and decode
209
210 //usage:#define base64_trivial_usage
211 //usage:        "[-d] [FILE]"
212 //usage:#define base64_full_usage "\n\n"
213 //usage:       "Base64 encode or decode FILE to standard output"
214 //usage:     "\nOptions:"
215 //usage:     "\n        -d      Decode data"
216 ////usage:     "\n      -w COL  Wrap lines at COL (default 76, 0 disables)"
217 ////usage:     "\n      -i      When decoding, ignore non-alphabet characters"
218
219 #if ENABLE_BASE64
220 int base64_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
221 int base64_main(int argc UNUSED_PARAM, char **argv)
222 {
223         FILE *src_stream;
224         unsigned opts;
225
226         opt_complementary = "?1"; /* 1 argument max */
227         opts = getopt32(argv, "d");
228         argv += optind;
229
230         if (!argv[0])
231                 *--argv = (char*)"-";
232         src_stream = xfopen_stdin(argv[0]);
233         if (opts) {
234                 read_base64(src_stream, stdout);
235         } else {
236                 enum {
237                         SRC_BUF_SIZE = 76/4*3,  /* This *MUST* be a multiple of 3 */
238                         DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3),
239                 };
240                 char src_buf[SRC_BUF_SIZE];
241                 char dst_buf[DST_BUF_SIZE + 1];
242                 int src_fd = fileno(src_stream);
243                 while (1) {
244                         size_t size = full_read(src_fd, src_buf, SRC_BUF_SIZE);
245                         if (!size)
246                                 break;
247                         if ((ssize_t)size < 0)
248                                 bb_perror_msg_and_die(bb_msg_read_error);
249                         /* Encode the buffer we just read in */
250                         bb_uuencode(dst_buf, src_buf, size, bb_uuenc_tbl_base64);
251                         xwrite(STDOUT_FILENO, dst_buf, 4 * ((size + 2) / 3));
252                         bb_putchar('\n');
253                         fflush(stdout);
254                 }
255         }
256
257         fflush_stdout_and_exit(EXIT_SUCCESS);
258 }
259 #endif
260
261 /* Test script.
262 Put this into an empty dir with busybox binary, an run.
263
264 #!/bin/sh
265 test -x busybox || { echo "No ./busybox?"; exit; }
266 ln -sf busybox uudecode
267 ln -sf busybox uuencode
268 >A_null
269 echo -n A >A
270 echo -n AB >AB
271 echo -n ABC >ABC
272 echo -n ABCD >ABCD
273 echo -n ABCDE >ABCDE
274 echo -n ABCDEF >ABCDEF
275 cat busybox >A_bbox
276 for f in A*; do
277     echo uuencode $f
278     ./uuencode    $f <$f >u_$f
279     ./uuencode -m $f <$f >m_$f
280 done
281 mkdir unpk_u unpk_m 2>/dev/null
282 for f in u_*; do
283     ./uudecode <$f -o unpk_u/${f:2}
284     diff -a ${f:2} unpk_u/${f:2} >/dev/null 2>&1
285     echo uudecode $f: $?
286 done
287 for f in m_*; do
288     ./uudecode <$f -o unpk_m/${f:2}
289     diff -a ${f:2} unpk_m/${f:2} >/dev/null 2>&1
290     echo uudecode $f: $?
291 done
292 */