>From 81b7619144a526971a3b19c7bf7c263e133081d1 Mon Sep 17 00:00:00 2001
From: Dennis Wehrle <dennis.wehrle@rz.uni-freiburg.de>
Date: Thu, 7 Jul 2011 18:56:33 +0200
Subject: [PATCH] sms: Due to a misuse of user_data_len the sms where cropped (if sent from vty) and wrongly stored on the database.
     Additionally it wasn't possible to send concatenated sms from the vty.
     To send multiple sms, it is necessary to use padding bits and add a user_data_header.
     Therefore the gsm_7bit_encode function was splitted to gsm_7bit_encode and gsm_septets2octets.
     gsm_septets2octets: this is the old gsm_7bit_encode function + additional padding parameter

    Additionally the gsm_7bit_decode function was modified to take account for the user_data_header.
    With the new gsm_get_octet_len function you can get the octet length for a given septet length.

    I also added several sms tests.
---
 include/osmocom/gsm/gsm_utils.h |    6 +-
 include/osmocom/vty/command.h   |    2 +-
 src/gsm/gsm0480.c               |    2 +-
 src/gsm/gsm_utils.c             |  109 ++++++++++++++-----
 tests/sms/sms_test.c            |  226 ++++++++++++++++++++++++++++++++++++---
 5 files changed, 298 insertions(+), 47 deletions(-)

diff --git a/include/osmocom/gsm/gsm_utils.h b/include/osmocom/gsm/gsm_utils.h
index 405dfe3..bdf982e 100644
--- a/include/osmocom/gsm/gsm_utils.h
+++ b/include/osmocom/gsm/gsm_utils.h
@@ -56,9 +56,13 @@ enum gsm_band {
 const char *gsm_band_name(enum gsm_band band);
 enum gsm_band gsm_band_parse(const char *mhz);
 
-int gsm_7bit_decode(char *decoded, const uint8_t *user_data, uint8_t length);
+int gsm_7bit_decode(char *decoded, const uint8_t *user_data, uint8_t length, uint8_t ud_hdr_ind);
 int gsm_7bit_encode(uint8_t *result, const char *data);
 
+int gsm_septets2octets(uint8_t *result, uint8_t *rdata, uint8_t septet_len, uint8_t padding);
+int gsm_septet_encode(uint8_t *result, const char *data);
+uint8_t gsm_get_octet_len(const uint8_t sept_len);
+
 unsigned int ms_class_gmsk_dbm(enum gsm_band band, int class);
 
 int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm);
diff --git a/include/osmocom/vty/command.h b/include/osmocom/vty/command.h
index caf0414..c13912f 100644
--- a/include/osmocom/vty/command.h
+++ b/include/osmocom/vty/command.h
@@ -134,7 +134,7 @@ struct desc {
 #define CMD_SUCCESS_DAEMON      10
 
 /* Argc max counts. */
-#define CMD_ARGC_MAX   25
+#define CMD_ARGC_MAX   256
 
 /* Turn off these macros when uisng cpp with extract.pl */
 #ifndef VTYSH_EXTRACT_PL
diff --git a/src/gsm/gsm0480.c b/src/gsm/gsm0480.c
index b9b3ed9..74d091c 100644
--- a/src/gsm/gsm0480.c
+++ b/src/gsm/gsm0480.c
@@ -402,7 +402,7 @@ static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length,
 				if (num_chars > MAX_LEN_USSD_STRING)
 					num_chars = MAX_LEN_USSD_STRING;
 				gsm_7bit_decode(req->text,
-						&(uss_req_data[7]), num_chars);
+						&(uss_req_data[7]), num_chars, 0);
 				rc = 1;
 			}
 		}
diff --git a/src/gsm/gsm_utils.c b/src/gsm/gsm_utils.c
index 5da713c..8ac0d07 100644
--- a/src/gsm/gsm_utils.c
+++ b/src/gsm/gsm_utils.c
@@ -73,28 +73,44 @@ static int gsm_septet_lookup(uint8_t ch)
 	return -1;
 }
 
+/* Compute the number of octets from the number of septets, for instance: 47 septets needs 41,125 = 42 octets */
+uint8_t gsm_get_octet_len(const uint8_t sept_len){
+	int octet_len = (sept_len * 7) / 8;
+	if ((sept_len * 7) % 8 != 0)
+		octet_len++;
+
+	return octet_len;
+}
+
 /* GSM 03.38 6.2.1 Character unpacking */
-int gsm_7bit_decode(char *text, const uint8_t *user_data, uint8_t length)
+int gsm_7bit_decode(char *text, const uint8_t *user_data, uint8_t septet_l, uint8_t ud_hdr_ind)
 {
 	int i = 0;
-	int l = 0;
-	int septet_l = (length * 8) / 7;
+	int shift = 0;
+
 	uint8_t *rtext = calloc(septet_l, sizeof(uint8_t));
 	uint8_t tmp;
 
-	/* FIXME: We need to account for user data headers here */
-	i += l;
-	for (; i < septet_l; i++){
+	/* skip the user data header */
+	if(ud_hdr_ind){
+		/* get user data header length + 1 (for the 'user data header length'-field) */
+		shift = ((user_data[0] + 1) * 8) / 7;
+		if((((user_data[0] + 1) * 8) % 7) != 0)
+			shift++;
+		septet_l = septet_l - shift;
+	}
+
+	for (i = 0; i < septet_l; i++){
 		rtext[i] =
-			((user_data[(i * 7 + 7) >> 3] <<
-			  (7 - ((i * 7 + 7) & 7))) |
-			 (user_data[(i * 7) >> 3] >>
-			  ((i * 7) & 7))) & 0x7f;
+			((user_data[((i + shift) * 7 + 7) >> 3] <<
+			  (7 - (((i + shift) * 7 + 7) & 7))) |
+			 (user_data[((i + shift) * 7) >> 3] >>
+			  (((i + shift) * 7) & 7))) & 0x7f;
 	}
 
 	for(i = 0; i < septet_l; i++){
 		/* this is an extension character */
-		if(rtext[i] == 0x1b && i + 1 < length){
+		if(rtext[i] == 0x1b && i + 1 < septet_l){
 			tmp = rtext[i+1];
 			*(text++) = gsm_7bit_alphabet[0x7f + tmp];
 			i++;
@@ -104,6 +120,9 @@ int gsm_7bit_decode(char *text, const uint8_t *user_data, uint8_t length)
 		*(text++) = gsm_septet_lookup(rtext[i]);
 	}
 
+	if(ud_hdr_ind){
+		i += shift;
+	}
 	*text = '\0';
 	free(rtext);
 
@@ -111,7 +130,7 @@ int gsm_7bit_decode(char *text, const uint8_t *user_data, uint8_t length)
 }
 
 /* GSM 03.38 6.2.1 Prepare character packing */
-static int gsm_septet_encode(uint8_t *result, const char *data)
+int gsm_septet_encode(uint8_t *result, const char *data)
 {
 	int i, y = 0;
 	uint8_t ch;
@@ -139,38 +158,68 @@ static int gsm_septet_encode(uint8_t *result, const char *data)
 	return y;
 }
 
-/* GSM 03.38 6.2.1 Character packing */
-int gsm_7bit_encode(uint8_t *result, const char *data)
-{
-	int i,y,z = 0;
-	/* prepare for the worst case, every character expanding to two bytes */
-	uint8_t *rdata = calloc(strlen(data) * 2, sizeof(uint8_t));
+/* 7bit to octet packing */
+int gsm_septets2octets(uint8_t *result, uint8_t *rdata, uint8_t septet_len, uint8_t padding){
+	int i = 0, z = 0;
 	uint8_t cb, nb;
 	int shift = 0;
-
-	y = gsm_septet_encode(rdata, data);
-
-	for(i = 0; i < y; i++) {
-		if(shift == 7 && i + 1 < y){
-			shift = 0;
-			continue;
+	uint8_t *data = calloc(septet_len + 1, sizeof(uint8_t));
+
+	if(padding){
+		shift = 7 - padding;
+		/* the first zero is needed for padding */
+		memcpy(data + 1, rdata, septet_len);
+		septet_len++;
+	}else
+		memcpy(data, rdata, septet_len);
+
+	for(i = 0; i < septet_len; i++) {
+		if(shift == 7){
+			if(i + 1 < septet_len){
+				shift = 0;
+				continue;
+			}
+		/* special end case. This is necessary if the last septet fits into the previous octet. */
+		/* E.g. 48 non-extension characters: ....ag ( a = 1100001, g = 1100111) */
+		/* result[40] = 100001 XX */
+		/* result[41] = 1100111 1 */
+			else if (i + 1 == septet_len)
+				break;
 		}
 
-		cb = (rdata[i] & 0x7f) >> shift;
-		if(i + 1 < y){
-			nb = (rdata[i + 1] & 0x7f) << (7 - shift);
+		cb = (data[i] & 0x7f) >> shift;
+		if(i + 1 < septet_len){
+			nb = (data[i + 1] & 0x7f) << (7 - shift);
 			cb = cb | nb;
 		}
 
 		result[z++] = cb;
-
 		shift++;
 	}
 
-	free(rdata);
+	free(data);
+
 	return z;
 }
 
+/* GSM 03.38 6.2.1 Character packing */
+int gsm_7bit_encode(uint8_t *result, const char *data)
+{
+	int y = 0, z = 0;
+	/* prepare for the worst case, every character expanding to two bytes */
+	uint8_t *rdata = calloc(strlen(data) * 2, sizeof(uint8_t));
+	y = gsm_septet_encode(rdata, data);
+	z = gsm_septets2octets(result, rdata, y, 0);
+
+	free(rdata);
+
+	/* we don't care about the number of octets (z), because they are not unique. E.g.: */
+	/* 1.) 46 non-extension characters + 1 extension character => (46 * 7 bit + (1 * (2 * 7 bit))) / 8 bit =  42 octets */
+	/* 2.) 47 non-extension characters => (47 * 7 bit) / 8 bit = 41,125 = 42 octets */
+	/* 3.) 48 non-extension characters => (48 * 7 bit) / 8 bit = 42 octects */
+	return y;
+}
+
 /* convert power class to dBm according to GSM TS 05.05 */
 unsigned int ms_class_gmsk_dbm(enum gsm_band band, int class)
 {
diff --git a/tests/sms/sms_test.c b/tests/sms/sms_test.c
index b4ed631..afb00ad 100644
--- a/tests/sms/sms_test.c
+++ b/tests/sms/sms_test.c
@@ -31,15 +31,19 @@ struct test_case {
 	const uint16_t input_length;
 
 	const uint8_t *expected;
-	const uint16_t expected_length;
+	const uint16_t expected_octet_length;
+	const uint16_t expected_septet_length;
+	const uint8_t ud_hdr_ind;
 };
 
 static const char simple_text[] = "test text";
+#define simple_septet_length 9
 static const uint8_t simple_enc[] = {
 	0xf4, 0xf2, 0x9c, 0x0e, 0xa2, 0x97, 0xf1, 0x74
 };
 
-static const char escape_text[] = "!$ a more#^- complicated test@@?_\%! case";
+static const char escape_text[] = "!$ a more#^- complicated test@@?_%! case";
+#define escape_septet_length 41 /* note: the ^ counts as two, because it is a extension character */
 static const uint8_t escape_enc[] = {
 	0x21, 0x01, 0x28, 0x0c, 0x6a, 0xbf, 0xe5, 0xe5, 0xd1,
 	0x86, 0xd2, 0x02, 0x8d, 0xdf, 0x6d, 0x38, 0x3b, 0x3d,
@@ -47,17 +51,115 @@ static const uint8_t escape_enc[] = {
 	0x00, 0xbf, 0x48, 0x29, 0x04, 0x1a, 0x87, 0xe7, 0x65,
 };
 
+static const char enhanced_text[] = "enhanced ^ {][} test |+~ ^ test";
+#define enhanced_septet_length 39 /* note: the characters { } [ ] ^ | ~ count as two (each of them), because they are extension characters */
+static const uint8_t enhanced_enc[] = {
+	0x65, 0x37, 0x3A, 0xEC, 0x1E, 0x97, 0xC9, 0xA0, 0x0D,
+	0x05, 0xB4, 0x41, 0x6D, 0x7C, 0x1B, 0xDE, 0x26, 0x05,
+	0xA2, 0x97, 0xE7, 0x74, 0xD0, 0x06, 0xB8, 0xDA, 0xF4,
+	0x40, 0x1B, 0x0A, 0x88, 0x5E, 0x9E, 0xD3, 0x01,
+};
+
+static const char enhancedV2_text[] = "enhanced ^ {][} test |+~ ^ tests";
+#define enhancedV2_septet_length 40 /* note: number of octets are equal to the enhanced_text! */
+static const uint8_t enhancedV2_enc[] = {
+	0x65, 0x37, 0x3A, 0xEC, 0x1E, 0x97, 0xC9, 0xA0, 0x0D,
+	0x05, 0xB4, 0x41, 0x6D, 0x7C, 0x1B, 0xDE, 0x26, 0x05,
+	0xA2, 0x97, 0xE7, 0x74, 0xD0, 0x06, 0xB8, 0xDA, 0xF4,
+	0x40, 0x1B, 0x0A, 0x88, 0x5E, 0x9E, 0xD3, 0xE7,
+};
+
+
+
+static const char concatenated_text[] =
+		"this is a testmessage. this is a testmessage. this is a testmessage. this is a testmessage. "
+		"this is a testmessage. this is a testmessage. cut here .....: this is a second testmessage. end here.";
+
+static const char splitted_text_part1[] =
+		"this is a testmessage. this is a testmessage. this is a testmessage. this is a testmessage. "
+		"this is a testmessage. this is a testmessage. cut here .....:";
+#define concatenated_part1_septet_length_with_header 160
+#define concatenated_part1_septet_length 153
+static const uint8_t concatenated_part1_enc[] = {
+		0x05, 0x00, 0x03, 0x6f, 0x02, 0x01,
+		0xe8, 0xe8, 0xf4, 0x1c, 0x94, 0x9e, 0x83, 0xc2,
+		0x20, 0x7a, 0x79, 0x4e, 0x6f, 0x97, 0xe7, 0xf3,
+		0xf0, 0xb9, 0xec, 0x02, 0xd1, 0xd1, 0xe9, 0x39,
+		0x28, 0x3d, 0x07, 0x85, 0x41, 0xf4, 0xf2, 0x9c,
+		0xde, 0x2e, 0xcf, 0xe7, 0xe1, 0x73, 0xd9, 0x05,
+		0xa2, 0xa3, 0xd3, 0x73, 0x50, 0x7a, 0x0e, 0x0a,
+		0x83, 0xe8, 0xe5, 0x39, 0xbd, 0x5d, 0x9e, 0xcf,
+		0xc3, 0xe7, 0xb2, 0x0b, 0x44, 0x47, 0xa7, 0xe7,
+		0xa0, 0xf4, 0x1c, 0x14, 0x06, 0xd1, 0xcb, 0x73,
+		0x7a, 0xbb, 0x3c, 0x9f, 0x87, 0xcf, 0x65, 0x17,
+		0x88, 0x8e, 0x4e, 0xcf, 0x41, 0xe9, 0x39, 0x28,
+		0x0c, 0xa2, 0x97, 0xe7, 0xf4, 0x76, 0x79, 0x3e,
+		0x0f, 0x9f, 0xcb, 0x2e, 0x10, 0x1d, 0x9d, 0x9e,
+		0x83, 0xd2, 0x73, 0x50, 0x18, 0x44, 0x2f, 0xcf,
+		0xe9, 0xed, 0xf2, 0x7c, 0x1e, 0x3e, 0x97, 0x5d,
+		0xa0, 0x71, 0x9d, 0x0e, 0x42, 0x97, 0xe5, 0x65,
+		0x90, 0xcb, 0xe5, 0x72, 0xb9, 0x74,
+};
+
+static const char splitted_text_part2[] = " this is a second testmessage. end here.";
+#define concatenated_part2_septet_length_with_header 47
+#define concatenated_part2_septet_length 40
+static const uint8_t concatenated_part2_enc[] = {
+		0x05, 0x00, 0x03, 0x6f, 0x02, 0x02,
+		0x40, 0x74, 0x74, 0x7a, 0x0e, 0x4a, 0xcf, 0x41,
+		0x61, 0xd0, 0xbc, 0x3c, 0x7e, 0xbb, 0xc9, 0x20,
+		0x7a, 0x79, 0x4e, 0x6f, 0x97, 0xe7, 0xf3, 0xf0,
+		0xb9, 0xec, 0x02, 0x95, 0xdd, 0x64, 0x10, 0xba,
+		0x2c, 0x2f, 0xbb, 0x00,
+};
+
+static const struct test_case test_multiple_encode[] =
+{
+	{
+		.input = concatenated_text,
+		.expected = concatenated_part1_enc,
+		.expected_octet_length = sizeof(concatenated_part1_enc),
+		.expected_septet_length = concatenated_part1_septet_length,
+		.ud_hdr_ind = 1,
+	},
+	{
+		.input = concatenated_text,
+		.expected = concatenated_part2_enc,
+		.expected_octet_length = sizeof(concatenated_part2_enc),
+		.expected_septet_length = concatenated_part2_septet_length,
+		.ud_hdr_ind = 1,
+	},
+};
+
 static const struct test_case test_encode[] =
 {
 	{
 		.input = simple_text,
 		.expected = simple_enc,
-		.expected_length = sizeof(simple_enc),
+		.expected_octet_length = sizeof(simple_enc),
+		.expected_septet_length = simple_septet_length,
+		.ud_hdr_ind = 0,
 	},
 	{
 		.input = escape_text,
 		.expected = escape_enc,
-		.expected_length = sizeof(escape_enc),
+		.expected_octet_length = sizeof(escape_enc),
+		.expected_septet_length = escape_septet_length,
+		.ud_hdr_ind = 0,
+	},
+	{
+		.input = enhanced_text,
+		.expected = enhanced_enc,
+		.expected_octet_length = sizeof(enhanced_enc),
+		.expected_septet_length = enhanced_septet_length,
+		.ud_hdr_ind = 0,
+	},
+	{
+		.input = enhancedV2_text,
+		.expected = enhancedV2_enc,
+		.expected_octet_length = sizeof(enhancedV2_enc),
+		.expected_septet_length = enhancedV2_septet_length,
+		.ud_hdr_ind = 0,
 	},
 };
 
@@ -67,11 +169,43 @@ static const struct test_case test_decode[] =
 		.input = simple_enc,
 		.input_length = sizeof(simple_enc),
 		.expected = simple_text,
+		.expected_septet_length = simple_septet_length,
+		.ud_hdr_ind = 0,
 	},
 	{
 		.input = escape_enc,
 		.input_length = sizeof(escape_enc),
 		.expected = escape_text,
+		.expected_septet_length = escape_septet_length,
+		.ud_hdr_ind = 0,
+	},
+	{
+		.input = enhanced_enc,
+		.input_length = sizeof(enhanced_enc),
+		.expected = enhanced_text,
+		.expected_septet_length = enhanced_septet_length,
+		.ud_hdr_ind = 0,
+	},
+	{
+		.input = enhancedV2_enc,
+		.input_length = sizeof(enhancedV2_enc),
+		.expected = enhancedV2_text,
+		.expected_septet_length = enhancedV2_septet_length,
+		.ud_hdr_ind = 0,
+	},
+	{
+		.input = concatenated_part1_enc,
+		.input_length = sizeof(concatenated_part1_enc),
+		.expected = splitted_text_part1,
+		.expected_septet_length = concatenated_part1_septet_length_with_header,
+		.ud_hdr_ind = 1,
+	},
+	{
+		.input = concatenated_part2_enc,
+		.input_length = sizeof(concatenated_part2_enc),
+		.expected = splitted_text_part2,
+		.expected_septet_length = concatenated_part2_septet_length_with_header,
+		.ud_hdr_ind = 1,
 	},
 };
 
@@ -79,41 +213,105 @@ int main(int argc, char** argv)
 {
 	printf("SMS testing\n");
 	struct msgb *msg;
-	uint8_t *sms;
 	uint8_t i;
 
-	uint8_t length;
+	uint8_t octet_length;
+	uint8_t septet_length;
+	uint8_t gsm_septet_length;
 	uint8_t coded[256];
+	uint8_t tmp[160];
+	uint8_t septet_data[256];
+	uint8_t ud_header[6];
 	char result[256];
 
 	/* test 7-bit encoding */
 	for (i = 0; i < ARRAY_SIZE(test_encode); ++i) {
 		memset(coded, 0x42, sizeof(coded));
-		length = gsm_7bit_encode(coded, test_encode[i].input);
+		septet_length = gsm_7bit_encode(coded, test_encode[i].input);
+		octet_length = gsm_get_octet_len(septet_length);
+		if (octet_length != test_encode[i].expected_octet_length) {
+			fprintf(stderr, "Encode case %d: Octet length failure. Got %d, expected %d\n",
+				i, octet_length, test_encode[i].expected_octet_length);
+			return -1;
+		}
 
-		if (length != test_encode[i].expected_length) {
-			fprintf(stderr, "Failed to encode case %d. Got %d, expected %d\n",
-				i, length, test_encode[i].expected_length);
+		if (septet_length != test_encode[i].expected_septet_length){
+			fprintf(stderr, "Encode case %d: Septet length failure. Got %d, expected %d\n",
+				i, septet_length, test_encode[i].expected_septet_length);
 			return -1;
 		}
 
-		if (memcmp(coded, test_encode[i].expected, length) != 0) {
-			fprintf(stderr, "Encoded content does not match for %d\n",
+		if (memcmp(coded, test_encode[i].expected, octet_length) != 0) {
+			fprintf(stderr, "Encoded content does not match for case %d\n",
 				i);
 			return -1;
 		}
 	}
 
+
+	/* Test: encode multiple SMS */
+	int number_of_septets = gsm_septet_encode(septet_data, test_multiple_encode[0].input);
+
+	/* SMS part 1 */
+	memset(tmp, 0x42, sizeof(tmp));
+	memset(coded, 0x42, sizeof(coded));
+	memcpy(tmp, septet_data, concatenated_part1_septet_length);
+
+	/* In our case: test_multiple_decode[0].ud_hdr_ind equals number of padding bits*/
+	octet_length = gsm_septets2octets(coded, tmp, concatenated_part1_septet_length, test_multiple_encode[0].ud_hdr_ind);
+
+	/* copy header */
+	memset(tmp, 0x42, sizeof(tmp));
+	int udh_length = test_multiple_encode[0].expected[0] + 1;
+	memcpy(tmp, test_multiple_encode[0].expected, udh_length);
+	memcpy(tmp + udh_length, coded, octet_length);
+	memset(coded, 0x42, sizeof(coded));
+	memcpy(coded, tmp, octet_length + 6);
+
+	if (memcmp(coded, test_multiple_encode[0].expected, octet_length) != 0) {
+		fprintf(stderr, "Multiple-SMS encoded content does not match for part 1\n");
+		return -1;
+	}
+
+
+	/* SMS part 2 */
+	memset(tmp, 0x42, sizeof(tmp));
+	memset(coded, 0x42, sizeof(coded));
+	memcpy(tmp, septet_data + concatenated_part1_septet_length, concatenated_part2_septet_length);
+
+	/* In our case: test_multiple_decode[1].ud_hdr_ind equals number of padding bits*/
+	octet_length = gsm_septets2octets(coded, tmp, concatenated_part2_septet_length, test_multiple_encode[1].ud_hdr_ind);
+
+	/* copy header */
+	memset(tmp, 0x42, sizeof(tmp));
+	udh_length = test_multiple_encode[1].expected[0] + 1;
+	memcpy(tmp, test_multiple_encode[1].expected, udh_length);
+	memcpy(tmp + udh_length, coded, octet_length);
+	memset(coded, 0x42, sizeof(coded));
+	memcpy(coded, tmp, octet_length + 6);
+
+	if (memcmp(coded, test_multiple_encode[1].expected, octet_length) != 0) {
+		fprintf(stderr, "Multiple-SMS encoded content does not match for part 2\n");
+		return -1;
+	}
+
+
+
 	/* test 7-bit decoding */
 	for (i = 0; i < ARRAY_SIZE(test_decode); ++i) {
 		memset(result, 0x42, sizeof(coded));
-		gsm_7bit_decode(result, test_decode[i].input,
-				test_decode[i].input_length);
+		septet_length = gsm_7bit_decode(result, test_decode[i].input,
+				test_decode[i].expected_septet_length, test_decode[i].ud_hdr_ind);
 
 		if (strcmp(result, test_decode[i].expected) != 0) {
 			fprintf(stderr, "Test case %d failed to decode.\n", i);
 			return -1;
 		}
+		if (septet_length != test_decode[i].expected_septet_length){
+			fprintf(stderr, "Decode case %d: Septet length failure. Got %d, expected %d\n",
+				i, septet_length, test_decode[i].expected_septet_length);
+			return -1;
+		}
 	}
 
 	printf("OK\n");
-- 
1.7.1

