Annotation of libwww/Library/src/HTML.c, revision 1.1

1.1     ! timbl       1: /*             HTML Parser
        !             2: **             ===========
        !             3: **
        !             4: **  An HTML displayable object has associated with it
        !             5: **
        !             6: **             - The underlying text object for display
        !             7: **             - An SGML parsing context
        !             8: **             - An anchor representing the whole object
        !             9: **             - A style sheet, in the case os a style-oriented version
        !            10: **
        !            11: **  The first three could logically be represented by multiple inheritance if
        !            12: **  that were supported, as an HTML object is like a subclass of all three.
        !            13: **
        !            14: **  In practice in C,
        !            15: **
        !            16: **     - a HText object is created by this module (when needed)
        !            17: **     - an SGML parsing object is created by this module
        !            18: **     - the anchor representing the object is given at creation time
        !            19: **
        !            20: **  Those using structured HTML objects will wish to override this module
        !            21: **  completely
        !            22: */
        !            23: #include "HTML.h"
        !            24: 
        !            25: #include <ctype.h>
        !            26: #include <stdio.h>
        !            27: 
        !            28: #include "HTAtom.h"
        !            29: #include "HTChunk.h"
        !            30: #include "HText.h"
        !            31: #include "HTStyle.h"
        !            32: 
        !            33: 
        !            34: /*                             SPECIAL HTML CODE
        !            35: **                             =================
        !            36: */
        !            37: 
        !            38: extern HTStyleSheet * styleSheet;      /* Application-wide */
        !            39: 
        !            40: /*     Module-wide style cache
        !            41: */
        !            42: PRIVATE HTStyle * glossary_style;
        !            43: PRIVATE HTStyle * list_compact_style;
        !            44: PRIVATE HTStyle * glossary_compact_style;
        !            45: PRIVATE int            got_styles = 0;
        !            46: 
        !            47: 
        !            48: /*             HTML Object
        !            49: **             -----------
        !            50: */
        !            51: struct _HTML {
        !            52:     HTParentAnchor *   node_anchor;
        !            53:     HText *            text;
        !            54:     HTSGMLContext      context;
        !            55: 
        !            56:     HTChunk title;     /* Grow by 128 */
        !            57: 
        !            58: /* Used in parsing: */
        !            59: 
        !            60:     BOOL       style_change;
        !            61:     HTStyle *  new_style;
        !            62:     HTStyle *  old_style;
        !            63:     BOOL       in_word;  /* Have just had a non-white character */
        !            64: };
        !            65: 
        !            66: 
        !            67: /*             Forward declarations of routines
        !            68: */
        !            69: PRIVATE void get_styles NOPARAMS;
        !            70: 
        !            71: /* For dtd: */
        !            72: PRIVATE void no_change PARAMS((void*this, HTTag * t, HTElement * e));
        !            73: PRIVATE void begin_litteral PARAMS((void*this, HTTag * t, HTElement * e));
        !            74: PRIVATE void begin_element PARAMS((void*this, HTTag * t, HTElement * e));
        !            75: PRIVATE void end_element PARAMS((void*this, HTTag * t, HTElement * e));
        !            76: PRIVATE void begin_document PARAMS((void*this, HTTag * t, HTElement * e));
        !            77: PRIVATE void end_document PARAMS((void*this, HTTag * t, HTElement * e));
        !            78: PRIVATE void begin_anchor PARAMS((void*this, HTTag * t, HTElement * e));
        !            79: PRIVATE void end_anchor PARAMS((void*this, HTTag * t, HTElement * e));
        !            80: PRIVATE void begin_list PARAMS((void*this, HTTag * t, HTElement * e));
        !            81: PRIVATE void list_element PARAMS((void*this, HTTag * t, HTElement * e));
        !            82: PRIVATE void end_list PARAMS((void*this, HTTag * t, HTElement * e));
        !            83: PRIVATE void begin_glossary PARAMS((void*this, HTTag * t, HTElement * e));
        !            84: PRIVATE void end_glossary PARAMS((void*this, HTTag * t, HTElement * e));
        !            85: 
        !            86: PRIVATE void actually_set_style PARAMS((HTML_id this));
        !            87: PRIVATE void change_style PARAMS((HTML_id this, HTStyle * style));
        !            88: 
        !            89: /*     Style buffering avoids dummy paragraph begin/ends.
        !            90: */
        !            91: #define UPDATE_STYLE if (THIS->style_change) { actually_set_style(THIS); }
        !            92: 
        !            93: #define THIS ((HTML_id)this)
        !            94: 
        !            95: /*     Things affecting the anchor but not the document itself
        !            96: **     -------------------------------------------------------
        !            97: */
        !            98: 
        !            99: 
        !           100: /*             TITLE
        !           101: */
        !           102: 
        !           103: /*     Accumulate a character of title
        !           104: */
        !           105: static void accumulate_string ARGS2(void *, this, char, c)
        !           106: 
        !           107: {
        !           108:     HTChunkPutc(&THIS->title, c);
        !           109: }
        !           110: 
        !           111: 
        !           112: /*             Clear the title
        !           113: */
        !           114: PRIVATE  void clear_string ARGS3(void *, this, HTTag *,t, HTElement *,e)
        !           115: {
        !           116:     HTChunkClear(&THIS->title);
        !           117: }
        !           118: 
        !           119: PRIVATE void set_title ARGS3(void *, this, HTTag *,t, HTElement *,e)
        !           120: {
        !           121:     HTChunkTerminate(&THIS->title);
        !           122:     HTAnchor_setTitle(THIS->node_anchor, THIS->title.data);
        !           123: }
        !           124: 
        !           125: PRIVATE void set_index ARGS3(void *, this, HTTag *,t, HTElement *,e)
        !           126: {
        !           127:     HTAnchor_setIndex(THIS->node_anchor);
        !           128: }
        !           129: 
        !           130: /*                     Things affecting the document
        !           131: **                     -----------------------------
        !           132: */
        !           133: /*             Character handling
        !           134: */
        !           135: PRIVATE void pass_character ARGS2(void *, this, char, c)
        !           136: {
        !           137:     if (THIS->style_change) {
        !           138:         if ((c=='\n') || (c==' ')) return;     /* Ignore it */
        !           139:         UPDATE_STYLE;
        !           140:     }
        !           141:     if (c=='\n') {
        !           142:         if (THIS->in_word) {
        !           143:            HText_appendCharacter(THIS->text, ' ');
        !           144:            THIS->in_word = NO;
        !           145:        }
        !           146:     } else {
        !           147:         HText_appendCharacter(THIS->text, c);
        !           148:        THIS->in_word = YES;
        !           149:     }
        !           150: }
        !           151: 
        !           152: PRIVATE void litteral_text ARGS2(void *, this, char, c)
        !           153: {
        !           154: /*     We guarrantee that the style is up-to-date in begin_litteral
        !           155: */
        !           156:     HText_appendCharacter(THIS->text, c);              /* @@@@@ */
        !           157: }
        !           158: 
        !           159: PRIVATE void ignore_text ARGS2(void *, this, char, c)
        !           160: {
        !           161:     /* Do nothing */
        !           162: }
        !           163: 
        !           164: PRIVATE void set_next_id  ARGS3(void *, this, HTTag *,t, HTElement *,e)
        !           165: {
        !           166:     /* Not needed */
        !           167: }
        !           168: 
        !           169: PRIVATE void new_paragraph  ARGS3(void *, this, HTTag *,t, HTElement *,e)
        !           170: {
        !           171:     UPDATE_STYLE;
        !           172:     HText_appendParagraph(THIS->text);
        !           173:     THIS->in_word = NO;
        !           174: }
        !           175: 
        !           176: PRIVATE void term ARGS3(void *, this, HTTag *,t, HTElement *,e)
        !           177: {
        !           178:     if (!THIS->style_change) {
        !           179:         HText_appendParagraph(THIS->text);
        !           180:        THIS->in_word = NO;
        !           181:     }
        !           182: }
        !           183: 
        !           184: PRIVATE void definition ARGS3(void *, this, HTTag *,t, HTElement *,e)
        !           185: {
        !           186:     UPDATE_STYLE;
        !           187:     pass_character(this, '\t');        /* Just tab out one stop */
        !           188:     THIS->in_word = NO;
        !           189: }
        !           190: 
        !           191: /*             Our Static DTD for HTML
        !           192: **             -----------------------
        !           193: */
        !           194: 
        !           195: static entity entities[] = {
        !           196:        { "lt", "<" },
        !           197:        { "gt", ">" },
        !           198:        { "amp", "&" },
        !           199: #ifdef NeXT
        !           200:        { "bullet" , "\267" },                  /* @@@ NeXT only */
        !           201: #endif
        !           202: /* The following accented characters are from peter Flynn, curia project */
        !           203: 
        !           204: /* these ifdefs don't solve the problem of a simple terminal emulator
        !           205: ** with a different character set to the client machine. But nothing does,
        !           206: ** except looking at the TERM setting */
        !           207: 
        !           208:         { "ocus" , "&" },       /* for CURIA */
        !           209: #ifdef IBMPC
        !           210:         { "aacute" , "\240" }, /* For PC display */
        !           211:         { "eacute" , "\202" },
        !           212:         { "iacute" , "\241" },
        !           213:         { "oacute" , "\242" },
        !           214:         { "uacute" , "\243" },
        !           215:         { "Aacute" , "\101" },
        !           216:         { "Eacute" , "\220" },
        !           217:         { "Iacute" , "\111" },
        !           218:         { "Oacute" , "\117" },
        !           219:         { "Uacute" , "\125" },
        !           220: #else
        !           221:         { "aacute" , "\341" }, /* Works for openwindows -- Peter Flynn */
        !           222:         { "eacute" , "\351" },
        !           223:         { "iacute" , "\355" },
        !           224:         { "oacute" , "\363" },
        !           225:         { "uacute" , "\372" },
        !           226:         { "Aacute" , "\301" },
        !           227:         { "Eacute" , "\310" },
        !           228:         { "Iacute" , "\315" },
        !           229:         { "Oacute" , "\323" },
        !           230:         { "Uacute" , "\332" }, 
        !           231: #endif
        !           232:        { 0,    0 }  /* Terminate list */
        !           233: };
        !           234: 
        !           235: static attr no_attr[] = {{ 0, 0 , 0}};
        !           236: 
        !           237: static attr a_attr[] = {                               /* Anchor attributes */
        !           238: #define A_ID 0
        !           239:        { "NAME", 0, 0 },                               /* Should be ID */
        !           240: #define A_TYPE 1
        !           241:        { "TYPE", 0, 0 },
        !           242: #define A_HREF 2
        !           243:        { "HREF", 0, 0 },
        !           244:        { 0, 0 , 0}     /* Terminate list */
        !           245: };     
        !           246: static attr list_attr[] = {
        !           247: #define LIST_COMPACT 0
        !           248:        { "COMPACT", 0, 0 },
        !           249:        { 0, 0, 0 }     /* Terminate list */
        !           250: };
        !           251: 
        !           252: static attr glossary_attr[] = {
        !           253: #define GLOSSARY_COMPACT 0
        !           254:        { "COMPACT", 0, 0 },
        !           255:        { 0, 0, 0 }     /* Terminate list */
        !           256: };
        !           257: 
        !           258: static HTTag default_tag =
        !           259:     { "DOCUMENT", no_attr , 0, 0, begin_document, pass_character, end_document };
        !           260: /*     NAME ATTR  STYLE LITERAL?  ON_BEGIN   ON__CHARACTER     ON_END
        !           261: */
        !           262: static HTTag tags[] = {
        !           263: #define TITLE_TAG 0
        !           264:     { "TITLE", no_attr, 0, 0, clear_string, accumulate_string, set_title },
        !           265: #define ISINDEX_TAG 1
        !           266:     { "ISINDEX", no_attr, 0, 0, set_index, 0 , 0 },
        !           267: #define NEXTID_TAG 2
        !           268:     { "NEXTID", no_attr, 0, 0, set_next_id, 0, 0 },
        !           269: #define ADDRESS_TAG 3
        !           270:     { "ADDRESS"        , no_attr, 0, 0, begin_element, pass_character, end_element },
        !           271: #define H1_TAG 4
        !           272:     { "H1"     , no_attr, 0, 0, begin_element, pass_character, end_element },
        !           273:     { "H2"     , no_attr, 0, 0, begin_element, pass_character, end_element },
        !           274:     { "H3"     , no_attr, 0, 0, begin_element, pass_character, end_element },
        !           275:     { "H4"     , no_attr, 0, 0, begin_element, pass_character, end_element },
        !           276:     { "H5"     , no_attr, 0, 0, begin_element, pass_character, end_element },
        !           277:     { "H6"     , no_attr, 0, 0, begin_element, pass_character, end_element },
        !           278:     { "H7"     , no_attr, 0, 0, begin_element, pass_character, end_element },
        !           279: #define UL_TAG 11
        !           280:     { "UL"     , list_attr, 0, 0, begin_list, pass_character, end_list },
        !           281: #define OL_TAG 12
        !           282:     { "OL"     , list_attr, 0, 0, begin_list, pass_character, end_list },
        !           283: #define MENU_TAG 13
        !           284:     { "MENU"   , list_attr, 0, 0, begin_list, pass_character, end_list },
        !           285: #define DIR_TAG 14
        !           286:     { "DIR"    , list_attr, 0, 0, begin_list, pass_character, end_list },
        !           287: #define LI_TAG 15
        !           288:     { "LI"     , list_attr, 0, 0, list_element, pass_character, 0 },
        !           289: #define DL_TAG 16
        !           290:     { "DL"     , glossary_attr, 0, 0, begin_glossary, pass_character, end_glossary },
        !           291:     { "DT"     , no_attr, 0, 0, term, pass_character, 0 },
        !           292:     { "DD"     , no_attr, 0, 0, definition, pass_character, 0 },
        !           293:     { "A"      , a_attr,  0, 0, begin_anchor, pass_character, end_anchor },
        !           294: #define P_TAG 20
        !           295:     { "P"      , no_attr, 0, 0, new_paragraph, pass_character, 0 },
        !           296: #define XMP_TAG 21
        !           297:   { "XMP"      , no_attr, 0, YES, begin_litteral, litteral_text, end_element },
        !           298: #define PRE_TAG 22
        !           299:   { "PRE"      , no_attr, 0, 0, begin_litteral, litteral_text, end_element },
        !           300: #define LISTING_TAG 23
        !           301:   { "LISTING"  , no_attr, 0, YES,begin_litteral, litteral_text, end_element },
        !           302: #define PLAINTEXT_TAG 24
        !           303:   { "PLAINTEXT", no_attr, 0, YES, begin_litteral, litteral_text, end_element },
        !           304: #define COMMENT_TAG 25
        !           305:     { "COMMENT", no_attr, 0, YES, no_change, ignore_text, no_change },
        !           306:     { 0, 0, 0, 0,  0, 0 , 0}   /* Terminate list */
        !           307: };
        !           308: 
        !           309: PUBLIC SGML_dtd HTML_dtd = { tags, &default_tag, entities };
        !           310: 
        !           311: 
        !           312: /*             Flattening the style structure
        !           313: **             ------------------------------
        !           314: **
        !           315: On the NeXT, and on any read-only browser, it is simpler for the text to have
        !           316: a sequence of styles, rather than a nested tree of styles. In this
        !           317: case we have to flatten the structure as it arrives from SGML tags into
        !           318: a sequence of styles.
        !           319: */
        !           320: 
        !           321: /*             If style really needs to be set, call this
        !           322: */
        !           323: PRIVATE void actually_set_style ARGS1(HTML_id, this)
        !           324: {
        !           325:     if (!THIS->text) {                 /* First time through */
        !           326:            THIS->text = HText_new(THIS->node_anchor);
        !           327:            HText_beginAppend(THIS->text);
        !           328:            HText_setStyle(THIS->text, THIS->new_style);
        !           329:            THIS->in_word = NO;
        !           330:     } else {
        !           331:            HText_setStyle(THIS->text, THIS->new_style);
        !           332:     }
        !           333:     THIS->old_style = THIS->new_style;
        !           334:     THIS->style_change = NO;
        !           335: }
        !           336: 
        !           337: /*      If you THINK you need to change style, call this
        !           338: */
        !           339: 
        !           340: PRIVATE void change_style ARGS2(HTML_id, this, HTStyle *,style)
        !           341: {
        !           342:     if (THIS->new_style!=style) {
        !           343:        THIS->style_change = YES /* was old_style == new_style */ ;
        !           344:        THIS->new_style = style;
        !           345:     }
        !           346: }
        !           347: 
        !           348: /*     Anchor handling
        !           349: **     ---------------
        !           350: */
        !           351: PRIVATE void begin_anchor ARGS3(void *, this, HTTag *,t, HTElement *,e)
        !           352: {
        !           353:     HTChildAnchor * source = HTAnchor_findChildAndLink(
        !           354:        THIS->node_anchor,                                              /* parent */
        !           355:        a_attr[A_ID].present    ? a_attr[A_ID].value : 0,       /* Tag */
        !           356:        a_attr[A_HREF].present  ? a_attr[A_HREF].value : 0,     /* Addresss */
        !           357:        a_attr[A_TYPE].present  ? 
        !           358:                (HTLinkType*)HTAtom_for(a_attr[A_TYPE].value)
        !           359:                 : 0);
        !           360:     
        !           361:     UPDATE_STYLE;
        !           362:     HText_beginAnchor(THIS->text, source);
        !           363: }
        !           364: 
        !           365: PRIVATE void end_anchor ARGS3(void *, this, HTTag *,    t,
        !           366:                        HTElement *,    e)
        !           367: {
        !           368:     UPDATE_STYLE;
        !           369:     HText_endAnchor(THIS->text);
        !           370: }
        !           371: 
        !           372: 
        !           373: /*     General SGML Element Handling
        !           374: **     -----------------------------
        !           375: */
        !           376: PRIVATE void begin_element ARGS3(void *, this, HTTag *,t, HTElement *,e)
        !           377: {
        !           378:     change_style(THIS, (HTStyle*)(t->style));
        !           379: }
        !           380: PRIVATE void no_change ARGS3(void *, this, HTTag *,t, HTElement *,e)
        !           381: {
        !           382:     /* Do nothing */;
        !           383: }
        !           384: PRIVATE void begin_litteral ARGS3(void *, this, HTTag *,t, HTElement *,e)
        !           385: {
        !           386:     change_style(THIS, t->style);
        !           387:     UPDATE_STYLE;
        !           388: }
        !           389: /*             End Element
        !           390: **
        !           391: **     When we end an element, the style must be returned to that
        !           392: **     in effect before that element.  Note that anchors (etc?)
        !           393: **     don't have an associated style, so that we must scan down the
        !           394: **     stack for an element with a defined style. (In fact, the styles
        !           395: **     should be linked to the whole stack not just the top one.)
        !           396: **     TBL 921119
        !           397: */
        !           398: PRIVATE void end_element ARGS3(void *, this, HTTag *,t, HTElement *,e)
        !           399: {
        !           400:  /*   if (e) change_style(THIS, e->tag->style); */
        !           401:     while (e) {
        !           402:        if (e->tag->style) {
        !           403:                change_style(THIS, e->tag->style);
        !           404:                return;
        !           405:        }
        !           406:        e = e->next;
        !           407:     }
        !           408: }
        !           409: 
        !           410: /*                     Lists
        !           411: */
        !           412: PRIVATE void begin_list ARGS3(void *, this, HTTag *,t, HTElement *,e)
        !           413: {
        !           414:     change_style(THIS, list_attr[LIST_COMPACT].present
        !           415:                ? list_compact_style
        !           416:                : (HTStyle*)(t->style));
        !           417:     THIS->in_word = NO;
        !           418: }
        !           419: 
        !           420: PRIVATE void end_list ARGS3(void *, this, HTTag *,t, HTElement *,e)
        !           421: {
        !           422:     change_style(THIS, e->tag->style);
        !           423:     THIS->in_word = NO;
        !           424: }
        !           425: 
        !           426: PRIVATE void list_element  ARGS3(void *, this, HTTag *,t, HTElement *,e)
        !           427: {
        !           428:     UPDATE_STYLE;
        !           429:     if (e->tag != &tags[DIR_TAG])
        !           430:        HText_appendParagraph(THIS->text);
        !           431:     else
        !           432:         HText_appendCharacter(THIS->text, '\t');       /* Tab @@ nl for UL? */
        !           433:     THIS->in_word = NO;
        !           434: }
        !           435: 
        !           436: 
        !           437: PRIVATE void begin_glossary ARGS3(void *, this, HTTag *,t, HTElement *,e)
        !           438: {
        !           439:     change_style(THIS, glossary_attr[GLOSSARY_COMPACT].present
        !           440:                ? glossary_compact_style
        !           441:                : glossary_style);
        !           442:     THIS->in_word = NO;
        !           443: }
        !           444: 
        !           445: PRIVATE void end_glossary ARGS3(void *, this, HTTag *,t, HTElement *,e)
        !           446: {
        !           447:     change_style(THIS, e->tag->style);
        !           448:     THIS->in_word = NO;
        !           449: }
        !           450: 
        !           451: 
        !           452: /*     Create an HTML object
        !           453: **     ---------------------
        !           454: */
        !           455: PUBLIC HTML_id HTML_new ARGS1(HTParentAnchor *,anchor)
        !           456: {
        !           457: 
        !           458:     HTML_id this = malloc(sizeof(*this));
        !           459: 
        !           460:     if (!got_styles) get_styles();
        !           461: 
        !           462:     this->node_anchor = anchor;
        !           463:     this->title.size = 0;
        !           464:     this->title.growby = 128;
        !           465:     this->title.allocated = 0;
        !           466:     this->title.data = 0;
        !           467:     this->text = 0;
        !           468:     this->style_change = YES; /* Force check leading to text creation */
        !           469:     this->new_style = this->old_style = 0;
        !           470:     
        !           471:     this->context = SGML_begin(&HTML_dtd);
        !           472:     SGML_setCallerData(this->context, this);
        !           473:     
        !           474:     return this;
        !           475: }
        !           476: 
        !           477: 
        !           478: /*     Free an HTML object
        !           479: **     -------------------
        !           480: **
        !           481: **     Note that the SGML parsing context is freed, but the created object is not,
        !           482: **     as it takes on an existence of its own unless explicitly freed.
        !           483: */
        !           484: PUBLIC void HTML_free ARGS1(HTML_id, this)
        !           485: {
        !           486:     SGML_end(this->context);
        !           487:     free(this);
        !           488: }
        !           489: 
        !           490: PUBLIC HTSGMLContext HTML_SGMLContext ARGS1(HTML_id, this)
        !           491: {
        !           492:     return this->context;
        !           493: }
        !           494: 
        !           495: PRIVATE void begin_document ARGS3(void *, this, HTTag *, t, HTElement *, e)
        !           496: {
        !           497:     /* Can't do much, THIS is undefined here */
        !           498: }
        !           499: 
        !           500: PRIVATE void end_document ARGS3(void *, this, HTTag *, t, HTElement *, e)
        !           501: /* If the document is empty, the text object will not yet exist.
        !           502:    So we could in fact abandon creating the document and return
        !           503:    an error code.  In fact an empty document is an important type
        !           504:    of document, so we don't.
        !           505: */
        !           506: {
        !           507:     UPDATE_STYLE;              /* Create empty document here! */
        !           508:     HText_endAppend(THIS->text);
        !           509: 
        !           510: }
        !           511: 
        !           512: /*     Get Styles from style sheet
        !           513: **     ---------------------------
        !           514: */
        !           515: PRIVATE void get_styles NOARGS
        !           516: {
        !           517:     got_styles = YES;
        !           518:     
        !           519:     tags[P_TAG].style =
        !           520:     default_tag.style =                HTStyleNamed(styleSheet, "Normal");
        !           521:     tags[H1_TAG].style =       HTStyleNamed(styleSheet, "Heading1");
        !           522:     tags[H1_TAG+1].style =     HTStyleNamed(styleSheet, "Heading2");
        !           523:     tags[H1_TAG+2].style =     HTStyleNamed(styleSheet, "Heading3");
        !           524:     tags[H1_TAG+3].style =     HTStyleNamed(styleSheet, "Heading4");
        !           525:     tags[H1_TAG+4].style =     HTStyleNamed(styleSheet, "Heading5");
        !           526:     tags[H1_TAG+5].style =     HTStyleNamed(styleSheet, "Heading6");
        !           527:     tags[H1_TAG+6].style =     HTStyleNamed(styleSheet, "Heading7");
        !           528:     tags[DL_TAG].style =       HTStyleNamed(styleSheet, "Glossary");
        !           529:     tags[UL_TAG].style =       HTStyleNamed(styleSheet, "List");
        !           530:     tags[OL_TAG].style =       HTStyleNamed(styleSheet, "List");
        !           531:     tags[MENU_TAG].style =     HTStyleNamed(styleSheet, "Menu");
        !           532:     list_compact_style =
        !           533:     tags[DIR_TAG].style =      HTStyleNamed(styleSheet, "Dir");    
        !           534:     glossary_style =           HTStyleNamed(styleSheet, "Glossary");
        !           535:     glossary_compact_style =   HTStyleNamed(styleSheet, "GlossaryCompact");
        !           536:     tags[ADDRESS_TAG].style=   HTStyleNamed(styleSheet, "Address");
        !           537:     tags[PLAINTEXT_TAG].style =
        !           538:     tags[XMP_TAG].style =      HTStyleNamed(styleSheet, "Example");
        !           539:     tags[PRE_TAG].style =      HTStyleNamed(styleSheet, "Preformatted");
        !           540:     tags[LISTING_TAG].style =  HTStyleNamed(styleSheet, "Listing");
        !           541: }
        !           542: 
        !           543: 
        !           544: /*     Parse an HTML file
        !           545: **     ------------------
        !           546: **
        !           547: **     This version takes a pointer to the routine to call
        !           548: **     to get each character.
        !           549: */
        !           550: BOOL HTML_Parse
        !           551: #ifdef __STDC__
        !           552:   (HTParentAnchor * anchor, char (*next_char)() )
        !           553: #else
        !           554:   (anchor, next_char)
        !           555:     HTParentAnchor * anchor;
        !           556:     char (*next_char)();
        !           557: #endif
        !           558: {
        !           559:        HTSGMLContext context;
        !           560:         HTML_id this = HTML_new(anchor);
        !           561:        context = SGML_begin(&HTML_dtd);
        !           562:        SGML_setCallerData(context, this);
        !           563:        for(;;) {
        !           564:            char character;
        !           565:            character = (*next_char)();
        !           566:            if (character == (char)EOF) break;
        !           567:     
        !           568:            SGML_character(context, character);           
        !           569:          }
        !           570:        SGML_end(context);
        !           571:        free(this);
        !           572:        return YES;
        !           573: }

Webmaster