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