OSDN Git Service

Allow psql multi-line column values to align in the proper columns
authorBruce Momjian <bruce@momjian.us>
Fri, 10 Feb 2006 00:39:04 +0000 (00:39 +0000)
committerBruce Momjian <bruce@momjian.us>
Fri, 10 Feb 2006 00:39:04 +0000 (00:39 +0000)
  If the second output column value is 'a\nb', the 'b' should appear
  in the second display column, rather than the first column as it
  does now.

Change libpq's PQdsplen() to return more useful values.

> Note: this changes the PQdsplen function, it can now return zero or
> minus one which was not possible before. It doesn't appear anyone is
> actually using the functions other than psql but it is a change. The
> functions are not actually documentated anywhere so it's not like we're
> breaking a defined interface. The new semantics follow the Unicode
> standard.

BACKWARD COMPATIBLE CHANGE.

The only user-visible change I saw in the regression tests is that a
SELECT * on a table where all the columns have been dropped doesn't
return a blank line like before.  This seems like a step forward.

Martijn van Oosterhout

src/backend/utils/mb/wchar.c
src/bin/psql/mbprint.c
src/bin/psql/mbprint.h
src/bin/psql/print.c
src/test/regress/expected/alter_table.out
src/test/regress/expected/prepare.out

index 3466687..2f07253 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * conversion functions between pg_wchar and multibyte streams.
  * Tatsuo Ishii
- * $PostgreSQL: pgsql/src/backend/utils/mb/wchar.c,v 1.52 2005/12/26 19:30:44 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/mb/wchar.c,v 1.53 2006/02/10 00:39:04 momjian Exp $
  *
  * WIN1250 client encoding updated by Pavel Behal
  *
  * 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.
+ *
  */
 
 /*
@@ -53,6 +60,11 @@ pg_ascii_mblen(const unsigned char *s)
 static int
 pg_ascii_dsplen(const unsigned char *s)
 {
+       if (*s == '\0')
+               return 0;
+       if (*s < 0x20 || *s == 0x7f)
+               return -1;
+               
        return 1;
 }
 
@@ -125,7 +137,7 @@ pg_euc_dsplen(const unsigned char *s)
        else if (IS_HIGHBIT_SET(*s))
                len = 2;
        else
-               len = 1;
+               len = pg_ascii_dsplen(s);
        return len;
 }
 
@@ -156,7 +168,7 @@ pg_eucjp_dsplen(const unsigned char *s)
        else if (IS_HIGHBIT_SET(*s))
                len = 2;
        else
-               len = 1;
+               len = pg_ascii_dsplen(s);
        return len;
 }
 
@@ -244,7 +256,7 @@ pg_euccn_dsplen(const unsigned char *s)
        if (IS_HIGHBIT_SET(*s))
                len = 2;
        else
-               len = 1;
+               len = pg_ascii_dsplen(s);
        return len;
 }
 
@@ -304,7 +316,7 @@ pg_euctw_mblen(const unsigned char *s)
        else if (IS_HIGHBIT_SET(*s))
                len = 2;
        else
-               len = 1;
+               len = pg_ascii_dsplen(s);
        return len;
 }
 
@@ -320,7 +332,7 @@ pg_euctw_dsplen(const unsigned char *s)
        else if (IS_HIGHBIT_SET(*s))
                len = 2;
        else
-               len = 1;
+               len = pg_ascii_dsplen(s);
        return len;
 }
 
@@ -419,10 +431,179 @@ pg_utf_mblen(const unsigned char *s)
        return len;
 }
 
+/*
+ * This is an implementation of wcwidth() and wcswidth() as defined in
+ * "The Single UNIX Specification, Version 2, The Open Group, 1997"
+ * <http://www.UNIX-systems.org/online.html>
+ *
+ * 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 < 0x20 || (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 1;                                       /* XXX fix me! */
+       return ucs_wcwidth(utf2ucs(s));
 }
 
 /*
@@ -499,7 +680,7 @@ pg_mule_mblen(const unsigned char *s)
 static int
 pg_mule_dsplen(const unsigned char *s)
 {
-       return 1;                                       /* XXX fix me! */
+       return pg_ascii_dsplen(s);                                      /* XXX fix me! */
 }
 
 /*
@@ -529,7 +710,7 @@ pg_latin1_mblen(const unsigned char *s)
 static int
 pg_latin1_dsplen(const unsigned char *s)
 {
-       return 1;
+       return pg_ascii_dsplen(s);
 }
 
 /*
@@ -559,7 +740,7 @@ pg_sjis_dsplen(const unsigned char *s)
        else if (IS_HIGHBIT_SET(*s))
                len = 2;        /* kanji? */
        else
-               len = 1;        /* should be ASCII */
+               len = pg_ascii_dsplen(s);       /* should be ASCII */
        return len;
 }
 
@@ -586,7 +767,7 @@ pg_big5_dsplen(const unsigned char *s)
        if (IS_HIGHBIT_SET(*s))
                len = 2;        /* kanji? */
        else
-               len = 1;        /* should be ASCII */
+               len = pg_ascii_dsplen(s);       /* should be ASCII */
        return len;
 }
 
@@ -613,7 +794,7 @@ pg_gbk_dsplen(const unsigned char *s)
        if (IS_HIGHBIT_SET(*s))
                len = 2;        /* kanji? */
        else
-               len = 1;        /* should be ASCII */
+               len = pg_ascii_dsplen(s);       /* should be ASCII */
        return len;
 }
 
@@ -640,7 +821,7 @@ pg_uhc_dsplen(const unsigned char *s)
        if (IS_HIGHBIT_SET(*s))
                len = 2;        /* 2byte? */
        else
-               len = 1;        /* should be ASCII */
+               len = pg_ascii_dsplen(s);       /* should be ASCII */
        return len;
 }
 
@@ -672,10 +853,10 @@ pg_gb18030_dsplen(const unsigned char *s)
 {
        int                     len;
 
-       if (!IS_HIGHBIT_SET(*s))
-               len = 1;        /* ASCII */
-       else
+       if (IS_HIGHBIT_SET(*s))
                len = 2;
+       else
+               len = pg_ascii_dsplen(s);       /* ASCII */
        return len;
 }
 
index 7bced92..68850b1 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2005, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/mbprint.c,v 1.18 2005/10/15 02:49:40 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/mbprint.c,v 1.19 2006/02/10 00:39:04 momjian Exp $
  */
 
 #include "postgres_fe.h"
 
 #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"
- * <http://www.UNIX-systems.org/online.html>
- *
- * 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)
 {
@@ -191,35 +48,16 @@ utf2ucs(const unsigned char *c)
        }
 }
 
-/* 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;
-}
 
+/*
+ * Unicode 3.1 compliant validation : for each category, it checks the
+ * combination of each byte to make sure it maps to a valid range. It also
+ * returns -1 for the following UCS values: ucs > 0x10ffff ucs & 0xfffe =
+ * 0xfffe 0xfdd0 < ucs < 0xfdef ucs & 0xdb00 = 0xd800 (surrogates)
+ */
 static int
 utf_charcheck(const unsigned char *c)
 {
-       /*
-        * Unicode 3.1 compliant validation : for each category, it checks the
-        * combination of each byte to make sur it maps to a valid range. It also
-        * returns -1 for the following UCS values: ucs > 0x10ffff ucs & 0xfffe =
-        * 0xfffe 0xfdd0 < ucs < 0xfdef ucs & 0xdb00 = 0xd800 (surrogates)
-        */
        if ((*c & 0x80) == 0)
                return 1;
        else if ((*c & 0xe0) == 0xc0)
@@ -270,6 +108,7 @@ utf_charcheck(const unsigned char *c)
        return -1;
 }
 
+
 static void
 mb_utf_validate(unsigned char *pwcs)
 {
@@ -277,28 +116,26 @@ mb_utf_validate(unsigned char *pwcs)
 
        while (*pwcs)
        {
-               int                     l;
+               int                     len;
 
-               if ((l = utf_charcheck(pwcs)) > 0)
+               if ((len = utf_charcheck(pwcs)) > 0)
                {
                        if (p != pwcs)
                        {
                                int                     i;
 
-                               for (i = 0; i < l; i++)
+                               for (i = 0; i < len; i++)
                                        *p++ = *pwcs++;
                        }
                        else
                        {
-                               pwcs += l;
-                               p += l;
+                               pwcs += len;
+                               p += len;
                        }
                }
                else
-               {
                        /* we skip the char */
                        pwcs++;
-               }
        }
        if (p != pwcs)
                *p = '\0';
@@ -308,23 +145,193 @@ mb_utf_validate(unsigned char *pwcs)
  * 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 char *pwcs, size_t len, int encoding)
+pg_wcswidth(const unsigned char *pwcs, size_t len, int encoding)
 {
-       if (encoding == PG_UTF8)
-               return mb_utf_wcswidth((const unsigned char *) pwcs, len);
-       else
+       int width = 0;
+
+       while (len > 0)
        {
-               /*
-                * obviously, other encodings may want to fix this, but I don't know
-                * them myself, unfortunately.
-                */
-               return len;
+               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,
+               chlen = 0,
+               linewidth = 0;
+       int width = 0;
+       int height = 1;
+       int format_size = 0;
+
+       for (; *pwcs && len > 0; pwcs += chlen)
+       {
+               chlen = PQmblen(pwcs, encoding);
+               if (len < (size_t)chlen)
+                       break;
+               w = PQdsplen(pwcs, encoding);
+
+               if (chlen == 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 += chlen;
+               }
+               len -= chlen;
+       }
+       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,
+                               chlen = 0;
+       int linewidth = 0;
+       
+       char *ptr = lines->ptr;   /* Pointer to data area */
+
+       for (; *pwcs && len > 0; pwcs += chlen)
+       {
+               chlen = PQmblen(pwcs,encoding);
+               if (len < (size_t)chlen)
+                       break;
+               w = PQdsplen(pwcs,encoding);
+
+               if (chlen == 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; i < chlen; i++)
+                               *ptr++ = pwcs[i];
+                       linewidth += w;
+               }
+               len -= chlen;
        }
+       *ptr++ = 0;
+       lines->width = linewidth;
+       lines++;
+       count--;
+       if (count > 0)
+               lines->ptr = NULL;
+       return;
 }
 
-char *
-mbvalidate(char *pwcs, int encoding)
+unsigned char *
+mbvalidate(unsigned char *pwcs, int encoding)
 {
        if (encoding == PG_UTF8)
                mb_utf_validate((unsigned char *) pwcs);
index 17a8b98..a1e5b0f 100644 (file)
@@ -1,11 +1,18 @@
-/* $PostgreSQL: pgsql/src/bin/psql/mbprint.h,v 1.8 2005/09/24 17:53:27 tgl Exp $ */
+/* $PostgreSQL: pgsql/src/bin/psql/mbprint.h,v 1.9 2006/02/10 00:39:04 momjian Exp $ */
 #ifndef MBPRINT_H
 #define MBPRINT_H
 
 #include "mb/pg_wchar.h"
 
-extern char *mbvalidate(char *pwcs, int encoding);
+struct lineptr {
+       unsigned char *ptr;
+       int width;
+};
 
-extern int     pg_wcswidth(const char *pwcs, size_t len, int encoding);
+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 65d107a..b232906 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2005, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/print.c,v 1.79 2005/10/27 13:34:47 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/print.c,v 1.80 2006/02/10 00:39:04 momjian Exp $
  */
 #include "postgres_fe.h"
 #include "common.h"
@@ -49,6 +49,20 @@ pg_local_malloc(size_t size)
        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,6 +101,7 @@ strlen_with_numeric_locale(const char *my_str)
        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,13 +357,20 @@ print_aligned_text(const char *title, const char *const * headers,
 {
        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;
+       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,64 +378,61 @@ print_aligned_text(const char *title, const char *const * headers,
 
        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);
-               }
+               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;
-               head_w = 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++;
 
-       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);
+               /* 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;
-               head_w[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                     add_numeric_locale_len;
+               int numeric_locale_len;
+               int height, space;
 
                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;
+                       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;
-               cell_w[i] = 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,10 +445,38 @@ print_aligned_text(const char *title, const char *const * headers,
        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)
        {
-               tmp = pg_wcswidth(title, strlen(title), encoding);
+               /* 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,90 +486,138 @@ print_aligned_text(const char *title, const char *const * headers,
        /* 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);
 
-               if (opt_border == 2)
-                       fputs("| ", fout);
-               else if (opt_border == 1)
-                       fputc(' ', 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)
                {
-                       unsigned int nbspace;
+                       if (opt_border == 2)
+                               fprintf(fout, "|%c", line_count ? '+' : ' ');
+                       else if (opt_border == 1)
+                               fputc(line_count ? '+' : ' ', fout);
 
-                       nbspace = widths[i] - head_w[i];
+                       for (i = 0; i < col_count; i++)
+                       {
+                               unsigned int nbspace;
 
-                       /* centered */
-                       fprintf(fout, "%-*s%s%-*s",
-                                       nbspace / 2, "", headers[i], (nbspace + 1) / 2, "");
+                               struct lineptr *this_line = col_lineptrs[i] + line_count;
+                               if (!complete[i])
+                               {
+                                       nbspace = widths[i] - this_line->width;
 
-                       if (i < col_count - 1)
-                       {
-                               if (opt_border == 0)
-                                       fputc(' ', fout);
+                                       /* 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
-                                       fputs(" | ", fout);
+                                       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);
                }
 
-               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++)
+       for (i = 0, ptr = cells; *ptr; i+=col_count, ptr+=col_count)
        {
-               /* beginning of line */
-               if (i % col_count == 0)
+               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);
-               }
 
-               /* content */
-               if (opt_align[i % col_count] == 'r')
-               {
-                       if (opt_numeric_locale)
+                       for (j = 0; j < col_count; j++)
                        {
-                               char       *my_cell = format_numeric_locale(*ptr);
+                               struct lineptr *this_line = col_lineptrs[j] + line_count;
+                               if (complete[j])  /* Just print spaces... */
+                                       fprintf(fout, "%*s", widths[j], "");
+                               else
+                               {
 
-                               fprintf(fout, "%*s%s", widths[i % col_count] - cell_w[i], "", my_cell);
-                               free(my_cell);
+                                       /* 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] ? ' ' : ':');
+                               }
                        }
-                       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);
+                       line_count++;
                }
        }
 
@@ -543,9 +638,15 @@ print_aligned_text(const char *title, const char *const * headers,
 #endif
 
        /* clean up */
-       free(cell_w);
-       free(head_w);
        free(widths);
+       free(heights);
+       free(col_lineptrs);
+       free(format_space);
+       free(complete);
+       free(lineptr_list);
+       for (i= 0; i < col_count; i++)
+               free(format_buf[i]);
+       free(format_buf);
 }
 
 
@@ -563,12 +664,15 @@ print_aligned_vertical(const char *title, const char *const * headers,
        unsigned int i,
                                tmp = 0,
                                hwidth = 0,
-                               dwidth = 0;
+                               dwidth = 0,
+                               hheight = 1,
+                               dheight = 1,
+                               hformatsize = 0,
+                               dformatsize = 0;
        char       *divider;
        unsigned int cell_count = 0;
-       unsigned int *cell_w,
-                          *head_w;
-
+       struct lineptr *hlineptr, *dlineptr;
+       
        if (cells[0] == NULL)
        {
                fprintf(fout, _("(No rows)\n"));
@@ -578,58 +682,52 @@ print_aligned_vertical(const char *title, const char *const * headers,
        /* count headers and find longest one */
        for (ptr = headers; *ptr; ptr++)
                col_count++;
-       if (col_count > 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;
 
+       /* Find the maximum dimensions for the headers */
        for (i = 0; i < col_count; i++)
        {
-               tmp = pg_wcswidth(headers[i], strlen(headers[i]), encoding);
+               int height, fs;
+               pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &tmp, &height, &fs);
                if (tmp > hwidth)
                        hwidth = tmp;
-               head_w[i] = tmp;
+               if (height > hheight)
+                       hheight = height;
+               if (fs > hformatsize)
+                       hformatsize = fs;
        }
 
        /* 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;
+               int numeric_locale_len;
+               int height, fs;
 
                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;
+                       numeric_locale_len = additional_numeric_locale_len(*ptr);
+               else 
+                       numeric_locale_len = 0;
 
-               tmp = pg_wcswidth(*ptr, strlen(*ptr), encoding) + add_numeric_locale_len;
+               pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &tmp, &height, &fs);
+               tmp += numeric_locale_len;
                if (tmp > dwidth)
                        dwidth = tmp;
-               cell_w[i] = 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,6 +751,8 @@ print_aligned_vertical(const char *title, const char *const * headers,
        /* 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,33 +788,66 @@ print_aligned_vertical(const char *title, const char *const * headers,
                                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)
+               /* 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)
                {
-                       char       *my_cell = format_numeric_locale(*ptr);
-
-                       if (opt_border < 2)
-                               fprintf(fout, "%s\n", my_cell);
+                       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%*s |\n", my_cell, dwidth - cell_w[i], "");
-                       free(my_cell);
-               }
-               else
-               {
-                       if (opt_border < 2)
-                               fprintf(fout, "%s\n", *ptr);
+                               fprintf(fout, "%*s", hwidth, "");
+       
+                       if (opt_border > 0)
+                               fprintf(fout, " %c ", (line_count==0)?'|':':');
                        else
-                               fprintf(fout, "%-s%*s |\n", *ptr, dwidth - cell_w[i], "");
-               }
+                               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,9 +865,10 @@ print_aligned_vertical(const char *title, const char *const * headers,
 
        fputc('\n', fout);
        free(divider);
-
-       free(cell_w);
-       free(head_w);
+       free(hlineptr->ptr);
+       free(dlineptr->ptr);
+       free(hlineptr);
+       free(dlineptr);
 }
 
 
@@ -1613,24 +1747,14 @@ printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *f
        /* extract headers */
        nfields = PQnfields(result);
 
-       headers = calloc(nfields + 1, sizeof(*headers));
-       if (!headers)
-       {
-               fprintf(stderr, _("out of memory\n"));
-               exit(EXIT_FAILURE);
-       }
+       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 = calloc(ncells + 1, sizeof(*cells));
-       if (!cells)
-       {
-               fprintf(stderr, _("out of memory\n"));
-               exit(EXIT_FAILURE);
-       }
+       cells = pg_local_calloc(ncells + 1, sizeof(*cells));
 
        for (i = 0; i < ncells; i++)
        {
@@ -1646,12 +1770,7 @@ printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *f
                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 = pg_local_calloc(2, sizeof(*footers));
 
                footers[0] = pg_local_malloc(100);
                if (PQntuples(result) == 1)
@@ -1663,12 +1782,7 @@ printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *f
                footers = NULL;
 
        /* set alignment */
-       align = calloc(nfields + 1, sizeof(*align));
-       if (!align)
-       {
-               fprintf(stderr, _("out of memory\n"));
-               exit(EXIT_FAILURE);
-       }
+       align = pg_local_calloc(nfields + 1, sizeof(*align));
 
        for (i = 0; i < nfields; i++)
        {
index b6e4639..02723d6 100644 (file)
@@ -797,7 +797,6 @@ alter table atacc1 drop c;
 alter table atacc1 drop d;
 alter table atacc1 drop b;
 select * from atacc1;
-  
 --
 (1 row)
 
index 3532953..df5dc5d 100644 (file)
@@ -149,18 +149,18 @@ PREPARE q7(unknown) AS
 SELECT name, statement, parameter_types FROM pg_prepared_statements
     ORDER BY name;
  name |                                                                                    statement                                                                                    |                    parameter_types                     
-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------
- q2   | PREPARE q2(text) AS
-       SELECT datname, datistemplate, datallowconn
-       FROM pg_database WHERE datname = $1;                                                                          | {text}
- q3   | PREPARE q3(text, int, float, boolean, oid, smallint) AS
-       SELECT * FROM tenk1 WHERE string4 = $1 AND (four = $2 OR
-       ten = $3::bigint OR true = $4 OR oid = $5 OR odd = $6::int); | {text,integer,"double precision",boolean,oid,smallint}
- q5   | PREPARE q5(int, text) AS
-       SELECT * FROM tenk1 WHERE unique1 = $1 OR stringu1 = $2;                                                                                              | {integer,text}
- q6   | PREPARE q6 AS
-    SELECT * FROM tenk1 WHERE unique1 = $1 AND stringu1 = $2;                                                                                                     | {integer,name}
- q7   | PREPARE q7(unknown) AS
-    SELECT * FROM road WHERE thepath = $1;                                                                                                               | {path}
+------+------------------------------------------------------------------+--------------------------------------------------------
+ q2   | PREPARE q2(text) AS                                              | {text}                                                
+      : \x09SELECT datname, datistemplate, datallowconn                                                                          
+      : \x09FROM pg_database WHERE datname = $1;                                                                                 
+ q3   | PREPARE q3(text, int, float, boolean, oid, smallint) AS          | {text,integer,"double precision",boolean,oid,smallint}
+      : \x09SELECT * FROM tenk1 WHERE string4 = $1 AND (four = $2 OR                                                             
+      : \x09ten = $3::bigint OR true = $4 OR oid = $5 OR odd = $6::int);                                                         
+ q5   | PREPARE q5(int, text) AS                                         | {integer,text}                                        
+      : \x09SELECT * FROM tenk1 WHERE unique1 = $1 OR stringu1 = $2;                                                             
+ q6   | PREPARE q6 AS                                                    | {integer,name}                                        
+      :     SELECT * FROM tenk1 WHERE unique1 = $1 AND stringu1 = $2;                                                            
+ q7   | PREPARE q7(unknown) AS                                           | {path}                                                
+      :     SELECT * FROM road WHERE thepath = $1;                                                                               
 (5 rows)