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 | 200804211529.m3LFT5S05321@momjian.us Whole thread Raw |
In response to | Re: Proposed patch - psql wraps at window width (Bryce Nesbitt <bryce2@obviously.com>) |
Responses |
Re: Proposed patch - psql wraps at window width
|
List | pgsql-patches |
Bryce Nesbitt wrote: > 1) "\pset columns XX" should make it clear that's for file output only. OK, docs updated. > 2) There's an extra space, which breaks \pset border 2 > > 717c717 > < fputc(' ', fout);; > --- > > fputc(' ', fout); > 842c842 > < fputs(" | ", fout); > --- > > fputs(" |", f OK, got them fixed. > 2) With \pset border 2, the far left border, for symmetry, should work > like the middle borders. OK, how does it look now with this patch? > 3) I'm getting bolder: how about having \pset format wrapped as the > default? Any downsides? I think we are going to want it as the default for many psql informational commands, like \df. Not sure about a more general default. We were just discussing using \x as a default for wide output but it seems this wrap style is probably a better solution than switching for \x for wide columns (less distracting for the user and cleaner). That will have to be a separate discussion once we are done. Oh, I found a problem in my coding of the new wrap function I added. While I handled the case where a character might span multiple bytes, I assumed all characters had a display width of one. You can see from pg_wcsformat()'s use of PQdsplen() that this isn't always the case. I have modified the patch to properly use PQdsplen() but we are going to need multi-byte users to test this once we are done. -- 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 21 Apr 2008 15:27:55 -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,1532 **** 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 language. They are not complete documents! (This might not be --- 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 detected screen width or <literal>\pset ! columns</>. 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 language. They are not complete documents! (This might not be *************** *** 1537,1542 **** --- 1540,1556 ---- </varlistentry> <varlistentry> + <term><literal>columns</literal></term> + <listitem> + <para> + Controls the target width for the <literal>wrap</> format when + output is to a file, or the operating system does not support + screen width detection. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><literal>border</literal></term> <listitem> <para> 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 21 Apr 2008 15:27:56 -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; } *************** *** 1748,1753 **** --- 1753,1768 ---- } } + /* set border style/width */ + else if (strcmp(param, "columns") == 0) + { + if (value) + popt->topt.columns = atoi(value); + + if (!quiet) + printf(_("Target column width for \"wrap\" format is %d.\n"), popt->topt.columns); + } + else { psql_error("\\pset: unknown option: %s\n", param); 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 21 Apr 2008 15:28:01 -0000 *************** *** 204,210 **** /* * 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_height: Number of lines in display output * result_format_size: Number of bytes required to store formatted representation of string */ --- 204,210 ---- /* * pg_wcssize takes the given string in the given encoding and returns three * values: ! * result_width: Width in display characters of the longest line in string * result_height: Number of lines in display output * result_format_size: Number of bytes required to store formatted representation of string */ *************** *** 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 21 Apr 2008 15:28:02 -0000 *************** *** 28,33 **** --- 28,35 ---- #include "mbprint.h" + static int strlen_max_width(unsigned char *str, int *target_width, int encoding); + /* * We define the cancel_pressed flag in this file, rather than common.c where * it naturally belongs, because this file is also used by non-psql programs *************** *** 43,48 **** --- 45,51 ---- static char *grouping; static char *thousands_sep; + static void * pg_local_malloc(size_t size) { *************** *** 396,401 **** --- 399,407 ---- } + /* + * 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; --- 410,441 ---- { 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; + unsigned int total_header_width; const char *const * ptr; ! struct lineptr **col_lineptrs; /* pointers to line pointer per column */ struct lineptr *lineptr_list; /* complete list of linepointers */ ! bool *header_done; /* Have all header lines been output? */ ! int *bytes_output; /* Bytes output for column value */ ! int tcolumns = 0; /* Width of interactive console */ if (cancel_pressed) return; *************** *** 437,533 **** 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) ! 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]; } --- 449,570 ---- 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)); format_buf = pg_local_calloc(col_count, sizeof(*format_buf)); ! header_done = pg_local_calloc(col_count, sizeof(*header_done)); ! bytes_output = pg_local_calloc(col_count, sizeof(*bytes_output)); } else { ! width_header = NULL; ! width_average = NULL; ! width_max = NULL; ! width_wrap = NULL; heights = NULL; col_lineptrs = NULL; format_space = NULL; format_buf = NULL; ! header_done = NULL; ! bytes_output = 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; ! total_header_width = width_total; for (i = 0; i < col_count; i++) ! { ! width_total += width_max[i]; ! total_header_width += width_header[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,571 **** 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 */ 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 ? '+' : ' '); --- 572,678 ---- 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) + { + /* Get terminal width */ + #ifdef TIOCGWINSZ + if (fout == stdout && isatty(fileno(stdout))) + { + struct winsize screen_size; + + if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) != -1) + tcolumns = screen_size.ws_col; + } + else + #endif + tcolumns = opt->columns; + + /* + * If available columns is positive... + * and greater than the width of the unshrinkable column headers + */ + if (tcolumns > 0 && tcolumns >= total_header_width) + { + /* While there is still excess width... */ + while (width_total > tcolumns) + { + double max_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]) + { + /* Penalize wide columns by +1% of their width (0.01) */ + double ratio = (double)width_wrap[i] / width_average[i] + + width_max[i] * 0.01; + + if (ratio > max_ratio) + { + max_ratio = ratio; + worst_col = i; + } + } + } + + /* Exit loop if we can't squeeze any more. */ + if (worst_col == -1) + break; + + 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 */ if (!opt_tuples_only) { ! int more_col_wrapping; 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]); ! more_col_wrapping = col_count; line_count = 0; ! memset(header_done, false, col_count * sizeof(bool)); ! while (more_col_wrapping) { if (opt_border == 2) fprintf(fout, "|%c", line_count ? '+' : ' '); *************** *** 578,586 **** struct lineptr *this_line = col_lineptrs[i] + line_count; ! if (!complete[i]) { ! nbspace = widths[i] - this_line->width; /* centered */ fprintf(fout, "%-*s%s%-*s", --- 685,693 ---- struct lineptr *this_line = col_lineptrs[i] + line_count; ! if (!header_done[i]) { ! nbspace = width_wrap[i] - this_line->width; /* centered */ fprintf(fout, "%-*s%s%-*s", *************** *** 588,599 **** 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) --- 695,706 ---- if (line_count == (heights[i] - 1) || !(this_line + 1)->ptr) { ! more_col_wrapping--; ! header_done[i] = 1; } } else ! fprintf(fout, "%*s", width_wrap[i], ""); if (i < col_count - 1) { if (opt_border == 0) *************** *** 607,711 **** 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 */ 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) --- 714,877 ---- if (opt_border == 2) fputs(" |", fout); else if (opt_border == 1) ! fputc(' ', fout); 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 more_newline_values, ! more_col_wrapping; ! int line_count = 0, col_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(bytes_output, 0, col_count * sizeof(int)); ! ! do ! { ! col_line_count = 0; ! do { ! /* left border */ ! if (opt_border == 2) ! fputs("| ", fout); ! else if (opt_border == 1) ! fputc(' ', fout); ! more_col_wrapping = false; ! ! /* for each column */ ! for (j = 0; j < col_count; j++) { ! /* Past column height so pad for other columns */ ! if (line_count >= heights[j]) ! fprintf(fout, "%*s", width_wrap[j], ""); ! else { ! struct lineptr *this_line = &col_lineptrs[j][line_count]; ! int bytes_to_output, chars_to_output = width_wrap[j]; ! ! /* Get strlen() of the width_wrap character */ ! bytes_to_output = strlen_max_width(this_line->ptr + ! bytes_output[j], &chars_to_output, encoding); ! ! /* ! * If we exceeded width_wrap, it means the display width ! * of a single character was wider than our target width. ! * In that case, we have to pretend we are only printing ! * the target display width and make the best of it. ! */ ! if (chars_to_output > width_wrap[j]) ! chars_to_output = width_wrap[j]; ! ! if (opt_align[j] == 'r') /* Right aligned cell */ { ! /* spaces first */ ! fprintf(fout, "%*s", width_wrap[j] - chars_to_output, ""); ! fprintf(fout, "%.*s", bytes_to_output, ! this_line->ptr + bytes_output[j]); } ! else /* Left aligned cell */ ! { ! /* spaces second */ ! fprintf(fout, "%.*s", bytes_to_output, ! this_line->ptr + bytes_output[j]); ! fprintf(fout, "%*s", width_wrap[j] - chars_to_output, ""); ! } ! ! bytes_output[j] += bytes_to_output; ! ! /* Are we at the end of the string? */ ! more_col_wrapping = more_col_wrapping || ! (*(this_line->ptr + bytes_output[j]) != 0); } ! ! /* print a divider, middle columns only */ ! if ((j + 1) % col_count) { ! if (opt_border == 0) ! fputc(' ', fout); ! /* first line for values? */ ! else if (line_count == 0 && col_line_count == 0) ! fputs(" | ", fout); ! /* next value is beyond height? */ ! else if (line_count >= heights[j + 1]) ! fputs(" ", fout); ! /* start of another newline string? */ ! else if (col_line_count == 0) ! fputs(" : ", fout); ! else ! { ! /* Does the next column wrap to this line? */ ! struct lineptr *this_line = &col_lineptrs[j+1][line_count]; ! bool string_done = *(this_line->ptr + bytes_output[j+1]) == 0; ! ! fputs(string_done ? " " : " ; ", fout); ! } } } ! /* end of row border */ ! if (opt_border == 2) ! fputs(" |", fout); ! fputc('\n', fout); ! col_line_count++; ! ! } while (more_col_wrapping); ! ! /* ! * Check if any columns have line continuations due to \n in the ! * cell. ! */ ! line_count++; ! ! /* Do we have more lines for any column value, i.e embedded \n? */ ! more_newline_values = false; ! for (j = 0; j < col_count; j++) ! { ! if (line_count < heights[j]) { ! if (col_lineptrs[j][line_count].ptr) ! { ! more_newline_values = true; ! bytes_output[j] = 0; ! } } } ! ! } while (more_newline_values); } 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,732 **** } /* 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]); --- 888,902 ---- } /* clean up */ ! free(width_header); ! free(width_average); ! free(width_max); ! free(width_wrap); free(heights); free(col_lineptrs); free(format_space); ! free(header_done); ! free(bytes_output); free(lineptr_list); for (i = 0; i < col_count; i++) free(format_buf[i]); *************** *** 754,760 **** dheight = 1, hformatsize = 0, dformatsize = 0; - int tmp = 0; char *divider; unsigned int cell_count = 0; struct lineptr *hlineptr, --- 924,929 ---- *************** *** 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) --- 948,960 ---- /* 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) --- 969,976 ---- 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) --- 978,987 ---- 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 **** --- 2050,2056 ---- opt, output); break; case PRINT_ALIGNED: + case PRINT_WRAP: if (opt->expanded) print_aligned_vertical(title, headers, cells, footers, align, opt, output); *************** *** 2066,2068 **** --- 2238,2275 ---- else thousands_sep = "."; } + + /* + * Returns the byte length to the end of the specified character + * and number of display characters processed (useful if the string + * is shorter then dpylen). + */ + static int + strlen_max_width(unsigned char *str, int *target_width, int encoding) + { + unsigned char *start = str; + int curr_width = 0; + + while (*str && curr_width < *target_width) + { + int char_width = PQdsplen((char *) str, encoding); + + /* + * If the display width of the new character causes + * the string to exceed its target width, skip it + * and return. However, if this is the first character + * of the string (*width == 0), we have to accept it. + */ + if (*target_width - curr_width < char_width && curr_width != 0) + break; + + str += PQmblen((char *)str, encoding); + + curr_width += char_width; + } + + *target_width = curr_width; + + /* last byte */ + return str - start; + } 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 21 Apr 2008 15:28:02 -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 *************** *** 47,52 **** --- 48,54 ---- * decimal marker */ char *tableAttr; /* attributes for HTML <table ...> */ int encoding; /* character encoding */ + int columns; /* target width for wrap format */ } printTableOpt;
pgsql-patches by date: