Index: src/backend/utils/mb/wchar.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/utils/mb/wchar.c,v retrieving revision 1.47 diff -c -r1.47 wchar.c *** src/backend/utils/mb/wchar.c 29 Oct 2005 00:31:52 -0000 1.47 --- src/backend/utils/mb/wchar.c 26 Nov 2005 22:27:57 -0000 *************** *** 23,28 **** --- 23,35 ---- * for the particular encoding. Note that if the encoding is only * supported in the client, you don't need to define * mb2wchar_with_len() function (SJIS is the case). + * + * Note: for the display output of psql to work properly, the return values + * of these functions must conform to the Unicode standard. In particular + * the NUL character is zero width and control characters are generally + * width -1. It is recommended that non-ASCII encodings refer their ASCII + * subset to the ASCII routines to ensure consistancy. + * */ /* *************** *** 52,57 **** --- 59,69 ---- static int pg_ascii_dsplen(const unsigned char *s) { + if( *s == '\0' ) + return 0; + if( *s < 32 || *s == 127 ) + return -1; + return (1); } *************** *** 125,131 **** else if (*s & 0x80) len = 2; else ! len = 1; return (len); } --- 137,143 ---- else if (*s & 0x80) len = 2; else ! len = pg_ascii_dsplen(s); return (len); } *************** *** 156,162 **** else if (*s & 0x80) len = 2; else ! len = 1; return (len); } --- 168,174 ---- else if (*s & 0x80) len = 2; else ! len = pg_ascii_dsplen(s); return (len); } *************** *** 243,249 **** if (*s & 0x80) len = 2; else ! len = 1; return (len); } --- 255,261 ---- if (*s & 0x80) len = 2; else ! len = pg_ascii_dsplen(s); return (len); } *************** *** 318,324 **** else if (*s & 0x80) len = 2; else ! len = 1; return (len); } --- 330,336 ---- else if (*s & 0x80) len = 2; else ! len = pg_ascii_dsplen(s); return (len); } *************** *** 417,426 **** return (len); } static int pg_utf_dsplen(const unsigned char *s) { ! return 1; /* XXX fix me! */ } /* --- 429,615 ---- return (len); } + /* + * This is an implementation of wcwidth() and wcswidth() as defined in + * "The Single UNIX Specification, Version 2, The Open Group, 1997" + * + * + * Markus Kuhn -- 2001-09-08 -- public domain + * + * customised for PostgreSQL + * + * original available at : http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c + */ + + struct mbinterval + { + unsigned short first; + unsigned short last; + }; + + /* auxiliary function for binary search in interval table */ + static int + mbbisearch(pg_wchar ucs, const struct mbinterval * table, int max) + { + int min = 0; + int mid; + + if (ucs < table[0].first || ucs > table[max].last) + return 0; + while (max >= min) + { + mid = (min + max) / 2; + if (ucs > table[mid].last) + min = mid + 1; + else if (ucs < table[mid].first) + max = mid - 1; + else + return 1; + } + + return 0; + } + + + /* The following functions define the column width of an ISO 10646 + * character as follows: + * + * - The null character (U+0000) has a column width of 0. + * + * - Other C0/C1 control characters and DEL will lead to a return + * value of -1. + * + * - Non-spacing and enclosing combining characters (general + * category code Mn or Me in the Unicode database) have a + * column width of 0. + * + * - Other format characters (general category code Cf in the Unicode + * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. + * + * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) + * have a column width of 0. + * + * - Spacing characters in the East Asian Wide (W) or East Asian + * FullWidth (F) category as defined in Unicode Technical + * Report #11 have a column width of 2. + * + * - All remaining characters (including all printable + * ISO 8859-1 and WGL4 characters, Unicode control characters, + * etc.) have a column width of 1. + * + * This implementation assumes that wchar_t characters are encoded + * in ISO 10646. + */ + + static int + ucs_wcwidth(pg_wchar ucs) + { + /* sorted list of non-overlapping intervals of non-spacing characters */ + static const struct mbinterval combining[] = { + {0x0300, 0x034E}, {0x0360, 0x0362}, {0x0483, 0x0486}, + {0x0488, 0x0489}, {0x0591, 0x05A1}, {0x05A3, 0x05B9}, + {0x05BB, 0x05BD}, {0x05BF, 0x05BF}, {0x05C1, 0x05C2}, + {0x05C4, 0x05C4}, {0x064B, 0x0655}, {0x0670, 0x0670}, + {0x06D6, 0x06E4}, {0x06E7, 0x06E8}, {0x06EA, 0x06ED}, + {0x070F, 0x070F}, {0x0711, 0x0711}, {0x0730, 0x074A}, + {0x07A6, 0x07B0}, {0x0901, 0x0902}, {0x093C, 0x093C}, + {0x0941, 0x0948}, {0x094D, 0x094D}, {0x0951, 0x0954}, + {0x0962, 0x0963}, {0x0981, 0x0981}, {0x09BC, 0x09BC}, + {0x09C1, 0x09C4}, {0x09CD, 0x09CD}, {0x09E2, 0x09E3}, + {0x0A02, 0x0A02}, {0x0A3C, 0x0A3C}, {0x0A41, 0x0A42}, + {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A70, 0x0A71}, + {0x0A81, 0x0A82}, {0x0ABC, 0x0ABC}, {0x0AC1, 0x0AC5}, + {0x0AC7, 0x0AC8}, {0x0ACD, 0x0ACD}, {0x0B01, 0x0B01}, + {0x0B3C, 0x0B3C}, {0x0B3F, 0x0B3F}, {0x0B41, 0x0B43}, + {0x0B4D, 0x0B4D}, {0x0B56, 0x0B56}, {0x0B82, 0x0B82}, + {0x0BC0, 0x0BC0}, {0x0BCD, 0x0BCD}, {0x0C3E, 0x0C40}, + {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, + {0x0CBF, 0x0CBF}, {0x0CC6, 0x0CC6}, {0x0CCC, 0x0CCD}, + {0x0D41, 0x0D43}, {0x0D4D, 0x0D4D}, {0x0DCA, 0x0DCA}, + {0x0DD2, 0x0DD4}, {0x0DD6, 0x0DD6}, {0x0E31, 0x0E31}, + {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E}, {0x0EB1, 0x0EB1}, + {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC}, {0x0EC8, 0x0ECD}, + {0x0F18, 0x0F19}, {0x0F35, 0x0F35}, {0x0F37, 0x0F37}, + {0x0F39, 0x0F39}, {0x0F71, 0x0F7E}, {0x0F80, 0x0F84}, + {0x0F86, 0x0F87}, {0x0F90, 0x0F97}, {0x0F99, 0x0FBC}, + {0x0FC6, 0x0FC6}, {0x102D, 0x1030}, {0x1032, 0x1032}, + {0x1036, 0x1037}, {0x1039, 0x1039}, {0x1058, 0x1059}, + {0x1160, 0x11FF}, {0x17B7, 0x17BD}, {0x17C6, 0x17C6}, + {0x17C9, 0x17D3}, {0x180B, 0x180E}, {0x18A9, 0x18A9}, + {0x200B, 0x200F}, {0x202A, 0x202E}, {0x206A, 0x206F}, + {0x20D0, 0x20E3}, {0x302A, 0x302F}, {0x3099, 0x309A}, + {0xFB1E, 0xFB1E}, {0xFE20, 0xFE23}, {0xFEFF, 0xFEFF}, + {0xFFF9, 0xFFFB} + }; + + /* test for 8-bit control characters */ + if (ucs == 0) + return 0; + + if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0) || ucs > 0x0010ffff) + return -1; + + /* binary search in table of non-spacing characters */ + if (mbbisearch(ucs, combining, + sizeof(combining) / sizeof(struct mbinterval) - 1)) + return 0; + + /* + * if we arrive here, ucs is not a combining or C0/C1 control character + */ + + return 1 + + (ucs >= 0x1100 && + (ucs <= 0x115f || /* Hangul Jamo init. consonants */ + (ucs >= 0x2e80 && ucs <= 0xa4cf && (ucs & ~0x0011) != 0x300a && + ucs != 0x303f) || /* CJK ... Yi */ + (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ + (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility + * Ideographs */ + (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ + (ucs >= 0xff00 && ucs <= 0xff5f) || /* Fullwidth Forms */ + (ucs >= 0xffe0 && ucs <= 0xffe6) || + (ucs >= 0x20000 && ucs <= 0x2ffff))); + } + + static pg_wchar + utf2ucs(const unsigned char *c) + { + /* + * one char version of pg_utf2wchar_with_len. no control here, c must + * point to a large enough string + */ + if ((*c & 0x80) == 0) + return (pg_wchar) c[0]; + else if ((*c & 0xe0) == 0xc0) + { + return (pg_wchar) (((c[0] & 0x1f) << 6) | + (c[1] & 0x3f)); + } + else if ((*c & 0xf0) == 0xe0) + { + return (pg_wchar) (((c[0] & 0x0f) << 12) | + ((c[1] & 0x3f) << 6) | + (c[2] & 0x3f)); + } + else if ((*c & 0xf0) == 0xf0) + { + return (pg_wchar) (((c[0] & 0x07) << 18) | + ((c[1] & 0x3f) << 12) | + ((c[2] & 0x3f) << 6) | + (c[3] & 0x3f)); + } + else + { + /* that is an invalid code on purpose */ + return 0xffffffff; + } + } + static int pg_utf_dsplen(const unsigned char *s) { ! return ucs_wcwidth(utf2ucs(s)); } /* *************** *** 529,535 **** static int pg_latin1_dsplen(const unsigned char *s) { ! return (1); } /* --- 718,724 ---- static int pg_latin1_dsplen(const unsigned char *s) { ! return pg_ascii_dsplen(s); } /* *************** *** 570,576 **** } else { /* should be ASCII */ ! len = 1; } return (len); } --- 759,765 ---- } else { /* should be ASCII */ ! len = pg_ascii_dsplen(s); } return (len); } *************** *** 605,611 **** } else { /* should be ASCII */ ! len = 1; } return (len); } --- 794,800 ---- } else { /* should be ASCII */ ! len = pg_ascii_dsplen(s); } return (len); } *************** *** 640,646 **** } else { /* should be ASCII */ ! len = 1; } return (len); } --- 829,835 ---- } else { /* should be ASCII */ ! len = pg_ascii_dsplen(s); } return (len); } *************** *** 675,681 **** } else { /* should be ASCII */ ! len = 1; } return (len); } --- 864,870 ---- } else { /* should be ASCII */ ! len = pg_ascii_dsplen(s); } return (len); } *************** *** 712,718 **** if (*s <= 0x7f) { /* ASCII */ ! len = 1; } else len = 2; --- 901,907 ---- if (*s <= 0x7f) { /* ASCII */ ! len = pg_ascii_dsplen(s); } else len = 2; Index: src/bin/psql/mbprint.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/bin/psql/mbprint.c,v retrieving revision 1.18 diff -c -r1.18 mbprint.c *** src/bin/psql/mbprint.c 15 Oct 2005 02:49:40 -0000 1.18 --- src/bin/psql/mbprint.c 26 Nov 2005 22:27:57 -0000 *************** *** 14,162 **** #include "mb/pg_wchar.h" - /* - * This is an implementation of wcwidth() and wcswidth() as defined in - * "The Single UNIX Specification, Version 2, The Open Group, 1997" - * - * - * Markus Kuhn -- 2001-09-08 -- public domain - * - * customised for PostgreSQL - * - * original available at : http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c - */ - - struct mbinterval - { - unsigned short first; - unsigned short last; - }; - - /* auxiliary function for binary search in interval table */ - static int - mbbisearch(pg_wchar ucs, const struct mbinterval * table, int max) - { - int min = 0; - int mid; - - if (ucs < table[0].first || ucs > table[max].last) - return 0; - while (max >= min) - { - mid = (min + max) / 2; - if (ucs > table[mid].last) - min = mid + 1; - else if (ucs < table[mid].first) - max = mid - 1; - else - return 1; - } - - return 0; - } - - - /* The following functions define the column width of an ISO 10646 - * character as follows: - * - * - The null character (U+0000) has a column width of 0. - * - * - Other C0/C1 control characters and DEL will lead to a return - * value of -1. - * - * - Non-spacing and enclosing combining characters (general - * category code Mn or Me in the Unicode database) have a - * column width of 0. - * - * - Other format characters (general category code Cf in the Unicode - * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. - * - * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) - * have a column width of 0. - * - * - Spacing characters in the East Asian Wide (W) or East Asian - * FullWidth (F) category as defined in Unicode Technical - * Report #11 have a column width of 2. - * - * - All remaining characters (including all printable - * ISO 8859-1 and WGL4 characters, Unicode control characters, - * etc.) have a column width of 1. - * - * This implementation assumes that wchar_t characters are encoded - * in ISO 10646. - */ - - static int - ucs_wcwidth(pg_wchar ucs) - { - /* sorted list of non-overlapping intervals of non-spacing characters */ - static const struct mbinterval combining[] = { - {0x0300, 0x034E}, {0x0360, 0x0362}, {0x0483, 0x0486}, - {0x0488, 0x0489}, {0x0591, 0x05A1}, {0x05A3, 0x05B9}, - {0x05BB, 0x05BD}, {0x05BF, 0x05BF}, {0x05C1, 0x05C2}, - {0x05C4, 0x05C4}, {0x064B, 0x0655}, {0x0670, 0x0670}, - {0x06D6, 0x06E4}, {0x06E7, 0x06E8}, {0x06EA, 0x06ED}, - {0x070F, 0x070F}, {0x0711, 0x0711}, {0x0730, 0x074A}, - {0x07A6, 0x07B0}, {0x0901, 0x0902}, {0x093C, 0x093C}, - {0x0941, 0x0948}, {0x094D, 0x094D}, {0x0951, 0x0954}, - {0x0962, 0x0963}, {0x0981, 0x0981}, {0x09BC, 0x09BC}, - {0x09C1, 0x09C4}, {0x09CD, 0x09CD}, {0x09E2, 0x09E3}, - {0x0A02, 0x0A02}, {0x0A3C, 0x0A3C}, {0x0A41, 0x0A42}, - {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A70, 0x0A71}, - {0x0A81, 0x0A82}, {0x0ABC, 0x0ABC}, {0x0AC1, 0x0AC5}, - {0x0AC7, 0x0AC8}, {0x0ACD, 0x0ACD}, {0x0B01, 0x0B01}, - {0x0B3C, 0x0B3C}, {0x0B3F, 0x0B3F}, {0x0B41, 0x0B43}, - {0x0B4D, 0x0B4D}, {0x0B56, 0x0B56}, {0x0B82, 0x0B82}, - {0x0BC0, 0x0BC0}, {0x0BCD, 0x0BCD}, {0x0C3E, 0x0C40}, - {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, - {0x0CBF, 0x0CBF}, {0x0CC6, 0x0CC6}, {0x0CCC, 0x0CCD}, - {0x0D41, 0x0D43}, {0x0D4D, 0x0D4D}, {0x0DCA, 0x0DCA}, - {0x0DD2, 0x0DD4}, {0x0DD6, 0x0DD6}, {0x0E31, 0x0E31}, - {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E}, {0x0EB1, 0x0EB1}, - {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC}, {0x0EC8, 0x0ECD}, - {0x0F18, 0x0F19}, {0x0F35, 0x0F35}, {0x0F37, 0x0F37}, - {0x0F39, 0x0F39}, {0x0F71, 0x0F7E}, {0x0F80, 0x0F84}, - {0x0F86, 0x0F87}, {0x0F90, 0x0F97}, {0x0F99, 0x0FBC}, - {0x0FC6, 0x0FC6}, {0x102D, 0x1030}, {0x1032, 0x1032}, - {0x1036, 0x1037}, {0x1039, 0x1039}, {0x1058, 0x1059}, - {0x1160, 0x11FF}, {0x17B7, 0x17BD}, {0x17C6, 0x17C6}, - {0x17C9, 0x17D3}, {0x180B, 0x180E}, {0x18A9, 0x18A9}, - {0x200B, 0x200F}, {0x202A, 0x202E}, {0x206A, 0x206F}, - {0x20D0, 0x20E3}, {0x302A, 0x302F}, {0x3099, 0x309A}, - {0xFB1E, 0xFB1E}, {0xFE20, 0xFE23}, {0xFEFF, 0xFEFF}, - {0xFFF9, 0xFFFB} - }; - - /* test for 8-bit control characters */ - if (ucs == 0) - return 0; - - if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0) || ucs > 0x0010ffff) - return -1; - - /* binary search in table of non-spacing characters */ - if (mbbisearch(ucs, combining, - sizeof(combining) / sizeof(struct mbinterval) - 1)) - return 0; - - /* - * if we arrive here, ucs is not a combining or C0/C1 control character - */ - - return 1 + - (ucs >= 0x1100 && - (ucs <= 0x115f || /* Hangul Jamo init. consonants */ - (ucs >= 0x2e80 && ucs <= 0xa4cf && (ucs & ~0x0011) != 0x300a && - ucs != 0x303f) || /* CJK ... Yi */ - (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ - (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility - * Ideographs */ - (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ - (ucs >= 0xff00 && ucs <= 0xff5f) || /* Fullwidth Forms */ - (ucs >= 0xffe0 && ucs <= 0xffe6) || - (ucs >= 0x20000 && ucs <= 0x2ffff))); - } - static pg_wchar utf2ucs(const unsigned char *c) { --- 14,19 ---- *************** *** 191,216 **** } } - /* mb_utf_wcwidth : calculate column length for the utf8 string pwcs - */ - static int - mb_utf_wcswidth(const unsigned char *pwcs, size_t len) - { - int w, - l = 0; - int width = 0; - - for (; *pwcs && len > 0; pwcs += l) - { - l = pg_utf_mblen(pwcs); - if ((len < (size_t) l) || ((w = ucs_wcwidth(utf2ucs(pwcs))) < 0)) - return width; - len -= l; - width += w; - } - return width; - } - static int utf_charcheck(const unsigned char *c) { --- 48,53 ---- *************** *** 308,330 **** * public functions : wcswidth and mbvalidate */ int ! pg_wcswidth(const char *pwcs, size_t len, int encoding) { ! if (encoding == PG_UTF8) ! return mb_utf_wcswidth((const unsigned char *) pwcs, len); ! else { ! /* ! * obviously, other encodings may want to fix this, but I don't know ! * them myself, unfortunately. ! */ ! return len; } } ! char * ! mbvalidate(char *pwcs, int encoding) { if (encoding == PG_UTF8) mb_utf_validate((unsigned char *) pwcs); --- 145,331 ---- * public functions : wcswidth and mbvalidate */ + /* + * pg_wcswidth is the dumb width function. It assumes that everything will + * only appear on one line. OTOH it is easier to use if this applies to you. + */ int ! pg_wcswidth(const unsigned char *pwcs, size_t len, int encoding) { ! int width = 0; ! while( len > 0 ) { ! int chlen, chwidth; ! chlen = PQmblen( pwcs, encoding ); ! if( chlen > len ) ! break; /* Invalid string */ ! ! chwidth = PQdsplen( pwcs, encoding ); ! ! if( chwidth > 0 ) ! width += chwidth; ! pwcs += chlen; ! } ! return width; ! } ! ! /* ! * pg_wcssize takes the given string in the given encoding and returns three ! * values: ! * result_width: Width in display character of longest line in string ! * result_hieght: Number of lines in display output ! * result_format_size: Number of bytes required to store formatted representation of string ! */ ! int ! pg_wcssize( unsigned char *pwcs, size_t len, int encoding, int *result_width, int *result_height, int *result_format_size ) ! { ! int w, ! l = 0, ! linewidth = 0; ! int width = 0; ! int height = 1; ! int format_size = 0; ! ! for (; *pwcs && len > 0; pwcs += l) ! { ! l = PQmblen(pwcs, encoding); ! if ( len < (size_t) l) ! break; ! w = PQdsplen(pwcs, encoding); ! ! if( l == 1 ) /* ASCII char */ ! { ! if( *pwcs == '\n' ) /* Newline */ ! { ! if( linewidth > width ) ! width = linewidth; ! linewidth = 0; ! height+=1; ! format_size += 1; /* For NUL char */ ! } ! else if( *pwcs == '\r' ) /* Linefeed */ ! { ! linewidth+=2; ! format_size+=2; ! } ! else if( w <= 0 ) /* Other control char */ ! { ! linewidth+=4; ! format_size+=4; ! } ! else /* Output itself */ ! { ! linewidth++; ! format_size += 1; ! } ! } ! else if( w <= 0 ) /* Non-ascii control char */ ! { ! linewidth+=6; /* \u0000 */ ! format_size += 6; ! } ! else /* All other chars */ ! { ! linewidth += w; ! format_size += l; ! } ! len -= l; ! } ! if( linewidth > width ) ! width = linewidth; ! format_size += 1; ! ! /* Set results */ ! if( result_width ) ! *result_width = width; ! if( result_height ) ! *result_height = height; ! if( result_format_size ) ! *result_format_size = format_size; ! ! return width; ! } ! ! void ! pg_wcsformat( unsigned char *pwcs, size_t len, int encoding, struct lineptr *lines, int count ) ! { ! int w, ! l = 0; ! int linewidth = 0; ! ! char *ptr = lines->ptr; /* Pointer to data area */ ! ! for (; *pwcs && len > 0; pwcs += l) ! { ! l = PQmblen(pwcs,encoding); ! if ( len < (size_t) l) ! break; ! w = PQdsplen(pwcs,encoding); ! ! if( l == 1 ) /* single byte char char */ ! { ! if( *pwcs == '\n' ) /* Newline */ ! { ! *ptr++ = 0; /* NULL char */ ! lines->width = linewidth; ! linewidth = 0; ! lines++, count--; ! if( count == 0 ) ! exit(1); /* Screwup */ ! ! lines->ptr = ptr; ! } ! else if( *pwcs == '\r' ) /* Linefeed */ ! { ! strcpy( ptr, "\\r" ); ! linewidth += 2; ! ptr+=2; ! } ! else if( w <= 0 ) /* Other control char */ ! { ! sprintf( ptr, "\\x%02X", *pwcs ); ! linewidth += 4; ! ptr += 4; ! } ! else /* Output itself */ ! { ! linewidth++; ! *ptr++ = *pwcs; ! } ! } ! else if( w <= 0 ) /* Non-ascii control char */ ! { ! if( encoding == PG_UTF8 ) ! sprintf( ptr, "\\u%04X", utf2ucs(pwcs) ); ! else ! /* This case cannot happen in the current ! * code because only UTF-8 signals multibyte ! * control characters. But we may need to ! * support it at some stage */ ! sprintf( ptr, "\\u????" ); ! ! ptr += 6; ! linewidth += 6; ! } ! else /* All other chars */ ! { ! int i; ! for( i=0; iwidth = linewidth; + lines++, count--; + if( count > 0 ) + lines->ptr = NULL; + return; } ! unsigned char * ! mbvalidate(unsigned char *pwcs, int encoding) { if (encoding == PG_UTF8) mb_utf_validate((unsigned char *) pwcs); Index: src/bin/psql/mbprint.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/bin/psql/mbprint.h,v retrieving revision 1.8 diff -c -r1.8 mbprint.h *** src/bin/psql/mbprint.h 24 Sep 2005 17:53:27 -0000 1.8 --- src/bin/psql/mbprint.h 26 Nov 2005 22:27:57 -0000 *************** *** 4,11 **** #include "mb/pg_wchar.h" ! extern char *mbvalidate(char *pwcs, int encoding); ! extern int pg_wcswidth(const char *pwcs, size_t len, int encoding); #endif /* MBPRINT_H */ --- 4,18 ---- #include "mb/pg_wchar.h" ! struct lineptr { ! unsigned char *ptr; ! int width; ! }; ! extern unsigned char *mbvalidate(unsigned char *pwcs, int encoding); ! ! extern int pg_wcswidth(const unsigned char *pwcs, size_t len, int encoding); ! extern void pg_wcsformat( unsigned char *pwcs, size_t len, int encoding, struct lineptr *lines, int count ); ! extern int pg_wcssize( unsigned char *pwcs, size_t len, int encoding, int *width, int *height, int *format_size ); #endif /* MBPRINT_H */ Index: src/bin/psql/print.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/bin/psql/print.c,v retrieving revision 1.79 diff -c -r1.79 print.c *** src/bin/psql/print.c 27 Oct 2005 13:34:47 -0000 1.79 --- src/bin/psql/print.c 26 Nov 2005 22:27:57 -0000 *************** *** 49,54 **** --- 49,68 ---- return tmp; } + static void * + pg_local_calloc(int count, size_t size) + { + void *tmp; + + tmp = calloc(count, size); + if (!tmp) + { + fprintf(stderr, _("out of memory\n")); + exit(EXIT_FAILURE); + } + return tmp; + } + static int integer_digits(const char *my_str) { *************** *** 87,92 **** --- 101,107 ---- return strlen(my_str) + additional_numeric_locale_len(my_str); } + /* Returns the appropriately formatted string in a new allocated block, caller must free */ static char * format_numeric_locale(const char *my_str) { *************** *** 342,354 **** { unsigned int col_count = 0; unsigned int cell_count = 0; - unsigned int *head_w, - *cell_w; unsigned int i, tmp; unsigned int *widths, total_w; ! const char *const * ptr; /* count columns */ for (ptr = headers; *ptr; ptr++) --- 357,376 ---- { unsigned int col_count = 0; unsigned int cell_count = 0; unsigned int i, tmp; unsigned int *widths, total_w; ! unsigned int *heights; ! unsigned int *format_space; ! unsigned char **format_buf; ! ! const char *const *ptr; ! ! struct lineptr **col_lineptrs; /* pointers to line pointer for each column */ ! struct lineptr *lineptr_list; /* complete list of linepointers */ ! ! int *complete; /* Array remembering which columns have completed output */ /* count columns */ for (ptr = headers; *ptr; ptr++) *************** *** 356,419 **** if (col_count > 0) { ! widths = calloc(col_count, sizeof(*widths)); ! if (!widths) ! { ! fprintf(stderr, _("out of memory\n")); ! exit(EXIT_FAILURE); ! } ! ! head_w = calloc(col_count, sizeof(*head_w)); ! if (!head_w) ! { ! fprintf(stderr, _("out of memory\n")); ! exit(EXIT_FAILURE); ! } } else { widths = NULL; ! head_w = NULL; } ! /* count cells (rows * cols) */ for (ptr = cells; *ptr; ptr++) cell_count++; - if (cell_count > 0) - { - cell_w = calloc(cell_count, sizeof(*cell_w)); - if (!cell_w) - { - fprintf(stderr, _("out of memory\n")); - exit(EXIT_FAILURE); - } - } - else - cell_w = NULL; - /* calc column widths */ for (i = 0; i < col_count; i++) { ! tmp = pg_wcswidth(headers[i], strlen(headers[i]), encoding); if (tmp > widths[i]) widths[i] = tmp; ! head_w[i] = tmp; } for (i = 0, ptr = cells; *ptr; ptr++, i++) { ! int add_numeric_locale_len; if (opt_align[i % col_count] == 'r' && opt_numeric_locale) ! add_numeric_locale_len = additional_numeric_locale_len(*ptr); ! else ! add_numeric_locale_len = 0; ! ! tmp = pg_wcswidth(*ptr, strlen(*ptr), encoding) + add_numeric_locale_len; if (tmp > widths[i % col_count]) widths[i % col_count] = tmp; ! cell_w[i] = tmp; } if (opt_border == 0) --- 378,438 ---- if (col_count > 0) { ! widths = pg_local_calloc(col_count, sizeof(*widths)); ! heights = pg_local_calloc(col_count, sizeof(*heights)); ! col_lineptrs = pg_local_calloc( col_count, sizeof(*col_lineptrs) ); ! format_space = pg_local_calloc( col_count, sizeof(*format_space) ); ! format_buf = pg_local_calloc( col_count, sizeof(*format_buf) ); ! complete = pg_local_calloc( col_count, sizeof(*complete) ); ! } else { widths = NULL; ! heights = NULL; ! col_lineptrs = NULL; ! format_space = NULL; ! format_buf = NULL; ! complete = NULL; } ! /* count cells (rows * cols) */ for (ptr = cells; *ptr; ptr++) cell_count++; /* calc column widths */ for (i = 0; i < col_count; i++) { ! /* Get width & height */ ! int height, space; ! pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &tmp, &height, &space); if (tmp > widths[i]) widths[i] = tmp; ! if (height > heights[i]) ! heights[i] = height; ! if (space > format_space[i]) ! format_space[i] = space; } for (i = 0, ptr = cells; *ptr; ptr++, i++) { ! int numeric_locale_len; ! int height, space; if (opt_align[i % col_count] == 'r' && opt_numeric_locale) ! numeric_locale_len = additional_numeric_locale_len(*ptr); ! else ! numeric_locale_len = 0; ! ! /* Get width, ignore height */ ! pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &tmp, &height, &space); ! tmp += numeric_locale_len; if (tmp > widths[i % col_count]) widths[i % col_count] = tmp; ! if (height > heights[i % col_count]) ! heights[i % col_count] = height; ! if (space > format_space[i % col_count]) ! format_space[i % col_count] = space; } if (opt_border == 0) *************** *** 426,435 **** for (i = 0; i < col_count; i++) total_w += widths[i]; /* print title */ if (title && !opt_tuples_only) { ! tmp = pg_wcswidth(title, strlen(title), encoding); if (tmp >= total_w) fprintf(fout, "%s\n", title); else --- 445,482 ---- for (i = 0; i < col_count; i++) total_w += widths[i]; + /* At this point: + * widths contains the max width of each column + * heights contains the max height of a cell of each column + * format_space contains maximum space required to store formatted string + * so we prepare the formatting structures + */ + { + int heights_total = 0; + struct lineptr *lineptr; + + for( i=0; i < col_count; i++ ) + heights_total += heights[i]; + + lineptr = lineptr_list = pg_local_calloc( heights_total, sizeof(*lineptr_list) ); + + for( i=0; i < col_count; i++ ) + { + col_lineptrs[i] = lineptr; + lineptr += heights[i]; + + format_buf[i] = pg_local_malloc( format_space[i] ); + + col_lineptrs[i]->ptr = format_buf[i]; + } + } + /* print title */ if (title && !opt_tuples_only) { ! /* Get width & height */ ! int height; ! pg_wcssize((unsigned char *) title, strlen(title), encoding, &tmp, &height, NULL ); if (tmp >= total_w) fprintf(fout, "%s\n", title); else *************** *** 439,528 **** /* print headers */ if (!opt_tuples_only) { if (opt_border == 2) _print_horizontal_line(col_count, widths, opt_border, fout); - if (opt_border == 2) - fputs("| ", fout); - else if (opt_border == 1) - fputc(' ', fout); - for (i = 0; i < col_count; i++) { ! unsigned int nbspace; ! nbspace = widths[i] - head_w[i]; ! /* centered */ ! fprintf(fout, "%-*s%s%-*s", ! nbspace / 2, "", headers[i], (nbspace + 1) / 2, ""); ! if (i < col_count - 1) ! { ! if (opt_border == 0) ! fputc(' ', fout); else ! fputs(" | ", fout); } } - if (opt_border == 2) - fputs(" |", fout); - else if (opt_border == 1) - fputc(' ', fout);; - fputc('\n', fout); _print_horizontal_line(col_count, widths, opt_border, fout); } /* print cells */ ! for (i = 0, ptr = cells; *ptr; i++, ptr++) { ! /* beginning of line */ ! if (i % col_count == 0) { if (opt_border == 2) fputs("| ", fout); else if (opt_border == 1) fputc(' ', fout); - } ! /* content */ ! if (opt_align[i % col_count] == 'r') ! { ! if (opt_numeric_locale) { ! char *my_cell = format_numeric_locale(*ptr); ! fprintf(fout, "%*s%s", widths[i % col_count] - cell_w[i], "", my_cell); ! free(my_cell); } - else - fprintf(fout, "%*s%s", widths[i % col_count] - cell_w[i], "", *ptr); - } - else - { - if ((i + 1) % col_count == 0 && opt_border != 2) - fputs(cells[i], fout); - else - fprintf(fout, "%-s%*s", cells[i], - widths[i % col_count] - cell_w[i], ""); - } - - /* divider */ - if ((i + 1) % col_count) - { - if (opt_border == 0) - fputc(' ', fout); - else - fputs(" | ", fout); - } - /* end of line */ - else - { if (opt_border == 2) fputs(" |", fout); fputc('\n', fout); } } --- 486,629 ---- /* print headers */ if (!opt_tuples_only) { + int cols_todo; + int line_count; + if (opt_border == 2) _print_horizontal_line(col_count, widths, opt_border, fout); for (i = 0; i < col_count; i++) + pg_wcsformat( (unsigned char *)headers[i], strlen(headers[i]), encoding, col_lineptrs[i], heights[i] ); + + cols_todo = col_count; + line_count = 0; + memset( complete, 0, col_count*sizeof(int) ); + while( cols_todo ) { ! if (opt_border == 2) ! fprintf(fout, "|%c", line_count ? '+' : ' ' ); ! else if (opt_border == 1) ! fputc(line_count ? '+' : ' ', fout); ! for (i = 0; i < col_count; i++) ! { ! unsigned int nbspace; ! struct lineptr *this_line = col_lineptrs[i] + line_count; ! if( !complete[i] ) ! { ! nbspace = widths[i] - this_line->width; ! /* centered */ ! fprintf(fout, "%-*s%s%-*s", ! nbspace / 2, "", this_line->ptr, (nbspace + 1) / 2, ""); ! ! if( line_count == (heights[i]-1) || !(this_line+1)->ptr ) ! { ! cols_todo--; ! complete[i] = 1; ! } ! } else ! { ! fprintf( fout, "%*s", widths[i], "" ); ! } ! if (i < col_count - 1) ! { ! if (opt_border == 0) ! fputc(line_count ? '+' : ' ', fout); ! else ! fprintf( fout, " |%c", line_count ? '+' : ' '); ! } } + line_count++; + + if (opt_border == 2) + fputs(" |", fout); + else if (opt_border == 1) + fputc(' ', fout);; + fputc('\n', fout); } _print_horizontal_line(col_count, widths, opt_border, fout); } /* print cells */ ! for (i = 0, ptr = cells; *ptr; i+=col_count, ptr+=col_count) { ! int j; ! int cols_todo = col_count; ! int line_count; /* Number of lines output so far in row */ ! ! for (j = 0; j < col_count; j++) ! pg_wcsformat( (unsigned char*)ptr[j], strlen(ptr[j]), encoding, col_lineptrs[j], heights[j] ); ! ! line_count = 0; ! memset( complete, 0, col_count*sizeof(int) ); ! while( cols_todo ) { + /* beginning of line */ if (opt_border == 2) fputs("| ", fout); else if (opt_border == 1) fputc(' ', fout); ! for (j = 0; j < col_count; j++) { ! struct lineptr *this_line = col_lineptrs[j] + line_count; ! if( complete[j] ) /* Just print spaces... */ ! { ! fprintf(fout, "%*s", widths[j], ""); ! } ! else ! { ! /* content */ ! if (opt_align[j] == 'r') ! { ! if (opt_numeric_locale) ! { ! /* Assumption: This code used only on strings ! * without multibyte characters, otherwise ! * this_line->width < strlen(this_ptr) and we ! * get an overflow */ ! ! char *my_cell = format_numeric_locale(this_line->ptr); ! fprintf(fout, "%*s%s", widths[i % col_count] - strlen(my_cell), "", my_cell); ! free(my_cell); ! } ! else ! fprintf(fout, "%*s%s", widths[j] - this_line->width, "", this_line->ptr); ! } ! else ! { ! fprintf(fout, "%-s%*s", this_line->ptr, ! widths[j] - this_line->width, ""); ! } ! /* If at the right height, done this col */ ! if( line_count == heights[j]-1 || !this_line[1].ptr ) ! { ! complete[j] = 1; ! cols_todo--; ! } ! } ! ! /* divider */ ! if ((j + 1) % col_count) ! { ! if (opt_border == 0) ! fputc(' ', fout); ! else if( line_count == 0 ) ! fputs(" | ", fout); ! else ! fprintf(fout, " %c ", complete[j+1] ? ' ' : ':'); ! } } if (opt_border == 2) fputs(" |", fout); fputc('\n', fout); + line_count++; } } *************** *** 543,551 **** #endif /* clean up */ - free(cell_w); - free(head_w); free(widths); } --- 644,658 ---- #endif /* clean up */ free(widths); + free(heights); + free(col_lineptrs); + free(format_space); + free(complete); + free(lineptr_list); + for( i=0; i 0) - { - head_w = calloc(col_count, sizeof(*head_w)); - if (!head_w) - { - fprintf(stderr, _("out of memory\n")); - exit(EXIT_FAILURE); - } - } - else - head_w = NULL; for (i = 0; i < col_count; i++) { ! tmp = pg_wcswidth(headers[i], strlen(headers[i]), encoding); if (tmp > hwidth) hwidth = tmp; ! head_w[i] = tmp; } /* Count cells, find their lengths */ for (ptr = cells; *ptr; ptr++) cell_count++; - if (cell_count > 0) - { - cell_w = calloc(cell_count, sizeof(*cell_w)); - if (!cell_w) - { - fprintf(stderr, _("out of memory\n")); - exit(EXIT_FAILURE); - } - } - else - cell_w = NULL; - /* find longest data cell */ for (i = 0, ptr = cells; *ptr; ptr++, i++) { ! int add_numeric_locale_len; if (opt_align[i % col_count] == 'r' && opt_numeric_locale) ! add_numeric_locale_len = additional_numeric_locale_len(*ptr); ! else ! add_numeric_locale_len = 0; ! tmp = pg_wcswidth(*ptr, strlen(*ptr), encoding) + add_numeric_locale_len; if (tmp > dwidth) dwidth = tmp; ! cell_w[i] = tmp; ! } ! /* print title */ if (!opt_tuples_only && title) fprintf(fout, "%s\n", title); --- 688,739 ---- /* count headers and find longest one */ for (ptr = headers; *ptr; ptr++) col_count++; + /* Find the maximum dimensions for the headers */ for (i = 0; i < col_count; i++) { ! int height, fs; ! pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &tmp, &height, &fs); if (tmp > hwidth) hwidth = tmp; ! if (height > hheight ) ! hheight = height; ! if (fs > hformatsize) ! hformatsize = fs; } /* Count cells, find their lengths */ for (ptr = cells; *ptr; ptr++) cell_count++; /* find longest data cell */ for (i = 0, ptr = cells; *ptr; ptr++, i++) { ! int numeric_locale_len; ! int height, fs; if (opt_align[i % col_count] == 'r' && opt_numeric_locale) ! numeric_locale_len = additional_numeric_locale_len(*ptr); ! else ! numeric_locale_len = 0; ! pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &tmp, &height, &fs); ! tmp += numeric_locale_len; if (tmp > dwidth) dwidth = tmp; ! if (height > dheight) ! dheight = height; ! if (fs > dformatsize) ! dformatsize = fs; ! } ! ! /* We now have all the information we need to setup the formatting structures */ ! dlineptr = pg_local_malloc( sizeof(*dlineptr) * dheight ); ! hlineptr = pg_local_malloc( sizeof(*hlineptr) * hheight ); ! ! dlineptr->ptr = pg_local_malloc( dformatsize ); ! hlineptr->ptr = pg_local_malloc( hformatsize ); ! /* print title */ if (!opt_tuples_only && title) fprintf(fout, "%s\n", title); *************** *** 653,658 **** --- 757,764 ---- /* print records */ for (i = 0, ptr = cells; *ptr; i++, ptr++) { + int line_count, dcomplete, hcomplete; + if (i % col_count == 0) { if (!opt_tuples_only) *************** *** 688,720 **** fprintf(fout, "%s\n", divider); } ! if (opt_border == 2) ! fputs("| ", fout); ! fprintf(fout, "%-s%*s", headers[i % col_count], ! hwidth - head_w[i % col_count], ""); ! ! if (opt_border > 0) ! fputs(" | ", fout); ! else ! fputs(" ", fout); ! ! if (opt_align[i % col_count] == 'r' && opt_numeric_locale) { ! char *my_cell = format_numeric_locale(*ptr); ! ! if (opt_border < 2) ! fprintf(fout, "%s\n", my_cell); else ! fprintf(fout, "%-s%*s |\n", my_cell, dwidth - cell_w[i], ""); ! free(my_cell); ! } ! else ! { ! if (opt_border < 2) ! fprintf(fout, "%s\n", *ptr); else ! fprintf(fout, "%-s%*s |\n", *ptr, dwidth - cell_w[i], ""); ! } } if (opt_border == 2) --- 794,859 ---- fprintf(fout, "%s\n", divider); } ! /* Format the header */ ! pg_wcsformat( (unsigned char*)headers[i % col_count], strlen(headers[i % col_count]), encoding, hlineptr, hheight ); ! /* Format the data */ ! pg_wcsformat( (unsigned char*)*ptr, strlen(*ptr), encoding, dlineptr, dheight ); ! ! line_count = 0; ! dcomplete = hcomplete = 0; ! while( !dcomplete || !hcomplete ) { ! if (opt_border == 2) ! fputs("| ", fout); ! if( !hcomplete ) ! { ! fprintf(fout, "%-s%*s", hlineptr[line_count].ptr, ! hwidth - hlineptr[line_count].width, ""); ! ! if( line_count == (hheight-1) || !hlineptr[line_count+1].ptr ) ! hcomplete = 1; ! } else ! { ! fprintf( fout, "%*s", hwidth, "" ); ! } ! ! if (opt_border > 0) ! fprintf(fout, " %c ", (line_count==0)?'|':':' ); else ! fputs(" ", fout); ! ! if( !dcomplete ) ! { ! if (opt_align[i % col_count] == 'r' && opt_numeric_locale) ! { ! char *my_cell = format_numeric_locale(dlineptr[line_count].ptr); ! if (opt_border < 2) ! fprintf(fout, "%s\n", my_cell); ! else ! fprintf(fout, "%-s%*s |\n", my_cell, dwidth - strlen(my_cell), ""); ! free(my_cell); ! } ! else ! { ! if (opt_border < 2) ! fprintf(fout, "%s\n", dlineptr[line_count].ptr); ! else ! fprintf(fout, "%-s%*s |\n", dlineptr[line_count].ptr, dwidth - dlineptr[line_count].width, ""); ! } ! ! if( line_count == (dheight-1) || !dlineptr[line_count+1].ptr ) ! dcomplete = 1; ! } ! else ! { ! if( opt_border < 2 ) ! fputc('\n', fout); ! else ! fprintf( fout, "%*s |\n", dwidth, "" ); ! } ! line_count++; ! } } if (opt_border == 2) *************** *** 732,740 **** fputc('\n', fout); free(divider); ! ! free(cell_w); ! free(head_w); } --- 871,880 ---- fputc('\n', fout); free(divider); ! free(hlineptr->ptr); ! free(dlineptr->ptr); ! free(hlineptr); ! free(dlineptr); } *************** *** 1613,1636 **** /* extract headers */ nfields = PQnfields(result); ! headers = calloc(nfields + 1, sizeof(*headers)); ! if (!headers) ! { ! fprintf(stderr, _("out of memory\n")); ! exit(EXIT_FAILURE); ! } for (i = 0; i < nfields; i++) headers[i] = mbvalidate(PQfname(result, i), opt->topt.encoding); /* set cells */ ncells = PQntuples(result) * nfields; ! cells = calloc(ncells + 1, sizeof(*cells)); ! if (!cells) ! { ! fprintf(stderr, _("out of memory\n")); ! exit(EXIT_FAILURE); ! } for (i = 0; i < ncells; i++) { --- 1753,1766 ---- /* extract headers */ nfields = PQnfields(result); ! headers = pg_local_calloc(nfields + 1, sizeof(*headers)); for (i = 0; i < nfields; i++) headers[i] = mbvalidate(PQfname(result, i), opt->topt.encoding); /* set cells */ ncells = PQntuples(result) * nfields; ! cells = pg_local_calloc(ncells + 1, sizeof(*cells)); for (i = 0; i < ncells; i++) { *************** *** 1646,1657 **** footers = opt->footers; else if (!opt->topt.expanded && opt->default_footer) { ! footers = calloc(2, sizeof(*footers)); ! if (!footers) ! { ! fprintf(stderr, _("out of memory\n")); ! exit(EXIT_FAILURE); ! } footers[0] = pg_local_malloc(100); if (PQntuples(result) == 1) --- 1776,1782 ---- footers = opt->footers; else if (!opt->topt.expanded && opt->default_footer) { ! footers = pg_local_calloc(2, sizeof(*footers)); footers[0] = pg_local_malloc(100); if (PQntuples(result) == 1) *************** *** 1663,1674 **** footers = NULL; /* set alignment */ ! align = calloc(nfields + 1, sizeof(*align)); ! if (!align) ! { ! fprintf(stderr, _("out of memory\n")); ! exit(EXIT_FAILURE); ! } for (i = 0; i < nfields; i++) { --- 1788,1794 ---- footers = NULL; /* set alignment */ ! align = pg_local_calloc(nfields + 1, sizeof(*align)); for (i = 0; i < nfields; i++) {