Thread: [PATCH] psql formatting patch (round 2)

[PATCH] psql formatting patch (round 2)

From
Martijn van Oosterhout
Date:
[Please CC any replies, thanks]

This patch has the same effect as the last one except it now uses the
PQmblen and PQdsplen functions exported by libpq as suggested by Tom.
This clears a lot of stuff from psql's mbprint.c which is a good thing.
This means it should work for all for encodings (though I can't say I
tested them all).

The PQdsplen function in libpq has been improved so it can actually
work and provide the necessary information for formatting. That file
(wchar.c) is actually shared with the backend but the backend doesn't
use the dsplen functions, so it does add some dead code (on top of what
was already there).

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.

The other uses of PQdsplen in psql (in ReportSyntaxErrorPosition) would
seem to be slightly affected by the change, except it's not clear how
psql should react to control characters in the string it's trying to
print...

Patch available at:
http://svana.org/kleptog/pgsql/psql-format.patch

Comments welcome,

Have a nice day,
--
Martijn van Oosterhout   <kleptog@svana.org>   http://svana.org/kleptog/
> Patent. n. Genius is 5% inspiration and 95% perspiration. A patent is a
> tool for doing 5% of the work and then sitting around waiting for someone
> else to do the other 95% so you can sue them.

Attachment

Re: [PATCH] psql formatting patch (round 2)

From
Bruce Momjian
Date:
Modified patch attached and applied.  I made some style changes and had
some merge conflicts because wchar.c has changed since 8.1.

The change to libpq's PQdsplen() seems like a good one and I will
mention it in the release notes.

---------------------------------------------------------------------------

Martijn van Oosterhout wrote:
-- Start of PGP signed section.
> [Please CC any replies, thanks]
>
> This patch has the same effect as the last one except it now uses the
> PQmblen and PQdsplen functions exported by libpq as suggested by Tom.
> This clears a lot of stuff from psql's mbprint.c which is a good thing.
> This means it should work for all for encodings (though I can't say I
> tested them all).
>
> The PQdsplen function in libpq has been improved so it can actually
> work and provide the necessary information for formatting. That file
> (wchar.c) is actually shared with the backend but the backend doesn't
> use the dsplen functions, so it does add some dead code (on top of what
> was already there).
>
> 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.
>
> The other uses of PQdsplen in psql (in ReportSyntaxErrorPosition) would
> seem to be slightly affected by the change, except it's not clear how
> psql should react to control characters in the string it's trying to
> print...
>
> Patch available at:
> http://svana.org/kleptog/pgsql/psql-format.patch
>
> Comments welcome,
>
> Have a nice day,
> --
> Martijn van Oosterhout   <kleptog@svana.org>   http://svana.org/kleptog/
> > Patent. n. Genius is 5% inspiration and 95% perspiration. A patent is a
> > tool for doing 5% of the work and then sitting around waiting for someone
> > else to do the other 95% so you can sue them.

[ Attachment, skipping... ]
-- End of PGP section, PGP failed!

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
Index: src/backend/utils/mb/wchar.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/mb/wchar.c,v
retrieving revision 1.52
diff -c -c -r1.52 wchar.c
*** src/backend/utils/mb/wchar.c    26 Dec 2005 19:30:44 -0000    1.52
--- src/backend/utils/mb/wchar.c    10 Feb 2006 00:23:32 -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.
+  *
   */

  /*
***************
*** 53,58 ****
--- 60,70 ----
  static int
  pg_ascii_dsplen(const unsigned char *s)
  {
+     if (*s == '\0')
+         return 0;
+     if (*s < 0x20 || *s == 0x7f)
+         return -1;
+
      return 1;
  }

***************
*** 125,131 ****
      else if (IS_HIGHBIT_SET(*s))
          len = 2;
      else
!         len = 1;
      return len;
  }

--- 137,143 ----
      else if (IS_HIGHBIT_SET(*s))
          len = 2;
      else
!         len = pg_ascii_dsplen(s);
      return len;
  }

***************
*** 156,162 ****
      else if (IS_HIGHBIT_SET(*s))
          len = 2;
      else
!         len = 1;
      return len;
  }

--- 168,174 ----
      else if (IS_HIGHBIT_SET(*s))
          len = 2;
      else
!         len = pg_ascii_dsplen(s);
      return len;
  }

***************
*** 244,250 ****
      if (IS_HIGHBIT_SET(*s))
          len = 2;
      else
!         len = 1;
      return len;
  }

--- 256,262 ----
      if (IS_HIGHBIT_SET(*s))
          len = 2;
      else
!         len = pg_ascii_dsplen(s);
      return len;
  }

***************
*** 304,310 ****
      else if (IS_HIGHBIT_SET(*s))
          len = 2;
      else
!         len = 1;
      return len;
  }

--- 316,322 ----
      else if (IS_HIGHBIT_SET(*s))
          len = 2;
      else
!         len = pg_ascii_dsplen(s);
      return len;
  }

***************
*** 320,326 ****
      else if (IS_HIGHBIT_SET(*s))
          len = 2;
      else
!         len = 1;
      return len;
  }

--- 332,338 ----
      else if (IS_HIGHBIT_SET(*s))
          len = 2;
      else
!         len = pg_ascii_dsplen(s);
      return len;
  }

***************
*** 419,428 ****
      return len;
  }

  static int
  pg_utf_dsplen(const unsigned char *s)
  {
!     return 1;                    /* XXX fix me! */
  }

  /*
--- 431,609 ----
      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 ucs_wcwidth(utf2ucs(s));
  }

  /*
***************
*** 499,505 ****
  static int
  pg_mule_dsplen(const unsigned char *s)
  {
!     return 1;                    /* XXX fix me! */
  }

  /*
--- 680,686 ----
  static int
  pg_mule_dsplen(const unsigned char *s)
  {
!     return pg_ascii_dsplen(s);                    /* XXX fix me! */
  }

  /*
***************
*** 529,535 ****
  static int
  pg_latin1_dsplen(const unsigned char *s)
  {
!     return 1;
  }

  /*
--- 710,716 ----
  static int
  pg_latin1_dsplen(const unsigned char *s)
  {
!     return pg_ascii_dsplen(s);
  }

  /*
***************
*** 559,565 ****
      else if (IS_HIGHBIT_SET(*s))
          len = 2;    /* kanji? */
      else
!         len = 1;    /* should be ASCII */
      return len;
  }

--- 740,746 ----
      else if (IS_HIGHBIT_SET(*s))
          len = 2;    /* kanji? */
      else
!         len = pg_ascii_dsplen(s);    /* should be ASCII */
      return len;
  }

***************
*** 586,592 ****
      if (IS_HIGHBIT_SET(*s))
          len = 2;    /* kanji? */
      else
!         len = 1;    /* should be ASCII */
      return len;
  }

--- 767,773 ----
      if (IS_HIGHBIT_SET(*s))
          len = 2;    /* kanji? */
      else
!         len = pg_ascii_dsplen(s);    /* should be ASCII */
      return len;
  }

***************
*** 613,619 ****
      if (IS_HIGHBIT_SET(*s))
          len = 2;    /* kanji? */
      else
!         len = 1;    /* should be ASCII */
      return len;
  }

--- 794,800 ----
      if (IS_HIGHBIT_SET(*s))
          len = 2;    /* kanji? */
      else
!         len = pg_ascii_dsplen(s);    /* should be ASCII */
      return len;
  }

***************
*** 640,646 ****
      if (IS_HIGHBIT_SET(*s))
          len = 2;    /* 2byte? */
      else
!         len = 1;    /* should be ASCII */
      return len;
  }

--- 821,827 ----
      if (IS_HIGHBIT_SET(*s))
          len = 2;    /* 2byte? */
      else
!         len = pg_ascii_dsplen(s);    /* should be ASCII */
      return len;
  }

***************
*** 672,681 ****
  {
      int            len;

!     if (!IS_HIGHBIT_SET(*s))
!         len = 1;    /* ASCII */
!     else
          len = 2;
      return len;
  }

--- 853,862 ----
  {
      int            len;

!     if (IS_HIGHBIT_SET(*s))
          len = 2;
+     else
+         len = pg_ascii_dsplen(s);    /* ASCII */
      return len;
  }

Index: src/bin/psql/mbprint.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/mbprint.c,v
retrieving revision 1.18
diff -c -c -r1.18 mbprint.c
*** src/bin/psql/mbprint.c    15 Oct 2005 02:49:40 -0000    1.18
--- src/bin/psql/mbprint.c    10 Feb 2006 00:23:33 -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"
-  * <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)
  {
--- 14,19 ----
***************
*** 191,225 ****
      }
  }

- /* 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)
  {
-     /*
-      * 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)
--- 48,63 ----
      }
  }


+ /*
+  * 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)
  {
      if ((*c & 0x80) == 0)
          return 1;
      else if ((*c & 0xe0) == 0xc0)
***************
*** 270,275 ****
--- 108,114 ----
      return -1;
  }

+
  static void
  mb_utf_validate(unsigned char *pwcs)
  {
***************
*** 277,304 ****

      while (*pwcs)
      {
!         int            l;

!         if ((l = utf_charcheck(pwcs)) > 0)
          {
              if (p != pwcs)
              {
                  int            i;

!                 for (i = 0; i < l; i++)
                      *p++ = *pwcs++;
              }
              else
              {
!                 pwcs += l;
!                 p += l;
              }
          }
          else
-         {
              /* we skip the char */
              pwcs++;
-         }
      }
      if (p != pwcs)
          *p = '\0';
--- 116,141 ----

      while (*pwcs)
      {
!         int            len;

!         if ((len = utf_charcheck(pwcs)) > 0)
          {
              if (p != pwcs)
              {
                  int            i;

!                 for (i = 0; i < len; i++)
                      *p++ = *pwcs++;
              }
              else
              {
!                 pwcs += len;
!                 p += len;
              }
          }
          else
              /* we skip the char */
              pwcs++;
      }
      if (p != pwcs)
          *p = '\0';
***************
*** 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,337 ----
   * 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,
!         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;
  }

! 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: /cvsroot/pgsql/src/bin/psql/mbprint.h,v
retrieving revision 1.8
diff -c -c -r1.8 mbprint.h
*** src/bin/psql/mbprint.h    24 Sep 2005 17:53:27 -0000    1.8
--- src/bin/psql/mbprint.h    10 Feb 2006 00:23:33 -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: /cvsroot/pgsql/src/bin/psql/print.c,v
retrieving revision 1.79
diff -c -c -r1.79 print.c
*** src/bin/psql/print.c    27 Oct 2005 13:34:47 -0000    1.79
--- src/bin/psql/print.c    10 Feb 2006 00:23:33 -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,623 ----
      /* 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);
  }


--- 638,652 ----
  #endif

      /* clean up */
      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,574 ****
      unsigned int i,
                  tmp = 0,
                  hwidth = 0,
!                 dwidth = 0;
      char       *divider;
      unsigned int cell_count = 0;
!     unsigned int *cell_w,
!                *head_w;
!
      if (cells[0] == NULL)
      {
          fprintf(fout, _("(No rows)\n"));
--- 664,678 ----
      unsigned int i,
                  tmp = 0,
                  hwidth = 0,
!                 dwidth = 0,
!                 hheight = 1,
!                 dheight = 1,
!                 hformatsize = 0,
!                 dformatsize = 0;
      char       *divider;
      unsigned int cell_count = 0;
!     struct lineptr *hlineptr, *dlineptr;
!
      if (cells[0] == NULL)
      {
          fprintf(fout, _("(No rows)\n"));
***************
*** 578,635 ****
      /* 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;

      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);
--- 682,733 ----
      /* 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 ****
--- 751,758 ----
      /* 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)
--- 788,853 ----
                  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);
  }


--- 865,874 ----

      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++)
      {
--- 1747,1760 ----
      /* 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)
--- 1770,1776 ----
          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++)
      {
--- 1782,1788 ----
          footers = NULL;

      /* set alignment */
!     align = pg_local_calloc(nfields + 1, sizeof(*align));

      for (i = 0; i < nfields; i++)
      {

Re: [PATCH] psql formatting patch (round 2)

From
Bruce Momjian
Date:
Oh, one more thing.  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.

Usage example:

    test=> SELECT '1\n2\n3', 'a\nb\nc';
     ?column? | ?column?
    ----------+----------
     1        | a
     2        : b
     3        : c
    (1 row)

---------------------------------------------------------------------------

Martijn van Oosterhout wrote:
-- Start of PGP signed section.
> [Please CC any replies, thanks]
>
> This patch has the same effect as the last one except it now uses the
> PQmblen and PQdsplen functions exported by libpq as suggested by Tom.
> This clears a lot of stuff from psql's mbprint.c which is a good thing.
> This means it should work for all for encodings (though I can't say I
> tested them all).
>
> The PQdsplen function in libpq has been improved so it can actually
> work and provide the necessary information for formatting. That file
> (wchar.c) is actually shared with the backend but the backend doesn't
> use the dsplen functions, so it does add some dead code (on top of what
> was already there).
>
> 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.
>
> The other uses of PQdsplen in psql (in ReportSyntaxErrorPosition) would
> seem to be slightly affected by the change, except it's not clear how
> psql should react to control characters in the string it's trying to
> print...
>
> Patch available at:
> http://svana.org/kleptog/pgsql/psql-format.patch
>
> Comments welcome,
>
> Have a nice day,
> --
> Martijn van Oosterhout   <kleptog@svana.org>   http://svana.org/kleptog/
> > Patent. n. Genius is 5% inspiration and 95% perspiration. A patent is a
> > tool for doing 5% of the work and then sitting around waiting for someone
> > else to do the other 95% so you can sue them.

[ Attachment, skipping... ]
-- End of PGP section, PGP failed!

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073