[git commit] libbb: split decode_base64 off read_base64

Denys Vlasenko vda.linux at googlemail.com
Fri Oct 28 11:59:55 UTC 2011


commit: http://git.busybox.net/busybox/commit/?id=328f27fe447761f355104e7f524dc1115f16ca44
branch: http://git.busybox.net/busybox/commit/?id=refs/heads/master

function                                             old     new   delta
decode_base64                                          -     182    +182
read_base64                                          378     255    -123
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 0/1 up/down: 182/-123)           Total: 59 bytes

Signed-off-by: Leonid Lisovskiy <lly.dev at gmail.com>
Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 include/libbb.h          |    3 +-
 libbb/uuencode.c         |  125 ++++++++++++++++++++++++++++++++++++---------
 testsuite/uuencode.tests |  101 ++++++++++++++++++++++++++++++++++++-
 3 files changed, 200 insertions(+), 29 deletions(-)

diff --git a/include/libbb.h b/include/libbb.h
index d248781..791cdd9 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -1591,7 +1591,8 @@ enum {
 	/* Sign-extends to a value which never matches fgetc result: */
 	BASE64_FLAG_NO_STOP_CHAR = 0x80,
 };
-void FAST_FUNC read_base64(FILE *src_stream, FILE *dst_stream, int flags);
+const char *decode_base64(char **pp_dst, const char *src) FAST_FUNC;
+void read_base64(FILE *src_stream, FILE *dst_stream, int flags) FAST_FUNC;
 
 typedef struct md5_ctx_t {
 	uint8_t wbuffer[64]; /* always correctly aligned for uint64_t */
diff --git a/libbb/uuencode.c b/libbb/uuencode.c
index 03e708f..23e2123 100644
--- a/libbb/uuencode.c
+++ b/libbb/uuencode.c
@@ -73,23 +73,23 @@ void FAST_FUNC bb_uuencode(char *p, const void *src, int length, const char *tbl
 }
 
 /*
- * Decode base64 encoded stream.
- * Can stop on EOF, specified char, or on uuencode-style "====" line:
- * flags argument controls it.
+ * Decode base64 encoded string. Stops on '\0'.
+ *
+ * Returns: pointer to the undecoded part of source.
+ * If points to '\0', then the source was fully decoded.
+ * (*dst): advanced past the last written byte.
  */
-void FAST_FUNC read_base64(FILE *src_stream, FILE *dst_stream, int flags)
+const char* FAST_FUNC decode_base64(char **pp_dst, const char *src)
 {
-/* Note that EOF _can_ be passed as exit_char too */
-#define exit_char    ((int)(signed char)flags)
-#define uu_style_end (flags & BASE64_FLAG_UU_STOP)
-
-	int term_count = 0;
+	char *dst = *pp_dst;
+	const char *src_tail;
 
 	while (1) {
 		unsigned char translated[4];
 		int count = 0;
 
 		/* Process one group of 4 chars */
+		src_tail = src;
 		while (count < 4) {
 			char *table_ptr;
 			int ch;
@@ -101,11 +101,20 @@ void FAST_FUNC read_base64(FILE *src_stream, FILE *dst_stream, int flags)
 			 * "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n"
 			 */
 			do {
-				ch = fgetc(src_stream);
-				if (ch == exit_char && count == 0)
-					return;
-				if (ch == EOF)
-					bb_error_msg_and_die("truncated base64 input");
+				ch = *src;
+				if (ch == '\0') {
+					if (count == 0) {
+						/* Example:
+						 * If we decode "QUJD <NUL>", we want
+						 * to return ptr to NUL, not to ' ',
+						 * because we did fully decode
+						 * the string (to "ABC").
+						 */
+						src_tail = src;
+					}
+					goto ret;
+				}
+				src++;
 				table_ptr = strchr(bb_uuenc_tbl_base64, ch);
 //TODO: add BASE64_FLAG_foo to die on bad char?
 //Note that then we may need to still allow '\r' (for mail processing)
@@ -114,21 +123,15 @@ void FAST_FUNC read_base64(FILE *src_stream, FILE *dst_stream, int flags)
 			/* Convert encoded character to decimal */
 			ch = table_ptr - bb_uuenc_tbl_base64;
 
-			if (ch == 65 /* '\n' */) {
-				/* Terminating "====" line? */
-				if (uu_style_end && term_count == 4)
-					return; /* yes */
-				term_count = 0;
+			if (ch == 65) {  /* '\n' */
 				continue;
 			}
 			/* ch is 64 if char was '=', otherwise 0..63 */
 			translated[count] = ch & 63; /* 64 -> 0 */
-			if (ch == 64) {
-				term_count++;
+			if (ch == 64) {  /* '=' */
 				break;
 			}
 			count++;
-			term_count = 0;
 		}
 
 		/* Merge 6 bit chars to 8 bit.
@@ -136,10 +139,82 @@ void FAST_FUNC read_base64(FILE *src_stream, FILE *dst_stream, int flags)
 		 * "eQ==" -> "y", not "y NUL NUL"
 		 */
 		if (count > 1)
-			fputc(translated[0] << 2 | translated[1] >> 4, dst_stream);
+			*dst++ = translated[0] << 2 | translated[1] >> 4;
 		if (count > 2)
-			fputc(translated[1] << 4 | translated[2] >> 2, dst_stream);
+			*dst++ = translated[1] << 4 | translated[2] >> 2;
 		if (count > 3)
-			fputc(translated[2] << 6 | translated[3], dst_stream);
+			*dst++ = translated[2] << 6 | translated[3];
 	} /* while (1) */
+ ret:
+	*pp_dst = dst;
+	return src_tail;
+}
+
+/*
+ * Decode base64 encoded stream.
+ * Can stop on EOF, specified char, or on uuencode-style "====" line:
+ * flags argument controls it.
+ */
+void FAST_FUNC read_base64(FILE *src_stream, FILE *dst_stream, int flags)
+{
+/* Note that EOF _can_ be passed as exit_char too */
+#define exit_char    ((int)(signed char)flags)
+#define uu_style_end (flags & BASE64_FLAG_UU_STOP)
+
+	/* uuencoded files have 61 byte lines. Use 64 byte buffer
+	 * to process line at a time.
+	 */
+	enum { BUFFER_SIZE = 64 };
+
+	char in_buf[BUFFER_SIZE + 2];
+	char out_buf[BUFFER_SIZE / 4 * 3 + 2];
+	char *out_tail;
+	const char *in_tail;
+	int term_seen = 0;
+	int in_count = 0;
+
+	while (1) {
+		while (in_count < BUFFER_SIZE) {
+			int ch = fgetc(src_stream);
+			if (ch == exit_char) {
+				if (in_count == 0)
+					return;
+				term_seen = 1;
+				break;
+			}
+			if (ch == EOF) {
+				term_seen = 1;
+				break;
+			}
+			/* Prevent "====" line to be split: stop if we see '\n'.
+			 * We can also skip other whitespace and skirt the problem
+			 * of files with NULs by stopping on any control char or space:
+			 */
+			if (ch <= ' ')
+				break;
+			in_buf[in_count++] = ch;
+		}
+		in_buf[in_count] = '\0';
+
+		/* Did we encounter "====" line? */
+		if (uu_style_end && strcmp(in_buf, "====") == 0)
+			return;
+
+		out_tail = out_buf;
+		in_tail = decode_base64(&out_tail, in_buf);
+
+                fwrite(out_buf, (out_tail - out_buf), 1, dst_stream);
+
+		if (term_seen) {
+			/* Did we consume ALL characters? */
+			if (*in_tail == '\0')
+				return;
+			/* No */
+			bb_error_msg_and_die("truncated base64 input");
+		}
+
+		/* It was partial decode */
+		in_count = strlen(in_tail);
+		memmove(in_buf, in_tail, in_count);
+	}
 }
diff --git a/testsuite/uuencode.tests b/testsuite/uuencode.tests
index cd6191b..6ce70f7 100755
--- a/testsuite/uuencode.tests
+++ b/testsuite/uuencode.tests
@@ -8,9 +8,9 @@
 
 . ./testing.sh
 
-# testing "test name" "options" "expected result" "file input" "stdin"
-#   file input will be file called "input"
-#   test can create a file "actual" instead of writing to stdout
+# testing "test name" "command(s)" "expected result" "file input" "stdin"
+# file input will be file called "input"
+# test can create a file "actual" instead of writing to stdout
 
 # Test setup of standard input
 umask 0
@@ -24,4 +24,99 @@ testing "uuencode correct encoding" "uuencode bb_uuenc_test.out" \
 testing "uuencode correct base64 encoding" "uuencode -m bb_uuenc_test.out" \
 "begin-base64 644 bb_uuenc_test.out\nVGhlIGZhc3QgZ3JleSBmb3gganVtcGVkIG92ZXIgdGhlIGxhenkgYnJvd24g\nZG9nLgo=\n====\n" \
         "" "The fast grey fox jumped over the lazy brown dog.\n"
+
+testing "uuencode empty file" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin 644 FILE
+`
+end
+' "" ""
+testing "uuencode -m empty file" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin-base64 644 FILE
+====
+' "" ""
+
+testing "uuencode file 'A'" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin 644 FILE
+!00``
+`
+end
+A' "" "A"
+testing "uuencode -m file 'A'" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin-base64 644 FILE
+QQ==
+====
+A' "" "A"
+
+testing "uuencode file 'AB'" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin 644 FILE
+"04(`
+`
+end
+AB' "" "AB"
+testing "uuencode -m file 'AB'" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin-base64 644 FILE
+QUI=
+====
+AB' "" "AB"
+
+testing "uuencode file 'ABC'" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin 644 FILE
+#04)#
+`
+end
+ABC' "" "ABC"
+testing "uuencode -m file 'ABC'" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin-base64 644 FILE
+QUJD
+====
+ABC' "" "ABC"
+
+testing "uuencode file 'ABCD'" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin 644 FILE
+$04)#1```
+`
+end
+ABCD' "" "ABCD"
+testing "uuencode -m file 'ABCD'" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin-base64 644 FILE
+QUJDRA==
+====
+ABCD' "" "ABCD"
+
+testing "uuencode file 'ABCDE'" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin 644 FILE
+%04)#1$4`
+`
+end
+ABCDE' "" "ABCDE"
+testing "uuencode -m file 'ABCDE'" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin-base64 644 FILE
+QUJDREU=
+====
+ABCDE' "" "ABCDE"
+
+testing "uuencode file 'ABCDEF'" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin 644 FILE
+&04)#1$5&
+`
+end
+ABCDEF' "" "ABCDEF"
+testing "uuencode -m file 'ABCDEF'" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin-base64 644 FILE
+QUJDREVG
+====
+ABCDEF' "" "ABCDEF"
+
+testing "uuencode file 'A<NUL><0xff>Z'" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin 644 FILE
+$00#_6@``
+`
+end
+A\x0\xffZ' "" "A\x0\xffZ"
+testing "uuencode -m file 'A<NUL><0xff>Z'" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin-base64 644 FILE
+QQD/Wg==
+====
+A\x0\xffZ' "" "A\x0\xffZ"
+
 exit $FAILCOUNT


More information about the busybox-cvs mailing list