home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Leser 15 / Amiga Plus Leser CD 15.iso / Tools / Development / MosaicSRC / src / HTMLparse.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-03-13  |  25.8 KB  |  1,367 lines

  1. // MDF - PORT FROM NCSA VERSION 2.1
  2.  
  3. /****************************************************************************
  4.  * NCSA Mosaic for the X Window System                        *
  5.  * Software Development Group                            *
  6.  * National Center for Supercomputing Applications                *
  7.  * University of Illinois at Urbana-Champaign                    *
  8.  * 605 E. Springfield, Champaign IL 61820                    *
  9.  * mosaic@ncsa.uiuc.edu                             *
  10.  *                                        *
  11.  * Copyright (C) 1993, Board of Trustees of the University of Illinois        *
  12.  *                                        *
  13.  * NCSA Mosaic software, both binary and source (hereafter, Software) is    *
  14.  * copyrighted by The Board of Trustees of the University of Illinois        *
  15.  * (UI), and ownership remains with the UI.                    *
  16.  *                                        *
  17.  * The UI grants you (hereafter, Licensee) a license to use the Software    *
  18.  * for academic, research and internal business purposes only, without a    *
  19.  * fee.  Licensee may distribute the binary and source code (if released)   *
  20.  * to third parties provided that the copyright notice and this statement   *
  21.  * appears on all copies and that no charge is associated with such        *
  22.  * copies.                                    *
  23.  *                                        *
  24.  * Licensee may make derivative works.    However, if Licensee distributes    *
  25.  * any derivative work based on or derived from the Software, then        *
  26.  * Licensee will (1) notify NCSA regarding its distribution of the        *
  27.  * derivative work, and (2) clearly notify users that such derivative        *
  28.  * work is a modified version and not the original NCSA Mosaic            *
  29.  * distributed by the UI.                            *
  30.  *                                        *
  31.  * Any Licensee wishing to make commercial use of the Software should        *
  32.  * contact the UI, c/o NCSA, to negotiate an appropriate license for such   *
  33.  * commercial use.  Commercial use includes (1) integration of all or        *
  34.  * part of the source code into a product for sale or license by or on        *
  35.  * behalf of Licensee to third parties, or (2) distribution of the binary   *
  36.  * code or source code to third parties that need it to utilize a        *
  37.  * commercial product sold or licensed by or on behalf of Licensee.        *
  38.  *                                        *
  39.  * UI MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR   *
  40.  * ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED          *
  41.  * WARRANTY.  THE UI SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY THE    *
  42.  * USERS OF THIS SOFTWARE.                            *
  43.  *                                        *
  44.  * By using or copying this Software, Licensee agrees to abide by the        *
  45.  * copyright law and all other applicable laws of the U.S. including, but   *
  46.  * not limited to, export control laws, and the terms of this license.        *
  47.  * UI shall have the right to terminate this license immediately by        *
  48.  * written notice upon Licensee's breach of, or non-compliance with, any    *
  49.  * of its terms.  Licensee may be held legally responsible for any        *
  50.  * copyright infringement that is caused or encouraged by Licensee's        *
  51.  * failure to abide by the terms of this license.                *
  52.  *                                        *
  53.  * Comments and questions are welcome and can be sent to            *
  54.  * mosaic-x@ncsa.uiuc.edu.                            *
  55.  ****************************************************************************/
  56.  
  57. #ifdef TIMING
  58. #include <sys/time.h>
  59. struct timeval Tv;
  60. struct timezone Tz;
  61. #endif
  62.  
  63. #include <stdio.h>
  64. #include <ctype.h>
  65. #ifndef sun
  66. /* To get atoi. */
  67. #include <stdlib.h>
  68. #endif
  69. #include "HTML.h"
  70. #include "HTMLamp.h"
  71.  
  72. #ifdef _AMIGA
  73. #include <string.h>
  74. #endif
  75.  
  76. extern void FreeObjList(struct mark_up *);
  77. extern struct mark_up *AddObj(struct mark_up **, struct mark_up *,
  78.                   struct mark_up *, int);
  79.  
  80.  
  81. #ifdef NOT_ASCII
  82. #define TOLOWER(x)    (tolower(x))
  83. #else
  84.  
  85. /*
  86.  * A hack to speed up caseless_equal.  Thanks to Quincey Koziol for
  87.  * developing it for me
  88.  */
  89. unsigned char map_table[256]={
  90.     0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,
  91.     24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,
  92.     45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,97,98,
  93.     99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,
  94.     116,117,118,119,120,121,122,91,92,93,94,95,96,97,98,99,100,101,102,
  95.     103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,
  96.     120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,
  97.     137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,
  98.     154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,
  99.     171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,
  100.     188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,
  101.     205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,
  102.     222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,
  103.     239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255};
  104.  
  105. #define TOLOWER(x)    (map_table[x])
  106. #endif /* NOT_ASCII */
  107.  
  108. /* ANSI declarations added by MDF */
  109.  
  110. int ParseMarkType(char *);
  111. // FreeObjList
  112. // AddObj
  113.  
  114.  
  115. /*
  116.  * Check if two strings are equal, ignoring case.
  117.  * The strings must be of the same length to be equal.
  118.  * return 1 if equal, 0 otherwise.
  119.  */
  120. int
  121. caseless_equal(char *str1, char *str2)
  122. {
  123.     if ((str1 == NULL)||(str2 == NULL))
  124.     {
  125.         return(0);
  126.     }
  127.  
  128.     while ((*str1 != '\0')&&(*str2 != '\0'))
  129.     {
  130.         if (TOLOWER(*str1) != TOLOWER(*str2))
  131.         {
  132.             return(0);
  133.         }
  134.         str1++;
  135.         str2++;
  136.     }
  137.  
  138.     if ((*str1 == '\0')&&(*str2 == '\0'))
  139.     {
  140.         return(1);
  141.     }
  142.     else
  143.     {
  144.         return(0);
  145.     }
  146. }
  147.  
  148.  
  149. /*
  150.  * Check if two strings are equal in the first count characters, ignoring case.
  151.  * The strings must both be at least of length count to be equal.
  152.  * return 1 if equal, 0 otherwise.
  153.  */
  154. int
  155. caseless_equal_prefix(char *str1, char *str2, int cnt)
  156. {
  157.     int i;
  158.  
  159.     if ((str1 == NULL)||(str2 == NULL))
  160.     {
  161.         return(0);
  162.     }
  163.  
  164.     if (cnt < 1)
  165.     {
  166.         return(1);
  167.     }
  168.  
  169.     for (i=0; i < cnt; i++)
  170.     {
  171.         if (TOLOWER(*str1) != TOLOWER(*str2))
  172.         {
  173.             return(0);
  174.         }
  175.         str1++;
  176.         str2++;
  177.     }
  178.  
  179.     return(1);
  180. }
  181.  
  182.  
  183. /*
  184.  * Clean up the white space in a string.
  185.  * Remove all leading and trailing whitespace, and turn all
  186.  * internal whitespace into single spaces separating words.
  187.  * The cleaning is done by rearranging the chars in the passed
  188.  * txt buffer.    The resultant string will probably be shorter,
  189.  * it can never get longer.
  190.  */
  191. void
  192. clean_white_space(char *txt)
  193. {
  194.     char *ptr;
  195.     char *start;
  196.  
  197.     start = txt;
  198.     ptr = txt;
  199.  
  200.     /*
  201.      * Remove leading white space
  202.      */
  203.     while (isspace((int)*ptr))
  204.     {
  205.         ptr++;
  206.     }
  207.  
  208.     /*
  209.      * find a word, copying if we removed some space already
  210.      */
  211.     if (start == ptr)
  212.     {
  213.         while ((!isspace((int)*ptr))&&(*ptr != '\0'))
  214.         {
  215.             ptr++;
  216.         }
  217.         start = ptr;
  218.     }
  219.     else
  220.     {
  221.         while ((!isspace((int)*ptr))&&(*ptr != '\0'))
  222.         {
  223.             *start++ = *ptr++;
  224.         }
  225.     }
  226.  
  227.     while (*ptr != '\0')
  228.     {
  229.         /*
  230.          * Remove trailing whitespace.
  231.          */
  232.         while (isspace((int)*ptr))
  233.         {
  234.             ptr++;
  235.         }
  236.         if (*ptr == '\0')
  237.         {
  238.             break;
  239.         }
  240.  
  241.         /*
  242.          * If there are more words, insert a space and if space was
  243.          * removed move up remaining text.
  244.          */
  245.         *start++ = ' ';
  246.         if (start == ptr)
  247.         {
  248.             while ((!isspace((int)*ptr))&&(*ptr != '\0'))
  249.             {
  250.                 ptr++;
  251.             }
  252.             start = ptr;
  253.         }
  254.         else
  255.         {
  256.             while ((!isspace((int)*ptr))&&(*ptr != '\0'))
  257.             {
  258.                 *start++ = *ptr++;
  259.             }
  260.         }
  261.     }
  262.  
  263.     *start = '\0';
  264. }
  265.  
  266.  
  267. /*
  268.  * parse an amperstand escape, and return the appropriate character, or
  269.  * '\0' on error.
  270.  * we should really only use caseless_equal_prefix for unterminated, and use
  271.  * caseless_equal otherwise, but since there are so many escapes, and I
  272.  * don't want to type everything twice, I always use caseless_equal_prefix
  273.  * Turns out the escapes are case sensitive, use strncmp.
  274.  */
  275. char
  276. ExpandEscapes(char *esc, char **endp, int unterminated)
  277. {
  278.     int cnt;
  279.     char val;
  280.  
  281.     esc++;
  282.     if (*esc == '#')
  283.     {
  284.         if (unterminated)
  285.         {
  286.             char *tptr;
  287.             char tchar;
  288.  
  289.             tptr = (char *)(esc + 1);
  290.             while (isdigit((int)*tptr))
  291.             {
  292.                 tptr++;
  293.             }
  294.             tchar = *tptr;
  295.             *tptr = '\0';
  296.             val = (char)atoi((esc + 1));
  297.             *tptr = tchar;
  298.             *endp = tptr;
  299.         }
  300.         else
  301.         {
  302.             val = (char)atoi((esc + 1));
  303.             *endp = (char *)(esc + strlen(esc));
  304.         }
  305.     }
  306.     else
  307.     {
  308.         cnt = 0;
  309.         while (AmpEscapes[cnt].tag != NULL)
  310.         {
  311.             if (strncmp(esc, AmpEscapes[cnt].tag,
  312.                 strlen(AmpEscapes[cnt].tag)) == 0)
  313.             {
  314.                 val = AmpEscapes[cnt].value;
  315.                 *endp = (char *)(esc +
  316.                     strlen(AmpEscapes[cnt].tag));
  317.                 break;
  318.             }
  319.             cnt++;
  320.         }
  321.         if (AmpEscapes[cnt].tag == NULL)
  322.         {
  323. #ifdef VERBOSE
  324.             fprintf(stderr, "Error bad & string\n");
  325. #endif
  326.             val = '\0';
  327.             *endp = (char *)NULL;
  328.         }
  329.     }
  330.  
  331.     return(val);
  332. }
  333.  
  334.  
  335. /*
  336.  * Clean the special HTML character escapes out of the text and replace
  337.  * them with the appropriate characters "<" = "<", ">" = ">",
  338.  * "&" = "&"
  339.  * GAG:  apperantly < etc. can be left unterminated, what a nightmare.
  340.  * the '&' character must be immediately followed by a letter to be
  341.  * a valid escape sequence.  Other &'s are left alone.
  342.  * The cleaning is done by rearranging chars in the passed txt buffer.
  343.  * if any escapes are replaced, the string becomes shorter.
  344.  */
  345. void
  346. clean_text(char *txt)
  347. {
  348.     int unterminated;
  349.     char *ptr;
  350.     char *ptr2;
  351. //    char *ptr3;
  352.     char *start;
  353.     char *text;
  354.     char *tend;
  355.     char tchar;
  356.     char val;
  357.  
  358.     if (txt == NULL)
  359.     {
  360.         return;
  361.     }
  362.  
  363.     /*
  364.      * Quick scan to find escape sequences.
  365.      * Escape is '&' followed by a letter (or a hash mark).
  366.      * return if there are none.
  367.      */
  368.     ptr = txt;
  369.     while (*ptr != '\0')
  370.     {
  371.         if ((*ptr == '&')&&
  372.             ((isalpha((int)*(ptr + 1)))||(*(ptr + 1) == '#')))
  373.         {
  374.             break;
  375.         }
  376.         ptr++;
  377.     }
  378.     if (*ptr == '\0')
  379.     {
  380.         return;
  381.     }
  382.  
  383.     /*
  384.      * Loop, replaceing escape sequences, and moving up remaining
  385.      * text.
  386.      */
  387.     ptr2 = ptr;
  388.     while (*ptr != '\0')
  389.     {
  390.  
  391.         unterminated = 0;
  392.         /*
  393.          * Extract the escape sequence from start to ptr
  394.          */
  395.         start = ptr;
  396.         while ((*ptr != ';')&&(*ptr != '\0'))
  397.         {
  398.             ptr++;
  399.         }
  400.         if (*ptr == '\0')
  401.         {
  402. #ifdef VERBOSE
  403.             fprintf(stderr, "warning:  unterminated & (%s)\n",
  404.                 start);
  405. #endif
  406.             unterminated = 1;
  407.         }
  408.  
  409.         /*
  410.          * Copy the escape sequence into a separate buffer.
  411.          * Then clean spaces so the "& lt ;" = "<" etc.
  412.          * The cleaning should be unnecessary.
  413.          */
  414.         tchar = *ptr;
  415.         *ptr = '\0';
  416.         text = (char *)malloc(strlen(start) + 1);
  417.         if (text == NULL)
  418.         {
  419.             fprintf(stderr, "Cannot malloc space for & text\n");
  420.             *ptr = tchar;
  421.             return;
  422.         }
  423.         strcpy(text, start);
  424.         *ptr = tchar;
  425.         clean_white_space(text);
  426.  
  427.         /*
  428.          * Replace escape sequence with appropriate character
  429.          */
  430.         val = ExpandEscapes(text, &tend, unterminated);
  431.         if (val != '\0')
  432.         {
  433.             if (unterminated)
  434.             {
  435.                 tchar = *tend;
  436.                 *tend = '\0';
  437.                 ptr = (char *)(start + strlen(text) - 1);
  438.                 *tend = tchar;
  439.             }
  440.             *ptr2 = val;
  441. //mjw            unterminated = 0;
  442.         }
  443.         /*
  444.          * invalid escape sequence. skip it.
  445.          */
  446.         else
  447.         {
  448. #ifdef VERBOSE
  449.             fprintf(stderr, "Error bad & string\n");
  450. #endif
  451.             ptr = start;
  452.             *ptr2 = *ptr;
  453.         }
  454.         free(text);
  455.  
  456.         /*
  457.          * Copy forward remaining text until you find the next
  458.          * escape sequence
  459.          */
  460.         ptr2++;
  461.         ptr++;
  462.         while (*ptr != '\0')
  463.         {
  464.             if ((*ptr == '&')&&
  465.                 ((isalpha((int)*(ptr + 1)))||(*(ptr + 1) == '#')))
  466.             {
  467.                 break;
  468.             }
  469.             *ptr2++ = *ptr++;
  470.         }
  471.     }
  472.     *ptr2 = '\0';
  473. }
  474.  
  475.  
  476. /*
  477.  * Get a block of text from a HTML document.
  478.  * All text from start to the end, or the first mark
  479.  * (a mark is '<' or '</' followed by any letter or a '!')
  480.  * is returned in a malloced buffer.  Also, endp returns
  481.  * a pointer to the next '<' or '\0'
  482.  * The returned text has already expanded '&' escapes.
  483.  */
  484. char *
  485. get_text(char *start, char **endp)
  486. {
  487.     char *ptr;
  488.     char *text;
  489.     char tchar;
  490.  
  491.     if (start == NULL)
  492.     {
  493.         return(NULL);
  494.     }
  495.  
  496.     /*
  497.      * Copy text up to beginning of a mark, or the end
  498.      */
  499.     ptr = start;
  500.     while (*ptr != '\0')
  501.     {
  502.         if (*ptr == '<')
  503.         {
  504.             if (isalpha((int)(*(ptr + 1))))
  505.             {
  506.                 break;
  507.             }
  508.             else if (*(ptr + 1) == '/')
  509.             {
  510.                 if (isalpha((int)(*(ptr + 2))))
  511.                 {
  512.                     break;
  513.                 }
  514.             }
  515.             else if (*(ptr + 1) == '!')  /* a comment */
  516.             {
  517.                 break;
  518.             }
  519.         }
  520.         ptr++;
  521.     }
  522.     *endp = ptr;
  523.  
  524.     if (ptr == start)
  525.     {
  526.         return(NULL);
  527.     }
  528.  
  529.     /*
  530.      * Copy the text into its own buffer, and clean it
  531.      * of escape sequences.
  532.      */
  533.     tchar = *ptr;
  534.     *ptr = '\0';
  535.     text = (char *)malloc(strlen(start) + 1);
  536.     if (text == NULL)
  537.     {
  538.         fprintf(stderr, "Cannot malloc space for text\n");
  539.         *ptr = tchar;
  540.         return(NULL);
  541.     }
  542.     strcpy(text, start);
  543.     *ptr = tchar;
  544.     clean_text(text);
  545.  
  546.     return(text);
  547. }
  548.  
  549.  
  550. /*
  551.  * Get the mark text between '<' and '>'.  From the text, determine
  552.  * its type, and fill in a mark_up structure to return.  Also returns
  553.  * endp pointing to the ttrailing '>' in the original string.
  554.  */
  555. struct mark_up *
  556. get_mark(char *start, char **endp)
  557. {
  558.     char *ptr;
  559.     char *text;
  560.     char tchar;
  561. //    int type;
  562.     struct mark_up *mark;
  563.  
  564.     if (start == NULL)
  565.     {
  566.         return(NULL);
  567.     }
  568.  
  569.     if (*start != '<')
  570.     {
  571.         return(NULL);
  572.     }
  573.  
  574.     start++;
  575.  
  576.     mark = (struct mark_up *)malloc(sizeof(struct mark_up));
  577.     if (mark == NULL)
  578.     {
  579.         fprintf(stderr, "Cannot malloc space for mark_up struct\n");
  580.         return(NULL);
  581.     }
  582.  
  583.     /*
  584.      * Grab the mark text
  585.      */
  586.     ptr = start;
  587.     while ((*ptr != '>')&&(*ptr != '\0'))
  588.     {
  589. #if 0
  590.         if (*ptr == '"') {
  591.             ptr += 1 ;
  592.             while (*ptr && *ptr != '"') {
  593.             if (*ptr == '\n' || *ptr == '\r') *ptr = ' ' ;
  594.             ptr += 1 ;
  595.             }
  596.             }
  597.         if (*ptr == '\'') {
  598.             ptr += 1 ;
  599.             while (*ptr && *ptr != '\'') {
  600.             if (*ptr == '\n' || *ptr == '\r') *ptr = ' ' ;
  601.             ptr += 1 ;
  602.             }
  603.             }
  604.         if (*ptr == '-' && ptr[1] == '-') { /* Comment */
  605.             ptr += 2 ;
  606.             for (;;) {
  607.             while (*ptr && *ptr != '-')
  608.                 ptr += 1 ;
  609.             if (!*ptr || ptr[1] == '-') break ;
  610.             }
  611.             }
  612. #endif
  613.         ptr++;
  614.     }
  615.     *endp = ptr;
  616.  
  617.     if (*ptr != '>')
  618.     {
  619. #ifdef VERBOSE
  620.         fprintf(stderr, "error: bad mark format\n");
  621. #endif
  622.         return(NULL);
  623.     }
  624.  
  625.     /*
  626.      * Copy the mark text to its own buffer, and
  627.      * clean it of escapes, and odd white space.
  628.      */
  629.     tchar = *ptr;
  630.     *ptr = '\0';
  631.     text = (char *)malloc(strlen(start) + 1);
  632.     if (text == NULL)
  633.     {
  634.         fprintf(stderr, "Cannot malloc space for mark\n");
  635.         *ptr = tchar;
  636.         return(NULL);
  637.     }
  638.     strcpy(text, start);
  639.     *ptr = tchar;
  640.     clean_text(text);
  641. /*
  642.  * No longer needed because the parsing code is now smarter
  643.  *
  644.     clean_white_space(text);
  645.  *
  646.  */
  647.  
  648.     /*
  649.      * Set whether this is the start or end of a mark
  650.      * block, as well as determining its type.
  651.      */
  652.     if (*text == '/')
  653.     {
  654.         mark->is_end = 1;
  655.         mark->type = ParseMarkType((char *)(text + 1));
  656.         mark->start = NULL;
  657.         mark->text = NULL;
  658.         mark->end = text;
  659.     }
  660.     else
  661.     {
  662.         mark->is_end = 0;
  663.         mark->type = ParseMarkType(text);
  664.         mark->start = text;
  665.         mark->text = NULL;
  666.         mark->end = NULL;
  667.     }
  668.     mark->text = NULL;
  669.     mark->next = NULL;
  670.  
  671.     return(mark);
  672. }
  673.  
  674.  
  675. /*
  676.  * Special version of get_text.  It reads all text up to the
  677.  * end of the plain text mark, or the end of the file.
  678.  */
  679. char *
  680. get_plain_text(char *start, char **endp)
  681. {
  682.     char *ptr;
  683.     char *text;
  684.     char tchar;
  685.  
  686.     if (start == NULL)
  687.     {
  688.         return(NULL);
  689.     }
  690.  
  691.     /*
  692.      * Read until stopped by end plain text mark.
  693.      */
  694.     ptr = start;
  695.     while (*ptr != '\0')
  696.     {
  697.         /*
  698.          * Beginning of a mark is '<' followed by any letter,
  699.          * or followed by '!' for a comment,
  700.          * or '</' followed by any letter.
  701.          */
  702.         if ((*ptr == '<')&&
  703.             ((isalpha((int)(*(ptr + 1))))||
  704.             (*(ptr + 1) == '!')||
  705.             ((*(ptr + 1) == '/')&&(isalpha((int)(*(ptr + 2)))))))
  706.         {
  707.             struct mark_up *mp;
  708.             char *ep;
  709.  
  710.             /*
  711.              * We think we found a mark.  If it is the
  712.              * end of plain text, break out
  713.              */
  714.             mp = get_mark(ptr, &ep);
  715.             if (mp != NULL)
  716.             {
  717.                 if (((mp->type == M_PLAIN_TEXT)||
  718.                     (mp->type == M_LISTING_TEXT))&&(mp->is_end))
  719.                 {
  720.                     if (mp->end != NULL)
  721.                     {
  722.                         free((char *)mp->end);
  723.                     }
  724.                     free((char *)mp);
  725.                     break;
  726.                 }
  727.                 if (mp->start != NULL)
  728.                 {
  729.                     free((char *)mp->start);
  730.                 }
  731.                 if (mp->end != NULL)
  732.                 {
  733.                     free((char *)mp->end);
  734.                 }
  735.                 free((char *)mp);
  736.             }
  737.         }
  738.         ptr++;
  739.     }
  740.     *endp = ptr;
  741.  
  742.     if (ptr == start)
  743.     {
  744.         return(NULL);
  745.     }
  746.  
  747.     /*
  748.      * Copy text to its own malloced buffer, and clean it of
  749.      * HTML escapes.
  750.      */
  751.     tchar = *ptr;
  752.     *ptr = '\0';
  753.     text = (char *)malloc(strlen(start) + 1);
  754.     if (text == NULL)
  755.     {
  756.         fprintf(stderr, "Cannot malloc space for text\n");
  757.         *ptr = tchar;
  758.         return(NULL);
  759.     }
  760.     strcpy(text, start);
  761.     *ptr = tchar;
  762.     clean_text(text);
  763.  
  764.     return(text);
  765. }
  766.  
  767.  
  768. /*
  769.  * Main parser of HTML text.  Takes raw text, and produces a linked
  770.  * list of mark objects.  Mark objects are either text strings, or
  771.  * starting and ending mark delimiters.
  772.  * The old list is passed in so it can be freed, and in the future we
  773.  * may want to add code to append to the old list.
  774.  */
  775. struct mark_up *
  776. HTMLParse(struct mark_up *old_list, char *str)
  777. {
  778.     int preformat;
  779.     char *start, *end;
  780.     char *text, *tptr;
  781.     struct mark_up *mark;
  782.     struct mark_up *list;
  783.     struct mark_up *current;
  784. #ifdef TIMING
  785. gettimeofday(&Tv, &Tz);
  786. fprintf(stderr, "HTMLParse enter (%d.%d)\n", Tv.tv_sec, Tv.tv_usec);
  787. #endif
  788.  
  789.     preformat = 0;
  790.  
  791.     /*
  792.      * Free up the previous Object List if one exists
  793.      */
  794.     FreeObjList(old_list);
  795.  
  796.     if (str == NULL)
  797.     {
  798.         return(NULL);
  799.     }
  800.  
  801.     list = NULL;
  802.     current = NULL;
  803.  
  804.     start = str;
  805.     end = str;
  806.  
  807.     mark = NULL;
  808.     while (*start != '\0')
  809.     {
  810.         /*
  811.          * Get some text (if any).  If our last mark was
  812.          * a begin plain text we call different function
  813.          * If last mark was <PLAINTEXT> we lump all the rest of
  814.          * the text in.
  815.          */
  816.         if ((mark != NULL)&&(mark->type == M_PLAIN_FILE)&&
  817.             (!mark->is_end))
  818.         {
  819.             text = start;
  820.             end = text;
  821.             while (*end != '\0')
  822.             {
  823.                 end++;
  824.             }
  825.             /*
  826.              * Copy text to its own malloced buffer, and clean it of
  827.              * HTML escapes.
  828.              */
  829.             tptr = (char *)malloc(strlen(text) + 1);
  830.             if (tptr == NULL)
  831.             {
  832.                 fprintf(stderr,
  833.                     "Cannot malloc space for text\n");
  834.                 return(list);
  835.             }
  836.             strcpy(tptr, text);
  837.             text = tptr;
  838.         }
  839.         else if ((mark != NULL)&&
  840.              ((mark->type == M_PLAIN_TEXT)||
  841.               (mark->type == M_LISTING_TEXT))&&
  842.              (!mark->is_end))
  843.         {
  844.             text = get_plain_text(start, &end);
  845.         }
  846.         else
  847.         {
  848.             text = get_text(start, &end);
  849.         }
  850.  
  851.         /*
  852.          * If text is OK, put it into a mark structure, and add
  853.          * it to the linked list.
  854.          */
  855.         if (text == NULL)
  856.         {
  857.             if (start != end)
  858.             {
  859.                 fprintf(stderr, "error parsing text, bailing out\n");
  860.                 return(list);
  861.             }
  862.         }
  863.         else
  864.         {
  865.             mark = (struct mark_up *)malloc(sizeof(struct mark_up));
  866.             if (mark == NULL)
  867.             {
  868.                 fprintf(stderr, "Cannot malloc for mark_up struct\n");
  869.                 return(list);
  870.             }
  871.             mark->type = M_NONE;
  872.             mark->is_end = 0;
  873.             mark->start = NULL;
  874.             mark->text = text;
  875.             mark->end = NULL;
  876.             mark->next = NULL;
  877.             current = AddObj(&list, current, mark, preformat);
  878.         }
  879.         start = end;
  880.  
  881.         if (*start == '\0')
  882.         {
  883.             break;
  884.         }
  885.  
  886.         /*
  887.          * Get the next mark if any, and if it is
  888.          * valid, add it to the linked list.
  889.          */
  890.         mark = get_mark(start, &end);
  891.         if (mark == NULL)
  892.         {
  893.             if (start != end)
  894.             {
  895.                 fprintf(stderr, "error parsing mark, bailing out\n");
  896.                 return(list);
  897.             }
  898.         }
  899.         else
  900.         {
  901.             mark->next = NULL;
  902.             current = AddObj(&list, current, mark, preformat);
  903.         }
  904.  
  905.         start = (char *)(end + 1);
  906.  
  907.         if ((mark != NULL)&&(mark->type == M_PLAIN_FILE)&&
  908.             (!mark->is_end))
  909.         {
  910.             /*
  911.              * A linefeed immediately after the <PLAINTEXT>
  912.              * mark is to be ignored.
  913.              */
  914.             if (*start == '\n')
  915.             {
  916.                 start++;
  917.             }
  918.         }
  919.         else if ((mark != NULL)&&((mark->type == M_PLAIN_TEXT)||
  920.             (mark->type == M_LISTING_TEXT))&&
  921.             (!mark->is_end))
  922.         {
  923.             /*
  924.              * A linefeed immediately after the <XMP>
  925.              * or <LISTING> mark is to be ignored.
  926.              */
  927.             if (*start == '\n')
  928.             {
  929.                 start++;
  930.             }
  931.         }
  932.         /*
  933.          * If we are parsing pre-formatted text we need to set a
  934.          * flag so we don't throw out needed linefeeds.
  935.          */
  936.         else if ((mark != NULL)&&(mark->type == M_PREFORMAT))
  937.         {
  938.             if (mark->is_end)
  939.             {
  940.                 preformat = 0;
  941.             }
  942.             else
  943.             {
  944.                 preformat = 1;
  945.                 /*
  946.                  * A linefeed immediately after the <PRE>
  947.                  * mark is to be ignored.
  948.                  */
  949.                 if (*start == '\n')
  950.                 {
  951.                     start++;
  952.                 }
  953.             }
  954.         }
  955.     }
  956. #ifdef TIMING
  957. gettimeofday(&Tv, &Tz);
  958. fprintf(stderr, "HTMLParse exit (%d.%d)\n", Tv.tv_sec, Tv.tv_usec);
  959. #endif
  960.     return(list);
  961. }
  962.  
  963.  
  964. /*
  965.  * Determine mark type from the identifying string passed
  966.  */
  967. int
  968. ParseMarkType(char *str)
  969. {
  970.     int type;
  971.     char *tptr;
  972.     char tchar;
  973.  
  974.     if (str == NULL)
  975.     {
  976.         return(M_NONE);
  977.     }
  978.  
  979. //mjw    type = M_UNKNOWN;
  980.     tptr = str;
  981.     while (*tptr != '\0')
  982.     {
  983.         if (isspace((int)*tptr))
  984.         {
  985.             break;
  986.         }
  987.         tptr++;
  988.     }
  989.     tchar = *tptr;
  990.     *tptr = '\0';
  991.  
  992.     if (caseless_equal(str, MT_ANCHOR))
  993.     {
  994.         type = M_ANCHOR;
  995.     }
  996.     else if (caseless_equal(str, MT_TITLE))
  997.     {
  998.         type = M_TITLE;
  999.     }
  1000.     else if (caseless_equal(str, MT_FIXED))
  1001.     {
  1002.         type = M_FIXED;
  1003.     }
  1004.     else if (caseless_equal(str, MT_BOLD))
  1005.     {
  1006.         type = M_BOLD;
  1007.     }
  1008.     else if (caseless_equal(str, MT_ITALIC))
  1009.     {
  1010.         type = M_ITALIC;
  1011.     }
  1012.     else if (caseless_equal(str, MT_EMPHASIZED))
  1013.     {
  1014.         type = M_EMPHASIZED;
  1015.     }
  1016.     else if (caseless_equal(str, MT_STRONG))
  1017.     {
  1018.         type = M_STRONG;
  1019.     }
  1020.     else if (caseless_equal(str, MT_CODE))
  1021.     {
  1022.         type = M_CODE;
  1023.     }
  1024.     else if (caseless_equal(str, MT_SAMPLE))
  1025.     {
  1026.         type = M_SAMPLE;
  1027.     }
  1028.     else if (caseless_equal(str, MT_KEYBOARD))
  1029.     {
  1030.         type = M_KEYBOARD;
  1031.     }
  1032.     else if (caseless_equal(str, MT_VARIABLE))
  1033.     {
  1034.         type = M_VARIABLE;
  1035.     }
  1036.     else if (caseless_equal(str, MT_CITATION))
  1037.     {
  1038.         type = M_CITATION;
  1039.     }
  1040.     else if (caseless_equal(str, MT_STRIKEOUT))
  1041.     {
  1042.         type = M_STRIKEOUT;
  1043.     }
  1044.     else if (caseless_equal(str, MT_HEADER_1))
  1045.     {
  1046.         type = M_HEADER_1;
  1047.     }
  1048.     else if (caseless_equal(str, MT_HEADER_2))
  1049.     {
  1050.         type = M_HEADER_2;
  1051.     }
  1052.     else if (caseless_equal(str, MT_HEADER_3))
  1053.     {
  1054.         type = M_HEADER_3;
  1055.     }
  1056.     else if (caseless_equal(str, MT_HEADER_4))
  1057.     {
  1058.         type = M_HEADER_4;
  1059.     }
  1060.     else if (caseless_equal(str, MT_HEADER_5))
  1061.     {
  1062.         type = M_HEADER_5;
  1063.     }
  1064.     else if (caseless_equal(str, MT_HEADER_6))
  1065.     {
  1066.         type = M_HEADER_6;
  1067.     }
  1068.     else if (caseless_equal(str, MT_ADDRESS))
  1069.     {
  1070.         type = M_ADDRESS;
  1071.     }
  1072.     else if (caseless_equal(str, MT_PLAIN_TEXT))
  1073.     {
  1074.         type = M_PLAIN_TEXT;
  1075.     }
  1076.     else if (caseless_equal(str, MT_LISTING_TEXT))
  1077.     {
  1078.         type = M_LISTING_TEXT;
  1079.     }
  1080.     else if (caseless_equal(str, MT_PLAIN_FILE))
  1081.     {
  1082.         type = M_PLAIN_FILE;
  1083.     }
  1084.     else if (caseless_equal(str, MT_PARAGRAPH))
  1085.     {
  1086.         type = M_PARAGRAPH;
  1087.     }
  1088.     else if (caseless_equal(str, MT_UNUM_LIST))
  1089.     {
  1090.         type = M_UNUM_LIST;
  1091.     }
  1092.     else if (caseless_equal(str, MT_NUM_LIST))
  1093.     {
  1094.         type = M_NUM_LIST;
  1095.     }
  1096.     else if (caseless_equal(str, MT_MENU))
  1097.     {
  1098.         type = M_MENU;
  1099.     }
  1100.     else if (caseless_equal(str, MT_DIRECTORY))
  1101.     {
  1102.         type = M_DIRECTORY;
  1103.     }
  1104.     else if (caseless_equal(str, MT_LIST_ITEM))
  1105.     {
  1106.         type = M_LIST_ITEM;
  1107.     }
  1108.     else if (caseless_equal(str, MT_DESC_LIST))
  1109.     {
  1110.         type = M_DESC_LIST;
  1111.     }
  1112.     else if (caseless_equal(str, MT_DESC_TITLE))
  1113.     {
  1114.         type = M_DESC_TITLE;
  1115.     }
  1116.     else if (caseless_equal(str, MT_DESC_TEXT))
  1117.     {
  1118.         type = M_DESC_TEXT;
  1119.     }
  1120.     else if (caseless_equal(str, MT_PREFORMAT))
  1121.     {
  1122.         type = M_PREFORMAT;
  1123.     }
  1124.     else if (caseless_equal(str, MT_BLOCKQUOTE))
  1125.     {
  1126.         type = M_BLOCKQUOTE;
  1127.     }
  1128.     else if (caseless_equal(str, MT_INDEX))
  1129.     {
  1130.         type = M_INDEX;
  1131.     }
  1132.     else if (caseless_equal(str, MT_HRULE))
  1133.     {
  1134.         type = M_HRULE;
  1135.     }
  1136.     else if (caseless_equal(str, MT_BASE))
  1137.     {
  1138.         type = M_BASE;
  1139.     }
  1140.     else if (caseless_equal(str, MT_LINEBREAK))
  1141.     {
  1142.         type = M_LINEBREAK;
  1143.     }
  1144.     else if (caseless_equal(str, MT_IMAGE))
  1145.     {
  1146.         type = M_IMAGE;
  1147.     }
  1148.     else if (caseless_equal(str, MT_SELECT))
  1149.     {
  1150.         type = M_SELECT;
  1151.     }
  1152.     else if (caseless_equal(str, MT_OPTION))
  1153.     {
  1154.         type = M_OPTION;
  1155.     }
  1156.     else if (caseless_equal(str, MT_INPUT))
  1157.     {
  1158.         type = M_INPUT;
  1159.     }
  1160.     else if (caseless_equal(str, MT_TEXTAREA))
  1161.     {
  1162.         type = M_TEXTAREA;
  1163.     }
  1164.     else if (caseless_equal(str, MT_FORM))
  1165.     {
  1166.         type = M_FORM;
  1167.     }
  1168.     else
  1169.     {
  1170. #ifdef VERBOSE
  1171.         fprintf(stderr, "warning: unknown mark (%s)\n", str);
  1172. #endif
  1173.         type = M_UNKNOWN;
  1174.     }
  1175.  
  1176.     *tptr = tchar;
  1177.     return(type);
  1178. }
  1179.  
  1180.  
  1181. /*
  1182.  * Parse a single anchor tag.  ptrp is a pointer to a pointer to the
  1183.  * string to be parsed.  On return, the ptr should be changed to
  1184.  * point to after the text we have parsed.
  1185.  * On return start and end should point to the beginning, and just
  1186.  * after the end of the tag's name in the original anchor string.
  1187.  * Finally the function returns the tag value in a malloced buffer.
  1188.  */
  1189. char *
  1190. AnchorTag(char **ptrp, char **startp, char **endp)
  1191. {
  1192.     char *tag_val;
  1193.     char *ptr;
  1194.     char *start;
  1195.     char tchar;
  1196.     int quoted;
  1197.     int has_value;
  1198.  
  1199.     quoted = 0;
  1200.  
  1201.     /*
  1202.      * remove leading spaces, and set start
  1203.      */
  1204.     ptr = *ptrp;
  1205.     while (isspace((int)*ptr))
  1206.     {
  1207.         ptr++;
  1208.     }
  1209.     *startp = ptr;
  1210.  
  1211.     /*
  1212.      * Find and set the end of the tag
  1213.      */
  1214.     while ((!isspace((int)*ptr))&&(*ptr != '=')&&(*ptr != '\0'))
  1215.     {
  1216.         ptr++;
  1217.     }
  1218.     *endp = ptr;
  1219.  
  1220.     if (*ptr == '\0')
  1221.     {
  1222.         *ptrp = ptr;
  1223.         return(NULL);
  1224.     }
  1225.  
  1226.     /*
  1227.      * Move to the start of the tag value, if there is one.
  1228.      * set the has_value flag.
  1229.      */
  1230.     has_value = 0;
  1231.     while ((isspace((int)*ptr))||(*ptr == '='))
  1232.     {
  1233.         if (*ptr == '=')
  1234.         {
  1235.             has_value = 1;
  1236.         }
  1237.         ptr++;
  1238.     }
  1239.  
  1240.     /*
  1241.      * For a tag with no value, this is a boolean flag.
  1242.      * Return the string "1" so we know the tag is there.
  1243.      */
  1244.     if (!has_value)
  1245.     {
  1246.         *ptrp = *endp;
  1247.         /*
  1248.          * set a tag value of 1.
  1249.          */
  1250.         tag_val = (char *)malloc(strlen("1") + 1);
  1251.         if (tag_val == NULL)
  1252.         {
  1253.             fprintf(stderr, "can't malloc space for tag value\n");
  1254.             return(NULL);
  1255.         }
  1256.         strcpy(tag_val, "1");
  1257.  
  1258.         return(tag_val);
  1259.     }
  1260.  
  1261.     if (*ptr == '\"')
  1262.     {
  1263.         quoted = 1;
  1264.         ptr++;
  1265.     }
  1266.  
  1267.     start = ptr;
  1268.     /*
  1269.      * Get tag value.  Either a quoted string or a single word
  1270.      */
  1271.     if (quoted)
  1272.     {
  1273.         while ((*ptr != '\"')&&(*ptr != '\0'))
  1274.         {
  1275.             ptr++;
  1276.         }
  1277.     }
  1278.     else
  1279.     {
  1280.         while ((!isspace((int)*ptr))&&(*ptr != '\0'))
  1281.         {
  1282.             ptr++;
  1283.         }
  1284.     }
  1285.     if ((quoted)&&(*ptr == '\0'))
  1286.     {
  1287.         *ptrp = ptr;
  1288.         return(NULL);
  1289.     }
  1290.  
  1291.     /*
  1292.      * Copy the tag value out into a malloced string
  1293.      */
  1294.     tchar = *ptr;
  1295.     *ptr = '\0';
  1296.     tag_val = (char *)malloc(strlen(start) + 1);
  1297.     if (tag_val == NULL)
  1298.     {
  1299.         fprintf(stderr, "can't malloc space for tag value\n");
  1300.         *ptr = tchar;
  1301.         *ptrp = ptr;
  1302.         return(NULL);
  1303.     }
  1304.     strcpy(tag_val, start);
  1305.     *ptr = tchar;
  1306.     if (quoted)
  1307.     {
  1308.         ptr++;
  1309.     }
  1310.     *ptrp = ptr;
  1311.  
  1312.     return(tag_val);
  1313. }
  1314.  
  1315.  
  1316. /*
  1317.  * Parse mark text for the value associated with the
  1318.  * passed mark tag.
  1319.  * If the passed tag is not found, return NULL.
  1320.  * If the passed tag is found but has no value, return "".
  1321.  */
  1322. char *
  1323. ParseMarkTag(char *text, char *mtext, char *mtag)
  1324. {
  1325.     char *ptr;
  1326.     char *start;
  1327.     char *end;
  1328.     char *tag_val;
  1329.     char tchar;
  1330.  
  1331.     if ((text == NULL)||(mtext == NULL)||(mtag == NULL))
  1332.     {
  1333.         return(NULL);
  1334.     }
  1335.  
  1336.     ptr = (char *)(text + strlen(mtext));
  1337.  
  1338.     while (*ptr != '\0')
  1339.     {
  1340.         tag_val = AnchorTag(&ptr, &start, &end);
  1341.  
  1342.         tchar = *end;
  1343.         *end = '\0';
  1344.         if (caseless_equal(start, mtag))
  1345.         {
  1346.             *end = tchar;
  1347.             if (tag_val == NULL)
  1348.             {
  1349.                 tag_val = (char *)malloc(1);
  1350.                 *tag_val = '\0';
  1351.                 return(tag_val);
  1352.             }
  1353.             else
  1354.             {
  1355.                 return(tag_val);
  1356.             }
  1357.         }
  1358.         *end = tchar;
  1359.         if (tag_val != NULL)
  1360.         {
  1361.             free(tag_val);
  1362.         }
  1363.     }
  1364.     return(NULL);
  1365. }
  1366.  
  1367.