Header And Logo

PostgreSQL
| The world's most advanced open source database.

fe-print.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * fe-print.c
00004  *    functions for pretty-printing query results
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  * These functions were formerly part of fe-exec.c, but they
00010  * didn't really belong there.
00011  *
00012  * IDENTIFICATION
00013  *    src/interfaces/libpq/fe-print.c
00014  *
00015  *-------------------------------------------------------------------------
00016  */
00017 #include "postgres_fe.h"
00018 
00019 #include <signal.h>
00020 
00021 #ifdef WIN32
00022 #include "win32.h"
00023 #else
00024 #include <unistd.h>
00025 #include <sys/ioctl.h>
00026 #endif
00027 
00028 #ifdef HAVE_TERMIOS_H
00029 #include <termios.h>
00030 #else
00031 #ifndef WIN32
00032 #include <sys/termios.h>
00033 #endif
00034 #endif
00035 
00036 #include "libpq-fe.h"
00037 #include "libpq-int.h"
00038 
00039 
00040 static void do_field(const PQprintOpt *po, const PGresult *res,
00041          const int i, const int j, const int fs_len,
00042          char **fields,
00043          const int nFields, const char **fieldNames,
00044          unsigned char *fieldNotNum, int *fieldMax,
00045          const int fieldMaxLen, FILE *fout);
00046 static char *do_header(FILE *fout, const PQprintOpt *po, const int nFields,
00047           int *fieldMax, const char **fieldNames, unsigned char *fieldNotNum,
00048           const int fs_len, const PGresult *res);
00049 static void output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields,
00050            unsigned char *fieldNotNum, int *fieldMax, char *border,
00051            const int row_index);
00052 static void fill(int length, int max, char filler, FILE *fp);
00053 
00054 /*
00055  * PQprint()
00056  *
00057  * Format results of a query for printing.
00058  *
00059  * PQprintOpt is a typedef (structure) that contains
00060  * various flags and options. consult libpq-fe.h for
00061  * details
00062  *
00063  * This function should probably be removed sometime since psql
00064  * doesn't use it anymore. It is unclear to what extent this is used
00065  * by external clients, however.
00066  */
00067 void
00068 PQprint(FILE *fout, const PGresult *res, const PQprintOpt *po)
00069 {
00070     int         nFields;
00071 
00072     nFields = PQnfields(res);
00073 
00074     if (nFields > 0)
00075     {                           /* only print rows with at least 1 field.  */
00076         int         i,
00077                     j;
00078         int         nTups;
00079         int        *fieldMax = NULL;    /* in case we don't use them */
00080         unsigned char *fieldNotNum = NULL;
00081         char       *border = NULL;
00082         char      **fields = NULL;
00083         const char **fieldNames;
00084         int         fieldMaxLen = 0;
00085         int         numFieldName;
00086         int         fs_len = strlen(po->fieldSep);
00087         int         total_line_length = 0;
00088         int         usePipe = 0;
00089         char       *pagerenv;
00090 
00091 #if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
00092         sigset_t    osigset;
00093         bool        sigpipe_masked = false;
00094         bool        sigpipe_pending;
00095 #endif
00096 #if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
00097         pqsigfunc   oldsigpipehandler = NULL;
00098 #endif
00099 
00100 #ifdef TIOCGWINSZ
00101         struct winsize screen_size;
00102 #else
00103         struct winsize
00104         {
00105             int         ws_row;
00106             int         ws_col;
00107         }           screen_size;
00108 #endif
00109 
00110         nTups = PQntuples(res);
00111         if (!(fieldNames = (const char **) calloc(nFields, sizeof(char *))))
00112         {
00113             fprintf(stderr, libpq_gettext("out of memory\n"));
00114             abort();
00115         }
00116         if (!(fieldNotNum = (unsigned char *) calloc(nFields, 1)))
00117         {
00118             fprintf(stderr, libpq_gettext("out of memory\n"));
00119             abort();
00120         }
00121         if (!(fieldMax = (int *) calloc(nFields, sizeof(int))))
00122         {
00123             fprintf(stderr, libpq_gettext("out of memory\n"));
00124             abort();
00125         }
00126         for (numFieldName = 0;
00127              po->fieldName && po->fieldName[numFieldName];
00128              numFieldName++)
00129             ;
00130         for (j = 0; j < nFields; j++)
00131         {
00132             int         len;
00133             const char *s = (j < numFieldName && po->fieldName[j][0]) ?
00134             po->fieldName[j] : PQfname(res, j);
00135 
00136             fieldNames[j] = s;
00137             len = s ? strlen(s) : 0;
00138             fieldMax[j] = len;
00139             len += fs_len;
00140             if (len > fieldMaxLen)
00141                 fieldMaxLen = len;
00142             total_line_length += len;
00143         }
00144 
00145         total_line_length += nFields * strlen(po->fieldSep) + 1;
00146 
00147         if (fout == NULL)
00148             fout = stdout;
00149         if (po->pager && fout == stdout && isatty(fileno(stdin)) &&
00150             isatty(fileno(stdout)))
00151         {
00152             /*
00153              * If we think there'll be more than one screen of output, try to
00154              * pipe to the pager program.
00155              */
00156 #ifdef TIOCGWINSZ
00157             if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
00158                 screen_size.ws_col == 0 ||
00159                 screen_size.ws_row == 0)
00160             {
00161                 screen_size.ws_row = 24;
00162                 screen_size.ws_col = 80;
00163             }
00164 #else
00165             screen_size.ws_row = 24;
00166             screen_size.ws_col = 80;
00167 #endif
00168             pagerenv = getenv("PAGER");
00169             if (pagerenv != NULL &&
00170                 pagerenv[0] != '\0' &&
00171                 !po->html3 &&
00172                 ((po->expanded &&
00173                   nTups * (nFields + 1) >= screen_size.ws_row) ||
00174                  (!po->expanded &&
00175                   nTups * (total_line_length / screen_size.ws_col + 1) *
00176                   (1 + (po->standard != 0)) >= screen_size.ws_row -
00177                   (po->header != 0) *
00178                   (total_line_length / screen_size.ws_col + 1) * 2
00179                   - (po->header != 0) * 2       /* row count and newline */
00180                   )))
00181             {
00182                 fout = popen(pagerenv, "w");
00183                 if (fout)
00184                 {
00185                     usePipe = 1;
00186 #ifndef WIN32
00187 #ifdef ENABLE_THREAD_SAFETY
00188                     if (pq_block_sigpipe(&osigset, &sigpipe_pending) == 0)
00189                         sigpipe_masked = true;
00190 #else
00191                     oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
00192 #endif   /* ENABLE_THREAD_SAFETY */
00193 #endif   /* WIN32 */
00194                 }
00195                 else
00196                     fout = stdout;
00197             }
00198         }
00199 
00200         if (!po->expanded && (po->align || po->html3))
00201         {
00202             if (!(fields = (char **) calloc(nFields * (nTups + 1), sizeof(char *))))
00203             {
00204                 fprintf(stderr, libpq_gettext("out of memory\n"));
00205                 abort();
00206             }
00207         }
00208         else if (po->header && !po->html3)
00209         {
00210             if (po->expanded)
00211             {
00212                 if (po->align)
00213                     fprintf(fout, libpq_gettext("%-*s%s Value\n"),
00214                             fieldMaxLen - fs_len, libpq_gettext("Field"), po->fieldSep);
00215                 else
00216                     fprintf(fout, libpq_gettext("%s%sValue\n"), libpq_gettext("Field"), po->fieldSep);
00217             }
00218             else
00219             {
00220                 int         len = 0;
00221 
00222                 for (j = 0; j < nFields; j++)
00223                 {
00224                     const char *s = fieldNames[j];
00225 
00226                     fputs(s, fout);
00227                     len += strlen(s) + fs_len;
00228                     if ((j + 1) < nFields)
00229                         fputs(po->fieldSep, fout);
00230                 }
00231                 fputc('\n', fout);
00232                 for (len -= fs_len; len--; fputc('-', fout));
00233                 fputc('\n', fout);
00234             }
00235         }
00236         if (po->expanded && po->html3)
00237         {
00238             if (po->caption)
00239                 fprintf(fout, "<center><h2>%s</h2></center>\n", po->caption);
00240             else
00241                 fprintf(fout,
00242                         "<center><h2>"
00243                         "Query retrieved %d rows * %d fields"
00244                         "</h2></center>\n",
00245                         nTups, nFields);
00246         }
00247         for (i = 0; i < nTups; i++)
00248         {
00249             if (po->expanded)
00250             {
00251                 if (po->html3)
00252                     fprintf(fout,
00253                             "<table %s><caption align=\"top\">%d</caption>\n",
00254                             po->tableOpt ? po->tableOpt : "", i);
00255                 else
00256                     fprintf(fout, libpq_gettext("-- RECORD %d --\n"), i);
00257             }
00258             for (j = 0; j < nFields; j++)
00259                 do_field(po, res, i, j, fs_len, fields, nFields,
00260                          fieldNames, fieldNotNum,
00261                          fieldMax, fieldMaxLen, fout);
00262             if (po->html3 && po->expanded)
00263                 fputs("</table>\n", fout);
00264         }
00265         if (!po->expanded && (po->align || po->html3))
00266         {
00267             if (po->html3)
00268             {
00269                 if (po->header)
00270                 {
00271                     if (po->caption)
00272                         fprintf(fout,
00273                            "<table %s><caption align=\"top\">%s</caption>\n",
00274                                 po->tableOpt ? po->tableOpt : "",
00275                                 po->caption);
00276                     else
00277                         fprintf(fout,
00278                                 "<table %s><caption align=\"top\">"
00279                                 "Retrieved %d rows * %d fields"
00280                                 "</caption>\n",
00281                            po->tableOpt ? po->tableOpt : "", nTups, nFields);
00282                 }
00283                 else
00284                     fprintf(fout, "<table %s>", po->tableOpt ? po->tableOpt : "");
00285             }
00286             if (po->header)
00287                 border = do_header(fout, po, nFields, fieldMax, fieldNames,
00288                                    fieldNotNum, fs_len, res);
00289             for (i = 0; i < nTups; i++)
00290                 output_row(fout, po, nFields, fields,
00291                            fieldNotNum, fieldMax, border, i);
00292             free(fields);
00293             if (border)
00294                 free(border);
00295         }
00296         if (po->header && !po->html3)
00297             fprintf(fout, "(%d row%s)\n\n", PQntuples(res),
00298                     (PQntuples(res) == 1) ? "" : "s");
00299         free(fieldMax);
00300         free(fieldNotNum);
00301         free((void *) fieldNames);
00302         if (usePipe)
00303         {
00304 #ifdef WIN32
00305             _pclose(fout);
00306 #else
00307             pclose(fout);
00308 
00309 #ifdef ENABLE_THREAD_SAFETY
00310             /* we can't easily verify if EPIPE occurred, so say it did */
00311             if (sigpipe_masked)
00312                 pq_reset_sigpipe(&osigset, sigpipe_pending, true);
00313 #else
00314             pqsignal(SIGPIPE, oldsigpipehandler);
00315 #endif   /* ENABLE_THREAD_SAFETY */
00316 #endif   /* WIN32 */
00317         }
00318         if (po->html3 && !po->expanded)
00319             fputs("</table>\n", fout);
00320     }
00321 }
00322 
00323 
00324 static void
00325 do_field(const PQprintOpt *po, const PGresult *res,
00326          const int i, const int j, const int fs_len,
00327          char **fields,
00328          const int nFields, char const ** fieldNames,
00329          unsigned char *fieldNotNum, int *fieldMax,
00330          const int fieldMaxLen, FILE *fout)
00331 {
00332 
00333     const char *pval,
00334                *p;
00335     int         plen;
00336     bool        skipit;
00337 
00338     plen = PQgetlength(res, i, j);
00339     pval = PQgetvalue(res, i, j);
00340 
00341     if (plen < 1 || !pval || !*pval)
00342     {
00343         if (po->align || po->expanded)
00344             skipit = true;
00345         else
00346         {
00347             skipit = false;
00348             goto efield;
00349         }
00350     }
00351     else
00352         skipit = false;
00353 
00354     if (!skipit)
00355     {
00356         if (po->align && !fieldNotNum[j])
00357         {
00358             /* Detect whether field contains non-numeric data */
00359             char        ch = '0';
00360 
00361             for (p = pval; *p; p += PQmblen(p, res->client_encoding))
00362             {
00363                 ch = *p;
00364                 if (!((ch >= '0' && ch <= '9') ||
00365                       ch == '.' ||
00366                       ch == 'E' ||
00367                       ch == 'e' ||
00368                       ch == ' ' ||
00369                       ch == '-'))
00370                 {
00371                     fieldNotNum[j] = 1;
00372                     break;
00373                 }
00374             }
00375 
00376             /*
00377              * Above loop will believe E in first column is numeric; also, we
00378              * insist on a digit in the last column for a numeric. This test
00379              * is still not bulletproof but it handles most cases.
00380              */
00381             if (*pval == 'E' || *pval == 'e' ||
00382                 !(ch >= '0' && ch <= '9'))
00383                 fieldNotNum[j] = 1;
00384         }
00385 
00386         if (!po->expanded && (po->align || po->html3))
00387         {
00388             if (plen > fieldMax[j])
00389                 fieldMax[j] = plen;
00390             if (!(fields[i * nFields + j] = (char *) malloc(plen + 1)))
00391             {
00392                 fprintf(stderr, libpq_gettext("out of memory\n"));
00393                 abort();
00394             }
00395             strcpy(fields[i * nFields + j], pval);
00396         }
00397         else
00398         {
00399             if (po->expanded)
00400             {
00401                 if (po->html3)
00402                     fprintf(fout,
00403                             "<tr><td align=\"left\"><b>%s</b></td>"
00404                             "<td align=\"%s\">%s</td></tr>\n",
00405                             fieldNames[j],
00406                             fieldNotNum[j] ? "left" : "right",
00407                             pval);
00408                 else
00409                 {
00410                     if (po->align)
00411                         fprintf(fout,
00412                                 "%-*s%s %s\n",
00413                                 fieldMaxLen - fs_len, fieldNames[j],
00414                                 po->fieldSep,
00415                                 pval);
00416                     else
00417                         fprintf(fout,
00418                                 "%s%s%s\n",
00419                                 fieldNames[j], po->fieldSep, pval);
00420                 }
00421             }
00422             else
00423             {
00424                 if (!po->html3)
00425                 {
00426                     fputs(pval, fout);
00427             efield:
00428                     if ((j + 1) < nFields)
00429                         fputs(po->fieldSep, fout);
00430                     else
00431                         fputc('\n', fout);
00432                 }
00433             }
00434         }
00435     }
00436 }
00437 
00438 
00439 static char *
00440 do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax,
00441           const char **fieldNames, unsigned char *fieldNotNum,
00442           const int fs_len, const PGresult *res)
00443 {
00444 
00445     int         j;              /* for loop index */
00446     char       *border = NULL;
00447 
00448     if (po->html3)
00449         fputs("<tr>", fout);
00450     else
00451     {
00452         int         tot = 0;
00453         int         n = 0;
00454         char       *p = NULL;
00455 
00456         for (; n < nFields; n++)
00457             tot += fieldMax[n] + fs_len + (po->standard ? 2 : 0);
00458         if (po->standard)
00459             tot += fs_len * 2 + 2;
00460         border = malloc(tot + 1);
00461         if (!border)
00462         {
00463             fprintf(stderr, libpq_gettext("out of memory\n"));
00464             abort();
00465         }
00466         p = border;
00467         if (po->standard)
00468         {
00469             char       *fs = po->fieldSep;
00470 
00471             while (*fs++)
00472                 *p++ = '+';
00473         }
00474         for (j = 0; j < nFields; j++)
00475         {
00476             int         len;
00477 
00478             for (len = fieldMax[j] + (po->standard ? 2 : 0); len--; *p++ = '-');
00479             if (po->standard || (j + 1) < nFields)
00480             {
00481                 char       *fs = po->fieldSep;
00482 
00483                 while (*fs++)
00484                     *p++ = '+';
00485             }
00486         }
00487         *p = '\0';
00488         if (po->standard)
00489             fprintf(fout, "%s\n", border);
00490     }
00491     if (po->standard)
00492         fputs(po->fieldSep, fout);
00493     for (j = 0; j < nFields; j++)
00494     {
00495         const char *s = PQfname(res, j);
00496 
00497         if (po->html3)
00498         {
00499             fprintf(fout, "<th align=\"%s\">%s</th>",
00500                     fieldNotNum[j] ? "left" : "right", fieldNames[j]);
00501         }
00502         else
00503         {
00504             int         n = strlen(s);
00505 
00506             if (n > fieldMax[j])
00507                 fieldMax[j] = n;
00508             if (po->standard)
00509                 fprintf(fout,
00510                         fieldNotNum[j] ? " %-*s " : " %*s ",
00511                         fieldMax[j], s);
00512             else
00513                 fprintf(fout, fieldNotNum[j] ? "%-*s" : "%*s", fieldMax[j], s);
00514             if (po->standard || (j + 1) < nFields)
00515                 fputs(po->fieldSep, fout);
00516         }
00517     }
00518     if (po->html3)
00519         fputs("</tr>\n", fout);
00520     else
00521         fprintf(fout, "\n%s\n", border);
00522     return border;
00523 }
00524 
00525 
00526 static void
00527 output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields,
00528            unsigned char *fieldNotNum, int *fieldMax, char *border,
00529            const int row_index)
00530 {
00531 
00532     int         field_index;    /* for loop index */
00533 
00534     if (po->html3)
00535         fputs("<tr>", fout);
00536     else if (po->standard)
00537         fputs(po->fieldSep, fout);
00538     for (field_index = 0; field_index < nFields; field_index++)
00539     {
00540         char       *p = fields[row_index * nFields + field_index];
00541 
00542         if (po->html3)
00543             fprintf(fout, "<td align=\"%s\">%s</td>",
00544                     fieldNotNum[field_index] ? "left" : "right", p ? p : "");
00545         else
00546         {
00547             fprintf(fout,
00548                     fieldNotNum[field_index] ?
00549                     (po->standard ? " %-*s " : "%-*s") :
00550                     (po->standard ? " %*s " : "%*s"),
00551                     fieldMax[field_index],
00552                     p ? p : "");
00553             if (po->standard || field_index + 1 < nFields)
00554                 fputs(po->fieldSep, fout);
00555         }
00556         if (p)
00557             free(p);
00558     }
00559     if (po->html3)
00560         fputs("</tr>", fout);
00561     else if (po->standard)
00562         fprintf(fout, "\n%s", border);
00563     fputc('\n', fout);
00564 }
00565 
00566 
00567 
00568 /*
00569  * really old printing routines
00570  */
00571 
00572 void
00573 PQdisplayTuples(const PGresult *res,
00574                 FILE *fp,       /* where to send the output */
00575                 int fillAlign,  /* pad the fields with spaces */
00576                 const char *fieldSep,   /* field separator */
00577                 int printHeader,    /* display headers? */
00578                 int quiet
00579 )
00580 {
00581 #define DEFAULT_FIELD_SEP " "
00582 
00583     int         i,
00584                 j;
00585     int         nFields;
00586     int         nTuples;
00587     int        *fLength = NULL;
00588 
00589     if (fieldSep == NULL)
00590         fieldSep = DEFAULT_FIELD_SEP;
00591 
00592     /* Get some useful info about the results */
00593     nFields = PQnfields(res);
00594     nTuples = PQntuples(res);
00595 
00596     if (fp == NULL)
00597         fp = stdout;
00598 
00599     /* Figure the field lengths to align to */
00600     /* will be somewhat time consuming for very large results */
00601     if (fillAlign)
00602     {
00603         fLength = (int *) malloc(nFields * sizeof(int));
00604         if (!fLength)
00605         {
00606             fprintf(stderr, libpq_gettext("out of memory\n"));
00607             abort();
00608         }
00609 
00610         for (j = 0; j < nFields; j++)
00611         {
00612             fLength[j] = strlen(PQfname(res, j));
00613             for (i = 0; i < nTuples; i++)
00614             {
00615                 int         flen = PQgetlength(res, i, j);
00616 
00617                 if (flen > fLength[j])
00618                     fLength[j] = flen;
00619             }
00620         }
00621     }
00622 
00623     if (printHeader)
00624     {
00625         /* first, print out the attribute names */
00626         for (i = 0; i < nFields; i++)
00627         {
00628             fputs(PQfname(res, i), fp);
00629             if (fillAlign)
00630                 fill(strlen(PQfname(res, i)), fLength[i], ' ', fp);
00631             fputs(fieldSep, fp);
00632         }
00633         fprintf(fp, "\n");
00634 
00635         /* Underline the attribute names */
00636         for (i = 0; i < nFields; i++)
00637         {
00638             if (fillAlign)
00639                 fill(0, fLength[i], '-', fp);
00640             fputs(fieldSep, fp);
00641         }
00642         fprintf(fp, "\n");
00643     }
00644 
00645     /* next, print out the instances */
00646     for (i = 0; i < nTuples; i++)
00647     {
00648         for (j = 0; j < nFields; j++)
00649         {
00650             fprintf(fp, "%s", PQgetvalue(res, i, j));
00651             if (fillAlign)
00652                 fill(strlen(PQgetvalue(res, i, j)), fLength[j], ' ', fp);
00653             fputs(fieldSep, fp);
00654         }
00655         fprintf(fp, "\n");
00656     }
00657 
00658     if (!quiet)
00659         fprintf(fp, "\nQuery returned %d row%s.\n", PQntuples(res),
00660                 (PQntuples(res) == 1) ? "" : "s");
00661 
00662     fflush(fp);
00663 
00664     if (fLength)
00665         free(fLength);
00666 }
00667 
00668 
00669 
00670 void
00671 PQprintTuples(const PGresult *res,
00672               FILE *fout,       /* output stream */
00673               int PrintAttNames,    /* print attribute names or not */
00674               int TerseOutput,  /* delimiter bars or not? */
00675               int colWidth      /* width of column, if 0, use variable width */
00676 )
00677 {
00678     int         nFields;
00679     int         nTups;
00680     int         i,
00681                 j;
00682     char        formatString[80];
00683     char       *tborder = NULL;
00684 
00685     nFields = PQnfields(res);
00686     nTups = PQntuples(res);
00687 
00688     if (colWidth > 0)
00689         sprintf(formatString, "%%s %%-%ds", colWidth);
00690     else
00691         sprintf(formatString, "%%s %%s");
00692 
00693     if (nFields > 0)
00694     {                           /* only print rows with at least 1 field.  */
00695 
00696         if (!TerseOutput)
00697         {
00698             int         width;
00699 
00700             width = nFields * 14;
00701             tborder = (char *) malloc(width + 1);
00702             if (!tborder)
00703             {
00704                 fprintf(stderr, libpq_gettext("out of memory\n"));
00705                 abort();
00706             }
00707             for (i = 0; i < width; i++)
00708                 tborder[i] = '-';
00709             tborder[width] = '\0';
00710             fprintf(fout, "%s\n", tborder);
00711         }
00712 
00713         for (i = 0; i < nFields; i++)
00714         {
00715             if (PrintAttNames)
00716             {
00717                 fprintf(fout, formatString,
00718                         TerseOutput ? "" : "|",
00719                         PQfname(res, i));
00720             }
00721         }
00722 
00723         if (PrintAttNames)
00724         {
00725             if (TerseOutput)
00726                 fprintf(fout, "\n");
00727             else
00728                 fprintf(fout, "|\n%s\n", tborder);
00729         }
00730 
00731         for (i = 0; i < nTups; i++)
00732         {
00733             for (j = 0; j < nFields; j++)
00734             {
00735                 const char *pval = PQgetvalue(res, i, j);
00736 
00737                 fprintf(fout, formatString,
00738                         TerseOutput ? "" : "|",
00739                         pval ? pval : "");
00740             }
00741             if (TerseOutput)
00742                 fprintf(fout, "\n");
00743             else
00744                 fprintf(fout, "|\n%s\n", tborder);
00745         }
00746     }
00747 
00748     if (tborder)
00749         free(tborder);
00750 }
00751 
00752 
00753 /* simply send out max-length number of filler characters to fp */
00754 
00755 static void
00756 fill(int length, int max, char filler, FILE *fp)
00757 {
00758     int         count;
00759 
00760     count = max - length;
00761     while (count-- >= 0)
00762         putc(filler, fp);
00763 }