home *** CD-ROM | disk | FTP | other *** search
/ Collection of Internet / Collection of Internet.iso / msdos / lynx / source / www / library / implemen / sgml.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-25  |  17.6 KB  |  703 lines

  1. /*            General SGML Parser code        SGML.c
  2. **            ========================
  3. **
  4. **    This module implements an HTStream object. To parse an
  5. **    SGML file, create this object which is a parser. The object
  6. **    is (currently) created by being passed a DTD structure,
  7. **    and a target HTStructured oject at which to throw the parsed stuff.
  8. **    
  9. **     6 Feb 93  Binary seraches used. Intreface modified.
  10. */
  11. #include"capalloc.h"
  12. #include "SGML.h"
  13. #include"capstdio.h"
  14.  
  15. #include <ctype.h>
  16. #include <stdio.h>
  17. #include "HTUtils.h"
  18. #include "HTChunk.h"
  19. #include "tcp.h"        /* For FROMASCII */
  20.  
  21. #define INVALID (-1)
  22.  
  23. /*    The State (context) of the parser
  24. **
  25. **    This is passed with each call to make the parser reentrant
  26. **
  27. */
  28.  
  29. #define MAX_ATTRIBUTES 20    /* Max number of attributes per element */
  30.  
  31.     
  32. /*        Element Stack
  33. **        -------------
  34. **    This allows us to return down the stack reselcting styles.
  35. **    As we return, attribute values will be garbage in general.
  36. */
  37. typedef struct _HTElement HTElement;
  38. struct _HTElement {
  39.     HTElement *    next;    /* Previously nested element or 0 */
  40.     HTTag*        tag;    /* The tag at this level  */
  41. };
  42.  
  43.  
  44. /*    Internal Context Data Structure
  45. **    -------------------------------
  46. */
  47. struct _HTStream {
  48.  
  49.     CONST HTStreamClass *    isa;        /* inherited from HTStream */
  50.     
  51.     CONST SGML_dtd         *dtd;
  52.     HTStructuredClass    *actions;    /* target class  */
  53.     HTStructured    *target;    /* target object */
  54.  
  55.     HTTag         *current_tag;
  56.     int         current_attribute_number;
  57.     HTChunk        *string;
  58.     HTElement        *element_stack;
  59.     enum sgml_state { S_text, S_litteral, S_tag, S_tag_gap, 
  60.         S_attr, S_attr_gap, S_equals, S_value,
  61.         S_ero, S_cro,
  62.           S_squoted, S_dquoted, S_end, S_entity, S_junk_tag} state;
  63. #ifdef CALLERDATA          
  64.     void *        callerData;
  65. #endif
  66.     BOOL present[MAX_ATTRIBUTES];    /* Flags: attribute is present? */
  67.     char * value[MAX_ATTRIBUTES];    /* malloc'd strings or NULL if none */
  68. } ;
  69.  
  70.  
  71. #define PUTC(ch) ((*context->actions->put_character)(context->target, ch))
  72.  
  73.  
  74.  
  75. /*    Handle Attribute
  76. **    ----------------
  77. */
  78. /* PUBLIC CONST char * SGML_default = "";   ?? */
  79.  
  80. #ifdef __STDC__
  81. PRIVATE void handle_attribute_name(HTStream * context, const char * s)
  82. #else
  83. PRIVATE void handle_attribute_name(context, s)
  84.     HTStream * context;
  85.     char *s;
  86. #endif
  87. {
  88.  
  89.     HTTag * tag = context->current_tag;
  90.     attr* attributes = tag->attributes;
  91.  
  92.     int high, low, i, diff;        /* Binary search for attribute name */
  93.     for(low=0, high=tag->number_of_attributes;
  94.         high > low ;
  95.         diff < 0 ? (low = i+1) : (high = i) )  {
  96.     i = (low + (high-low)/2);
  97.     diff = strcasecomp(attributes[i].name, s);
  98.     if (diff==0) {            /* success: found it */
  99.         context->current_attribute_number = i;
  100.         context->present[i] = YES;
  101.         if (context->value[i]) {
  102.         free(context->value[i]);
  103.         context->value[i] = NULL;
  104.         }
  105.         return;
  106.     } /* if */
  107.  
  108.     } /* for */
  109.  
  110. #ifndef RELEASE
  111.     if (TRACE)
  112.     fprintf(stderr, "SGML: Unknown attribute %s for tag %s\n",
  113.         s, context->current_tag->name);
  114. #endif /* RELEAE */
  115.     context->current_attribute_number = INVALID;    /* Invalid */
  116. }
  117.  
  118.  
  119. /*    Handle attribute value
  120. **    ----------------------
  121. */
  122. #ifdef __STDC__
  123. PRIVATE void handle_attribute_value(HTStream * context, const char * s)
  124. #else
  125. PRIVATE void handle_attribute_value(context, s)
  126.     HTStream * context;
  127.     char *s;
  128. #endif
  129. {
  130.     if (context->current_attribute_number != INVALID) {
  131.     StrAllocCopy(context->value[context->current_attribute_number], s);
  132.     } else {
  133. #ifndef RELEASE
  134.     if (TRACE) fprintf(stderr, "SGML: Attribute value %s ignored\n", s);
  135. #endif /* RELEASE */
  136.     }
  137.     context->current_attribute_number = INVALID; /* can't have two assignments! */
  138. }
  139.  
  140.  
  141. /*    Handle entity
  142. **    -------------
  143. **
  144. ** On entry,
  145. **    s    contains the entity name zero terminated
  146. ** Bugs:
  147. **    If the entity name is unknown, the terminator is treated as
  148. **    a printable non-special character in all cases, even if it is '<'
  149. */
  150. #ifdef __STDC__
  151. PRIVATE void handle_entity(HTStream * context, char term)
  152. #else
  153. PRIVATE void handle_entity(context, term)
  154.     HTStream * context;
  155.     char term;
  156. #endif
  157. {
  158.  
  159.     CONST char ** entities = context->dtd->entity_names;
  160.     CONST char *s = context->string->data;
  161.  
  162.     int high, low, i, diff;
  163.     for(low=0, high = context->dtd->number_of_entities;
  164.         high > low ;
  165.         diff < 0 ? (low = i+1) : (high = i))   {  /* Binary serach */
  166.     i = (low + (high-low)/2);
  167.     diff = strcmp(entities[i], s);    /* Csse sensitive! */
  168.     if (diff==0) {            /* success: found it */
  169.         (*context->actions->put_entity)(context->target, i);
  170.         return;
  171.     }
  172.     }
  173.     /* If entity string not found, display as text */
  174. #ifndef RELEASE
  175.     if (TRACE)
  176.     fprintf(stderr, "SGML: Unknown entity %s\n", s);
  177. #endif /* RELEASE */
  178.     PUTC('&');
  179.     {
  180.     CONST char *p;
  181.     for (p=s; *p; p++) {
  182.         PUTC(*p);
  183.     }
  184.     }
  185.     PUTC(term);
  186. }
  187.  
  188.  
  189. /*    End element
  190. **    -----------
  191. */
  192. #ifdef __STDC__
  193. PRIVATE void end_element(HTStream * context, HTTag * old_tag)
  194. #else
  195. PRIVATE void end_element(context, old_tag)
  196.     HTTag * old_tag;
  197.     HTStream * context;
  198. #endif
  199. {
  200. #ifndef RELEASE
  201.     if (TRACE) fprintf(stderr, "SGML: End   </%s>\n", old_tag->name);
  202. #endif /* RELEASE */
  203.     if (old_tag->contents == SGML_EMPTY) {
  204. #ifndef RELEASE
  205.     if (TRACE) fprintf(stderr,"SGML: Illegal end tag </%s> found.\n",
  206.         old_tag->name);
  207. #endif /* RELEASE */
  208.     return;
  209.     }
  210.     while (context->element_stack)     {/* Loop is error path only */
  211.     HTElement * N = context->element_stack;
  212.     HTTag * t = N->tag;
  213.     
  214.     if (old_tag != t) {        /* Mismatch: syntax error */
  215.         if (context->element_stack->next) {    /* This is not the last level */
  216. #ifndef RELEASE
  217.         if (TRACE) fprintf(stderr,
  218.         "SGML: Found </%s> when expecting </%s>. </%s> assumed.\n",
  219.             old_tag->name, t->name, t->name);
  220. #endif /* RELEASE */
  221.         } else {            /* last level */
  222. #ifndef RELEASE
  223.         if (TRACE) fprintf(stderr,
  224.             "SGML: Found </%s> when expecting </%s>. </%s> Ignored.\n",
  225.             old_tag->name, t->name, old_tag->name);
  226. #endif /* RELEASE */
  227.             return;            /* Ignore */
  228.         }
  229.     }
  230.     
  231.     context->element_stack = N->next;        /* Remove from stack */
  232.     free(N);
  233.     (*context->actions->end_element)(context->target,
  234.          t - context->dtd->tags);
  235.     if (old_tag == t) return;  /* Correct sequence */
  236.     
  237.     /* Syntax error path only */
  238.     
  239.     }
  240. #ifndef RELEASE
  241.     if (TRACE) fprintf(stderr,
  242.     "SGML: Extra end tag </%s> found and ignored.\n", old_tag->name);
  243. #endif /* RELEASE */
  244. }
  245.  
  246.  
  247. /*    Start a element
  248. */
  249. #ifdef __STDC__
  250. PRIVATE void start_element(HTStream * context)
  251. #else
  252. PRIVATE void start_element(context)
  253.     HTStream * context;
  254. #endif
  255. {
  256.     HTTag * new_tag = context->current_tag;
  257.  
  258. #ifndef RELEASE
  259.     if (TRACE) fprintf(stderr, "SGML: Start <%s>\n", new_tag->name);
  260. #endif /* RELEASE */
  261. #ifdef MSDOSMEM
  262.     {
  263.         extern void *vp_msdosmem;
  264.         extern void **vpp_msdosmem;
  265.         vp_msdosmem = (void *)(context->present);
  266.         vpp_msdosmem = (void **)(context->value);
  267.     }
  268. #endif /* MSDOSMEM */
  269.     (*context->actions->start_element)(
  270.         context->target,
  271.     new_tag - context->dtd->tags,
  272.     context->present,
  273.     (CONST char**) context->value);  /* coerce type for think c */
  274.     if (new_tag->contents != SGML_EMPTY) {        /* i.e. tag not empty */
  275.     HTElement * N = (HTElement *)malloc(sizeof(HTElement));
  276.         if (N == NULL) outofmem(__FILE__, "start_element");
  277.     N->next = context->element_stack;
  278.     N->tag = new_tag;
  279.     context->element_stack = N;
  280.     }
  281. }
  282.  
  283.  
  284. /*        Find Tag in DTD tag list
  285. **        ------------------------
  286. **
  287. ** On entry,
  288. **    dtd    points to dtd structire including valid tag list
  289. **    string    points to name of tag in question
  290. **
  291. ** On exit,
  292. **    returns:
  293. **        NULL        tag not found
  294. **        else        address of tag structure in dtd
  295. */
  296. PUBLIC HTTag * SGMLFindTag ARGS2(CONST SGML_dtd*, dtd, CONST char *, string)
  297. {
  298.     int high, low, i, diff;
  299.     for(low=0, high=dtd->number_of_tags;
  300.             high > low ;
  301.         diff < 0 ? (low = i+1) : (high = i))   {  /* Binary serach */
  302.     i = (low + (high-low)/2);
  303.     diff = strcasecomp(dtd->tags[i].name, string);    /* Case insensitive */
  304.     if (diff==0) {            /* success: found it */
  305.         return &dtd->tags[i];
  306.     }
  307.     }
  308.     return NULL;
  309. }
  310.  
  311. /*________________________________________________________________________
  312. **            Public Methods
  313. */
  314.  
  315.  
  316. /*    Could check that we are back to bottom of stack! @@  */
  317.  
  318. PUBLIC void SGML_free  ARGS1(HTStream *, context)
  319. {
  320.     (*context->actions->free)(context->target);
  321.     HTChunkFree(context->string);
  322.     free(context);
  323. }
  324.  
  325. PUBLIC void SGML_abort  ARGS2(HTStream *, context, HTError, e)
  326. {
  327.     (*context->actions->abort)(context->target, e);
  328.     HTChunkFree(context->string);
  329.     free(context);
  330. }
  331.  
  332.  
  333. /*    Read and write user callback handle
  334. **    -----------------------------------
  335. **
  336. **   The callbacks from the SGML parser have an SGML context parameter.
  337. **   These calls allow the caller to associate his own context with a
  338. **   particular SGML context.
  339. */
  340.  
  341. #ifdef CALLERDATA          
  342. PUBLIC void* SGML_callerData ARGS1(HTStream *, context)
  343. {
  344.     return context->callerData;
  345. }
  346.  
  347. PUBLIC void SGML_setCallerData ARGS2(HTStream *, context, void*, data)
  348. {
  349.     context->callerData = data;
  350. }
  351. #endif
  352.  
  353. PUBLIC void SGML_character ARGS2(HTStream *, context, char,c)
  354.  
  355. {
  356.     CONST SGML_dtd    *dtd    =    context->dtd;
  357.     HTChunk    *string =     context->string;
  358.  
  359.     switch(context->state) {
  360.     case S_text:
  361.     if (c=='&' && (!context->element_stack || (
  362.                  context->element_stack->tag  &&
  363.                  ( context->element_stack->tag->contents == SGML_MIXED
  364.                || context->element_stack->tag->contents ==
  365.                                    SGML_RCDATA)
  366.             ))) {
  367.         string->size = 0;
  368.         context->state = S_ero;
  369.         
  370.     } else if (c=='<') {
  371.         string->size = 0;
  372.         context->state = (context->element_stack &&
  373.                 context->element_stack->tag  &&
  374.                 context->element_stack->tag->contents == SGML_LITTERAL) ?
  375.                     S_litteral : S_tag;
  376.     } else PUTC(c);
  377.     break;
  378.  
  379. /*    In litteral mode, waits only for specific end tag!
  380. **    Only foir compatibility with old servers.
  381. */
  382.     case S_litteral :
  383.     HTChunkPutc(string, c);
  384.     if ( TOUPPER(c) != ((string->size ==1) ? '/'
  385.         : context->element_stack->tag->name[string->size-2])) {
  386.         int i;
  387.  
  388.         /*    If complete match, end litteral */
  389.         if ((c=='>') && (!context->element_stack->tag->name[string->size-2])) {
  390.         end_element(context, context->element_stack->tag);
  391.         string->size = 0;
  392.         context->current_attribute_number = INVALID;
  393.         context->state = S_text;
  394.         break;
  395.         }        /* If Mismatch: recover string. */
  396.         PUTC( '<');
  397.         for (i=0; i<string->size; i++)    /* recover */
  398.            PUTC(
  399.                                  string->data[i]);
  400.         context->state = S_text;    
  401.     }
  402.     
  403.         break;
  404.  
  405. /*    Character reference or Entity
  406. */
  407.    case S_ero:
  408.        if (c=='#') {
  409.         context->state = S_cro;  /*   &# is Char Ref Open */ 
  410.         break;
  411.     }
  412.     context->state = S_entity;    /* Fall through! */
  413.     
  414. /*    Handle Entities
  415. */
  416.     case S_entity:
  417.     if (isalnum(c))
  418.         HTChunkPutc(string, c);
  419.     else {
  420.         HTChunkTerminate(string);
  421.         handle_entity(context, c);
  422.         context->state = S_text;
  423.     }
  424.     break;
  425.  
  426. /*    Character reference
  427. */
  428.     case S_cro:
  429.     if (isalnum(c))
  430.         HTChunkPutc(string, c);    /* accumulate a character NUMBER */
  431.     else {
  432.         int value;
  433.         HTChunkTerminate(string);
  434.         if (sscanf(string->data, "%d", &value)==1)
  435.             PUTC(FROMASCII((char)value));
  436.         context->state = S_text;
  437.     }
  438.     break;
  439.  
  440. /*        Tag
  441. */        
  442.     case S_tag:                /* new tag */
  443.     if (isalnum(c))
  444.         HTChunkPutc(string, c);
  445.     else {                /* End of tag name */
  446.         HTTag * t;
  447.         if (c=='/') {
  448. #ifndef RELEASE
  449.         if (TRACE) if (string->size!=0)
  450.             fprintf(stderr,"SGML:  `<%s/' found!\n", string->data);
  451. #endif /* RELEASE */
  452.         context->state = S_end;
  453.         break;
  454.         }
  455.         HTChunkTerminate(string) ;
  456.  
  457.         t = SGMLFindTag(dtd, string->data);
  458.         if (!t) {
  459. #ifndef RELEASE
  460.         if(TRACE) fprintf(stderr, "SGML: *** Unknown element %s\n",
  461.             string->data);
  462. #endif /* RELEASE */
  463.         context->state = (c=='>') ? S_text : S_junk_tag;
  464.         break;
  465.         }
  466.         context->current_tag = t;
  467.         
  468.         /*  Clear out attributes
  469.         */
  470.         
  471.         {
  472.             int i;
  473.             for (i=0; i< context->current_tag->number_of_attributes; i++)
  474.                 context->present[i] = NO;
  475.         }
  476.         string->size = 0;
  477.         context->current_attribute_number = INVALID;
  478.         
  479.         if (c=='>') {
  480.         if (context->current_tag->name) start_element(context);
  481.         context->state = S_text;
  482.         } else {
  483.             context->state = S_tag_gap;
  484.         }
  485.     }
  486.     break;
  487.  
  488.  
  489.     case S_tag_gap:        /* Expecting attribute or > */
  490.     if (WHITE(c)) break;    /* Gap between attributes */
  491.     if (c=='>') {        /* End of tag */
  492.         if (context->current_tag->name) start_element(context);
  493.         context->state = S_text;
  494.         break;
  495.     }
  496.     HTChunkPutc(string, c);
  497.     context->state = S_attr;        /* Get attribute */
  498.     break;
  499.     
  500.                    /* accumulating value */
  501.     case S_attr:
  502.     if (WHITE(c) || (c=='>') || (c=='=')) {        /* End of word */
  503.         HTChunkTerminate(string) ;
  504.         handle_attribute_name(context, string->data);
  505.         string->size = 0;
  506.         if (c=='>') {        /* End of tag */
  507.         if (context->current_tag->name) start_element(context);
  508.         context->state = S_text;
  509.         break;
  510.         }
  511.         context->state = (c=='=' ?  S_equals: S_attr_gap);
  512.     } else {
  513.         HTChunkPutc(string, c);
  514.     }
  515.     break;
  516.         
  517.     case S_attr_gap:        /* Expecting attribute or = or > */
  518.     if (WHITE(c)) break;    /* Gap after attribute */
  519.     if (c=='>') {        /* End of tag */
  520.         if (context->current_tag->name) start_element(context);
  521.         context->state = S_text;
  522.         break;
  523.     } else if (c=='=') {
  524.         context->state = S_equals;
  525.         break;
  526.     }
  527.     HTChunkPutc(string, c);
  528.     context->state = S_attr;        /* Get next attribute */
  529.     break;
  530.     
  531.     case S_equals:            /* After attr = */ 
  532.     if (WHITE(c)) break;    /* Before attribute value */
  533.     if (c=='>') {        /* End of tag */
  534. #ifndef RELEASE
  535.         if (TRACE) fprintf(stderr, "SGML: found = but no value\n");
  536. #endif /* RELEASE */
  537.         if (context->current_tag->name) start_element(context);
  538.         context->state = S_text;
  539.         break;
  540.         
  541.     } else if (c=='\'') {
  542.         context->state = S_squoted;
  543.         break;
  544.  
  545.     } else if (c=='"') {
  546.         context->state = S_dquoted;
  547.         break;
  548.     }
  549.     HTChunkPutc(string, c);
  550.     context->state = S_value;
  551.     break;
  552.     
  553.     case S_value:
  554.     if (WHITE(c) || (c=='>')) {        /* End of word */
  555.         HTChunkTerminate(string) ;
  556.         handle_attribute_value(context, string->data);
  557.         string->size = 0;
  558.         if (c=='>') {        /* End of tag */
  559.         if (context->current_tag->name) start_element(context);
  560.         context->state = S_text;
  561.         break;
  562.         }
  563.         else context->state = S_tag_gap;
  564.     } else {
  565.         HTChunkPutc(string, c);
  566.     }
  567.     break;
  568.         
  569.     case S_squoted:        /* Quoted attribute value */
  570.     if (c=='\'') {        /* End of attribute value */
  571.         HTChunkTerminate(string) ;
  572.         handle_attribute_value(context, string->data);
  573.         string->size = 0;
  574.         context->state = S_tag_gap;
  575.     } else {
  576.         HTChunkPutc(string, c);
  577.     }
  578.     break;
  579.     
  580.     case S_dquoted:        /* Quoted attribute value */
  581.     if (c=='"') {        /* End of attribute value */
  582.         HTChunkTerminate(string) ;
  583.         handle_attribute_value(context, string->data);
  584.         string->size = 0;
  585.         context->state = S_tag_gap;
  586.     } else {
  587.         HTChunkPutc(string, c);
  588.     }
  589.     break;
  590.     
  591.     case S_end:                    /* </ */
  592.     if (isalnum(c))
  593.         HTChunkPutc(string, c);
  594.     else {                /* End of end tag name */
  595.         HTTag * t;
  596.         HTChunkTerminate(string) ;
  597.         if (!*string->data)    {    /* Empty end tag */
  598.         t = context->element_stack->tag;
  599.         } else {
  600.         t = SGMLFindTag(dtd, string->data);
  601.         }
  602.         if (!t) {
  603. #ifndef RELEASE
  604.         if(TRACE) fprintf(stderr,
  605.             "Unknown end tag </%s>\n", string->data);
  606. #endif /* RELEASE */
  607.         } else {
  608.             context->current_tag = t;
  609.         end_element( context, context->current_tag);
  610.         }
  611.  
  612.         string->size = 0;
  613.         context->current_attribute_number = INVALID;
  614.         if (c!='>') {
  615. #ifndef RELEASE
  616.         if (TRACE && !WHITE(c))
  617.             fprintf(stderr,"SGML:  `</%s%c' found!\n",
  618.             string->data, c);
  619. #endif /* RELEASE */
  620.         context->state = S_junk_tag;
  621.         } else {
  622.         context->state = S_text;
  623.         }
  624.     }
  625.     break;
  626.  
  627.         
  628.     case S_junk_tag:
  629.     if (c=='>') {
  630.         context->state = S_text;
  631.     }
  632.     
  633.     } /* switch on context->state */
  634.  
  635. }  /* SGML_character */
  636.  
  637.  
  638. PUBLIC void SGML_string ARGS2(HTStream *, context, CONST char*, str)
  639. {
  640.     CONST char *p;
  641.     for(p=str; *p; p++)
  642.         SGML_character(context, *p);
  643. }
  644.  
  645.  
  646. PUBLIC void SGML_write ARGS3(HTStream *, context, CONST char*, str, int, l)
  647. {
  648.     CONST char *p;
  649.     CONST char *e = str+l;
  650.     for(p=str; p<e; p++)
  651.         SGML_character(context, *p);
  652. }
  653.  
  654. /*_______________________________________________________________________
  655. */
  656.  
  657. /*    Structured Object Class
  658. **    -----------------------
  659. */
  660. PUBLIC CONST HTStreamClass SGMLParser = 
  661. {        
  662.     "SGMLParser",
  663.     SGML_free,
  664.     SGML_abort,
  665.     SGML_character, 
  666.     SGML_string,
  667.     SGML_write,
  668. }; 
  669.  
  670. /*    Create SGML Engine
  671. **    ------------------
  672. **
  673. ** On entry,
  674. **    dtd        represents the DTD, along with
  675. **    actions        is the sink for the data as a set of routines.
  676. **
  677. */
  678.  
  679. PUBLIC HTStream* SGML_new  ARGS2(
  680.     CONST SGML_dtd *,    dtd,
  681.     HTStructured *,        target)
  682. {
  683.     int i;
  684.     HTStream* context = (HTStream *) malloc(sizeof(*context));
  685.     if (!context) outofmem(__FILE__, "SGML_begin");
  686.  
  687.     context->isa = &SGMLParser;
  688.     context->string = HTChunkCreate(128);    /* Grow by this much */
  689.     context->dtd = dtd;
  690.     context->target = target;
  691.     context->actions = (HTStructuredClass*)(((HTStream*)target)->isa);
  692.                     /* Ugh: no OO */
  693.     context->state = S_text;
  694.     context->element_stack = 0;            /* empty */
  695. #ifdef CALLERDATA
  696.     context->callerData = (void*) callerData;
  697. #endif
  698.     for(i=0; i<MAX_ATTRIBUTES; i++) context->value[i] = NULL;
  699.  
  700.     return context;
  701. }
  702.  
  703.