]> granicus.if.org Git - postgresql/blob - src/interfaces/libpq/fe-print.c
Support Subject Alternative Names in SSL server certificates.
[postgresql] / src / interfaces / libpq / fe-print.c
1 /*-------------------------------------------------------------------------
2  *
3  * fe-print.c
4  *        functions for pretty-printing query results
5  *
6  * Portions Copyright (c) 1996-2014, 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
40 static 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);
46 static 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);
49 static 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);
52 static 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  */
67 void
68 PQprint(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                         pagerenv = getenv("PAGER");
169                         if (pagerenv != NULL &&
170                                 pagerenv[0] != '\0' &&
171                                 !po->html3 &&
172                                 ((po->expanded &&
173                                   nTups * (nFields + 1) >= screen_size.ws_row) ||
174                                  (!po->expanded &&
175                                   nTups * (total_line_length / screen_size.ws_col + 1) *
176                                   (1 + (po->standard != 0)) >= screen_size.ws_row -
177                                   (po->header != 0) *
178                                   (total_line_length / screen_size.ws_col + 1) * 2
179                                   - (po->header != 0) * 2               /* row count and newline */
180                                   )))
181                         {
182                                 fout = popen(pagerenv, "w");
183                                 if (fout)
184                                 {
185                                         usePipe = 1;
186 #ifndef WIN32
187 #ifdef ENABLE_THREAD_SAFETY
188                                         if (pq_block_sigpipe(&osigset, &sigpipe_pending) == 0)
189                                                 sigpipe_masked = true;
190 #else
191                                         oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
192 #endif   /* ENABLE_THREAD_SAFETY */
193 #endif   /* WIN32 */
194                                 }
195                                 else
196                                         fout = stdout;
197                         }
198                 }
199
200                 if (!po->expanded && (po->align || po->html3))
201                 {
202                         if (!(fields = (char **) calloc(nFields * (nTups + 1), sizeof(char *))))
203                         {
204                                 fprintf(stderr, libpq_gettext("out of memory\n"));
205                                 abort();
206                         }
207                 }
208                 else if (po->header && !po->html3)
209                 {
210                         if (po->expanded)
211                         {
212                                 if (po->align)
213                                         fprintf(fout, libpq_gettext("%-*s%s Value\n"),
214                                                         fieldMaxLen - fs_len, libpq_gettext("Field"), po->fieldSep);
215                                 else
216                                         fprintf(fout, libpq_gettext("%s%sValue\n"), libpq_gettext("Field"), po->fieldSep);
217                         }
218                         else
219                         {
220                                 int                     len = 0;
221
222                                 for (j = 0; j < nFields; j++)
223                                 {
224                                         const char *s = fieldNames[j];
225
226                                         fputs(s, fout);
227                                         len += strlen(s) + fs_len;
228                                         if ((j + 1) < nFields)
229                                                 fputs(po->fieldSep, fout);
230                                 }
231                                 fputc('\n', fout);
232                                 for (len -= fs_len; len--; fputc('-', fout));
233                                 fputc('\n', fout);
234                         }
235                 }
236                 if (po->expanded && po->html3)
237                 {
238                         if (po->caption)
239                                 fprintf(fout, "<center><h2>%s</h2></center>\n", po->caption);
240                         else
241                                 fprintf(fout,
242                                                 "<center><h2>"
243                                                 "Query retrieved %d rows * %d fields"
244                                                 "</h2></center>\n",
245                                                 nTups, nFields);
246                 }
247                 for (i = 0; i < nTups; i++)
248                 {
249                         if (po->expanded)
250                         {
251                                 if (po->html3)
252                                         fprintf(fout,
253                                                         "<table %s><caption align=\"top\">%d</caption>\n",
254                                                         po->tableOpt ? po->tableOpt : "", i);
255                                 else
256                                         fprintf(fout, libpq_gettext("-- RECORD %d --\n"), i);
257                         }
258                         for (j = 0; j < nFields; j++)
259                                 do_field(po, res, i, j, fs_len, fields, nFields,
260                                                  fieldNames, fieldNotNum,
261                                                  fieldMax, fieldMaxLen, fout);
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                         free(fields);
293                         if (border)
294                                 free(border);
295                 }
296                 if (po->header && !po->html3)
297                         fprintf(fout, "(%d row%s)\n\n", PQntuples(res),
298                                         (PQntuples(res) == 1) ? "" : "s");
299                 free(fieldMax);
300                 free(fieldNotNum);
301                 free((void *) fieldNames);
302                 if (usePipe)
303                 {
304 #ifdef WIN32
305                         _pclose(fout);
306 #else
307                         pclose(fout);
308
309 #ifdef ENABLE_THREAD_SAFETY
310                         /* we can't easily verify if EPIPE occurred, so say it did */
311                         if (sigpipe_masked)
312                                 pq_reset_sigpipe(&osigset, sigpipe_pending, true);
313 #else
314                         pqsignal(SIGPIPE, oldsigpipehandler);
315 #endif   /* ENABLE_THREAD_SAFETY */
316 #endif   /* WIN32 */
317                 }
318                 if (po->html3 && !po->expanded)
319                         fputs("</table>\n", fout);
320         }
321 }
322
323
324 static void
325 do_field(const PQprintOpt *po, const PGresult *res,
326                  const int i, const int j, const int fs_len,
327                  char **fields,
328                  const int nFields, char const ** fieldNames,
329                  unsigned char *fieldNotNum, int *fieldMax,
330                  const int fieldMaxLen, FILE *fout)
331 {
332         const char *pval,
333                            *p;
334         int                     plen;
335         bool            skipit;
336
337         plen = PQgetlength(res, i, j);
338         pval = PQgetvalue(res, i, j);
339
340         if (plen < 1 || !pval || !*pval)
341         {
342                 if (po->align || po->expanded)
343                         skipit = true;
344                 else
345                 {
346                         skipit = false;
347                         goto efield;
348                 }
349         }
350         else
351                 skipit = false;
352
353         if (!skipit)
354         {
355                 if (po->align && !fieldNotNum[j])
356                 {
357                         /* Detect whether field contains non-numeric data */
358                         char            ch = '0';
359
360                         for (p = pval; *p; p += PQmblen(p, res->client_encoding))
361                         {
362                                 ch = *p;
363                                 if (!((ch >= '0' && ch <= '9') ||
364                                           ch == '.' ||
365                                           ch == 'E' ||
366                                           ch == 'e' ||
367                                           ch == ' ' ||
368                                           ch == '-'))
369                                 {
370                                         fieldNotNum[j] = 1;
371                                         break;
372                                 }
373                         }
374
375                         /*
376                          * Above loop will believe E in first column is numeric; also, we
377                          * insist on a digit in the last column for a numeric. This test
378                          * is still not bulletproof but it handles most cases.
379                          */
380                         if (*pval == 'E' || *pval == 'e' ||
381                                 !(ch >= '0' && ch <= '9'))
382                                 fieldNotNum[j] = 1;
383                 }
384
385                 if (!po->expanded && (po->align || po->html3))
386                 {
387                         if (plen > fieldMax[j])
388                                 fieldMax[j] = plen;
389                         if (!(fields[i * nFields + j] = (char *) malloc(plen + 1)))
390                         {
391                                 fprintf(stderr, libpq_gettext("out of memory\n"));
392                                 abort();
393                         }
394                         strcpy(fields[i * nFields + j], pval);
395                 }
396                 else
397                 {
398                         if (po->expanded)
399                         {
400                                 if (po->html3)
401                                         fprintf(fout,
402                                                         "<tr><td align=\"left\"><b>%s</b></td>"
403                                                         "<td align=\"%s\">%s</td></tr>\n",
404                                                         fieldNames[j],
405                                                         fieldNotNum[j] ? "left" : "right",
406                                                         pval);
407                                 else
408                                 {
409                                         if (po->align)
410                                                 fprintf(fout,
411                                                                 "%-*s%s %s\n",
412                                                                 fieldMaxLen - fs_len, fieldNames[j],
413                                                                 po->fieldSep,
414                                                                 pval);
415                                         else
416                                                 fprintf(fout,
417                                                                 "%s%s%s\n",
418                                                                 fieldNames[j], po->fieldSep, pval);
419                                 }
420                         }
421                         else
422                         {
423                                 if (!po->html3)
424                                 {
425                                         fputs(pval, fout);
426                         efield:
427                                         if ((j + 1) < nFields)
428                                                 fputs(po->fieldSep, fout);
429                                         else
430                                                 fputc('\n', fout);
431                                 }
432                         }
433                 }
434         }
435 }
436
437
438 static char *
439 do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax,
440                   const char **fieldNames, unsigned char *fieldNotNum,
441                   const int fs_len, const PGresult *res)
442 {
443         int                     j;                              /* for loop index */
444         char       *border = NULL;
445
446         if (po->html3)
447                 fputs("<tr>", fout);
448         else
449         {
450                 int                     tot = 0;
451                 int                     n = 0;
452                 char       *p = NULL;
453
454                 for (; n < nFields; n++)
455                         tot += fieldMax[n] + fs_len + (po->standard ? 2 : 0);
456                 if (po->standard)
457                         tot += fs_len * 2 + 2;
458                 border = malloc(tot + 1);
459                 if (!border)
460                 {
461                         fprintf(stderr, libpq_gettext("out of memory\n"));
462                         abort();
463                 }
464                 p = border;
465                 if (po->standard)
466                 {
467                         char       *fs = po->fieldSep;
468
469                         while (*fs++)
470                                 *p++ = '+';
471                 }
472                 for (j = 0; j < nFields; j++)
473                 {
474                         int                     len;
475
476                         for (len = fieldMax[j] + (po->standard ? 2 : 0); len--; *p++ = '-');
477                         if (po->standard || (j + 1) < nFields)
478                         {
479                                 char       *fs = po->fieldSep;
480
481                                 while (*fs++)
482                                         *p++ = '+';
483                         }
484                 }
485                 *p = '\0';
486                 if (po->standard)
487                         fprintf(fout, "%s\n", border);
488         }
489         if (po->standard)
490                 fputs(po->fieldSep, fout);
491         for (j = 0; j < nFields; j++)
492         {
493                 const char *s = PQfname(res, j);
494
495                 if (po->html3)
496                 {
497                         fprintf(fout, "<th align=\"%s\">%s</th>",
498                                         fieldNotNum[j] ? "left" : "right", fieldNames[j]);
499                 }
500                 else
501                 {
502                         int                     n = strlen(s);
503
504                         if (n > fieldMax[j])
505                                 fieldMax[j] = n;
506                         if (po->standard)
507                                 fprintf(fout,
508                                                 fieldNotNum[j] ? " %-*s " : " %*s ",
509                                                 fieldMax[j], s);
510                         else
511                                 fprintf(fout, fieldNotNum[j] ? "%-*s" : "%*s", fieldMax[j], s);
512                         if (po->standard || (j + 1) < nFields)
513                                 fputs(po->fieldSep, fout);
514                 }
515         }
516         if (po->html3)
517                 fputs("</tr>\n", fout);
518         else
519                 fprintf(fout, "\n%s\n", border);
520         return border;
521 }
522
523
524 static void
525 output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields,
526                    unsigned char *fieldNotNum, int *fieldMax, char *border,
527                    const int row_index)
528 {
529         int                     field_index;    /* for loop index */
530
531         if (po->html3)
532                 fputs("<tr>", fout);
533         else if (po->standard)
534                 fputs(po->fieldSep, fout);
535         for (field_index = 0; field_index < nFields; field_index++)
536         {
537                 char       *p = fields[row_index * nFields + field_index];
538
539                 if (po->html3)
540                         fprintf(fout, "<td align=\"%s\">%s</td>",
541                                         fieldNotNum[field_index] ? "left" : "right", p ? p : "");
542                 else
543                 {
544                         fprintf(fout,
545                                         fieldNotNum[field_index] ?
546                                         (po->standard ? " %-*s " : "%-*s") :
547                                         (po->standard ? " %*s " : "%*s"),
548                                         fieldMax[field_index],
549                                         p ? p : "");
550                         if (po->standard || field_index + 1 < nFields)
551                                 fputs(po->fieldSep, fout);
552                 }
553                 if (p)
554                         free(p);
555         }
556         if (po->html3)
557                 fputs("</tr>", fout);
558         else if (po->standard)
559                 fprintf(fout, "\n%s", border);
560         fputc('\n', fout);
561 }
562
563
564
565 /*
566  * really old printing routines
567  */
568
569 void
570 PQdisplayTuples(const PGresult *res,
571                                 FILE *fp,               /* where to send the output */
572                                 int fillAlign,  /* pad the fields with spaces */
573                                 const char *fieldSep,   /* field separator */
574                                 int printHeader,        /* display headers? */
575                                 int quiet
576 )
577 {
578 #define DEFAULT_FIELD_SEP " "
579
580         int                     i,
581                                 j;
582         int                     nFields;
583         int                     nTuples;
584         int                *fLength = NULL;
585
586         if (fieldSep == NULL)
587                 fieldSep = DEFAULT_FIELD_SEP;
588
589         /* Get some useful info about the results */
590         nFields = PQnfields(res);
591         nTuples = PQntuples(res);
592
593         if (fp == NULL)
594                 fp = stdout;
595
596         /* Figure the field lengths to align to */
597         /* will be somewhat time consuming for very large results */
598         if (fillAlign)
599         {
600                 fLength = (int *) malloc(nFields * sizeof(int));
601                 if (!fLength)
602                 {
603                         fprintf(stderr, libpq_gettext("out of memory\n"));
604                         abort();
605                 }
606
607                 for (j = 0; j < nFields; j++)
608                 {
609                         fLength[j] = strlen(PQfname(res, j));
610                         for (i = 0; i < nTuples; i++)
611                         {
612                                 int                     flen = PQgetlength(res, i, j);
613
614                                 if (flen > fLength[j])
615                                         fLength[j] = flen;
616                         }
617                 }
618         }
619
620         if (printHeader)
621         {
622                 /* first, print out the attribute names */
623                 for (i = 0; i < nFields; i++)
624                 {
625                         fputs(PQfname(res, i), fp);
626                         if (fillAlign)
627                                 fill(strlen(PQfname(res, i)), fLength[i], ' ', fp);
628                         fputs(fieldSep, fp);
629                 }
630                 fprintf(fp, "\n");
631
632                 /* Underline the attribute names */
633                 for (i = 0; i < nFields; i++)
634                 {
635                         if (fillAlign)
636                                 fill(0, fLength[i], '-', fp);
637                         fputs(fieldSep, fp);
638                 }
639                 fprintf(fp, "\n");
640         }
641
642         /* next, print out the instances */
643         for (i = 0; i < nTuples; i++)
644         {
645                 for (j = 0; j < nFields; j++)
646                 {
647                         fprintf(fp, "%s", PQgetvalue(res, i, j));
648                         if (fillAlign)
649                                 fill(strlen(PQgetvalue(res, i, j)), fLength[j], ' ', fp);
650                         fputs(fieldSep, fp);
651                 }
652                 fprintf(fp, "\n");
653         }
654
655         if (!quiet)
656                 fprintf(fp, "\nQuery returned %d row%s.\n", PQntuples(res),
657                                 (PQntuples(res) == 1) ? "" : "s");
658
659         fflush(fp);
660
661         if (fLength)
662                 free(fLength);
663 }
664
665
666
667 void
668 PQprintTuples(const PGresult *res,
669                           FILE *fout,           /* output stream */
670                           int PrintAttNames,    /* print attribute names or not */
671                           int TerseOutput,      /* delimiter bars or not? */
672                           int colWidth          /* width of column, if 0, use variable width */
673 )
674 {
675         int                     nFields;
676         int                     nTups;
677         int                     i,
678                                 j;
679         char            formatString[80];
680         char       *tborder = NULL;
681
682         nFields = PQnfields(res);
683         nTups = PQntuples(res);
684
685         if (colWidth > 0)
686                 sprintf(formatString, "%%s %%-%ds", colWidth);
687         else
688                 sprintf(formatString, "%%s %%s");
689
690         if (nFields > 0)
691         {                                                       /* only print rows with at least 1 field.  */
692
693                 if (!TerseOutput)
694                 {
695                         int                     width;
696
697                         width = nFields * 14;
698                         tborder = (char *) malloc(width + 1);
699                         if (!tborder)
700                         {
701                                 fprintf(stderr, libpq_gettext("out of memory\n"));
702                                 abort();
703                         }
704                         for (i = 0; i < width; i++)
705                                 tborder[i] = '-';
706                         tborder[width] = '\0';
707                         fprintf(fout, "%s\n", tborder);
708                 }
709
710                 for (i = 0; i < nFields; i++)
711                 {
712                         if (PrintAttNames)
713                         {
714                                 fprintf(fout, formatString,
715                                                 TerseOutput ? "" : "|",
716                                                 PQfname(res, i));
717                         }
718                 }
719
720                 if (PrintAttNames)
721                 {
722                         if (TerseOutput)
723                                 fprintf(fout, "\n");
724                         else
725                                 fprintf(fout, "|\n%s\n", tborder);
726                 }
727
728                 for (i = 0; i < nTups; i++)
729                 {
730                         for (j = 0; j < nFields; j++)
731                         {
732                                 const char *pval = PQgetvalue(res, i, j);
733
734                                 fprintf(fout, formatString,
735                                                 TerseOutput ? "" : "|",
736                                                 pval ? pval : "");
737                         }
738                         if (TerseOutput)
739                                 fprintf(fout, "\n");
740                         else
741                                 fprintf(fout, "|\n%s\n", tborder);
742                 }
743         }
744
745         if (tborder)
746                 free(tborder);
747 }
748
749
750 /* simply send out max-length number of filler characters to fp */
751
752 static void
753 fill(int length, int max, char filler, FILE *fp)
754 {
755         int                     count;
756
757         count = max - length;
758         while (count-- >= 0)
759                 putc(filler, fp);
760 }