1 /*-------------------------------------------------------------------------
2 *
3 * fe-print.c
4 * functions for pretty-printing query results
5 *
6 * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 * These functions were formerly part of fe-exec.c, but they
10 * didn't really belong there.
11 *
12 * IDENTIFICATION
13 * src/interfaces/libpq/fe-print.c
14 *
15 *-------------------------------------------------------------------------
16 */
17 #include "postgres_fe.h"
18
19 #include <signal.h>
20
21 #ifdef WIN32
22 #include "win32.h"
23 #else
24 #include <unistd.h>
25 #include <sys/ioctl.h>
26 #endif
27
28 #ifdef HAVE_TERMIOS_H
29 #include <termios.h>
30 #else
31 #ifndef WIN32
32 #include <sys/termios.h>
33 #endif
34 #endif
35
36 #include "libpq-fe.h"
37 #include "libpq-int.h"
38
39 #define PQmblenBounded(s, e) strnlen(s, PQmblen(s, e))
40
41 static bool do_field(const PQprintOpt *po, const PGresult *res,
42 const int i, const int j, const int fs_len,
43 char **fields,
44 const int nFields, const char **fieldNames,
45 unsigned char *fieldNotNum, int *fieldMax,
46 const int fieldMaxLen, FILE *fout);
47 static char *do_header(FILE *fout, const PQprintOpt *po, const int nFields,
48 int *fieldMax, const char **fieldNames, unsigned char *fieldNotNum,
49 const int fs_len, const PGresult *res);
50 static void output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields,
51 unsigned char *fieldNotNum, int *fieldMax, char *border,
52 const int row_index);
53 static void fill(int length, int max, char filler, FILE *fp);
54
55 /*
56 * PQprint()
57 *
58 * Format results of a query for printing.
59 *
60 * PQprintOpt is a typedef (structure) that contains
61 * various flags and options. consult libpq-fe.h for
62 * details
63 *
64 * This function should probably be removed sometime since psql
65 * doesn't use it anymore. It is unclear to what extent this is used
66 * by external clients, however.
67 */
68 void
PQprint(FILE * fout,const PGresult * res,const PQprintOpt * po)69 PQprint(FILE *fout, const PGresult *res, const PQprintOpt *po)
70 {
71 int nFields;
72
73 nFields = PQnfields(res);
74
75 if (nFields > 0)
76 { /* only print rows with at least 1 field. */
77 int i,
78 j;
79 int nTups;
80 int *fieldMax = NULL; /* in case we don't use them */
81 unsigned char *fieldNotNum = NULL;
82 char *border = NULL;
83 char **fields = NULL;
84 const char **fieldNames = NULL;
85 int fieldMaxLen = 0;
86 int numFieldName;
87 int fs_len = strlen(po->fieldSep);
88 int total_line_length = 0;
89 bool usePipe = false;
90 char *pagerenv;
91
92 #if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
93 sigset_t osigset;
94 bool sigpipe_masked = false;
95 bool sigpipe_pending;
96 #endif
97 #if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
98 pqsigfunc oldsigpipehandler = NULL;
99 #endif
100
101 #ifdef TIOCGWINSZ
102 struct winsize screen_size;
103 #else
104 struct winsize
105 {
106 int ws_row;
107 int ws_col;
108 } screen_size;
109 #endif
110
111 nTups = PQntuples(res);
112 fieldNames = (const char **) calloc(nFields, sizeof(char *));
113 fieldNotNum = (unsigned char *) calloc(nFields, 1);
114 fieldMax = (int *) calloc(nFields, sizeof(int));
115 if (!fieldNames || !fieldNotNum || !fieldMax)
116 {
117 fprintf(stderr, libpq_gettext("out of memory\n"));
118 goto exit;
119 }
120 for (numFieldName = 0;
121 po->fieldName && po->fieldName[numFieldName];
122 numFieldName++)
123 ;
124 for (j = 0; j < nFields; j++)
125 {
126 int len;
127 const char *s = (j < numFieldName && po->fieldName[j][0]) ?
128 po->fieldName[j] : PQfname(res, j);
129
130 fieldNames[j] = s;
131 len = s ? strlen(s) : 0;
132 fieldMax[j] = len;
133 len += fs_len;
134 if (len > fieldMaxLen)
135 fieldMaxLen = len;
136 total_line_length += len;
137 }
138
139 total_line_length += nFields * strlen(po->fieldSep) + 1;
140
141 if (fout == NULL)
142 fout = stdout;
143 if (po->pager && fout == stdout && isatty(fileno(stdin)) &&
144 isatty(fileno(stdout)))
145 {
146 /*
147 * If we think there'll be more than one screen of output, try to
148 * pipe to the pager program.
149 */
150 #ifdef TIOCGWINSZ
151 if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
152 screen_size.ws_col == 0 ||
153 screen_size.ws_row == 0)
154 {
155 screen_size.ws_row = 24;
156 screen_size.ws_col = 80;
157 }
158 #else
159 screen_size.ws_row = 24;
160 screen_size.ws_col = 80;
161 #endif
162 pagerenv = getenv("PAGER");
163 /* if PAGER is unset, empty or all-white-space, don't use pager */
164 if (pagerenv != NULL &&
165 strspn(pagerenv, " \t\r\n") != strlen(pagerenv) &&
166 !po->html3 &&
167 ((po->expanded &&
168 nTups * (nFields + 1) >= screen_size.ws_row) ||
169 (!po->expanded &&
170 nTups * (total_line_length / screen_size.ws_col + 1) *
171 (1 + (po->standard != 0)) >= screen_size.ws_row -
172 (po->header != 0) *
173 (total_line_length / screen_size.ws_col + 1) * 2
174 - (po->header != 0) * 2 /* row count and newline */
175 )))
176 {
177 fout = popen(pagerenv, "w");
178 if (fout)
179 {
180 usePipe = true;
181 #ifndef WIN32
182 #ifdef ENABLE_THREAD_SAFETY
183 if (pq_block_sigpipe(&osigset, &sigpipe_pending) == 0)
184 sigpipe_masked = true;
185 #else
186 oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
187 #endif /* ENABLE_THREAD_SAFETY */
188 #endif /* WIN32 */
189 }
190 else
191 fout = stdout;
192 }
193 }
194
195 if (!po->expanded && (po->align || po->html3))
196 {
197 fields = (char **) calloc((size_t) nTups + 1,
198 nFields * sizeof(char *));
199 if (!fields)
200 {
201 fprintf(stderr, libpq_gettext("out of memory\n"));
202 goto exit;
203 }
204 }
205 else if (po->header && !po->html3)
206 {
207 if (po->expanded)
208 {
209 if (po->align)
210 fprintf(fout, libpq_gettext("%-*s%s Value\n"),
211 fieldMaxLen - fs_len, libpq_gettext("Field"), po->fieldSep);
212 else
213 fprintf(fout, libpq_gettext("%s%sValue\n"), libpq_gettext("Field"), po->fieldSep);
214 }
215 else
216 {
217 int len = 0;
218
219 for (j = 0; j < nFields; j++)
220 {
221 const char *s = fieldNames[j];
222
223 fputs(s, fout);
224 len += strlen(s) + fs_len;
225 if ((j + 1) < nFields)
226 fputs(po->fieldSep, fout);
227 }
228 fputc('\n', fout);
229 for (len -= fs_len; len--; fputc('-', fout));
230 fputc('\n', fout);
231 }
232 }
233 if (po->expanded && po->html3)
234 {
235 if (po->caption)
236 fprintf(fout, "<center><h2>%s</h2></center>\n", po->caption);
237 else
238 fprintf(fout,
239 "<center><h2>"
240 "Query retrieved %d rows * %d fields"
241 "</h2></center>\n",
242 nTups, nFields);
243 }
244 for (i = 0; i < nTups; i++)
245 {
246 if (po->expanded)
247 {
248 if (po->html3)
249 fprintf(fout,
250 "<table %s><caption align=\"top\">%d</caption>\n",
251 po->tableOpt ? po->tableOpt : "", i);
252 else
253 fprintf(fout, libpq_gettext("-- RECORD %d --\n"), i);
254 }
255 for (j = 0; j < nFields; j++)
256 {
257 if (!do_field(po, res, i, j, fs_len, fields, nFields,
258 fieldNames, fieldNotNum,
259 fieldMax, fieldMaxLen, fout))
260 goto exit;
261 }
262 if (po->html3 && po->expanded)
263 fputs("</table>\n", fout);
264 }
265 if (!po->expanded && (po->align || po->html3))
266 {
267 if (po->html3)
268 {
269 if (po->header)
270 {
271 if (po->caption)
272 fprintf(fout,
273 "<table %s><caption align=\"top\">%s</caption>\n",
274 po->tableOpt ? po->tableOpt : "",
275 po->caption);
276 else
277 fprintf(fout,
278 "<table %s><caption align=\"top\">"
279 "Retrieved %d rows * %d fields"
280 "</caption>\n",
281 po->tableOpt ? po->tableOpt : "", nTups, nFields);
282 }
283 else
284 fprintf(fout, "<table %s>", po->tableOpt ? po->tableOpt : "");
285 }
286 if (po->header)
287 border = do_header(fout, po, nFields, fieldMax, fieldNames,
288 fieldNotNum, fs_len, res);
289 for (i = 0; i < nTups; i++)
290 output_row(fout, po, nFields, fields,
291 fieldNotNum, fieldMax, border, i);
292 }
293 if (po->header && !po->html3)
294 fprintf(fout, "(%d row%s)\n\n", PQntuples(res),
295 (PQntuples(res) == 1) ? "" : "s");
296 if (po->html3 && !po->expanded)
297 fputs("</table>\n", fout);
298
299 exit:
300 if (fieldMax)
301 free(fieldMax);
302 if (fieldNotNum)
303 free(fieldNotNum);
304 if (border)
305 free(border);
306 if (fields)
307 {
308 /* if calloc succeeded, this shouldn't overflow size_t */
309 size_t numfields = ((size_t) nTups + 1) * (size_t) nFields;
310
311 while (numfields-- > 0)
312 {
313 if (fields[numfields])
314 free(fields[numfields]);
315 }
316 free(fields);
317 }
318 if (fieldNames)
319 free((void *) fieldNames);
320 if (usePipe)
321 {
322 #ifdef WIN32
323 _pclose(fout);
324 #else
325 pclose(fout);
326
327 #ifdef ENABLE_THREAD_SAFETY
328 /* we can't easily verify if EPIPE occurred, so say it did */
329 if (sigpipe_masked)
330 pq_reset_sigpipe(&osigset, sigpipe_pending, true);
331 #else
332 pqsignal(SIGPIPE, oldsigpipehandler);
333 #endif /* ENABLE_THREAD_SAFETY */
334 #endif /* WIN32 */
335 }
336 }
337 }
338
339
340 static bool
do_field(const PQprintOpt * po,const PGresult * res,const int i,const int j,const int fs_len,char ** fields,const int nFields,char const ** fieldNames,unsigned char * fieldNotNum,int * fieldMax,const int fieldMaxLen,FILE * fout)341 do_field(const PQprintOpt *po, const PGresult *res,
342 const int i, const int j, const int fs_len,
343 char **fields,
344 const int nFields, char const ** fieldNames,
345 unsigned char *fieldNotNum, int *fieldMax,
346 const int fieldMaxLen, FILE *fout)
347 {
348 const char *pval,
349 *p;
350 int plen;
351 bool skipit;
352
353 plen = PQgetlength(res, i, j);
354 pval = PQgetvalue(res, i, j);
355
356 if (plen < 1 || !pval || !*pval)
357 {
358 if (po->align || po->expanded)
359 skipit = true;
360 else
361 {
362 skipit = false;
363 goto efield;
364 }
365 }
366 else
367 skipit = false;
368
369 if (!skipit)
370 {
371 if (po->align && !fieldNotNum[j])
372 {
373 /* Detect whether field contains non-numeric data */
374 char ch = '0';
375
376 for (p = pval; *p; p += PQmblenBounded(p, res->client_encoding))
377 {
378 ch = *p;
379 if (!((ch >= '0' && ch <= '9') ||
380 ch == '.' ||
381 ch == 'E' ||
382 ch == 'e' ||
383 ch == ' ' ||
384 ch == '-'))
385 {
386 fieldNotNum[j] = 1;
387 break;
388 }
389 }
390
391 /*
392 * Above loop will believe E in first column is numeric; also, we
393 * insist on a digit in the last column for a numeric. This test
394 * is still not bulletproof but it handles most cases.
395 */
396 if (*pval == 'E' || *pval == 'e' ||
397 !(ch >= '0' && ch <= '9'))
398 fieldNotNum[j] = 1;
399 }
400
401 if (!po->expanded && (po->align || po->html3))
402 {
403 if (plen > fieldMax[j])
404 fieldMax[j] = plen;
405 if (!(fields[i * nFields + j] = (char *) malloc(plen + 1)))
406 {
407 fprintf(stderr, libpq_gettext("out of memory\n"));
408 return false;
409 }
410 strcpy(fields[i * nFields + j], pval);
411 }
412 else
413 {
414 if (po->expanded)
415 {
416 if (po->html3)
417 fprintf(fout,
418 "<tr><td align=\"left\"><b>%s</b></td>"
419 "<td align=\"%s\">%s</td></tr>\n",
420 fieldNames[j],
421 fieldNotNum[j] ? "left" : "right",
422 pval);
423 else
424 {
425 if (po->align)
426 fprintf(fout,
427 "%-*s%s %s\n",
428 fieldMaxLen - fs_len, fieldNames[j],
429 po->fieldSep,
430 pval);
431 else
432 fprintf(fout,
433 "%s%s%s\n",
434 fieldNames[j], po->fieldSep, pval);
435 }
436 }
437 else
438 {
439 if (!po->html3)
440 {
441 fputs(pval, fout);
442 efield:
443 if ((j + 1) < nFields)
444 fputs(po->fieldSep, fout);
445 else
446 fputc('\n', fout);
447 }
448 }
449 }
450 }
451 return true;
452 }
453
454
455 static char *
do_header(FILE * fout,const PQprintOpt * po,const int nFields,int * fieldMax,const char ** fieldNames,unsigned char * fieldNotNum,const int fs_len,const PGresult * res)456 do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax,
457 const char **fieldNames, unsigned char *fieldNotNum,
458 const int fs_len, const PGresult *res)
459 {
460 int j; /* for loop index */
461 char *border = NULL;
462
463 if (po->html3)
464 fputs("<tr>", fout);
465 else
466 {
467 int tot = 0;
468 int n = 0;
469 char *p = NULL;
470
471 for (; n < nFields; n++)
472 tot += fieldMax[n] + fs_len + (po->standard ? 2 : 0);
473 if (po->standard)
474 tot += fs_len * 2 + 2;
475 border = malloc(tot + 1);
476 if (!border)
477 {
478 fprintf(stderr, libpq_gettext("out of memory\n"));
479 return NULL;
480 }
481 p = border;
482 if (po->standard)
483 {
484 char *fs = po->fieldSep;
485
486 while (*fs++)
487 *p++ = '+';
488 }
489 for (j = 0; j < nFields; j++)
490 {
491 int len;
492
493 for (len = fieldMax[j] + (po->standard ? 2 : 0); len--; *p++ = '-');
494 if (po->standard || (j + 1) < nFields)
495 {
496 char *fs = po->fieldSep;
497
498 while (*fs++)
499 *p++ = '+';
500 }
501 }
502 *p = '\0';
503 if (po->standard)
504 fprintf(fout, "%s\n", border);
505 }
506 if (po->standard)
507 fputs(po->fieldSep, fout);
508 for (j = 0; j < nFields; j++)
509 {
510 const char *s = PQfname(res, j);
511
512 if (po->html3)
513 {
514 fprintf(fout, "<th align=\"%s\">%s</th>",
515 fieldNotNum[j] ? "left" : "right", fieldNames[j]);
516 }
517 else
518 {
519 int n = strlen(s);
520
521 if (n > fieldMax[j])
522 fieldMax[j] = n;
523 if (po->standard)
524 fprintf(fout,
525 fieldNotNum[j] ? " %-*s " : " %*s ",
526 fieldMax[j], s);
527 else
528 fprintf(fout, fieldNotNum[j] ? "%-*s" : "%*s", fieldMax[j], s);
529 if (po->standard || (j + 1) < nFields)
530 fputs(po->fieldSep, fout);
531 }
532 }
533 if (po->html3)
534 fputs("</tr>\n", fout);
535 else
536 fprintf(fout, "\n%s\n", border);
537 return border;
538 }
539
540
541 static void
output_row(FILE * fout,const PQprintOpt * po,const int nFields,char ** fields,unsigned char * fieldNotNum,int * fieldMax,char * border,const int row_index)542 output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields,
543 unsigned char *fieldNotNum, int *fieldMax, char *border,
544 const int row_index)
545 {
546 int field_index; /* for loop index */
547
548 if (po->html3)
549 fputs("<tr>", fout);
550 else if (po->standard)
551 fputs(po->fieldSep, fout);
552 for (field_index = 0; field_index < nFields; field_index++)
553 {
554 char *p = fields[row_index * nFields + field_index];
555
556 if (po->html3)
557 fprintf(fout, "<td align=\"%s\">%s</td>",
558 fieldNotNum[field_index] ? "left" : "right", p ? p : "");
559 else
560 {
561 fprintf(fout,
562 fieldNotNum[field_index] ?
563 (po->standard ? " %-*s " : "%-*s") :
564 (po->standard ? " %*s " : "%*s"),
565 fieldMax[field_index],
566 p ? p : "");
567 if (po->standard || field_index + 1 < nFields)
568 fputs(po->fieldSep, fout);
569 }
570 }
571 if (po->html3)
572 fputs("</tr>", fout);
573 else if (po->standard)
574 fprintf(fout, "\n%s", border);
575 fputc('\n', fout);
576 }
577
578
579
580 /*
581 * really old printing routines
582 */
583
584 void
PQdisplayTuples(const PGresult * res,FILE * fp,int fillAlign,const char * fieldSep,int printHeader,int quiet)585 PQdisplayTuples(const PGresult *res,
586 FILE *fp, /* where to send the output */
587 int fillAlign, /* pad the fields with spaces */
588 const char *fieldSep, /* field separator */
589 int printHeader, /* display headers? */
590 int quiet
591 )
592 {
593 #define DEFAULT_FIELD_SEP " "
594
595 int i,
596 j;
597 int nFields;
598 int nTuples;
599 int *fLength = NULL;
600
601 if (fieldSep == NULL)
602 fieldSep = DEFAULT_FIELD_SEP;
603
604 /* Get some useful info about the results */
605 nFields = PQnfields(res);
606 nTuples = PQntuples(res);
607
608 if (fp == NULL)
609 fp = stdout;
610
611 /* Figure the field lengths to align to */
612 /* will be somewhat time consuming for very large results */
613 if (fillAlign)
614 {
615 fLength = (int *) malloc(nFields * sizeof(int));
616 if (!fLength)
617 {
618 fprintf(stderr, libpq_gettext("out of memory\n"));
619 return;
620 }
621
622 for (j = 0; j < nFields; j++)
623 {
624 fLength[j] = strlen(PQfname(res, j));
625 for (i = 0; i < nTuples; i++)
626 {
627 int flen = PQgetlength(res, i, j);
628
629 if (flen > fLength[j])
630 fLength[j] = flen;
631 }
632 }
633 }
634
635 if (printHeader)
636 {
637 /* first, print out the attribute names */
638 for (i = 0; i < nFields; i++)
639 {
640 fputs(PQfname(res, i), fp);
641 if (fillAlign)
642 fill(strlen(PQfname(res, i)), fLength[i], ' ', fp);
643 fputs(fieldSep, fp);
644 }
645 fprintf(fp, "\n");
646
647 /* Underline the attribute names */
648 for (i = 0; i < nFields; i++)
649 {
650 if (fillAlign)
651 fill(0, fLength[i], '-', fp);
652 fputs(fieldSep, fp);
653 }
654 fprintf(fp, "\n");
655 }
656
657 /* next, print out the instances */
658 for (i = 0; i < nTuples; i++)
659 {
660 for (j = 0; j < nFields; j++)
661 {
662 fprintf(fp, "%s", PQgetvalue(res, i, j));
663 if (fillAlign)
664 fill(strlen(PQgetvalue(res, i, j)), fLength[j], ' ', fp);
665 fputs(fieldSep, fp);
666 }
667 fprintf(fp, "\n");
668 }
669
670 if (!quiet)
671 fprintf(fp, "\nQuery returned %d row%s.\n", PQntuples(res),
672 (PQntuples(res) == 1) ? "" : "s");
673
674 fflush(fp);
675
676 if (fLength)
677 free(fLength);
678 }
679
680
681
682 void
PQprintTuples(const PGresult * res,FILE * fout,int PrintAttNames,int TerseOutput,int colWidth)683 PQprintTuples(const PGresult *res,
684 FILE *fout, /* output stream */
685 int PrintAttNames, /* print attribute names or not */
686 int TerseOutput, /* delimiter bars or not? */
687 int colWidth /* width of column, if 0, use variable width */
688 )
689 {
690 int nFields;
691 int nTups;
692 int i,
693 j;
694 char formatString[80];
695 char *tborder = NULL;
696
697 nFields = PQnfields(res);
698 nTups = PQntuples(res);
699
700 if (colWidth > 0)
701 sprintf(formatString, "%%s %%-%ds", colWidth);
702 else
703 sprintf(formatString, "%%s %%s");
704
705 if (nFields > 0)
706 { /* only print rows with at least 1 field. */
707
708 if (!TerseOutput)
709 {
710 int width;
711
712 width = nFields * 14;
713 tborder = (char *) malloc(width + 1);
714 if (!tborder)
715 {
716 fprintf(stderr, libpq_gettext("out of memory\n"));
717 return;
718 }
719 for (i = 0; i < width; i++)
720 tborder[i] = '-';
721 tborder[width] = '\0';
722 fprintf(fout, "%s\n", tborder);
723 }
724
725 for (i = 0; i < nFields; i++)
726 {
727 if (PrintAttNames)
728 {
729 fprintf(fout, formatString,
730 TerseOutput ? "" : "|",
731 PQfname(res, i));
732 }
733 }
734
735 if (PrintAttNames)
736 {
737 if (TerseOutput)
738 fprintf(fout, "\n");
739 else
740 fprintf(fout, "|\n%s\n", tborder);
741 }
742
743 for (i = 0; i < nTups; i++)
744 {
745 for (j = 0; j < nFields; j++)
746 {
747 const char *pval = PQgetvalue(res, i, j);
748
749 fprintf(fout, formatString,
750 TerseOutput ? "" : "|",
751 pval ? pval : "");
752 }
753 if (TerseOutput)
754 fprintf(fout, "\n");
755 else
756 fprintf(fout, "|\n%s\n", tborder);
757 }
758 }
759
760 if (tborder)
761 free(tborder);
762 }
763
764
765 /* simply send out max-length number of filler characters to fp */
766
767 static void
fill(int length,int max,char filler,FILE * fp)768 fill(int length, int max, char filler, FILE *fp)
769 {
770 int count;
771
772 count = max - length;
773 while (count-- >= 0)
774 putc(filler, fp);
775 }
776