uudecode.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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 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. #include "libbb.h"
  14. static void read_stduu(FILE *src_stream, FILE *dst_stream)
  15. {
  16. char *line;
  17. while ((line = xmalloc_getline(src_stream)) != NULL) {
  18. int encoded_len, str_len;
  19. char *line_ptr, *dst;
  20. if (strcmp(line, "end") == 0) {
  21. return; /* the only non-error exit */
  22. }
  23. line_ptr = line;
  24. while (*line_ptr) {
  25. *line_ptr = (*line_ptr - 0x20) & 0x3f;
  26. line_ptr++;
  27. }
  28. str_len = line_ptr - line;
  29. encoded_len = line[0] * 4 / 3;
  30. /* Check that line is not too short. (we tolerate
  31. * overly _long_ line to accomodate possible extra '`').
  32. * Empty line case is also caught here. */
  33. if (str_len <= encoded_len) {
  34. break; /* go to bb_error_msg_and_die("short file"); */
  35. }
  36. if (encoded_len <= 0) {
  37. /* Ignore the "`\n" line, why is it even in the encode file ? */
  38. free(line);
  39. continue;
  40. }
  41. if (encoded_len > 60) {
  42. bb_error_msg_and_die("line too long");
  43. }
  44. dst = line;
  45. line_ptr = line + 1;
  46. do {
  47. /* Merge four 6 bit chars to three 8 bit chars */
  48. *dst++ = line_ptr[0] << 2 | line_ptr[1] >> 4;
  49. encoded_len--;
  50. if (encoded_len == 0) {
  51. break;
  52. }
  53. *dst++ = line_ptr[1] << 4 | line_ptr[2] >> 2;
  54. encoded_len--;
  55. if (encoded_len == 0) {
  56. break;
  57. }
  58. *dst++ = line_ptr[2] << 6 | line_ptr[3];
  59. line_ptr += 4;
  60. encoded_len -= 2;
  61. } while (encoded_len > 0);
  62. fwrite(line, 1, dst - line, dst_stream);
  63. free(line);
  64. }
  65. bb_error_msg_and_die("short file");
  66. }
  67. static void read_base64(FILE *src_stream, FILE *dst_stream)
  68. {
  69. int term_count = 1;
  70. while (1) {
  71. char translated[4];
  72. int count = 0;
  73. while (count < 4) {
  74. char *table_ptr;
  75. int ch;
  76. /* Get next _valid_ character.
  77. * global vector bb_uuenc_tbl_base64[] contains this string:
  78. * "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n"
  79. */
  80. do {
  81. ch = fgetc(src_stream);
  82. if (ch == EOF) {
  83. bb_error_msg_and_die("short file");
  84. }
  85. table_ptr = strchr(bb_uuenc_tbl_base64, ch);
  86. } while (table_ptr == NULL);
  87. /* Convert encoded character to decimal */
  88. ch = table_ptr - bb_uuenc_tbl_base64;
  89. if (*table_ptr == '=') {
  90. if (term_count == 0) {
  91. translated[count] = '\0';
  92. break;
  93. }
  94. term_count++;
  95. } else if (*table_ptr == '\n') {
  96. /* Check for terminating line */
  97. if (term_count == 5) {
  98. return;
  99. }
  100. term_count = 1;
  101. continue;
  102. } else {
  103. translated[count] = ch;
  104. count++;
  105. term_count = 0;
  106. }
  107. }
  108. /* Merge 6 bit chars to 8 bit */
  109. if (count > 1) {
  110. fputc(translated[0] << 2 | translated[1] >> 4, dst_stream);
  111. }
  112. if (count > 2) {
  113. fputc(translated[1] << 4 | translated[2] >> 2, dst_stream);
  114. }
  115. if (count > 3) {
  116. fputc(translated[2] << 6 | translated[3], dst_stream);
  117. }
  118. }
  119. }
  120. int uudecode_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  121. int uudecode_main(int argc ATTRIBUTE_UNUSED, char **argv)
  122. {
  123. FILE *src_stream;
  124. char *outname = NULL;
  125. char *line;
  126. opt_complementary = "?1"; /* 1 argument max */
  127. getopt32(argv, "o:", &outname);
  128. argv += optind;
  129. if (!*argv)
  130. *--argv = (char*)"-";
  131. src_stream = xfopen_stdin(*argv);
  132. /* Search for the start of the encoding */
  133. while ((line = xmalloc_getline(src_stream)) != NULL) {
  134. void (*decode_fn_ptr)(FILE * src, FILE * dst);
  135. char *line_ptr;
  136. FILE *dst_stream;
  137. int mode;
  138. if (strncmp(line, "begin-base64 ", 13) == 0) {
  139. line_ptr = line + 13;
  140. decode_fn_ptr = read_base64;
  141. } else if (strncmp(line, "begin ", 6) == 0) {
  142. line_ptr = line + 6;
  143. decode_fn_ptr = read_stduu;
  144. } else {
  145. free(line);
  146. continue;
  147. }
  148. /* begin line found. decode and exit */
  149. mode = bb_strtou(line_ptr, NULL, 8);
  150. if (outname == NULL) {
  151. outname = strchr(line_ptr, ' ');
  152. if ((outname == NULL) || (*outname == '\0')) {
  153. break;
  154. }
  155. outname++;
  156. }
  157. dst_stream = stdout;
  158. if (NOT_LONE_DASH(outname)) {
  159. dst_stream = xfopen(outname, "w");
  160. fchmod(fileno(dst_stream), mode & (S_IRWXU | S_IRWXG | S_IRWXO));
  161. }
  162. free(line);
  163. decode_fn_ptr(src_stream, dst_stream);
  164. /* fclose_if_not_stdin(src_stream); - redundant */
  165. return EXIT_SUCCESS;
  166. }
  167. bb_error_msg_and_die("no 'begin' line");
  168. }
  169. /* Test script.
  170. Put this into an empty dir with busybox binary, an run.
  171. #!/bin/sh
  172. test -x busybox || { echo "No ./busybox?"; exit; }
  173. ln -sf busybox uudecode
  174. ln -sf busybox uuencode
  175. >A_null
  176. echo -n A >A
  177. echo -n AB >AB
  178. echo -n ABC >ABC
  179. echo -n ABCD >ABCD
  180. echo -n ABCDE >ABCDE
  181. echo -n ABCDEF >ABCDEF
  182. cat busybox >A_bbox
  183. for f in A*; do
  184. echo uuencode $f
  185. ./uuencode $f <$f >u_$f
  186. ./uuencode -m $f <$f >m_$f
  187. done
  188. mkdir unpk_u unpk_m 2>/dev/null
  189. for f in u_*; do
  190. ./uudecode <$f -o unpk_u/${f:2}
  191. diff -a ${f:2} unpk_u/${f:2} >/dev/null 2>&1
  192. echo uudecode $f: $?
  193. done
  194. for f in m_*; do
  195. ./uudecode <$f -o unpk_m/${f:2}
  196. diff -a ${f:2} unpk_m/${f:2} >/dev/null 2>&1
  197. echo uudecode $f: $?
  198. done
  199. */