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