From: asfernandes <asfernandes@de594faa-8d1b-4a0c-9a6a-a7de5f8bf859>
Date: Tue, 5 Apr 2011 21:30:27 +0000
Subject: Fixed CORE-3431 - ISQL pads UTF-8 data incorrectly.

git-svn-id: https://firebird.svn.sourceforge.net/svnroot/firebird/firebird/trunk@52676 de594faa-8d1b-4a0c-9a6a-a7de5f8bf859

Conflicts:

	src/isql/isql.epp
---
 src/isql/isql.epp |  159 +++++++++++++++++++++++++++++++++++++++++++---------
 src/isql/isql.h   |    1 +
 2 files changed, 132 insertions(+), 28 deletions(-)

diff --git a/src/isql/isql.epp b/src/isql/isql.epp
index d764102..dc0936d 100644
--- a/src/isql/isql.epp
+++ b/src/isql/isql.epp
@@ -131,6 +131,8 @@ using MsgFormat::SafeArg;
 #include "../isql/InputDevices.h"
 #include "../isql/OptionsBase.h"
 
+#include "../intl/charsets.h"
+#include <unicode/utf8.h>
 
 DATABASE DB = COMPILETIME "yachts.lnk";
 
@@ -245,6 +247,98 @@ const switch_info switches[] =
 };
 
 
+namespace IcuUtil
+{
+	// Duplicate from ICU to not need to link ISQL with it. It's used by U8_NEXT_UNSAFE.
+	static const uint8_t utf8_countTrailBytes[256] = {
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+		2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+		3, 3, 3, 3, 3,
+		3, 3, 3,    /* illegal in Unicode */
+		4, 4, 4, 4, /* illegal in Unicode */
+		5, 5,       /* illegal in Unicode */
+		0, 0        /* illegal bytes 0xfe and 0xff */
+	};
+
+	// Return the number of characters of a string.
+	static unsigned charLength(SSHORT sqlsubtype, unsigned len, const char* str)
+	{
+		if (sqlsubtype != CS_UNICODE_FSS && sqlsubtype != CS_UTF8)
+			return len;
+
+		unsigned charLen = 0;
+		unsigned i = 0;
+
+		while (i < len)
+		{
+			UChar32 c;
+			U8_NEXT_UNSAFE(str, i, c);
+			++charLen;
+		}
+
+		return charLen;
+	}
+
+	// Pads a string to a specified column width.
+	static void pad(char* buffer, SSHORT sqlsubtype, unsigned len, const char* str, unsigned width,
+		bool right)
+	{
+		if (sqlsubtype != CS_UNICODE_FSS && sqlsubtype != CS_UTF8)
+		{
+			// Truncate if necessary.
+			if (len > width)
+				len = width;
+
+			sprintf(buffer, (right ? "%*.*s" : "%-*.*s"), width, len, str);
+			return;
+		}
+
+		unsigned i = 0;
+
+		while (i < len && width > 0)
+		{
+			UChar32 c;
+			U8_NEXT_UNSAFE(str, i, c);
+			--width;
+		}
+
+		if (right)
+		{
+			while (width-- > 0)
+				*buffer++ = ' ';
+		}
+
+		memcpy(buffer, str, i);
+		buffer += i;
+
+		if (!right)
+		{
+			while (width-- > 0)
+				*buffer++ = ' ';
+		}
+
+		*buffer = '\0';
+	}
+}
+
+
 static inline bool commit_trans(isc_tr_handle* x)
 {
 	if (isc_commit_transaction (isc_status, x)) {
@@ -583,6 +677,7 @@ int ISQL_main(int argc, char* argv[])
 	TEXT tabname[WORDLENGTH];
 	tabname[0] = '\0';
 	isqlGlob.db_SQL_dialect = 0;
+	isqlGlob.att_charset = 0;
 
 	// Output goes to stdout by default
 	isqlGlob.Out = stdout;
@@ -5862,6 +5957,7 @@ void ISQL_get_version(bool call_by_create_db)
 		isc_info_ods_version,
 		isc_info_ods_minor_version,
 		isc_info_db_sql_dialect,
+		frb_info_att_charset,
 		Version_info ? isc_info_firebird_version: isc_info_end,
 		isc_info_end
 	};
@@ -6018,6 +6114,10 @@ void ISQL_get_version(bool call_by_create_db)
 			}
 			break;
 
+		case frb_info_att_charset:
+			isqlGlob.att_charset = gds__vax_integer(p, length);
+			break;
+
 		default:
 			isqlGlob.printf("Internal error: Unexpected isc_info_value %d%s",
 							item, NEWLINE);
@@ -7358,18 +7458,6 @@ static bool checkSpecial(TEXT* const p, const int length, const double value)
 }
 
 
-static void align_string(char* out, unsigned clen, unsigned slen, const char* s)
-{
-	if (slen > clen)
-		slen = clen;
-	memcpy(out, s, slen);
-	if (clen > slen)
-		memset(out + slen, ' ', clen - slen);
-	out[clen] = ' ';
-	out[clen + 1] = '\0';
-}
-
-
 static SSHORT print_item(TEXT** s, XSQLVAR* var, const int length)
 {
 /**************************************
@@ -7657,12 +7745,12 @@ static SSHORT print_item(TEXT** s, XSQLVAR* var, const int length)
 					sprintf(p, "%-*s ", length, buff2);
 				ISQL_FREE(buff2);
 			}
-			else if (List) {
+			else if (List)
 				isqlGlob.printf("%s%s", string, NEWLINE);
-			}
 			else
 			{
-				align_string(p, length, var->sqllen, var->sqldata);
+				IcuUtil::pad(p, var->sqlsubtype, strlen(var->sqldata), var->sqldata, length, false);
+				strcat(p, " ");
 			}
 			break;
 
@@ -7750,12 +7838,12 @@ static SSHORT print_item(TEXT** s, XSQLVAR* var, const int length)
 					}
 					ISQL_FREE(buff2);
 				}
-				else if (List) {
+				else if (List)
 					isqlGlob.printf("%s%s", avary->vary_string, NEWLINE);
-				}
 				else
 				{
-					align_string(p, length, avary->vary_length, avary->vary_string);
+					IcuUtil::pad(p, var->sqlsubtype, avary->vary_length, avary->vary_string, length, false);
+					strcat(p, " ");
 				}
 				break;
 			}
@@ -8193,16 +8281,20 @@ static void process_header(const XSQLDA* sqlda, const int pad[], TEXT header[],
 	for (const XSQLVAR* const end = var + sqlda->sqld; var < end; var++, i++)
 	{
 		const SSHORT type = var->sqltype & ~1;
-		if (type == SQL_TEXT || type == SQL_VARYING)
-			sprintf(p, "%-*.*s ", pad[i], pad[i], var->aliasname);
-		else
-			sprintf(p, "%*s ", pad[i], var->aliasname);
+
+		IcuUtil::pad(p, isqlGlob.att_charset, var->aliasname_length, var->aliasname, pad[i],
+			(type != SQL_TEXT && type != SQL_VARYING));
+		strcat(p, " ");
+
+		p += strlen(p);
+
 		// Separators need not go on forever no more than a line
-		size_t limit = strlen(p);
-		for (size_t j = 1; j < limit && j < 80; j++)
+		unsigned limit = IcuUtil::charLength(isqlGlob.att_charset, var->aliasname_length, var->aliasname);
+		limit = MAX(limit, pad[i]) + 1;
+
+		for (unsigned j = 1; j < limit && j < 80; j++)
 			*p2++ = '=';
 		*p2++ = BLANK;
-		p += limit;
 	}
 	*p2 = '\0';
 }
@@ -8441,7 +8533,7 @@ static SLONG process_sqlda_display(XSQLDA* const sqlda, SLONG buffer[], int pad[
 
 		USHORT data_length, disp_length, alignment;
 		data_length = disp_length = alignment = var->sqllen;
-		SSHORT namelength = var->aliasname_length;
+		SSHORT namelength = IcuUtil::charLength(isqlGlob.att_charset, var->aliasname_length, var->aliasname);
 
 		// Minimum display length should not be less than that needed
 		// for displaying null
@@ -8483,6 +8575,8 @@ static SLONG process_sqlda_display(XSQLDA* const sqlda, SLONG buffer[], int pad[
 			// OCTETS data is displayed in hex
 			if (var->sqlsubtype == 1)
 				disp_length = 2 * var->sqllen;
+			else if (var->sqlsubtype == 4)
+				disp_length /= 4;
 			break;
 		case SQL_VARYING:
 			data_length += sizeof(USHORT) + 1;
@@ -8490,6 +8584,8 @@ static SLONG process_sqlda_display(XSQLDA* const sqlda, SLONG buffer[], int pad[
 			// OCTETS data is displayed in hex
 			if (var->sqlsubtype == 1)
 				disp_length = 2 * var->sqllen;
+			else if (var->sqlsubtype == 4)
+				disp_length /= 4;
 			break;
 		case SQL_SHORT:
 			disp_length = SHORT_LEN;
@@ -8520,7 +8616,10 @@ static SLONG process_sqlda_display(XSQLDA* const sqlda, SLONG buffer[], int pad[
 
 		// This is the print width of each column
 
-		pad[i] = (disp_length > namelength ? disp_length : namelength);
+		if (disp_length < namelength)
+			disp_length = namelength;
+
+		pad[i] = disp_length;
 
 		// Is there a collist entry, then use that width, but only for text
 		if (type == SQL_TEXT || type == SQL_VARYING)
@@ -8529,8 +8628,11 @@ static SLONG process_sqlda_display(XSQLDA* const sqlda, SLONG buffer[], int pad[
 				pad[i] = global_Col_default;
 		}
 
+		if ((type == SQL_TEXT || type == SQL_VARYING) && var->sqlsubtype == 4)
+			disp_length *= 4;
+
 		// The total line length
-		linelength += pad[i] + 1;
+		linelength += disp_length + 1;
 
 		// Allocate space in buffer for data
 
@@ -8538,6 +8640,7 @@ static SLONG process_sqlda_display(XSQLDA* const sqlda, SLONG buffer[], int pad[
 		var->sqldata = (SCHAR*) buffer + offset;
 		offset += data_length;
 	}
+
 	return linelength;
 }
 
diff --git a/src/isql/isql.h b/src/isql/isql.h
index e076c3c..9d350e4 100644
--- a/src/isql/isql.h
+++ b/src/isql/isql.h
@@ -362,6 +362,7 @@ public:
 	// from isql.epp
 	USHORT major_ods;
 	USHORT minor_ods;
+	USHORT att_charset;
 	void printf(const char* buffer, ...);
 	void prints(const char* buffer);
 };
-- 
1.7.7

