Annotation of libwww/Library/src/SGML.c, revision 1.1.1.1

1.1       timbl       1: /*                     General SGML Parser code                SGML.c
                      2: **                     ========================
                      3: **
                      4: **     This module implements an HTSGMLContext object. To parse an
                      5: **     SGML file, create this object which is a parser. The object
                      6: **     is (currently) created by being parsed a DTD structure.
                      7: **
                      8: **     
                      9: */
                     10: #include "SGML.h"
                     11: 
                     12: #include <ctype.h>
                     13: #include <stdio.h>
                     14: #include "HTUtils.h"
                     15: #include "HTChunk.h"
                     16: #include "tcp.h"               /* For FROMASCII */
                     17: 
                     18: /*     The State (context) of the parser
                     19: **
                     20: **     This is passed with each call to make the parser recursive
                     21: **
                     22: */
                     23: 
                     24: struct _HTSGMLContext {
                     25:     SGML_dtd           *dtd;
                     26:     void               (*contents_treatment) PARAMS((void * data, char c));
                     27:     HTTag              *current_tag;
                     28:     attr               *current_attribute;
                     29:     HTChunk            *string;
                     30:     HTElement          *element_stack;
                     31:     enum sgml_state { S_text, S_litteral, S_tag, S_tag_gap, 
                     32:                S_attr, S_attr_gap, S_equals, S_value,
                     33:                S_ero, S_cro,
                     34:                  S_squoted, S_dquoted, S_end, S_entity, S_junk_tag} state;
                     35:     void *             callerData;
                     36: };
                     37: 
                     38: 
                     39: /*     Handle Attribute
                     40: **     ----------------
                     41: */
                     42: /* PUBLIC CONST char * SGML_default = "";   ?? */
                     43: 
                     44: #ifdef __STDC__
                     45: PRIVATE void handle_attribute_name(HTSGMLContext context, const char * s)
                     46: #else
                     47: PRIVATE void handle_attribute_name(context, s)
                     48:     HTSGMLContext context;
                     49:     char *s;
                     50: #endif
                     51: {
                     52:     attr* a;
                     53:     for(    a = context->current_tag->attributes;
                     54:            a->name;
                     55:            a++) {
                     56:        if (0==strcasecomp(a->name, s))
                     57:            break;
                     58:     }
                     59:     if (!a->name) {
                     60:        if (TRACE)
                     61:            fprintf(stderr, "SGML: Unknown attribute %s for tag %s\n",
                     62:                s, context->current_tag->name);
                     63:         context->current_attribute = 0;        /* Invalid */
                     64:        return;
                     65:     }
                     66:     a->present = YES;
                     67:     if (a->value) {
                     68:         free(a->value);
                     69:        a->value = 0;
                     70:     }
                     71:     context->current_attribute = a;
                     72: }
                     73: 
                     74: 
                     75: /*     Handle attribute value
                     76: **     ----------------------
                     77: */
                     78: #ifdef __STDC__
                     79: PRIVATE void handle_attribute_value(HTSGMLContext context, const char * s)
                     80: #else
                     81: PRIVATE void handle_attribute_value(context, s)
                     82:     HTSGMLContext context;
                     83:     char *s;
                     84: #endif
                     85: {
                     86:     if (context->current_attribute) {
                     87:        StrAllocCopy(context->current_attribute->value, s);
                     88:     } else {
                     89:         if (TRACE) fprintf(stderr, "SGML: Attribute value %s ignored\n", s);
                     90:     }
                     91:     context->current_attribute = 0;    /* can't have two assignments! */
                     92: }
                     93: 
                     94: /*     Handle entity
                     95: **     -------------
                     96: **
                     97: ** On entry,
                     98: **     s       contains the entity name zero terminated
                     99: ** Bugs:
                    100: **     If the entity name is unknown, the terminator is treated as
                    101: **     a printable non-special character in all cases, even if it is '<'
                    102: */
                    103: #ifdef __STDC__
                    104: PRIVATE void handle_entity(HTSGMLContext context, char term)
                    105: #else
                    106: PRIVATE void handle_entity(context, term)
                    107:     HTSGMLContext context;
                    108:     char term;
                    109: #endif
                    110: {
                    111:     entity * e;
                    112:     entity * entities = context->dtd->entities;
                    113:     CONST char *s = context->string->data;
                    114: 
                    115:     for(e = entities; e->name; e++) {
                    116:        if (0==strcmp(e->name, s)) {
                    117:            char * p;
                    118:            for (p=e->representation; *p; p++) {
                    119:                (*context->contents_treatment)(context->callerData, *p);
                    120:            }
                    121:            return;     /* Good */
                    122:        }
                    123:     }
                    124:     /* If entity string not found, display as text */
                    125:     if (TRACE)
                    126:        fprintf(stderr, "SGML: Unknown entity %s\n", s); 
                    127:     (*context->contents_treatment)(context->callerData, '&');
                    128:     {
                    129:        CONST char *p;
                    130:        for (p=s; *p; p++) {
                    131:            (*context->contents_treatment)(context->callerData, *p);
                    132:        }
                    133:     }
                    134:     (*context->contents_treatment)(context->callerData, term);
                    135: }
                    136: 
                    137: /*     End element
                    138: */
                    139: #ifdef __STDC__
                    140: PRIVATE void end_element(HTSGMLContext context, HTTag * old_tag)
                    141: #else
                    142: PRIVATE void end_element(context, old_tag)
                    143:     HTTag * old_tag;
                    144:     HTSGMLContext context;
                    145: #endif
                    146: {
                    147:     if (TRACE) fprintf(stderr, "SGML: End   </%s>\n", old_tag->name);
                    148:     if (!old_tag->end) {
                    149:         if (TRACE) fprintf(stderr,"SGML: Illegal end tag </%s> found.\n",
                    150:                old_tag->name);
                    151:        return;
                    152:     }
                    153:     while (context->element_stack)     {/* Loop is error path only */
                    154:        HTElement * N = context->element_stack;
                    155:        HTTag * t = N->tag;
                    156:        
                    157:        if (old_tag != t) {             /* Mismatch: syntax error */
                    158:            if (context->element_stack->next) { /* This is not the last level */
                    159:                if (TRACE) fprintf(stderr,
                    160:                "SGML: Found </%s> when expecting </%s>. </%s> assumed.\n",
                    161:                    old_tag->name, t->name, t->name);
                    162:            } else {                    /* last level */
                    163:                if (TRACE) fprintf(stderr,
                    164:                    "SGML: Found </%s> when expecting </%s>. </%s> Ignored.\n",
                    165:                    old_tag->name, t->name, old_tag->name);
                    166:                return;                 /* Ignore */
                    167:            }
                    168:        }
                    169:        
                    170:        context->element_stack = N->next;               /* Remove from stack */
                    171:        free(N);
                    172:        (t->end)(context->callerData,
                    173:                 t,
                    174:                 context->element_stack);       /* Assume tag end */
                    175:        if (context->element_stack)             /* not end of document */
                    176:            context->contents_treatment = context->element_stack->tag->treat;
                    177:        if (old_tag == t) return;  /* Correct sequence */
                    178:        
                    179:        /* Syntax error path only */
                    180:        
                    181:     }
                    182:     fprintf(stderr,
                    183:        "SGML: Extra end tag </%s> found and ignored.\n", old_tag->name);
                    184: }
                    185: 
                    186: 
                    187: /*     Start a element
                    188: */
                    189: #ifdef __STDC__
                    190: PRIVATE void start_element(HTSGMLContext context)
                    191: #else
                    192: PRIVATE void start_element(context)
                    193:     HTSGMLContext context;
                    194: #endif
                    195: {
                    196:     HTTag * new_tag = context->current_tag;
                    197:     
                    198:     if (TRACE) fprintf(stderr, "SGML: Start <%s>\n", new_tag->name);
                    199:     (*new_tag->begin)(context->callerData, new_tag, context->element_stack);
                    200:     if (new_tag->end) {                /* i.e. tag not empty */
                    201:        HTElement * N = (HTElement *)malloc(sizeof(HTElement));
                    202:         if (N == NULL) outofmem(__FILE__, "start_element");
                    203:        N->next = context->element_stack;
                    204:        N->tag = new_tag;
                    205:        context->element_stack = N;
                    206:        context->contents_treatment = new_tag->treat;
                    207:     }
                    208: }
                    209: 
                    210: /*________________________________________________________________________
                    211: **                     Public Methods
                    212: */
                    213: 
                    214: /*     Create SGML Engine
                    215: **     ------------------
                    216: **
                    217: ** On entry,
                    218: **     dtd->tags               represents the DTD, along with
                    219: **     dtd->entities
                    220: **
                    221: **     default_tag     represents the initial and final actions,
                    222: **                     and the character processing, for data outside
                    223: **                     any tags. May not be empty.
                    224: */
                    225: 
                    226: PUBLIC HTSGMLContext SGML_begin  ARGS1(SGML_dtd *,dtd)
                    227: {
                    228:     HTSGMLContext context = (HTSGMLContext) malloc(sizeof(*context));
                    229:     if (!context) outofmem(__FILE__, "SGML_begin");
                    230: 
                    231:     context->string = HTChunkCreate(128);      /* Grow by this much */
                    232:     context->dtd = dtd;
                    233:     context->state = S_text;
                    234:     context->element_stack = 0;                        /* empty */
                    235:     context->callerData = (void*) 0;           /* unspcified as yet */
                    236:     context->current_tag = dtd->default_tag;
                    237:     start_element(context);            /* Start document */
                    238:     return context;
                    239: }
                    240: 
                    241: 
                    242: PUBLIC void SGML_end  ARGS1(HTSGMLContext, context)
                    243: {
                    244:     end_element(context, context->dtd->default_tag);   /* End document */
                    245:     HTChunkFree(context->string);
                    246:     free(context);
                    247: }
                    248: 
                    249: /*     Read and write user callback handle
                    250: **     -----------------------------------
                    251: **
                    252: **   The callbacks from the SGML parser have an SGML context parameter.
                    253: **   These calls allow the caller to associate his own context with a
                    254: **   particular SGML context.
                    255: */
                    256: 
                    257: PUBLIC void* SGML_callerData ARGS1(HTSGMLContext, context)
                    258: {
                    259:     return context->callerData;
                    260: }
                    261: 
                    262: PUBLIC void SGML_setCallerData ARGS2(HTSGMLContext, context, void*, data)
                    263: {
                    264:     context->callerData = data;
                    265: }
                    266: 
                    267: 
                    268: PUBLIC void SGML_string ARGS2(HTSGMLContext, context, char*, str)
                    269: {
                    270:     char *p;
                    271:     for(p=str; *p; p++)
                    272:         SGML_character(context, *p);
                    273: }
                    274: 
                    275: PUBLIC void SGML_character ARGS2(HTSGMLContext, context, char,c)
                    276: 
                    277: {
                    278:     SGML_dtd   *dtd    =       context->dtd;
                    279:     HTChunk    *string =       context->string;
                    280: 
                    281:     switch(context->state) {
                    282:     case S_text:
                    283:        if (c=='&' && !(context->element_stack &&
                    284:                        context->element_stack->tag  &&
                    285:                        context->element_stack->tag->litteral)) {
                    286:            string->size = 0;
                    287:            context->state = S_ero;
                    288:            
                    289:        } else if (c=='<') {
                    290:            string->size = 0;
                    291:            context->state = (context->element_stack &&
                    292:                        context->element_stack->tag  &&
                    293:                        context->element_stack->tag->litteral) ?
                    294:                                S_litteral : S_tag;
                    295:        } else (*context->contents_treatment)(context->callerData, c);
                    296:        break;
                    297: 
                    298: /*     In litteral mode, waits only for specific end tag!
                    299: */
                    300:     case S_litteral :
                    301:        HTChunkPutc(string, c);
                    302:        if ( TOUPPER(c) != ((string->size ==1) ? '/'
                    303:                : context->element_stack->tag->name[string->size-2])) {
                    304:            int i;
                    305:            
                    306:            /*  If complete match, end litteral */
                    307:            if ((c=='>') && (!context->element_stack->tag->name[string->size-2])) {
                    308:                end_element(context, context->element_stack->tag);
                    309:                string->size = 0;
                    310:                context->current_attribute = (attr *) 0;
                    311:                context->state = S_text;
                    312:                break;
                    313:            }           /* If Mismatch: recover string. */
                    314:            (*context->contents_treatment)(context->callerData, '<');
                    315:            for (i=0; i<string->size; i++)      /* recover */
                    316:               (*context->contents_treatment)(context->callerData,
                    317:                                              string->data[i]);
                    318:            context->state = S_text;    
                    319:        }
                    320:        
                    321:         break;
                    322: 
                    323: /*     Character reference or Entity
                    324: */
                    325:    case S_ero:
                    326:        if (c=='#') {
                    327:            context->state = S_cro;  /*   &# is Char Ref Open */ 
                    328:            break;
                    329:        }
                    330:        context->state = S_entity;    /* Fall through! */
                    331:        
                    332: /*     Handle Entities
                    333: */
                    334:     case S_entity:
                    335:        if (isalnum(c))
                    336:            HTChunkPutc(string, c);
                    337:        else {
                    338:            HTChunkTerminate(string);
                    339:            handle_entity(context, c);
                    340:            context->state = S_text;
                    341:        }
                    342:        break;
                    343: 
                    344: /*     Character reference
                    345: */
                    346:     case S_cro:
                    347:        if (isalnum(c))
                    348:            HTChunkPutc(string, c);     /* accumulate a character NUMBER */
                    349:        else {
                    350:            int value;
                    351:            HTChunkTerminate(string);
                    352:            if (sscanf(string->data, "%d", &value)==1)
                    353:                (*context->contents_treatment)(context->callerData,
                    354:                                               FROMASCII((char)value));
                    355:            context->state = S_text;
                    356:        }
                    357:        break;
                    358: 
                    359: /*             Tag
                    360: */         
                    361:     case S_tag:                                /* new tag */
                    362:        if (isalnum(c))
                    363:            HTChunkPutc(string, c);
                    364:        else {                          /* End of tag name */
                    365:            attr * a;
                    366:            if (c=='/') {
                    367:                if (TRACE) if (string->size!=0)
                    368:                    fprintf(stderr,"SGML:  `<%s/' found!\n", string->data);
                    369:                context->state = S_end;
                    370:                break;
                    371:            }
                    372:            HTChunkTerminate(string) ;
                    373:            for(context->current_tag = dtd->tags;
                    374:                context->current_tag->name; context->current_tag++) {
                    375:                if (0==strcasecomp(context->current_tag->name, string->data)) {
                    376:                    break;
                    377:                }
                    378:            }
                    379:            if (!context->current_tag->name) {
                    380:                if(TRACE) fprintf(stderr, "Unknown tag %s\n",
                    381:                        string->data);
                    382:                context->state = (c=='>') ? S_text : S_junk_tag;
                    383:                break;
                    384:            }
                    385:            
                    386:            for (a = context->current_tag->attributes; a->name; a++ ) {
                    387:                a->present = NO;
                    388:            }
                    389:            string->size = 0;
                    390:            context->current_attribute = (attr *) 0;
                    391:            
                    392:            if (c=='>') {
                    393:                if (context->current_tag->name) start_element(context);
                    394:                context->state = S_text;
                    395:            } else {
                    396:                context->state = S_tag_gap;
                    397:            }
                    398:        }
                    399:        break;
                    400: 
                    401:                
                    402:     case S_tag_gap:            /* Expecting attribute or > */
                    403:        if (WHITE(c)) break;    /* Gap between attributes */
                    404:        if (c=='>') {           /* End of tag */
                    405:            if (context->current_tag->name) start_element(context);
                    406:            context->state = S_text;
                    407:            break;
                    408:        }
                    409:        HTChunkPutc(string, c);
                    410:        context->state = S_attr;                /* Get attribute */
                    411:        break;
                    412:        
                    413:                                /* accumulating value */
                    414:     case S_attr:
                    415:        if (WHITE(c) || (c=='>') || (c=='=')) {         /* End of word */
                    416:            HTChunkTerminate(string) ;
                    417:            handle_attribute_name(context, string->data);
                    418:            string->size = 0;
                    419:            if (c=='>') {               /* End of tag */
                    420:                if (context->current_tag->name) start_element(context);
                    421:                context->state = S_text;
                    422:                break;
                    423:            }
                    424:            context->state = (c=='=' ?  S_equals: S_attr_gap);
                    425:        } else {
                    426:            HTChunkPutc(string, c);
                    427:        }
                    428:        break;
                    429:                
                    430:     case S_attr_gap:           /* Expecting attribute or = or > */
                    431:        if (WHITE(c)) break;    /* Gap after attribute */
                    432:        if (c=='>') {           /* End of tag */
                    433:            if (context->current_tag->name) start_element(context);
                    434:            context->state = S_text;
                    435:            break;
                    436:        } else if (c=='=') {
                    437:            context->state = S_equals;
                    438:            break;
                    439:        }
                    440:        HTChunkPutc(string, c);
                    441:        context->state = S_attr;                /* Get next attribute */
                    442:        break;
                    443:        
                    444:     case S_equals:                     /* After attr = */ 
                    445:        if (WHITE(c)) break;    /* Before attribute value */
                    446:        if (c=='>') {           /* End of tag */
                    447:            fprintf(stderr, "SGML: found = but no value\n");
                    448:            if (context->current_tag->name) start_element(context);
                    449:            context->state = S_text;
                    450:            break;
                    451:            
                    452:        } else if (c=='\'') {
                    453:            context->state = S_squoted;
                    454:            break;
                    455: 
                    456:        } else if (c=='"') {
                    457:            context->state = S_dquoted;
                    458:            break;
                    459:        }
                    460:        HTChunkPutc(string, c);
                    461:        context->state = S_value;
                    462:        break;
                    463:        
                    464:     case S_value:
                    465:        if (WHITE(c) || (c=='>')) {             /* End of word */
                    466:            HTChunkTerminate(string) ;
                    467:            handle_attribute_value(context, string->data);
                    468:            string->size = 0;
                    469:            if (c=='>') {               /* End of tag */
                    470:                if (context->current_tag->name) start_element(context);
                    471:                context->state = S_text;
                    472:                break;
                    473:            }
                    474:            else context->state = S_tag_gap;
                    475:        } else {
                    476:            HTChunkPutc(string, c);
                    477:        }
                    478:        break;
                    479:                
                    480:     case S_squoted:            /* Quoted attribute value */
                    481:        if (c=='\'') {          /* End of attribute value */
                    482:            HTChunkTerminate(string) ;
                    483:            handle_attribute_value(context, string->data);
                    484:            string->size = 0;
                    485:            context->state = S_tag_gap;
                    486:        } else {
                    487:            HTChunkPutc(string, c);
                    488:        }
                    489:        break;
                    490:        
                    491:     case S_dquoted:            /* Quoted attribute value */
                    492:        if (c=='"') {           /* End of attribute value */
                    493:            HTChunkTerminate(string) ;
                    494:            handle_attribute_value(context, string->data);
                    495:            string->size = 0;
                    496:            context->state = S_tag_gap;
                    497:        } else {
                    498:            HTChunkPutc(string, c);
                    499:        }
                    500:        break;
                    501:        
                    502:     case S_end:                                        /* </ */
                    503:        if (isalnum(c))
                    504:            HTChunkPutc(string, c);
                    505:        else {                          /* End of end tag name */
                    506:            HTChunkTerminate(string) ;
                    507:            if (c!='>') {
                    508:                if (TRACE) fprintf(stderr,"SGML:  `</%s%c' found!\n",
                    509:                    string->data, c);
                    510:                context->state = S_junk_tag;
                    511:                break;
                    512:            }
                    513:            for(context->current_tag = dtd->tags;
                    514:                context->current_tag->name; context->current_tag++) {
                    515:                if (0==strcasecomp(context->current_tag->name, string->data)) {
                    516:                    end_element( context, context->current_tag);
                    517:                    break;
                    518:                }
                    519:            }
                    520:            if (!context->current_tag->name) {
                    521:                if(TRACE) fprintf(stderr,
                    522:                    "Unknown end tag </%s>\n", string->data); 
                    523:            }
                    524:            string->size = 0;
                    525:            context->current_attribute = (attr *) 0;
                    526:            context->state = S_text;
                    527:        }
                    528:        break;
                    529: 
                    530:                
                    531:     case S_junk_tag:
                    532:        if (c=='>') {
                    533:            context->state = S_text;
                    534:        }
                    535:        
                    536:     } /* switch on context->state */
                    537: 
                    538: }  /* SGML_character */

Webmaster