home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 35 Internet / 35-Internet.zip / quot210s.zip / src / html.c < prev    next >
C/C++ Source or Header  |  1998-09-08  |  16KB  |  506 lines

  1. /*
  2.  * html.c
  3.  *
  4.  * HTML parsing functions for the Quoteriser. These support only a subset
  5.  * of full HTML; refer to the documentation for a full description of the
  6.  * tags available.
  7.  *
  8.  *      Created: 4th March, 1997
  9.  * Version 1.00: 9th March, 1997
  10.  * Version 2.00: 8th December, 1997
  11.  * Version 2.10: 8th September, 1998
  12.  *
  13.  * (C) 1997-1998 Nicholas Paul Sheppard
  14.  *
  15.  * This file is distributed under the GNU General Public License. See the
  16.  * file copying.txt for details.
  17.  */
  18.  
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <ctype.h>
  22. #include "html.h"
  23. #include "general.h"
  24.  
  25.  
  26. int HTMLGetNextChunk(char *pszInput, char *pszChunk, char **ppszNext)
  27. /*
  28.  * Get the next portion of the input HTML that can be acted upon as a
  29.  * whole. Chunks can be a whole word, part of a word (i.e. a word
  30.  * interrupted by an HTML tag) or an HTML tag.
  31.  *
  32.  * We're fairly tolerant on broken HTML here; the block of code immediately
  33.  * following the case shows what we accept as a terminator to a tag, macro,
  34.  * etc., in the comment on the right "oops!" occurs for broken HTML.
  35.  *
  36.  * char *pszInput    - the input HTML
  37.  * char *pszChunk    - the next chunk (output)
  38.  * char *pszNext    - the continuation point (output)
  39.  *
  40.  * Return: HTML_WORD_END  - pszChunk points to a whole word
  41.  *         HTML_WORD_MID  - pszChunk points to part of a word
  42.  *         HTML_TAG_END   - pszChunk points to an HTML tag at the end of a word
  43.  *         HTML_TAG_MID   - pszChunk points to an HTML in the middle of a word
  44.  *         HTML_MACRO_END - pszChunk points to an HTML macro at the end of a word
  45.  *         HTML_MACRO_MID - pszChunk points to an HTML macro in the middle of a word
  46.  *         HTML_END       - we have reached the end of the input
  47.  */
  48. {
  49.     char *pszStartChunk;
  50.     int iLength, iRet;
  51.  
  52.     /* ignore white space */
  53.     pszStartChunk = pszInput;
  54.     while ((pszStartChunk[0] != '\0') && isspace(pszStartChunk[0]))
  55.         pszStartChunk++;
  56.  
  57.     switch (pszStartChunk[0]) {
  58.         case '<':
  59.             /* we at the start of an HTML tag */
  60.             (*ppszNext) = strchr(pszStartChunk, '>');        /* closing bracket */
  61.             (*ppszNext) = strfchr(pszStartChunk, *ppszNext, '\0');    /* end of input - oops! */
  62.             
  63.             /* copy the tag into pszChunk and append closing angle bracket */
  64.             iLength = (*ppszNext) - pszStartChunk;
  65.             strncpy(pszChunk, pszStartChunk, iLength);
  66.             pszChunk[iLength] = '\0';
  67.             strcat(pszChunk, ">");
  68.  
  69.             /* skip over the angle bracket if we did not insert it */
  70.             if ((*ppszNext)[0] == '>')
  71.                 (*ppszNext) += 1;
  72.  
  73.             /* determine return value */
  74.             if (isspace((*ppszNext)[0]))
  75.                 iRet = HTML_TAG_END;
  76.             else
  77.                 iRet = HTML_TAG_MID;
  78.             break;
  79.  
  80.         case '&':
  81.             /* we are at the start of an HTML macro */
  82.             (*ppszNext) = strchr(pszStartChunk, ';');        /* semi-colon */
  83.             (*ppszNext) = strfchr(pszStartChunk, *ppszNext, '\n');    /* line feed - oops! */
  84.             (*ppszNext) = strfchr(pszStartChunk, *ppszNext, '\r');    /* carriage return - oops! */
  85.             (*ppszNext) = strfchr(pszStartChunk, *ppszNext, '\t');    /* tab - oops! */
  86.             (*ppszNext) = strfchr(pszStartChunk, *ppszNext, ' ');    /* space - oops! */
  87.             (*ppszNext) = strfchr(pszStartChunk, *ppszNext, '\0');    /* end of input - oops! */
  88.  
  89.             /* copy the macro into pszChunk and append the closing semi-colon */
  90.             iLength = (*ppszNext) - pszStartChunk;
  91.             strncpy(pszChunk, pszStartChunk, iLength);
  92.             pszChunk[iLength] = '\0';
  93.             strcat(pszChunk, ";");
  94.  
  95.             /* skip over the semi-colon if we did not insert it */
  96.             if (*(*ppszNext) == ';')
  97.                 (*ppszNext) += 1;
  98.  
  99.             /* determine return value */
  100.             if (isspace((*ppszNext)[0]))
  101.                 iRet = HTML_MACRO_END;
  102.             else
  103.                 iRet = HTML_MACRO_MID;
  104.             break;
  105.  
  106.         case '\0':
  107.             /* we have reached the end of the input */
  108.             iRet = HTML_END;
  109.             break;
  110.  
  111.         default:
  112.                 /* we are at the start of a normal word */
  113.             (*ppszNext) = strchr(pszStartChunk, ' ');        /* space */
  114.             (*ppszNext) = strfchr(pszStartChunk, *ppszNext, '\t');    /* tab */
  115.             (*ppszNext) = strfchr(pszStartChunk, *ppszNext, '\n');    /* line feed */
  116.             (*ppszNext) = strfchr(pszStartChunk, *ppszNext, '\r');    /* carriage return */
  117.             (*ppszNext) = strfchr(pszStartChunk, *ppszNext, '<');    /* start of a tag */
  118.             (*ppszNext) = strfchr(pszStartChunk, *ppszNext, '&');    /* start of a macro */
  119.             (*ppszNext) = strfchr(pszStartChunk, *ppszNext, '\0');    /* end of input */
  120.  
  121.             /* copy the chunk into pszChunk */
  122.             iLength = (*ppszNext) - pszStartChunk;
  123.             strncpy(pszChunk, pszStartChunk, iLength);
  124.             pszChunk[iLength] = '\0';
  125.  
  126.             /* determine return value */
  127.             if (isspace((*ppszNext)[0]))
  128.                 iRet = HTML_WORD_END;
  129.             else
  130.                 iRet = HTML_WORD_MID;
  131.             break;
  132.     }
  133.  
  134.     return (iRet);
  135. }
  136.  
  137.  
  138. int HTMLParseTag(char *pszTag)
  139. /*
  140.  * Decode an HTML tag.
  141.  *
  142.  * char *pszTag    - the tag to be decoded (including enclosing brackets).
  143.  *
  144.  * Return: HTML_UNKNOWN       - unrecognised tag
  145.  *         HTML_PARAGRAPH     - new paragraph tag <P>
  146.  *         HTML_ITALICS_START - opening italices tag <I>
  147.  *         HTML_ITALICS_END   - closing italices tag </I>
  148.  *         HTML_BOLD_START    - opening "bold" tag <B>, <EM> or <STRONG>
  149.  *         HTML_BOLD_END      - closing "bold" tag </B>, </EM> or </STRONG>
  150.  *         HTML_LINEBREAK     - line break tag <BR>
  151.  *         HTML_TABLE_ROW     - table row start <TR>
  152.  *         HTML_TABLE_DATA    - table data (i.e. cell) start <TD>
  153.  *         HTML_INVALID       - this is an incorrect tag (i.e. missing brackets)
  154.  */
  155. {
  156.     char *pch, szElement[HTML_MAX_ELEM + 1];
  157.     int i, iRet;
  158.  
  159.     /* perform some sanity checking */
  160.     if ((pszTag[0] != '<') || (strchr(pszTag, '>') == NULL))
  161.         return (HTML_INVALID);
  162.  
  163.     /* extract the element from within the tag */
  164.     pch = pszTag + 1;
  165.     while (isspace(*pch) && ((*pch) != '>'))
  166.         pch++;
  167.     i = 0;
  168.     while (!isspace(*pch) && ((*pch) != '>') && (i < HTML_MAX_ELEM)) {
  169.         szElement[i++] = *pch;
  170.         pch++;
  171.     }
  172.     szElement[i] = '\0';
  173.  
  174.     /* find out what the element is and determine return value */
  175.     if (strcmpci(szElement, "P") == 0)
  176.         iRet = HTML_PARAGRAPH;
  177.     else if (strcmpci(szElement, "I") == 0)
  178.         iRet = HTML_ITALICS_START;
  179.     else if (strcmpci(szElement, "/I") == 0)
  180.         iRet = HTML_ITALICS_END;
  181.     else if (strcmpci(szElement, "B") == 0)
  182.         iRet = HTML_BOLD_START;
  183.     else if (strcmpci(szElement, "/B") == 0)
  184.         iRet = HTML_BOLD_END;
  185.     else if (strcmpci(szElement, "EM") == 0)
  186.         iRet = HTML_BOLD_START;
  187.     else if (strcmpci(szElement, "/EM") == 0)
  188.         iRet = HTML_BOLD_END;
  189.     else if (strcmpci(szElement, "STRONG") == 0)
  190.         iRet = HTML_BOLD_START;
  191.     else if (strcmpci(szElement, "/STRONG") == 0)
  192.         iRet = HTML_BOLD_END;
  193.     else if (strcmpci(szElement, "BR") == 0)
  194.         iRet = HTML_LINEBREAK;
  195.     else if (strcmpci(szElement, "TR") == 0)
  196.         iRet = HTML_TABLE_ROW;
  197.     else if (strcmpci(szElement, "TD") == 0)
  198.         iRet = HTML_TABLE_DATA;
  199.     else
  200.         iRet = HTML_UNKNOWN;
  201.  
  202.     return (iRet);
  203. }
  204.  
  205.  
  206. int HTMLParseMacro(char *pszMacro)
  207. /*
  208.  * Decode an HTML macro.
  209.  *
  210.  * char *pszMacro    - the macro to be decoded (including enclosing ampersand/semi-colon)
  211.  *
  212.  * Return: the ISO Latin-1 code corresponding to the macro
  213.  *         HTML_INVALID if this macro is not recognised
  214.  *
  215.  */
  216. {
  217.     char szStripped[HTML_MAX_MACRO + 1];
  218.     char *pchSemiColon;
  219.     int iLength, iRet;
  220.  
  221.     /* perform some sanity checking */
  222.     if ((pszMacro[0] != '&') || ((pchSemiColon = strchr(pszMacro, ';')) == NULL))
  223.         return (HTML_INVALID);
  224.  
  225.     /* strip the ampersand and semi-colon */
  226.     if ((iLength = pchSemiColon - pszMacro - 1) > HTML_MAX_MACRO)
  227.         iLength = HTML_MAX_MACRO;
  228.     strncpy(szStripped, pszMacro + 1, iLength);
  229.     szStripped[iLength] = '\0';
  230.  
  231.     iRet = HTML_INVALID;
  232.     if (szStripped[0] == '#') {
  233.         /* the macro gives the ISO Latin-1 code */
  234.         iRet = atoi(szStripped + 1);
  235.         if ((iRet == 0) || (iRet > 255))
  236.             iRet = HTML_INVALID;
  237.     } else if (strcmp(szStripped, "quot") == 0)
  238.         iRet = 34;
  239.     else if (strcmp(szStripped, "amp") == 0)
  240.         iRet = 38;
  241.     else if (strcmp(szStripped, "lt") == 0)
  242.         iRet = 60;
  243.     else if (strcmp(szStripped, "gt") == 0)
  244.         iRet = 62;
  245.        else if (strcmp(szStripped, "nbsp") == 0)
  246.         iRet = 160;
  247.     else if (strcmp(szStripped, "iexcl") == 0)
  248.         iRet = 161;
  249.     else if (strcmp(szStripped, "cent") == 0)
  250.         iRet = 162;
  251.     else if (strcmp(szStripped, "pound") == 0)
  252.         iRet = 163;
  253.     else if (strcmp(szStripped, "curren") == 0)
  254.         iRet = 164;
  255.     else if (strcmp(szStripped, "yen") == 0)
  256.         iRet = 165;
  257.     else if (strcmp(szStripped, "brvbar") == 0)
  258.         iRet = 166;
  259.     else if (strcmp(szStripped, "sect") == 0)
  260.         iRet = 167;
  261.     else if (strcmp(szStripped, "uml") == 0)
  262.         iRet = 168;
  263.     else if (strcmp(szStripped, "copy") == 0)
  264.         iRet = 169;
  265.     else if (strcmp(szStripped, "ordf") == 0)
  266.         iRet = 170;
  267.     else if (strcmp(szStripped, "laquo") == 0)
  268.         iRet = 171;
  269.     else if (strcmp(szStripped, "not") == 0)
  270.         iRet = 172;
  271.     else if (strcmp(szStripped, "shy") == 0)
  272.         iRet = 173;
  273.     else if (strcmp(szStripped, "reg") == 0)
  274.         iRet = 174;
  275.     else if (strcmp(szStripped, "macr") == 0)
  276.         iRet = 175;
  277.     else if (strcmp(szStripped, "deg") == 0)
  278.         iRet = 176;
  279.     else if (strcmp(szStripped, "plusmn") == 0)
  280.         iRet = 177;
  281.     else if (strcmp(szStripped, "sup2") == 0)
  282.         iRet = 178;
  283.     else if (strcmp(szStripped, "sup3") == 0)
  284.         iRet = 179;
  285.     else if (strcmp(szStripped, "acute") == 0)
  286.         iRet = 180;
  287.     else if (strcmp(szStripped, "micro") == 0)
  288.         iRet = 181;
  289.     else if (strcmp(szStripped, "para") == 0)
  290.         iRet = 182;
  291.     else if (strcmp(szStripped, "middot") == 0)
  292.         iRet = 183;
  293.     else if (strcmp(szStripped, "cedil") == 0)
  294.         iRet = 184;
  295.     else if (strcmp(szStripped, "sup1") == 0)
  296.         iRet = 185;
  297.     else if (strcmp(szStripped, "ordm") == 0)
  298.         iRet = 186;
  299.     else if (strcmp(szStripped, "raquo") == 0)
  300.         iRet = 187;
  301.     else if (strcmp(szStripped, "frac14") == 0)
  302.         iRet = 188;
  303.     else if (strcmp(szStripped, "frac12") == 0)
  304.         iRet = 189;
  305.     else if (strcmp(szStripped, "frac34") == 0)
  306.         iRet = 190;
  307.     else if (strcmp(szStripped, "iquest") == 0)
  308.         iRet = 191;
  309.     else if (strcmp(szStripped, "Agrave") == 0)
  310.         iRet = 192;
  311.     else if (strcmp(szStripped, "Aacute") == 0)
  312.         iRet = 193;
  313.     else if (strcmp(szStripped, "Acirc") == 0)
  314.         iRet = 194;
  315.     else if (strcmp(szStripped, "Atilde") == 0)
  316.         iRet = 195;
  317.     else if (strcmp(szStripped, "Auml") == 0)
  318.         iRet = 196;
  319.     else if (strcmp(szStripped, "Aring") == 0)
  320.         iRet = 197;
  321.     else if (strcmp(szStripped, "AElig") == 0)
  322.         iRet = 198;
  323.     else if (strcmp(szStripped, "Ccedil") == 0)
  324.         iRet = 199;
  325.     else if (strcmp(szStripped, "Egrave") == 0)
  326.         iRet = 200;
  327.     else if (strcmp(szStripped, "Eacute") == 0)
  328.         iRet = 201;
  329.     else if (strcmp(szStripped, "Ecirc") == 0)
  330.         iRet = 202;
  331.     else if (strcmp(szStripped, "Euml") == 0)
  332.         iRet = 203;
  333.     else if (strcmp(szStripped, "Igrave") == 0)
  334.         iRet = 204;
  335.     else if (strcmp(szStripped, "Iacute") == 0)
  336.         iRet = 205;
  337.     else if (strcmp(szStripped, "Ihat") == 0)
  338.         iRet = 206;
  339.     else if (strcmp(szStripped, "Iuml") == 0)
  340.         iRet = 207;
  341.     else if (strcmp(szStripped, "ETH") == 0)
  342.         iRet = 208;
  343.     else if (strcmp(szStripped, "Ntilde") == 0)
  344.         iRet = 209;
  345.     else if (strcmp(szStripped, "Ograve") == 0)
  346.         iRet = 210;
  347.     else if (strcmp(szStripped, "Oacute") == 0)
  348.         iRet = 211;
  349.     else if (strcmp(szStripped, "Ocirc") == 0)
  350.         iRet = 212;
  351.     else if (strcmp(szStripped, "Otilde") == 0)
  352.         iRet = 213;
  353.     else if (strcmp(szStripped, "Ouml") == 0)
  354.         iRet = 214;
  355.     else if (strcmp(szStripped, "times") == 0)
  356.         iRet = 215;
  357.     else if (strcmp(szStripped, "Oslash") == 0)
  358.         iRet = 216;
  359.     else if (strcmp(szStripped, "Ugrave") == 0)
  360.         iRet = 217;
  361.     else if (strcmp(szStripped, "Uacute") == 0)
  362.         iRet = 218;
  363.     else if (strcmp(szStripped, "Uhat") == 0)
  364.         iRet = 219;
  365.     else if (strcmp(szStripped, "Uuml") == 0)
  366.         iRet = 220;
  367.     else if (strcmp(szStripped, "Yacute") == 0)
  368.         iRet = 221;
  369.     else if (strcmp(szStripped, "THORN") == 0)
  370.         iRet = 222;
  371.     else if (strcmp(szStripped, "szlig") == 0)
  372.         iRet = 223;
  373.     else if (strcmp(szStripped, "agrave") == 0)
  374.         iRet = 224;
  375.     else if (strcmp(szStripped, "aacute") == 0)
  376.         iRet = 225;
  377.     else if (strcmp(szStripped, "acirc") == 0)
  378.         iRet = 226;
  379.     else if (strcmp(szStripped, "atilde") == 0)
  380.         iRet = 227;
  381.     else if (strcmp(szStripped, "auml") == 0)
  382.         iRet = 228;
  383.     else if (strcmp(szStripped, "aring") == 0)
  384.         iRet = 229;
  385.     else if (strcmp(szStripped, "aelig") == 0)
  386.         iRet = 230;
  387.     else if (strcmp(szStripped, "ccedil") == 0)
  388.         iRet = 231;
  389.     else if (strcmp(szStripped, "egrave") == 0)
  390.         iRet = 232;
  391.     else if (strcmp(szStripped, "eacute") == 0)
  392.         iRet = 233;
  393.     else if (strcmp(szStripped, "ehat") == 0)
  394.         iRet = 234;
  395.     else if (strcmp(szStripped, "euml") == 0)
  396.         iRet = 235;
  397.     else if (strcmp(szStripped, "igrave") == 0)
  398.         iRet = 236;
  399.     else if (strcmp(szStripped, "iacute") == 0)
  400.         iRet = 237;
  401.     else if (strcmp(szStripped, "ihat") == 0)
  402.         iRet = 238;
  403.     else if (strcmp(szStripped, "iuml") == 0)
  404.         iRet = 239;
  405.     else if (strcmp(szStripped, "eth") == 0)
  406.         iRet = 240;
  407.     else if (strcmp(szStripped, "ntilde") == 0)
  408.         iRet = 241;
  409.     else if (strcmp(szStripped, "ograve") == 0)
  410.         iRet = 242;
  411.     else if (strcmp(szStripped, "oacute") == 0)
  412.         iRet = 243;
  413.     else if (strcmp(szStripped, "ohat") == 0)
  414.         iRet = 244;
  415.     else if (strcmp(szStripped, "otilde") == 0)
  416.         iRet = 245;
  417.     else if (strcmp(szStripped, "ouml") == 0)
  418.         iRet = 246;
  419.     else if (strcmp(szStripped, "ugrave") == 0)
  420.         iRet = 249;
  421.     else if (strcmp(szStripped, "uacute") == 0)
  422.         iRet = 250;
  423.     else if (strcmp(szStripped, "uhat") == 0)
  424.         iRet = 251;
  425.     else if (strcmp(szStripped, "uuml") == 0)
  426.         iRet = 252;
  427.     else if (strcmp(szStripped, "yacute") == 0)
  428.         iRet = 253;
  429.     else if (strcmp(szStripped, "thorn") == 0)
  430.         iRet = 254;
  431.     else if (strcmp(szStripped, "yuml") == 0)
  432.         iRet = 255;
  433.     else
  434.         iRet = HTML_INVALID;
  435.  
  436.     return (iRet);
  437. }
  438.  
  439.  
  440. char *HTMLMakePlain(char *pszString)
  441. /*
  442.  * Make an HTML string into plain text, i.e.
  443.  *   (i)   remove all HTML tags,
  444.  *   (ii)  compress all white space into a single space character, and
  445.  *   (iii) perform macro substitutions.
  446.  *
  447.  * char *pszString    - the string to be made plain
  448.  *
  449.  * Return: pszString
  450.  */
  451. {
  452.     char *pch1, *pch2, *pch3;
  453.     char szMacro[HTML_MAX_MACRO + 1];
  454.  
  455.     /* start at the beginning of the string */
  456.     pch1 = pch2 = pszString;
  457.  
  458.     /* skip leading white space */
  459.     while (isspace(*pch2) && ((*pch2) != '\0'))
  460.         pch2++;
  461.  
  462.     while ((*pch2) != '\0') {
  463.         if (isspace(*pch2)) {
  464.             /* white space; add a space to pszString and skip the rest */
  465.             (*pch1) = ' ';
  466.             pch1++;
  467.             while (isspace(*pch2) && ((*pch2) != '\0'))
  468.                 pch2++;
  469.         } else if ((*pch2) == '<') {
  470.             /* tag; skip until end */
  471.             do
  472.                 pch2++;
  473.             while (((*pch2) != '>') && ((*pch2) != '\0'));
  474.             if ((*pch2) == '>')
  475.                 pch2++;
  476.         } else if ((*pch2) == '&') {
  477.             /* macro; substitute */
  478.             pch3 = strchr(pch2, ';');        /* semi-colon */
  479.             pch3 = strfchr(pch2, pch3, '\n');    /* line feed - oops! */
  480.             pch3 = strfchr(pch2, pch3, '\r');    /* carriage return - oops! */
  481.             pch3 = strfchr(pch2, pch3, '\t');    /* tab - oops! */
  482.             pch3 = strfchr(pch2, pch3, ' ');    /* space - oops! */
  483.             pch3 = strfchr(pch2, pch3, '\0');    /* end of input - oops! */
  484.  
  485.             /* copy the macro into szMacro and append the closing semi-colon */
  486.             strncpy(szMacro, pch2, pch3 - pch2);
  487.             szMacro[pch3 - pch2] = '\0';
  488.             strcat(szMacro, ";");
  489.  
  490.             /* substitute */
  491.             if (((*pch1) = HTMLParseMacro(szMacro)) == HTML_INVALID)
  492.                 (*pch1) = 255;
  493.             pch1++;
  494.             pch2 = pch3;
  495.         } else {
  496.             /* normal character; append to pszString and continue */
  497.             (*pch1) = (*pch2);
  498.             pch1++;
  499.             pch2++;
  500.         }
  501.     }
  502.     (*pch1) = '\0';
  503.  
  504.     return (pszString);
  505. }
  506.