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

  1. /*
  2.  * qcdata.c
  3.  *
  4.  * Routines for parsing quoterc data files.
  5.  *
  6.  *      Created: 12th 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 <stdlib.h>
  17. #include <stdio.h>
  18. #include <string.h>
  19. #include <xtype.h>
  20. #include "authors.h"
  21. #include "general.h"
  22. #include "quoterc.h"
  23. #include "quotes.h"
  24.  
  25.  
  26. void QCFreeBlock(QCBLOCK *pqb)
  27. /*
  28.  * Reclaim the memory created by a call to QCReadBlock().
  29.  *
  30.  * QCBLOCK *pqb    - a QCBLOCK structure from QCReadBlock()
  31.  */
  32. {
  33.     if (pqb != NULL) {
  34.         free(pqb->pszAuthorCode);
  35.         free(pqb->pszQuoteCode);
  36.         free(pqb->pai);
  37.         free(pqb->pqi);
  38.         free(pqb->pszAuthorDesc);
  39.         free(pqb->pszQuoteText);
  40.     }
  41. }
  42.  
  43.  
  44. char *QCMatchString(XSTR *xstrInput, char *pszFormat, XSTR *xstrOutput)
  45. /*
  46.  * Try to match a string. The input string will be changed to be the first
  47.  * sequence of symbols that could match the format, and the rest will be
  48.  * appended to the output string.
  49.  *
  50.  * XSTR *xstrInput    - input string (will be altered)
  51.  * char *pszFormat    - the format string to match
  52.  * XSTR *xstrOutput    - output string
  53.  *
  54.  * Returns        - NULL, if there was a memory allocation failure
  55.  *              the part of pszFormat which we have matched up to, otherwise
  56.  */
  57. {
  58.     char    *pszMatch;    /* return value */
  59.     char    *pchInput;    /* counter in xstrInput */
  60.     char    *pch;        /* working variable */
  61.     int    bAbort;        /* abort the process? */
  62.  
  63.     /* initialise variables */
  64.     pszMatch = pszFormat;
  65.     pchInput = xstrcast(xstrInput);
  66.     bAbort = 0;
  67.  
  68.     while (!bAbort && *pszMatch && *pchInput) {
  69.         if ((pch = QCMatchSymbol(pszMatch, *pchInput)) == NULL) {
  70.             /* no match; start searching for a new match */
  71.             pszMatch = pszFormat;
  72.             while (*pchInput && ((pch = QCMatchSymbol(pszMatch, *pchInput)) == NULL))
  73.                 pchInput++;
  74.             if (pch != NULL)
  75.                 pszMatch = pch;
  76.  
  77.             /* move the unmatched section to the output */
  78.             if (xstrncat(xstrOutput, xstrcast(xstrInput), pchInput - xstrcast(xstrInput) + 1) == NULL)
  79.                 bAbort = 1;
  80.             else {
  81.                 xstrdel(xstrInput, 0, pchInput - xstrcast(xstrInput) - 1);
  82.                 pchInput = xstrcast(xstrInput);
  83.             }
  84.         } else {
  85.             pszMatch = pch;
  86.             pchInput++;
  87.         }
  88.     }
  89.  
  90.     return ((bAbort)? NULL : pszMatch);
  91. }
  92.  
  93.  
  94. char *QCMatchSymbol(char *pszFormat, char ch)
  95. /*
  96.  * See if ch matches a given format string.
  97.  *
  98.  * char *pszFormat    - the format string to match
  99.  * char ch        - the character to match
  100.  *
  101.  * Returns        - NULL, if the ch does not match pszFormat
  102.  *              the point in pszFormat that we have matched up to, otherwise
  103.  */
  104. {
  105.     char    *pszMatch;    /* return value */
  106.  
  107.     /* initialise variables */
  108.     pszMatch = NULL;
  109.  
  110.     if (pszFormat[0] == '%') {
  111.         /* special character */
  112.         switch (pszFormat[1]) {
  113.             case 'n':
  114.                 /* new line */
  115.                 if (ch == '\n')
  116.                     pszMatch = pszFormat + 2;
  117.                 break;
  118.  
  119.             case '_':
  120.                 /* white space */
  121.                 if (isspace(ch))
  122.                     pszMatch = pszFormat + 2;
  123.                 break;
  124.  
  125.             case ';':
  126.                 /* semi-colon */
  127.                 if (ch == ';')
  128.                     pszMatch = pszFormat + 2;
  129.                 break;
  130.  
  131.             case '>':
  132.                 /* tab */
  133.                 if (ch == '\t')
  134.                     pszMatch = pszFormat + 2;
  135.                 break;
  136.  
  137.             default:
  138.                 /* escaped character */
  139.                 if (pszFormat[1] && (ch == pszFormat[1]))
  140.                     pszMatch = pszFormat + 2;
  141.                 break;
  142.         }
  143.     } else {
  144.         /* normal character */
  145.         if (ch == pszFormat[0])
  146.             pszMatch = pszFormat + 1;
  147.     }
  148.  
  149.     return (pszMatch);
  150. }
  151.  
  152.  
  153. QCBLOCK *QCReadBlock(FILE *f, char *pszFormat)
  154. /*
  155.  * Read a quote/author from a stream in a given format. The structure returned
  156.  * from this function should be de-allocated with QCFreeBlock().
  157.  *
  158.  * FILE *f        - the stream handle to read from
  159.  * char *pszFormat    - the format to read in
  160.  *
  161.  * Returns        - NULL, if there was an error (memory allocation if !feof() and !ferror())
  162.  *              (a pointer to) a new QCBLOCK containing the block, otherwise
  163.  */
  164. {
  165.     QCBLOCK *    pqb;        /* return value */
  166.     char *        pszSep;        /* string separtor one input makrer from the next */
  167.     char *        pszCurrent;    /* current position in pszFormat */
  168.     char *        pszNext;    /* place-holder in pszFormat */
  169.     XSTR *        xstr;        /* read buffer */
  170.     int        bDone;        /* have we found the next input marker yet? */
  171.     int        bTrivial;    /* is the input trivial? */
  172.     int        bStopped;    /* has the process been stopped (by end-of-file)? */
  173.     int        bFatal;        /* has the process been fatally wounded? */
  174.  
  175.     /* initialise variables */
  176.     pqb = NULL;
  177.     pszSep = NULL;
  178.     pszCurrent = pszFormat;
  179.     bTrivial = 1;
  180.     bStopped = 0;
  181.     bFatal = 0;
  182.  
  183.     /* allocate memory */
  184.     if ((pqb = (QCBLOCK *)malloc(sizeof(QCBLOCK))) == NULL)
  185.         bFatal = 1;
  186.     else {
  187.         memset(pqb, 0, sizeof(QCBLOCK));
  188.         if ((pqb->pai = (AUTHORINFO *)malloc(sizeof(AUTHORINFO))) == NULL)
  189.             bFatal = 1;
  190.         else if ((pqb->pqi = (QUOTEINFO *)malloc(sizeof(QUOTEINFO))) == NULL)
  191.             bFatal = 1;
  192.         else {
  193.             memset(pqb->pai, 0, sizeof(AUTHORINFO));
  194.             memset(pqb->pqi, 0, sizeof(QUOTEINFO));
  195.         }
  196.     }
  197.     if (!bFatal) {
  198.         if ((pszSep = (char *)malloc(strlen(pszFormat) + 1)) == NULL)
  199.             bFatal = 1;
  200.     }
  201.  
  202.     while (!bFatal && !bStopped && (*pszCurrent)) {
  203.         /* obtain the separator */
  204.         pszNext = pszCurrent + 2;
  205.         bDone = 0;
  206.         while (!bDone && *pszNext) {
  207.             if (*pszNext == '%') {
  208.                 switch (pszNext[1]) {
  209.                     case 'a':
  210.                     case 'b':
  211.                     case 'd':
  212.                     case 'f':
  213.                     case 'g':
  214.                     case 'q':
  215.                     case 's':
  216.                     case 't':
  217.                     case 'x':
  218.                     case '1':
  219.                     case '2':
  220.                     case '3':
  221.                     case '4':
  222.                     case '5':
  223.                         /* input item; stop */
  224.                         bDone = 1;
  225.                         break;
  226.  
  227.                     default:
  228.                         /* some special character; skip it */
  229.                         pszNext += 2;
  230.                         break;
  231.                 }
  232.             } else {
  233.                 /* ordinary character; continue */
  234.                 pszNext++;
  235.             }
  236.         }
  237.         strtncpy(pszSep, pszCurrent + 2, pszNext - pszCurrent - 1);
  238.  
  239.         /* read the input item */
  240.         if ((xstr = xstrnew(10, 10)) == NULL)
  241.             bFatal = 1;
  242.         else if (QCReadItem(f, pszSep, xstr) ==  NULL) {
  243.             if (ferror(f) || !feof(f))
  244.                 bFatal = 1;
  245.             else
  246.                 bStopped = 1;
  247.         } else if (xstrlen(xstr) > 0) {
  248.             bTrivial = 0;
  249.         }
  250.         if (!bFatal) {
  251.             switch (pszCurrent[1]) {
  252.                 case 'a':
  253.                     xstrtrunc(xstr, AUTHOR_MAX_CODE);
  254.                     pqb->pszAuthorCode = xstrcvt(xstr);
  255.                     break;
  256.  
  257.                 case 'b':
  258.                     strtncpy(pqb->pai->szBirthYear, xstrcast(xstr), AUTHOR_MAX_BIRTH);
  259.                     xstrfree(xstr);
  260.                     break;
  261.  
  262.                 case 'd':
  263.                     pqb->pszAuthorDesc = xstrcvt(xstr);
  264.                     break;
  265.  
  266.                 case 'f':
  267.                     strtncpy(pqb->pai->szGivenName, xstrcast(xstr), AUTHOR_MAX_GNAME);
  268.                     xstrfree(xstr);
  269.                     break;
  270.  
  271.                 case 'g':
  272.                     strtncpy(pqb->pai->szSurname, xstrcast(xstr), AUTHOR_MAX_SURNAME);
  273.                     xstrfree(xstr);
  274.                     break;
  275.  
  276.                 case 'q':
  277.                     xstrtrunc(xstr, QUOTE_MAX_CODE);
  278.                     pqb->pszQuoteCode = xstrcvt(xstr);
  279.                     break;
  280.  
  281.                 case 's':
  282.                     strtncpy(pqb->pqi->szSource, xstrcast(xstr), QUOTE_MAX_SOURCE);
  283.                     xstrfree(xstr);
  284.                     break;
  285.  
  286.                 case 't':
  287.                     pqb->pszQuoteText = xstrcvt(xstr);
  288.                     break;
  289.  
  290.                 case 'x':
  291.                     strtncpy(pqb->pai->szDeathYear, xstrcast(xstr), AUTHOR_MAX_DEATH);
  292.                     xstrfree(xstr);
  293.                     break;
  294.  
  295.                 case '1':
  296.                 case '2':
  297.                 case '3':
  298.                 case '4':
  299.                 case '5':
  300.                     strtncpy(pqb->pqi->aszKeyword[pszCurrent[1] - '1'], xstrcast(xstr), QUOTE_MAX_KEYWORD);
  301.                     xstrfree(xstr);
  302.                     break;
  303.             }
  304.         }
  305.  
  306.         pszCurrent = pszNext;
  307.     }
  308.  
  309.     /* clean up */
  310.     free(pszSep);
  311.  
  312.     if (bStopped && *pszCurrent) {
  313.         /* stopped before reading a whole block */
  314.         bFatal = 1;
  315.     }
  316.     if (bTrivial && feof(f)) {
  317.         /* garbage at end of file */
  318.         bFatal = 1;
  319.     }
  320.  
  321.     if (bFatal) {
  322.         /* back out of memory allocation */
  323.         QCFreeBlock(pqb);
  324.         pqb = NULL;
  325.     }
  326.  
  327.     return (pqb);
  328. }
  329.  
  330.  
  331. XSTR *QCReadItem(FILE *f, char *pszFormat, XSTR *xstr)
  332. /*
  333.  * Read a file into a string until a particular sequence of symbols is found.
  334.  *
  335.  * FILE *f        - the stream handle to read from
  336.  * char *pszFormat    - the format string terminating the input
  337.  * XSTR *xstr        - the string read (output)
  338.  *
  339.  * Returns        - NULL, if there is an error (memory allocation if !feof() and !ferror())
  340.  *              xstr, otherwise
  341.  */
  342. {
  343.     int    ch;        /* read buffer */
  344.     XSTR    *xstrBuffer;    /* match buffer */
  345.     char    *pszMatch;    /* current position in pszFormat */
  346.     char    *pch;        /* working variable */
  347.     int    bAbort;        /* abort the process? */
  348.  
  349.     /* initialise variables */
  350.     xstrBuffer = NULL;
  351.     xstrcpy(xstr, "");
  352.     pszMatch = pszFormat;
  353.     bAbort = 0;
  354.  
  355.     /* allocate some initial memory */
  356.     if ((xstrBuffer = xstrnew(10, 10)) == NULL)
  357.         bAbort = 1;
  358.  
  359.     while (!bAbort && *pszMatch) {
  360.         if ((ch = cgetc(f)) == EOF) {
  361.             /* end-of-file or read error */
  362.             bAbort = 1;
  363.         } else if ((pch = QCMatchSymbol(pszMatch, ch)) == NULL) {
  364.             /* not a match; append buffer to output */
  365.             if (xstrcatc(xstrBuffer, (char)ch) == NULL)
  366.                 bAbort = 1;
  367.             else if ((pszMatch = QCMatchString(xstrBuffer, pszFormat, xstr)) == NULL)
  368.                 bAbort = 1;
  369.         } else if (xstrcatc(xstrBuffer, (char)ch) == NULL) {
  370.             bAbort = 1;
  371.         } else {
  372.             pszMatch = pch;
  373.         }
  374.     }
  375.  
  376.     if (feof(f)) {
  377.         /* append the remainder of the buffer to the output */
  378.         if (xstrcat(xstr, xstrcast(xstrBuffer)) == NULL)
  379.             bAbort = 1;
  380.     }
  381.  
  382.     /* clean up */
  383.     xstrfree(xstrBuffer);
  384.  
  385.     return ((bAbort)? NULL : xstr);
  386. }
  387.  
  388.  
  389. int QCVerifyFormat(char *pszFormat, int flMode)
  390. /*
  391.  * Verify the correctness of a format string.
  392.  *
  393.  * char *pszFormat    - the format string to verify
  394.  * int flMode        - flags (QC_FLAUTHOR, QC_FLQUOTE)
  395.  *
  396.  * Returns        - error code from quoterc.h (QC_E*)
  397.  */
  398. {
  399.     char    *pch;        /* counter in pszFormat */
  400.     int    bDelimiter;    /* was the last character a delimiter? */
  401.     int    bFirst;        /* are we on the first symbol? */
  402.     int    iError;        /* return value */
  403.  
  404.     /* initialise variables */
  405.     pch = pszFormat;
  406.     bFirst = 1;
  407.     bDelimiter = 1;
  408.     iError = QC_ENONE;
  409.  
  410.     /* search the string */
  411.     while (*pch && (iError == QC_ENONE)) {
  412.         if (*pch == '%') {
  413.             pch++;
  414.             switch (*pch) {
  415.                 case 'n':
  416.                 case '%':
  417.                 case '_':
  418.                 case ';':
  419.                 case ' ':
  420.                 case '\t':
  421.                 case '>':
  422.                     /* legal for all uses, except as first symbol */
  423.                     bDelimiter = 1;
  424.                     if (bFirst)
  425.                         iError = QC_EBAD1STITEM;
  426.                     break;
  427.  
  428.                 case 'b':
  429.                 case 'd':
  430.                 case 'f':
  431.                 case 'g':
  432.                 case 'x':
  433.                     /* legal only if we have authors */
  434.                     if (!(flMode & QC_FLAUTHOR))
  435.                         iError = QC_EBADITEM;
  436.                     else if (!bDelimiter)
  437.                         iError = QC_ENODELIM;
  438.                     bDelimiter = 1;
  439.                     break;
  440.  
  441.                 case 'q':
  442.                 case 's':
  443.                 case 't':
  444.                 case '1':
  445.                 case '2':
  446.                 case '3':
  447.                 case '4':
  448.                 case '5':
  449.                     /* legal only if we have quotes */
  450.                     if (!(flMode & QC_FLQUOTE))
  451.                         iError = QC_EBADITEM;
  452.                     else if (!bDelimiter)
  453.                         iError = QC_ENODELIM;
  454.                     bDelimiter = 0;
  455.                     break;
  456.  
  457.                 case 'a':
  458.                     /* legal if we have either authors or quotes */
  459.                     if (!(flMode & (QC_FLAUTHOR | QC_FLQUOTE)))
  460.                         iError = QC_EBADITEM;
  461.                     else if (!bDelimiter)
  462.                         iError = QC_ENODELIM;
  463.                     bDelimiter = 0;
  464.                     break;
  465.             }
  466.         } else {
  467.             bDelimiter = 1;
  468.             if (bFirst) {
  469.                 /* first character is not an input item */
  470.                 iError = QC_EBAD1STITEM;
  471.             }
  472.         }
  473.         bFirst = 0;
  474.         pch++;
  475.     }
  476.  
  477.     if (!bDelimiter) {
  478.         /* no delimiter at the end of the block */
  479.         iError = QC_ENODELIM;
  480.     }
  481.  
  482.     return (iError);
  483. }
  484.  
  485.  
  486. void QCWriteBlock(FILE *f, char *pszFormat, QCBLOCK *pqb)
  487. /*
  488.  * Write an quote/author to a stream in a specified format.
  489.  *
  490.  * FILE *f        - the stream to write to
  491.  * char *pszFormat    - the format string
  492.  * QCBLOCK *pqb        - the quote and author information to write
  493.  */
  494. {
  495.     char    *pch;    /* counter in pszFormat */
  496.  
  497.     /* initialise variables */
  498.     pch = pszFormat;
  499.  
  500.     /* write out the block */
  501.     while (*pch) {
  502.         if (*pch == '%') {
  503.             pch++;
  504.             if (*pch == 'a') {
  505.                 if (pqb->pszAuthorCode)
  506.                     fprintf(f, "%s", pqb->pszAuthorCode);
  507.             } else if (*pch == 'b') {
  508.                 if (pqb->pai)
  509.                     fprintf(f, "%s", pqb->pai->szBirthYear);
  510.             } else if (*pch == 'd') {
  511.                 if (pqb->pszAuthorDesc)
  512.                     fprintf(f, "%s", pqb->pszAuthorDesc);
  513.             } else if (*pch == 'f') {
  514.                 if (pqb->pai)
  515.                     fprintf(f, "%s", pqb->pai->szGivenName);
  516.             } else if (*pch == 'g') {
  517.                 if (pqb->pai)
  518.                     fprintf(f, "%s", pqb->pai->szSurname);
  519.             } else if (*pch == 'n') {
  520.                 fprintf(f, "\n");
  521.             } else if (*pch == 'q') {
  522.                 if (pqb->pszQuoteCode)
  523.                     fprintf(f, "%s", pqb->pszQuoteCode);
  524.             } else if (*pch == 's') {
  525.                 if (pqb->pqi)
  526.                     fprintf(f, "%s", pqb->pqi->szSource);
  527.             } else if (*pch == 't') {
  528.                 if (pqb->pszQuoteText)
  529.                     fprintf(f, "%s", pqb->pszQuoteText);
  530.             } else if (*pch == 'x') {
  531.                 if (pqb->pai)
  532.                     fprintf(f, "%s", pqb->pai->szDeathYear);
  533.             } else if (*pch == '1') {
  534.                 if (pqb->pqi)
  535.                     fprintf(f, "%s", pqb->pqi->aszKeyword[0]);
  536.             } else if (*pch == '2') {
  537.                 if (pqb->pqi)
  538.                     fprintf(f, "%s", pqb->pqi->aszKeyword[1]);
  539.             } else if (*pch == '3') {
  540.                 if (pqb->pqi)
  541.                     fprintf(f, "%s", pqb->pqi->aszKeyword[2]);
  542.             } else if (*pch == '4') {
  543.                 if (pqb->pqi)
  544.                     fprintf(f, "%s", pqb->pqi->aszKeyword[3]);
  545.             } else if (*pch == '5') {
  546.                 if (pqb->pqi)
  547.                     fprintf(f, "%s", pqb->pqi->aszKeyword[4]);
  548.             } else if (*pch == '_') {
  549.                 putc(' ', f);
  550.             } else if (*pch == '>') {
  551.                 putc('\t', f);
  552.             } else if (*pch) {
  553.                 putc(*pch, f);
  554.             }
  555.         } else {
  556.             putc((int)(*pch), f);
  557.         }
  558.         pch++;
  559.     }
  560. }
  561.