Re: Proposed patch - psql wraps at window width - Mailing list pgsql-patches

From Bruce Momjian
Subject Re: Proposed patch - psql wraps at window width
Date
Msg-id 200804170249.m3H2n0f05297@momjian.us
Whole thread Raw
In response to Proposed patch - psql wraps at window width  (Bryce Nesbitt <bryce2@obviously.com>)
Responses Re: Proposed patch - psql wraps at window width  (Bryce Nesbitt <bryce2@obviously.com>)
Re: Proposed patch - psql wraps at window width  (Alvaro Herrera <alvherre@commandprompt.com>)
Re: Proposed patch - psql wraps at window width  (Peter Eisentraut <peter_e@gmx.net>)
List pgsql-patches
Bryce Nesbitt wrote:
> I've attached a patch, against current 8.4 cvs, which optionally sets a
> maximum width for psql output:
>
> # \pset format aligned-wrapped
> # \pset border 2
> # select * from distributors order by did;
> +------+--------------------+---------------------+---------------+
> | did  |        name        |        descr        | long_col_name |
> +------+--------------------+---------------------+---------------+
> |    1 | Food fish and wine | default             |               |
> |    2 | Cat Food Heaven 2  | abcdefghijklmnopqrs !               |
> |      |                    | tuvwxyz             |               |
> |    3 | Cat Food Heaven 3  | default             |               |
> |   10 | Lah                | default             |               |
> |   12 | name               | line one            |               |
> | 2892 ! short name         | short               |               |
> | 8732 |                    |                     |               |
> +------+--------------------+---------------------+---------------+
> (8 rows)
>
> The interactive terminal column width comes from
>         char * temp = getenv("COLUMNS");
> Which has the strong advantage of great simplicity and portability.  But
> it may not be 1000% universal.  If $COLUMNS is not defined, the code
> bails to assuming an infinitely wide terminal.
>
> I will also backport this to Postgres 8.1, for my own use.  Though the
> code is almost totally different in structure.

I spent time reviewing your patch --- quite impressive.  I have attached
and updated version with mostly stylistic changes.

In testing I found the regression tests were failing because of a divide
by zero error (fixed), and a missing case where the column delimiter has
to be ":".  In fact I now see all your line continuation cases using ":"
rather than "!".  It actually looks better --- "!" was too close to "|"
to be easily recognized.  (Did you update your patch to use ":".  I
didn't see "!" in your patch.)

I have added an XXX comment questioning whether the loop to find the
column to wrap is inefficient because it potentially loops over the
length of the longest column and for each character loops over the
number of columns.  Not sure if that is a problem.

I checked the use of COLUMNS and it seems bash updates the environment
variable when a window is resized.  I added ioctl(TIOCGWINSZ) if COLUMNS
isn't set.  We already had a call in print.c for detecting the
number of rows on the screen to determine if the pager should
be used.  Seems COLUMNS should take precedence over ioctl(), right?
I don't think Win32 supports that ioctl(), does it?

I added some comments and clarified some variable names.  I also renamed
the option to a shorter "wrapped".  I added documentation too.

For testers compare:

    \df

with:

    \pset format wrap
    \df

Impressive!

--
  Bruce Momjian  <bruce@momjian.us>        http://momjian.us
  EnterpriseDB                             http://enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +
Index: doc/src/sgml/ref/psql-ref.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v
retrieving revision 1.199
diff -c -c -r1.199 psql-ref.sgml
*** doc/src/sgml/ref/psql-ref.sgml    30 Mar 2008 18:10:20 -0000    1.199
--- doc/src/sgml/ref/psql-ref.sgml    17 Apr 2008 02:45:38 -0000
***************
*** 1513,1519 ****
            <listitem>
            <para>
            Sets the output format to one of <literal>unaligned</literal>,
!           <literal>aligned</literal>, <literal>html</literal>,
            <literal>latex</literal>, or <literal>troff-ms</literal>.
            Unique abbreviations are allowed.  (That would mean one letter
            is enough.)
--- 1513,1520 ----
            <listitem>
            <para>
            Sets the output format to one of <literal>unaligned</literal>,
!           <literal>aligned</literal>, <literal>wrapped</literal>,
!           <literal>html</literal>,
            <literal>latex</literal>, or <literal>troff-ms</literal>.
            Unique abbreviations are allowed.  (That would mean one letter
            is enough.)
***************
*** 1525,1531 ****
            is intended to create output that might be intended to be read
            in by other programs (tab-separated, comma-separated).
            <quote>Aligned</quote> mode is the standard, human-readable,
!           nicely formatted text output that is default. The
            <quote><acronym>HTML</acronym></quote> and
            <quote>LaTeX</quote> modes put out tables that are intended to
            be included in documents using the respective mark-up
--- 1526,1535 ----
            is intended to create output that might be intended to be read
            in by other programs (tab-separated, comma-separated).
            <quote>Aligned</quote> mode is the standard, human-readable,
!           nicely formatted text output that is default.
!           <quote>Wrapped</quote> is like <literal>aligned</> but wraps
!           the output to fit the screen's width, based on the environment
!           variable <envar>COLUMNS</> or the autodetected width. The
            <quote><acronym>HTML</acronym></quote> and
            <quote>LaTeX</quote> modes put out tables that are intended to
            be included in documents using the respective mark-up
***************
*** 2708,2713 ****
--- 2712,2730 ----

    <variablelist>
     <varlistentry>
+     <term><envar>COLUMNS</envar></term>
+
+     <listitem>
+      <para>
+       The character width to wrap output in <literal>wrapped</> format
+       mode.  Many shells automatically update <envar>COLUMNS</> when
+       a window is resized.  If not set the screen width is automatically
+       detected, if possible.
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
      <term><envar>PAGER</envar></term>

      <listitem>
Index: src/bin/psql/command.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/command.c,v
retrieving revision 1.186
diff -c -c -r1.186 command.c
*** src/bin/psql/command.c    1 Jan 2008 19:45:55 -0000    1.186
--- src/bin/psql/command.c    17 Apr 2008 02:45:38 -0000
***************
*** 1526,1531 ****
--- 1526,1534 ----
          case PRINT_ALIGNED:
              return "aligned";
              break;
+         case PRINT_WRAP:
+             return "wrapped";
+             break;
          case PRINT_HTML:
              return "html";
              break;
***************
*** 1559,1564 ****
--- 1562,1569 ----
              popt->topt.format = PRINT_UNALIGNED;
          else if (pg_strncasecmp("aligned", value, vallen) == 0)
              popt->topt.format = PRINT_ALIGNED;
+         else if (pg_strncasecmp("wrapped", value, vallen) == 0)
+             popt->topt.format = PRINT_WRAP;
          else if (pg_strncasecmp("html", value, vallen) == 0)
              popt->topt.format = PRINT_HTML;
          else if (pg_strncasecmp("latex", value, vallen) == 0)
***************
*** 1567,1573 ****
              popt->topt.format = PRINT_TROFF_MS;
          else
          {
!             psql_error("\\pset: allowed formats are unaligned, aligned, html, latex, troff-ms\n");
              return false;
          }

--- 1572,1578 ----
              popt->topt.format = PRINT_TROFF_MS;
          else
          {
!             psql_error("\\pset: allowed formats are unaligned, aligned, wrapped, html, latex, troff-ms\n");
              return false;
          }

Index: src/bin/psql/mbprint.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/mbprint.c,v
retrieving revision 1.30
diff -c -c -r1.30 mbprint.c
*** src/bin/psql/mbprint.c    16 Apr 2008 18:18:00 -0000    1.30
--- src/bin/psql/mbprint.c    17 Apr 2008 02:45:38 -0000
***************
*** 279,284 ****
--- 279,288 ----
      return width;
  }

+ /*
+  *  Filter out unprintable characters, companion to wcs_size.
+  *  Break input into lines (based on \n or \r).
+  */
  void
  pg_wcsformat(unsigned char *pwcs, size_t len, int encoding,
               struct lineptr * lines, int count)
***************
*** 353,364 ****
          }
          len -= chlen;
      }
!     *ptr++ = '\0';
      lines->width = linewidth;
!     lines++;
!     count--;
!     if (count > 0)
          lines->ptr = NULL;
  }

  unsigned char *
--- 357,373 ----
          }
          len -= chlen;
      }
!     *ptr++ = '\0';            /* Terminate formatted string */
!
      lines->width = linewidth;
!
!     /* Fill remaining array slots with nulls */
!     while (--count)
!     {
!         lines++;
          lines->ptr = NULL;
+         lines->width = 0;
+     }
  }

  unsigned char *
Index: src/bin/psql/print.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/print.c,v
retrieving revision 1.97
diff -c -c -r1.97 print.c
*** src/bin/psql/print.c    27 Mar 2008 03:57:34 -0000    1.97
--- src/bin/psql/print.c    17 Apr 2008 02:45:38 -0000
***************
*** 396,401 ****
--- 396,404 ----
  }


+ /*
+  *    Prety pretty boxes around cells.
+  */
  static void
  print_aligned_text(const char *title, const char *const * headers,
                     const char *const * cells, const char *const * footers,
***************
*** 404,429 ****
  {
      bool        opt_tuples_only = opt->tuples_only;
      bool        opt_numeric_locale = opt->numericLocale;
-     unsigned short int opt_border = opt->border;
      int            encoding = opt->encoding;
!     unsigned int col_count = 0;
!     unsigned int cell_count = 0;
!     unsigned int i;
!     int            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 */

      if (cancel_pressed)
          return;
--- 407,437 ----
  {
      bool        opt_tuples_only = opt->tuples_only;
      bool        opt_numeric_locale = opt->numericLocale;
      int            encoding = opt->encoding;
!     unsigned short int opt_border = opt->border;
!
!     unsigned int col_count = 0, cell_count = 0;
!
!     unsigned int i,
!                 j;
!
!     unsigned int *width_header,
!                *width_max,
!                *width_wrap,
!                *width_average;
!     unsigned int *heights,
!                *format_space;
      unsigned char **format_buf;
+     unsigned int width_total;

      const char *const * ptr;

!     struct lineptr **col_lineptrs;        /* pointers to line pointer per column */
      struct lineptr *lineptr_list;        /* complete list of linepointers */

      int           *complete;        /* Array remembering which columns have
                                   * completed output */
+     int            tcolumns = 0;    /* Width of interactive console */

      if (cancel_pressed)
          return;
***************
*** 437,443 ****

      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));
--- 445,454 ----

      if (col_count > 0)
      {
!         width_header = pg_local_calloc(col_count, sizeof(*width_header));
!         width_average = pg_local_calloc(col_count, sizeof(*width_average));
!         width_max = pg_local_calloc(col_count, sizeof(*width_max));
!         width_wrap = pg_local_calloc(col_count, sizeof(*width_wrap));
          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));
***************
*** 446,452 ****
      }
      else
      {
!         widths = NULL;
          heights = NULL;
          col_lineptrs = NULL;
          format_space = NULL;
--- 457,466 ----
      }
      else
      {
!         width_header = NULL;
!         width_average = NULL;
!         width_max = NULL;
!         width_wrap = NULL;
          heights = NULL;
          col_lineptrs = NULL;
          format_space = NULL;
***************
*** 454,533 ****
          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)
!         total_w = col_count - 1;
      else if (opt_border == 1)
!         total_w = col_count * 3 - 1;
      else
!         total_w = col_count * 3 + 1;

      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
       */
      if (col_count > 0)
      {
!         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];
          }
--- 468,560 ----
          complete = NULL;
      }

!     /* scan all column headers, find maximum width */
      for (i = 0; i < col_count; i++)
      {
          /* Get width & height */
!         int            width,
!                     height,
                      space;

!         pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &width, &height, &space);
!         if (width > width_max[i])
!             width_max[i] = width;
          if (height > heights[i])
              heights[i] = height;
          if (space > format_space[i])
              format_space[i] = space;
+
+         width_header[i] = width;
      }

!     /* scan all rows, find maximum width, compute cell_count */
!     for (i = 0, ptr = cells; *ptr; ptr++, i++, cell_count++)
      {
!         int            width,
!                     height,
                      space;

          /* Get width, ignore height */
!         pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &width, &height, &space);
!         if (opt_numeric_locale && opt_align[i % col_count] == 'r')
!         {
!             width += additional_numeric_locale_len(*ptr);
!             space += additional_numeric_locale_len(*ptr);
!         }
!
!         if (width > width_max[i % col_count])
!             width_max[i % col_count] = width;
          if (height > heights[i % col_count])
              heights[i % col_count] = height;
          if (space > format_space[i % col_count])
              format_space[i % col_count] = space;
+
+         width_average[i % col_count] += width;
+     }
+
+     /* If we have rows, compute average */
+     if (col_count != 0 && cell_count != 0)
+     {
+         int rows = cell_count / col_count;
+
+         for (i = 0; i < col_count; i++)
+             width_average[i % col_count] /= rows;
      }

+     /* adjust the total display width based on border style */
      if (opt_border == 0)
!         width_total = col_count - 1;
      else if (opt_border == 1)
!         width_total = col_count * 3 - 1;
      else
!         width_total = col_count * 3 + 1;

      for (i = 0; i < col_count; i++)
!         width_total += width_max[i];

      /*
!      * At this point: width_max[] contains the max width of each column,
!      * heights[] contains the max number of lines in each column,
!      * format_space[] contains the maximum storage space for formatting
!      * strings, width_total contains the giant width sum.  Now we allocate
!      * some memory...
       */
      if (col_count > 0)
      {
!         int            height_total = 0;
          struct lineptr *lineptr;

          for (i = 0; i < col_count; i++)
!             height_total += heights[i];

!         lineptr = lineptr_list = pg_local_calloc(height_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] + 1);

              col_lineptrs[i]->ptr = format_buf[i];
          }
***************
*** 535,553 ****
      else
          lineptr_list = NULL;

      if (opt->start_table)
      {
          /* 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
!                 fprintf(fout, "%-*s%s\n", (total_w - tmp) / 2, "", title);
          }

          /* print headers */
--- 562,650 ----
      else
          lineptr_list = NULL;

+
+     /* Default word wrap to the full width, i.e. no word wrap */
+     for (i = 0; i < col_count; i++)
+         width_wrap[i] = width_max[i];
+
+     /*
+      * Optional optimized word wrap. Shrink columns with a high max/avg ratio.
+      * Slighly bias against wider columns (increases chance a narrow column
+      * will fit in its cell)
+      */
+     if (opt->format == PRINT_WRAP)
+     {
+         /* If we can get the terminal width */
+         char       *env = getenv("COLUMNS");
+
+         if (env != NULL)
+             tcolumns = atoi(env);
+ #ifdef TIOCGWINSZ
+         else
+         {
+             struct winsize screen_size;
+
+             if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) != -1)
+                 tcolumns = screen_size.ws_col;
+         }
+ #endif
+
+         if (tcolumns > 0)
+         {
+             /* Shink high ratio columns */
+             while (width_total > tcolumns)
+             {
+                 double        ratio = 0;
+                 double        curr_ratio = 0;
+                 int            worst_col = -1;
+
+                 /*
+                  *    Find column that has the highest ratio of its maximum
+                  *    width compared to its average width.  This tells us which
+                  *    column will produce the fewest wrapped values if shortened.
+                  *    width_wrap starts as equal to width_max.
+                  */
+                 for (i = 0; i < col_count; i++)
+                     if (width_average[i] && width_wrap[i] > width_header[i])
+                     {
+                         curr_ratio = (double) width_wrap[i] / width_average[i];
+                         curr_ratio += width_max[i] * 0.01;        /* Penalize wide columns */
+                         if (curr_ratio > ratio)
+                         {
+                             ratio = curr_ratio;
+                             worst_col = i;
+                         }
+                     }
+
+                 /* Exit loop if we can't squeeze any more. */
+                 if (worst_col < 0)
+                     break;
+
+                 /* Squeeze the worst column.  Lather, rinse, repeat */
+                 /*
+                  *    XXX This only reduces one character at a time so might
+                  *    be inefficient for very long rows.
+                  */
+                 width_wrap[worst_col]--;
+                 width_total--;
+             }
+         }
+     }
+
+     /* time to output */
      if (opt->start_table)
      {
          /* print title */
          if (title && !opt_tuples_only)
          {
!             int            width,
!                         height;

!             pg_wcssize((unsigned char *) title, strlen(title), encoding, &width, &height, NULL);
!             if (width >= width_total)
!                 fprintf(fout, "%s\n", title);    /* Aligned */
              else
!                 fprintf(fout, "%-*s%s\n", (width_total - width) / 2, "", title);        /* Centered */
          }

          /* print headers */
***************
*** 557,563 ****
              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]);
--- 654,660 ----
              int            line_count;

              if (opt_border == 2)
!                 _print_horizontal_line(col_count, width_wrap, 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]);
***************
*** 580,586 ****

                      if (!complete[i])
                      {
!                         nbspace = widths[i] - this_line->width;

                          /* centered */
                          fprintf(fout, "%-*s%s%-*s",
--- 677,683 ----

                      if (!complete[i])
                      {
!                         nbspace = width_wrap[i] - this_line->width;

                          /* centered */
                          fprintf(fout, "%-*s%s%-*s",
***************
*** 593,599 ****
                          }
                      }
                      else
!                         fprintf(fout, "%*s", widths[i], "");
                      if (i < col_count - 1)
                      {
                          if (opt_border == 0)
--- 690,696 ----
                          }
                      }
                      else
!                         fprintf(fout, "%*s", width_wrap[i], "");
                      if (i < col_count - 1)
                      {
                          if (opt_border == 0)
***************
*** 611,711 ****
                  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 */

          if (cancel_pressed)
              break;

          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;
!                 bool        finalspaces = (opt_border == 2 || j != col_count - 1);

!                 if (complete[j])    /* Just print spaces... */
!                 {
!                     if (finalspaces)
!                         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((char *) this_line->ptr);
!
!                             fprintf(fout, "%*s%s",
!                                     (int) (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,
!                         finalspaces ? (widths[j] - this_line->width) : 0, "");
!                     /* 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++;
          }
      }

      if (opt->stop_table)
      {
          if (opt_border == 2 && !cancel_pressed)
!             _print_horizontal_line(col_count, widths, opt_border, fout);

          /* print footers */
          if (footers && !opt_tuples_only && !cancel_pressed)
--- 708,855 ----
                  fputc('\n', fout);
              }

!             _print_horizontal_line(col_count, width_wrap, opt_border, fout);
          }
      }

      /* print cells */
      for (i = 0, ptr = cells; *ptr; i += col_count, ptr += col_count)
      {
!         bool        line_todo,
!                     cols_todo;
!         int            line_count;

          if (cancel_pressed)
              break;

+         /*
+          * Format each cell.  Format again, it is a numeric formatting locale
+          * (e.g. 123,456 vs. 123456)
+          */
          for (j = 0; j < col_count; j++)
+         {
              pg_wcsformat((unsigned char *) ptr[j], strlen(ptr[j]), encoding, col_lineptrs[j], heights[j]);
+             if (opt_numeric_locale && opt_align[j % col_count] == 'r')
+             {
+                 char       *my_cell;

!                 my_cell = format_numeric_locale((char *) col_lineptrs[j]->ptr);
!                 strcpy((char *) col_lineptrs[j]->ptr, my_cell); /* Buffer IS large
!                                                                  * enough... now */
!                 free(my_cell);
!             }
!         }
!
!         /* Print rows to console */
          memset(complete, 0, col_count * sizeof(int));
!         line_count = 0;
!         line_todo = true;
!         while (line_todo)
          {
!             cols_todo = true;
!             while (cols_todo)
              {
!                 char    border_cell = '*';

!                 cols_todo = false;
!
!                 /* left border */
!                 if (opt_border == 2)
!                     fputs("| ", fout);
!                 else if (opt_border == 1)
!                     fputc(' ', fout);
!
!                 /* for each column */
!                 for (j = 0; j < col_count; j++)
                  {
!                     struct lineptr *this_line = &col_lineptrs[j][line_count];
!
!                     if (heights[j] <= line_count)        /* Blank column content */
!                     {
!                         fprintf(fout, "%*s", width_wrap[j], "");
!                         border_cell = '|';
!                     }
!                     else if (opt_align[j] == 'r')        /* Right aligned cell */
                      {
!                         int        strlen_remaining = strlen((char *) this_line->ptr + complete[j]);
!
!                         if (strlen_remaining > width_wrap[j])
                          {
!                             fprintf(fout, "%.*s", (int) width_wrap[j], this_line->ptr + complete[j]);
!                             complete[j] += width_wrap[j];        /* We've done THIS much */
!                             cols_todo = true;    /* And there is more to do... */
!                             border_cell = ':';
                          }
                          else
!                         {
!                             fprintf(fout, "%*s", width_wrap[j] - strlen_remaining, "");
!                             fprintf(fout, "%-s", this_line->ptr + complete[j]);
!                             complete[j] += strlen_remaining;
!                             border_cell = '|';
!                         }
                      }
!                     else    /* Left aligned cell */
                      {
!                         int        strlen_remaining = strlen((char *) this_line->ptr + complete[j]);
!
!                         if (strlen_remaining > width_wrap[j])
!                         {
!                             fprintf(fout, "%.*s", (int) width_wrap[j], this_line->ptr + complete[j]);
!                             complete[j] += width_wrap[j];        /* We've done THIS much */
!                             cols_todo = true;    /* And there is more to do... */
!                             border_cell = ':';
!                         }
!                         else
!                         {
!                             fprintf(fout, "%-s", this_line->ptr + complete[j]);
!                             fprintf(fout, "%*s", width_wrap[j] - strlen_remaining, "");
!                             complete[j] += strlen_remaining;
!                             border_cell = '|';
!                         }
!                     }
!
!                     /* print a divider, middle of columns only */
!                     if ((j + 1) % col_count)
!                     {
!                         if (opt_border == 0)
!                             fputc(' ', fout);
!                         else if (line_count == 0)
!                             fprintf(fout, " %c ", border_cell);
!                         else
!                             fprintf(fout, " %c ", complete[j + 1] ? ' ' : ':');
                      }
                  }

!                 /* end of row border */
!                 if (opt_border == 2)
!                     fprintf(fout, " %c", border_cell);
!                 fputc('\n', fout);
!             }
!
!             /*
!              * Check if any columns have line continuations due to \n in the
!              * cell.
!              */
!             line_count++;
!             line_todo = false;
!             for (j = 0; j < col_count; j++)
!             {
!                 if (line_count < heights[j])
                  {
!                     if (col_lineptrs[j][line_count].ptr)
!                     {
!                         line_todo = true;
!                         complete[j] = 0;
!                     }
                  }
              }
          }
      }

      if (opt->stop_table)
      {
          if (opt_border == 2 && !cancel_pressed)
!             _print_horizontal_line(col_count, width_wrap, opt_border, fout);

          /* print footers */
          if (footers && !opt_tuples_only && !cancel_pressed)
***************
*** 722,728 ****
      }

      /* clean up */
!     free(widths);
      free(heights);
      free(col_lineptrs);
      free(format_space);
--- 866,875 ----
      }

      /* clean up */
!     free(width_header);
!     free(width_average);
!     free(width_max);
!     free(width_wrap);
      free(heights);
      free(col_lineptrs);
      free(format_space);
***************
*** 754,760 ****
                  dheight = 1,
                  hformatsize = 0,
                  dformatsize = 0;
-     int            tmp = 0;
      char       *divider;
      unsigned int cell_count = 0;
      struct lineptr *hlineptr,
--- 901,906 ----
***************
*** 779,790 ****
      /* 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)
--- 925,937 ----
      /* Find the maximum dimensions for the headers */
      for (i = 0; i < col_count; i++)
      {
!         int            width,
!                     height,
                      fs;

!         pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &width, &height, &fs);
!         if (width > hwidth)
!             hwidth = width;
          if (height > hheight)
              hheight = height;
          if (fs > hformatsize)
***************
*** 799,805 ****
      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)
--- 946,953 ----
      for (i = 0, ptr = cells; *ptr; ptr++, i++)
      {
          int            numeric_locale_len;
!         int            width,
!                     height,
                      fs;

          if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
***************
*** 807,816 ****
          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)
--- 955,964 ----
          else
              numeric_locale_len = 0;

!         pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &width, &height, &fs);
!         width += numeric_locale_len;
!         if (width > dwidth)
!             dwidth = width;
          if (height > dheight)
              dheight = height;
          if (fs > dformatsize)
***************
*** 1879,1884 ****
--- 2027,2033 ----
                                       opt, output);
              break;
          case PRINT_ALIGNED:
+         case PRINT_WRAP:
              if (opt->expanded)
                  print_aligned_vertical(title, headers, cells, footers, align,
                                         opt, output);
Index: src/bin/psql/print.h
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/print.h,v
retrieving revision 1.35
diff -c -c -r1.35 print.h
*** src/bin/psql/print.h    1 Jan 2008 19:45:56 -0000    1.35
--- src/bin/psql/print.h    17 Apr 2008 02:45:38 -0000
***************
*** 21,26 ****
--- 21,27 ----
      PRINT_NOTHING = 0,            /* to make sure someone initializes this */
      PRINT_UNALIGNED,
      PRINT_ALIGNED,
+     PRINT_WRAP,
      PRINT_HTML,
      PRINT_LATEX,
      PRINT_TROFF_MS

pgsql-patches by date:

Previous
From: Alvaro Herrera
Date:
Subject: Re: printTable API (was: Show INHERIT in \du)
Next
From: Bryce Nesbitt
Date:
Subject: Re: Proposed patch - psql wraps at window width