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