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