home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 35 Internet / 35-Internet.zip / quot210s.zip / src / qccmd.c < prev    next >
C/C++ Source or Header  |  1998-12-12  |  19KB  |  664 lines

  1. /*
  2.  * qccmd.c
  3.  *
  4.  * Routines for parsing quoterc command files.
  5.  *
  6.  *      Created: 13th September, 1998
  7.  * Version 1.00: 11th December, 1998
  8.  *
  9.  * (C) 1998 Nicholas Paul Sheppard
  10.  *
  11.  * This file is distributed under the GNU General Public License. See the
  12.  * file copying.txt for details.
  13.  */
  14.  
  15. #include <ctype.h>
  16. #include <stdio.h>
  17. #include <string.h>
  18. #include <xtype.h>
  19. #include "general.h"
  20. #include "quoterc.h"
  21.  
  22.  
  23. /* delimiters */
  24. static const char *pszDelimSpace = " \t\r\n";        /* white space only */
  25. static const char *pszDelimLeftBrace = " \t\r\n{";    /* white space plus left brace, for section header */
  26. static const char *pszDelimSemiColon = " \t\r\n;";    /* white space plus semi-colon, for directives */
  27.  
  28.  
  29. int QCProcessCommands(FILE *f, QCERROR *pqe)
  30. /*
  31.  * Process a command file.
  32.  *
  33.  * FILE *f    - the stream handle of the file to be processed
  34.  * QCERROR *pqe    - an error structure (output)
  35.  *
  36.  * Return        - QC_E* code from quoterc.h
  37.  */
  38. {
  39.     XSTR *        xstrLine;    /* one line of the input file */
  40.     int        flMode;        /* flags */
  41.     AUTHORDB    adb;        /* author database */
  42.     QUOTEDB        qdb;        /* quote database */
  43.     char *        pszToken;    /* pointer to next token in input */
  44.  
  45.     /* initialise variables */
  46.     QuoteNullifyDB(&qdb);
  47.     AuthorNullifyDB(&adb);
  48.     pqe->iLine = 0;
  49.     pqe->iError = QC_ENONE;
  50.  
  51.     /* allocate initial memory */
  52.     if ((xstrLine = xstrnew(80, 20)) == NULL)
  53.         pqe->iError = QC_ENOMEM;
  54.  
  55.     while ((pqe->iError == QC_ENONE) && !feof(f)) {
  56.         /* read one line from the file */
  57.         if (fgetxstr(xstrLine, f) == NULL) {
  58.             if (feof(f))
  59.                 break;
  60.             else if (ferror(f))
  61.                 pqe->iError = QC_EFILE;
  62.             else
  63.                 pqe->iError = QC_ENOMEM;
  64.         } else {
  65.             pqe->iLine++;
  66.         }
  67.  
  68.         flMode = 0;
  69.         if ((pszToken = strtok(xstrcast(xstrLine), pszDelimSpace)) != NULL) {
  70.  
  71.             /* read "compile" or "decompile" directive */
  72.             if (strcmpci(pszToken, "compile") == 0)
  73.                 flMode |= QC_FLCOMPILE;
  74.             else if (strcmpci(pszToken, "decompile") == 0)
  75.                 flMode |= QC_FLDECOMPILE;
  76.             else
  77.                 pqe->iError = QC_EBADCMD;
  78.  
  79.             /* read "quotes" or "authors" descriptor */
  80.             if (pqe->iError == QC_ENONE) {
  81.                 if ((pszToken = strtok(NULL, pszDelimSpace)) == NULL)
  82.                     pqe->iError = QC_ENODBTYPE;
  83.                 else if (strcmpci(pszToken, "authors") == 0)
  84.                     flMode |= QC_FLAUTHOR;
  85.                 else if (strcmpci(pszToken, "quotes") == 0)
  86.                     flMode |= QC_FLQUOTE;
  87.                 else
  88.                     pqe->iError = QC_EBADDBTYPE;
  89.             }
  90.  
  91.             /* read database name */
  92.             if (pqe->iError == QC_ENONE) {
  93.                 if ((pszToken = strtok(NULL, pszDelimLeftBrace)) == NULL)
  94.                     pqe->iError = QC_ENOFILEN;
  95.             }
  96.  
  97.             /* open database */
  98.             if (pqe->iError == QC_ENONE) {
  99.                 if (flMode & QC_FLQUOTE) {
  100.                     if (!QuoteOpenDB(pszToken, &qdb, (flMode & QC_FLCOMPILE)? S_IWRITE : S_IREAD))
  101.                         pqe->iError = QC_EOPEN;
  102.                 } else {
  103.                     if (!AuthorOpenDB(pszToken, &adb, (flMode & QC_FLCOMPILE)? S_IWRITE : S_IREAD))
  104.                         pqe->iError = QC_EOPEN;
  105.                 }
  106.             }
  107.  
  108.             /* process command block */
  109.             if (pqe->iError == QC_ENONE) {
  110.                 if (flMode & QC_FLQUOTE) {
  111.                     QCProcessQuoteSection(f, &qdb, flMode, pqe, xstrLine);
  112.                 } else {
  113.                     QCProcessAuthorSection(f, &adb, flMode, pqe, xstrLine);
  114.                 }
  115.             }
  116.  
  117.             /* close database */
  118.             if (adb.dbfInfo != NULL)
  119.                 AuthorCloseDB(&adb);
  120.             if (qdb.dbfInfo != NULL)
  121.                 QuoteCloseDB(&qdb);
  122.         }
  123.     }
  124.  
  125.     /* clean up */
  126.     xstrfree(xstrLine);
  127.  
  128.     return (pqe->iError);
  129. }
  130.  
  131.  
  132. int QCProcessAuthorSection(FILE *fCmd, AUTHORDB *padb, int flMode, QCERROR *pqe, XSTR *xstrLine)
  133. /*
  134.  * Process the commands inside an author database section.
  135.  *
  136.  * FILE *fCmd        - stream handle of command file
  137.  * AUTHORDB *padb    - the auther database we're using
  138.  * int flMode        - mode (QC_FLCOMPILE, QCFL_DECOMPILE)
  139.  * QCERROR *pqe        - error structure (output)
  140.  * XSTR *xstrLine    - line buffer
  141.  *
  142.  * Returns        - error code from quoterc.h
  143.  */
  144. {
  145.     char *    pszKeyword;            /* keyword */
  146.     char *    pszArg1;            /* file name or code stem */
  147.     char *    pszFormat;            /* format string */
  148.     int    bStem;                /* are we changing the code stem? */
  149.     char    szStem[AUTHOR_MAX_CODE + 1];    /* author code stem */
  150.     int    bDone;                /* loop invariant */
  151.  
  152.     /* initialise variables */
  153.     pszArg1 = NULL;
  154.     pszFormat = NULL;
  155.     szStem[0] = '\0';
  156.     pqe->iError = QC_ENONE;
  157.     bDone = 0;
  158.  
  159.     while (!bDone && (pqe->iError == QC_ENONE)) {
  160.         /* initialise flags for this line */
  161.         flMode &= ~(QC_FLCREATE | QC_FLAPPEND);
  162.         bStem = 0;
  163.  
  164.         /* read one line from the file */
  165.         if (fgetxstr(xstrLine, fCmd) == NULL) {
  166.             if (feof(fCmd))
  167.                 break;
  168.             else if (ferror(fCmd))
  169.                 pqe->iError = QC_EFILE;
  170.             else
  171.                 pqe->iError = QC_ENOMEM;
  172.         } else {
  173.             pqe->iLine++;
  174.         }
  175.  
  176.         if ((pszKeyword = stresctok(xstrcast(xstrLine), pszDelimSemiColon, '%')) != NULL) {
  177.             /* read directive */
  178.             if (strcmpci(pszKeyword, "create") == 0)
  179.                 flMode |= QC_FLCREATE;
  180.             else if (strcmpci(pszKeyword, "append") == 0)
  181.                 flMode |= QC_FLAPPEND;
  182.             else if (strcmpci(pszKeyword, "stem") == 0)
  183.                 bStem = 1;
  184.             else if (pszKeyword[0] == '}')
  185.                 bDone = 1;
  186.             else
  187.                 pqe->iError = QC_EBADCMD;
  188.  
  189.             /* read first argument */
  190.             if (!bDone && (pqe->iError == QC_ENONE)) {
  191.                 if ((pszArg1 = stresctok(NULL, pszDelimSemiColon, '%')) == NULL)
  192.                     pqe->iError = QC_ENOARG;
  193.                 else
  194.                     strrmchr(pszArg1, '%');
  195.             }
  196.  
  197.             /* read format string, if required */
  198.             if (!bDone && (pqe->iError == QC_ENONE) && ((flMode & QC_FLCREATE) || (flMode & QC_FLAPPEND))) {
  199.                 if ((pszFormat = stresctok(NULL, pszDelimSemiColon, '%')) == NULL)
  200.                     pqe->iError = QC_ENOFORMAT;
  201.             }
  202.  
  203.             if (!bDone && (pqe->iError == QC_ENONE) && bStem) {
  204.                 /* change automatic author code stem */
  205.                 strtncpy(szStem, pszArg1, AUTHOR_MAX_CODE);
  206.             }
  207.  
  208.             if (!bDone && (pqe->iError == QC_ENONE) && ((flMode & QC_FLCREATE) || (flMode & QC_FLAPPEND))) {
  209.                 /* compile or de-compile */
  210.                 if (!bDone && (pqe->iError == QC_ENONE)) {
  211.                     if (flMode & QC_FLCOMPILE)
  212.                         pqe->iError = QCCompileAuthors(padb, pszArg1, pszFormat, szStem, flMode);
  213.                     else
  214.                         pqe->iError = QCDecompileAuthors(padb, pszArg1, pszFormat, flMode);
  215.                 }
  216.             }
  217.         }
  218.     }
  219.  
  220.     return (pqe->iError);
  221. }
  222.  
  223.  
  224. int QCProcessQuoteSection(FILE *fCmd, QUOTEDB *pqdb, int flMode, QCERROR *pqe, XSTR *xstrLine)
  225. /*
  226.  * Process the commands inside a quote database section.
  227.  *
  228.  * FILE *fCmd        - stream handle of command file
  229.  * QUOTEDB *pqdb    - the quote database we're using
  230.  * int flMode        - mode (QC_FLCOMPILE, QC_FLDECOMPILE)
  231.  * QCERROR *pqe        - error structure (output)
  232.  * XSTR *xstrLine    - line buffer
  233.  *
  234.  * Returns        - error code from quoterc.h
  235.  */
  236. {
  237.     char *        pszKeyword;            /* keyword */
  238.     char *        pszArg1;            /* code or file name */
  239.     char *        pszFormat;            /* format string */
  240.     int        bAuthorDB;            /* are we opening a new author database? */
  241.     int        bAuthor;            /* are we changing the current author code? */
  242.     int        bSource;            /* are we changing the current source? */
  243.     int        bStem;                /* are we changing the code stem? */
  244.     AUTHORDB    adb;                /* author database */
  245.     char        szAuthor[AUTHOR_MAX_CODE + 1];    /* author code */
  246.     char        szSource[QUOTE_MAX_SOURCE + 1];    /* quote source */
  247.     char        szStem[QUOTE_MAX_CODE + 1];    /* quote code stem */
  248.     int        bDone;                /* loop invariant */
  249.  
  250.     /* initialise variables */
  251.     pszArg1 = NULL;
  252.     pszFormat = NULL;
  253.     AuthorNullifyDB(&adb);
  254.     szAuthor[0] = '\0';
  255.     szSource[0] = '\0';
  256.     szStem[0] = '\0';
  257.     pqe->iError = QC_ENONE;
  258.     bDone = 0;
  259.  
  260.     while (!bDone && (pqe->iError == QC_ENONE)) {
  261.         /* initialise flags for this line */
  262.         flMode &= ~(QC_FLCREATE | QC_FLAPPEND);
  263.         bAuthorDB = 0;
  264.         bAuthor = 0;
  265.         bSource = 0;
  266.         bStem = 0;
  267.  
  268.         /* read one line from the file */
  269.         if (fgetxstr(xstrLine, fCmd) == NULL) {
  270.             if (feof(fCmd))
  271.                 break;
  272.             else if (ferror(fCmd))
  273.                 pqe->iError = QC_EFILE;
  274.             else
  275.                 pqe->iError = QC_ENOMEM;
  276.         } else {
  277.             pqe->iLine++;
  278.         }
  279.  
  280.         if ((pszKeyword = stresctok(xstrcast(xstrLine), pszDelimSemiColon, '%')) != NULL) {
  281.             /* read directive */
  282.             if (strcmpci(pszKeyword, "create") == 0)
  283.                 flMode |= QC_FLCREATE;
  284.             else if (strcmpci(pszKeyword, "append") == 0)
  285.                 flMode |= QC_FLAPPEND;
  286.             else if (strcmpci(pszKeyword, "authors") == 0)
  287.                 bAuthorDB = 1;
  288.             else if (strcmpci(pszKeyword, "author") == 0)
  289.                 bAuthor = 1;
  290.             else if (strcmpci(pszKeyword, "source") == 0)
  291.                 bSource = 1;
  292.             else if (strcmpci(pszKeyword, "stem") == 0)
  293.                 bStem = 1;
  294.             else if (pszKeyword[0] == '}')
  295.                 bDone = 1;
  296.             else
  297.                 pqe->iError = QC_EBADCMD;
  298.  
  299.             /* read first argument */
  300.             if (!bDone && (pqe->iError == QC_ENONE)) {
  301.                 if ((pszArg1 = stresctok(NULL, pszDelimSemiColon, '%')) == NULL)
  302.                     pqe->iError = QC_ENOARG;
  303.                 else
  304.                     strrmchr(pszArg1, '%');
  305.             }
  306.  
  307.             /* read format string, if required */
  308.             if (!bDone && (pqe->iError == QC_ENONE) && ((flMode & QC_FLCREATE) || (flMode & QC_FLAPPEND))) {
  309.                 if ((pszFormat = stresctok(NULL, pszDelimSemiColon, '%')) == NULL)
  310.                     pqe->iError = QC_ENOFORMAT;
  311.             }
  312.  
  313.             if (!bDone && (pqe->iError == QC_ENONE) && bAuthorDB) {
  314.                 /* open a new author database */
  315.                 if (adb.dbfInfo != NULL)
  316.                     AuthorCloseDB(&adb);
  317.                 if (!AuthorOpenDB(pszArg1, &adb, (flMode & QC_FLCOMPILE)? S_IWRITE : S_IREAD))
  318.                     pqe->iError = QC_EOPEN;
  319.             }
  320.  
  321.             if (!bDone && (pqe->iError == QC_ENONE) && bAuthor) {
  322.                 /* change current author */
  323.                 strtncpy(szAuthor, pszArg1, AUTHOR_MAX_CODE);
  324.             }
  325.  
  326.             if (!bDone && (pqe->iError == QC_ENONE) && bSource) {
  327.                 /* change current source */
  328.                 strtncpy(szSource, pszArg1, QUOTE_MAX_SOURCE);
  329.             }
  330.  
  331.             if (!bDone && (pqe->iError == QC_ENONE) && bStem) {
  332.                 /* change automatic quote code stem */
  333.                 strtncpy(szStem, pszArg1, QUOTE_MAX_CODE);
  334.             }
  335.  
  336.             if (!bDone && (pqe->iError == QC_ENONE) && ((flMode & QC_FLCREATE) || (flMode & QC_FLAPPEND))) {
  337.                 /* compile or de-compile */
  338.                 if (flMode & QC_FLCOMPILE)
  339.                     pqe->iError = QCCompileQuotes(pqdb, pszArg1, pszFormat, szStem, szSource, szAuthor, flMode);
  340.                 else
  341.                     pqe->iError = QCDecompileQuotes(pqdb, &adb, pszArg1, pszFormat, flMode);
  342.             }
  343.         }
  344.     }
  345.  
  346.     /* clean up */
  347.     if (adb.dbfInfo != NULL)
  348.         AuthorCloseDB(&adb);
  349.  
  350.     return (pqe->iError);
  351. }
  352.  
  353.  
  354. int QCCompileAuthors(AUTHORDB *padb, char *pszFileName, char *pszFormat, char *pszStem, int flMode)
  355. /*
  356.  * Compile an author database from a text file.
  357.  *
  358.  * AUTHORDB *padb    - author database to compile into
  359.  * char *pszFileName    - name of file to compile
  360.  * char *pszFormat    - format string
  361.  * char *pszStem    - stem for automatic author codes
  362.  * int flMode        - flags (QC_FLCREATE, QC_FLAPPEND)
  363.  *
  364.  * Returns        - error code from quoterc.h
  365.  */
  366. {
  367.     FILE *        f;                    /* stream handle of input file */
  368.     QCBLOCK *    pqb;                    /* block read from input */
  369.     int        iAuthorCode;                /* counter for generating author codes */
  370.     int        iAutoNumWidth;                /* width of automatic number */
  371.     char        szAuthorCode[QUOTE_MAX_CODE + 1];    /* generated author codes */
  372.     int        iError;                    /* return value */
  373.  
  374.     /* initialise variables */
  375.     f = NULL;
  376.     iAuthorCode = 0;
  377.     iAutoNumWidth = AUTHOR_MAX_CODE - strlen(pszStem);
  378.     iError = QC_ENONE;
  379.  
  380.     /* verify format string */
  381.     iError = QCVerifyFormat(pszFormat, flMode);
  382.  
  383.     if (iError == QC_ENONE) {
  384.         if (flMode & QC_FLCREATE) {
  385.             AuthorEmptyDB(padb);
  386.         }
  387.     }
  388.  
  389.     /* open the input file */
  390.     if (iError == QC_ENONE) {
  391.         if ((f = fopen(pszFileName, "r")) == NULL)
  392.             iError = QC_EOPEN;
  393.     }
  394.  
  395.     /* choose the initial quote code */
  396.     if (iError == QC_ENONE) {
  397.         do {
  398.             iAuthorCode++;
  399.             sprintf(szAuthorCode, "%s%0*d", pszStem, iAutoNumWidth, iAuthorCode);
  400.         } while (AuthorExists(padb, szAuthorCode));
  401.     }
  402.  
  403.     /* read quotes */
  404.     while ((iError == QC_ENONE) && !feof(f)) {
  405.         if ((pqb = QCReadBlock(f, pszFormat)) != NULL) {
  406.             AuthorAddAuthor(padb, (pqb->pszAuthorCode)? pqb->pszAuthorCode : szAuthorCode, pqb->pai, pqb->pszAuthorDesc);
  407.             if (!pqb->pszAuthorCode) {
  408.                 do {
  409.                     iAuthorCode++;
  410.                     sprintf(szAuthorCode, "%s%0*d", pszStem, iAutoNumWidth, iAuthorCode);
  411.                 } while (AuthorExists(padb, szAuthorCode));
  412.             }
  413.             QCFreeBlock(pqb);
  414.         } else if (ferror(f)) {
  415.             iError = QC_EFILE;
  416.         } else if (!feof(f)) {
  417.             iError = QC_ENOMEM;
  418.         }
  419.     }
  420.  
  421.     /* clean up */
  422.     if (f != NULL)
  423.         fclose(f);
  424.  
  425.     return (iError);
  426. }
  427.  
  428.  
  429. int QCDecompileAuthors(AUTHORDB *padb, char *pszFileName, char *pszFormat, int flMode)
  430. /*
  431.  * De-compile an author database to a text file.
  432.  *
  433.  * AUTHORDB *padb    - author database to de-compile
  434.  * char *pszFileName    - name of file to de-compile to
  435.  * char *pszFormat    - format string
  436.  * int flMode        - flags (QC_FLCREATE, QC_FLAPPEND)
  437.  *
  438.  * Returns        - error code from quoterc.h
  439.  */
  440. {
  441.     FILE *        f;    /* stream handle for output */
  442.     AUTHORSEARCH    as;    /* search structure */
  443.     QCBLOCK        qb;    /* output block */
  444.     int        bDone;    /* loop invariant */
  445.     int        iError;    /* return value */
  446.  
  447.     /* initialise variables */
  448.     f = NULL;
  449.     iError = QC_ENONE;
  450.  
  451.     /* validate format string and open output file */
  452.     if ((iError = QCVerifyFormat(pszFormat, flMode)) == QC_ENONE) {
  453.         if ((f = fopen(pszFileName, (flMode & QC_FLCREATE)? "w" : "a")) == NULL)
  454.             iError = QC_EOPEN;
  455.     }
  456.  
  457.     /* traverse database */
  458.     bDone = !AuthorFullSearchInit(padb, &as);
  459.     while ((iError == QC_ENONE) && !bDone) {
  460.         /* get next author */
  461.         qb.pszAuthorCode = as.szLastCode;
  462.         AuthorGetAuthor(padb, as.szLastCode, &qb.pai, &qb.pszAuthorDesc);
  463.  
  464.         /* output this author */
  465.         QCWriteBlock(f, pszFormat, &qb);
  466.  
  467.         /* move on to next author */
  468.         AuthorFreeAuthor(&qb.pai, &qb.pszAuthorDesc);
  469.         bDone = !AuthorFullSearchNext(&as);
  470.     }
  471.  
  472.     /* clean up */
  473.     AuthorFullSearchEnd(&as);
  474.     if (f != NULL)
  475.         fclose(f);
  476.  
  477.     return (iError);
  478. }
  479.  
  480.  
  481. int QCCompileQuotes(QUOTEDB *pqdb, char *pszFileName, char *pszFormat, char *pszStem, char *pszSource, char *pszAuthor, int flMode)
  482. /*
  483.  * Compile a quote database from a text file.
  484.  *
  485.  * QUOTEDB *pqdb    - the quote database to be compiled into
  486.  * char *pszFileName    - the name of the file to compile
  487.  * char *pszFormat    - format string
  488.  * char *pszStem    - stem for automatic quote codes
  489.  * char *pszSource    - source
  490.  * char *pszAuthor    - author code
  491.  * int flMode        - flags (QC_FLAPPEND, QC_FLCREATE)
  492.  *
  493.  * Returns        - error code from quoterc.h
  494.  */
  495. {
  496.     FILE *        f;                    /* stream handle of input file */
  497.     QCBLOCK *    pqb;                    /* block read from input */
  498.     int        iQuoteCode;                /* counter for generating quote codes */
  499.     int        iAutoNumWidth;                /* width of automatic number */
  500.     char        szQuoteCode[QUOTE_MAX_CODE + 1];    /* generated quote codes */
  501.     int        iError;                    /* return value */
  502.  
  503.     /* initialise variables */
  504.     f = NULL;
  505.     iQuoteCode = 0;
  506.     iAutoNumWidth = QUOTE_MAX_CODE - strlen(pszStem);
  507.     iError = QC_ENONE;
  508.  
  509.     /* verify format string */
  510.     iError = QCVerifyFormat(pszFormat, flMode);
  511.  
  512.     if (iError == QC_ENONE) {
  513.         if (flMode & QC_FLCREATE) {
  514.             QuoteEmptyDB(pqdb);
  515.         }
  516.     }
  517.  
  518.     /* open the input file */
  519.     if (iError == QC_ENONE) {
  520.         if ((f = fopen(pszFileName, "r")) == NULL)
  521.             iError = QC_EOPEN;
  522.     }
  523.  
  524.     /* choose the initial quote code */
  525.     if (iError == QC_ENONE) {
  526.         do {
  527.             iQuoteCode++;
  528.             sprintf(szQuoteCode, "%s%0*d", pszStem, iAutoNumWidth, iQuoteCode);
  529.         } while (QuoteExists(pqdb, szQuoteCode));
  530.     }
  531.  
  532.     /* read quotes */
  533.     while ((iError == QC_ENONE) && !feof(f)) {
  534.         if ((pqb = QCReadBlock(f, pszFormat)) != NULL) {
  535.             /* sort out source author details */
  536.             if (pqb->pszAuthorCode && (pqb->pszAuthorCode[0] != '\0'))
  537.                 strtncpy(pqb->pqi->szAuthorCode, pqb->pszAuthorCode, AUTHOR_MAX_CODE);
  538.             else
  539.                 strcpy(pqb->pqi->szAuthorCode, pszAuthor);
  540.             if (pqb->pqi->szSource[0] == '\0')
  541.                 strcpy(pqb->pqi->szSource, pszSource);
  542.  
  543.             /* add quote to database */
  544.             QuoteAddQuote(pqdb, (pqb->pszQuoteCode)? pqb->pszQuoteCode : szQuoteCode, pqb->pqi, pqb->pszQuoteText);
  545.             if (!pqb->pszQuoteCode) {
  546.                 do {
  547.                     iQuoteCode++;
  548.                     sprintf(szQuoteCode, "%s%0*d", pszStem, iAutoNumWidth, iQuoteCode);
  549.                 } while (QuoteExists(pqdb, szQuoteCode));
  550.             }
  551.             QCFreeBlock(pqb);
  552.         } else if (ferror(f)) {
  553.             iError = QC_EFILE;
  554.         } else if (!feof(f)) {
  555.             iError = QC_ENOMEM;
  556.         }
  557.     }
  558.  
  559.     /* clean up */
  560.     if (f != NULL)
  561.         fclose(f);
  562.  
  563.     return (iError);
  564. }
  565.  
  566.  
  567. int QCDecompileQuotes(QUOTEDB *pqdb, AUTHORDB *padb, char *pszFileName, char *pszFormat, int flMode)
  568. /*
  569.  * De-compile a quote database into a text file.
  570.  *
  571.  * QUOTEDB *pqdb    - the quote database to be de-compiled
  572.  * AUTHORDB *padb    - an author database to reference
  573.  * char *pszFileName    - the name of the file to de-compile into
  574.  * char *pszFormat    - format string
  575.  * int flMode        - flags
  576.  *
  577.  * Returns        - error code from quoterc.h
  578.  */
  579. {
  580.     FILE *        f;    /* stream handle for output */
  581.     QUOTESEARCH    qs;    /* search structure */
  582.     QCBLOCK        qb;    /* output block */
  583.     int        bDone;    /* loop invariant */
  584.     int        iError;    /* return value */
  585.  
  586.     /* initialise variables */
  587.     f = NULL;
  588.     iError = QC_ENONE;
  589.  
  590.     /* validate format string and open output file */
  591.     if (padb->dbfInfo != NULL)
  592.         flMode |= QC_FLAUTHOR;
  593.     if ((iError = QCVerifyFormat(pszFormat, flMode)) == QC_ENONE) {
  594.         if ((f = fopen(pszFileName, (flMode & QC_FLCREATE)? "w" : "a")) == NULL)
  595.             iError = QC_EOPEN;
  596.     }
  597.  
  598.     /* traverse database */
  599.     bDone = !QuoteFullSearchInit(pqdb, &qs);
  600.     while ((iError == QC_ENONE) && !bDone) {
  601.         /* get next quote */
  602.         qb.pszQuoteCode = qs.szLastCode;
  603.         QuoteGetQuote(pqdb, qs.szLastCode, &qb.pqi, &qb.pszQuoteText);
  604.         qb.pszAuthorCode = qb.pqi->szAuthorCode;
  605.  
  606.         /* get the associated author */
  607.         qb.pai = NULL;
  608.         qb.pszAuthorDesc = NULL;
  609.         if ((qb.pqi->szAuthorCode[0] != '\0') && (padb->dbfInfo != NULL))
  610.             AuthorGetAuthor(padb, qb.pqi->szAuthorCode, &qb.pai, &qb.pszAuthorDesc);
  611.  
  612.         /* output this quote */
  613.         QCWriteBlock(f, pszFormat, &qb);
  614.  
  615.         /* move on to next author */
  616.         QuoteFreeQuote(&qb.pqi, &qb.pszQuoteText);
  617.         AuthorFreeAuthor(&qb.pai, &qb.pszAuthorDesc);
  618.         bDone = !QuoteFullSearchNext(&qs);
  619.     }
  620.  
  621.     /* clean up */
  622.     QuoteFullSearchEnd(&qs);
  623.     if (f != NULL)
  624.         fclose(f);
  625.  
  626.     return (iError);
  627. }
  628.  
  629.  
  630. /* error strings */
  631. char *aszQCError[] = {
  632.     "no error",                    /* QC_ENONE */
  633.     "out of memory",                /* QC_ENOMEM */
  634.     "file error",                    /* QC_EFILE */
  635.     "unrecognised command keyword",            /* QC_EBADCMD */
  636.     "database type missing",            /* QC_ENODBTYPE */
  637.     "unrecognised database type",            /* QC_EBADDBTYPE */
  638.     "missing file name",                /* QC_ENOFILEN */
  639.     "could not open file",                /* QC_EOPEN */
  640.     "missing format string",            /* QC_ENOFORMAT */
  641.     "invalid data item",                /* QC_EBADITEM */
  642.     "permission denied",                /* QC_EPERM */
  643.     "missing argument",                /* QC_ENOARG */
  644.     "format doesn't start with a data item",    /* QC_EBAD1STITEM */
  645.     "undelimited data items",            /* QC_ENODELIM */
  646.     "unknown error"                    /* QC_EUNKNOWN */
  647. };
  648.  
  649.  
  650. char *QCErrorString(int iError)
  651. /*
  652.  * Get a pointer to a string describing an error (like strerror()).
  653.  *
  654.  * int iError    - the error code (QC_E*)
  655.  *
  656.  * Returns    - a pointer a string describing error iError
  657.  */
  658. {
  659.     if ((iError < 0) || (iError > QC_EUNKNOWN))
  660.         return (aszQCError[QC_EUNKNOWN]);
  661.     else
  662.         return (aszQCError[iError]);
  663. }
  664.