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 | |
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 *; |
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 | |
332 | static void |
333 | do_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 | |
446 | static char * |
447 | (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 | |
532 | static void |
533 | output_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 | |
577 | void |
578 | PQdisplayTuples(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 , /* 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 | |
675 | void |
676 | PQprintTuples(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 | |
760 | static void |
761 | fill(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 | |