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