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