OSDN Git Service

* updated copyright.
[modchxj/mod_chxj.git] / src / qs_parse_string.c
1 /*
2  * Copyright (C) 2005-2011 Atsushi Konno All rights reserved.
3  * Copyright (C) 2005 QSDN,Inc. All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 #include <stdio.h>
18 #include <strings.h>
19 #include <stdlib.h>
20
21 #include "chxj_apache.h"
22 #include "qs_parse_string.h"
23 #include "qs_parse_tag.h"
24 #include "qs_log.h"
25
26 #include "mod_chxj.h"
27 #include <iconv.h>
28
29
30 #define NL_COUNT_MAX  (10)
31
32 typedef struct node_stack_element {
33   Node *node;
34   struct node_stack_element *next;
35   struct node_stack_element **ref;
36 } *NodeStackElement;
37
38 typedef struct node_stack {
39   NodeStackElement head;
40   NodeStackElement tail;
41 } *NodeStack;
42
43 static int s_cut_tag (const char *s, int len);
44 static int s_cut_text(const char *s, int len, int script);
45 static void qs_dump_node(Doc *doc, Node *node, int indent);
46 static void qs_push_node(Doc *doc, Node *node, NodeStack stack);
47 static Node *qs_pop_node(Doc *doc, NodeStack stack);
48 #ifdef DUMP_NODE_STACK
49 static void qs_dump_node_stack(Doc *doc, NodeStack stack);
50 #endif
51 static void qs_free_node_stack(Doc *doc, NodeStack stack);
52 static void s_error_check(Doc *doc, const char *name, int line, NodeStack node_stack, NodeStack err_stack);
53 static Node *qs_new_nl_node(Doc *doc);
54
55
56 Node *
57 qs_parse_string(Doc *doc, const char *src, int srclen) 
58 {
59   int     ii;
60   int     nl_cnt = 0;
61   char    encoding[256];
62   char    *osrc;
63   char    *ibuf;
64   size_t  olen;
65   size_t  ilen;
66   int     script_flag = 0;
67   iconv_t cd;
68
69   osrc = NULL;
70   ibuf = NULL;
71   NodeStack node_stack;
72   NodeStack err_stack;
73
74   memset(encoding, 0, 256);
75
76   doc->now_parent_node = qs_init_root_node(doc);
77   if (! src || srclen <= 0) {
78     return doc->root_node;
79   }
80   if (doc->r != NULL) {
81     node_stack = apr_palloc(doc->r->pool, sizeof(struct node_stack));
82     memset(node_stack, 0, sizeof(struct node_stack));
83     err_stack = apr_palloc(doc->r->pool, sizeof(struct node_stack));
84     memset(err_stack, 0, sizeof(struct node_stack));
85   }
86   else {
87     node_stack = calloc(sizeof(struct node_stack), 1);
88     err_stack  = calloc(sizeof(struct node_stack), 1);
89   }
90
91   /* 
92    * It is the pre reading. 
93    * Because I want to specify encoding.
94    */
95   for (ii=0; ii<srclen; ii++) {
96     if (src[ii] == '\n')  nl_cnt++;
97     if (nl_cnt >= NL_COUNT_MAX) break; /* not found <?xml ...> */
98
99     if (is_white_space(src[ii]))
100       continue;
101
102     if ((unsigned char)'<' == src[ii]) {
103       int endpoint = s_cut_tag(&src[ii], srclen - ii);
104       Node *node   = qs_parse_tag(doc, &src[ii], endpoint);
105       if (! node) {
106         QX_LOGGER_FATAL("runtime exception: qs_parse_string(): Out of memory.");
107         return doc->root_node;
108       }
109       ii += endpoint;
110
111       if (node->name[0] != '?') break; 
112
113       if (strcasecmp(node->name, "?xml") == 0) {
114         Attr *parse_attr;
115         for(parse_attr = node->attr;
116             parse_attr && *encoding == '\0'; 
117             parse_attr = parse_attr->next) {
118           if (STRCASEEQ('e','E',"encoding",parse_attr->name)) {
119             switch (*parse_attr->value) {
120             case 'X':
121             case 'x':
122               if (strcasecmp(parse_attr->value, "x-sjis"   ) == 0) {
123                 strcpy((char *)encoding, (char *)"NONE");
124               }
125               break;
126
127             case 'S':
128             case 's':
129               if ((strcasecmp(parse_attr->value, "Shift_JIS") == 0)
130               ||  (strcasecmp(parse_attr->value, "SJIS"     ) == 0)
131               ||  (strcasecmp(parse_attr->value, "Shift-JIS") == 0)) {
132                 strcpy((char *)encoding, (char *)"NONE");
133               }
134               break;
135
136             case 'e':
137             case 'E':
138               if ((strcasecmp(parse_attr->value, "EUC_JP") == 0)
139               ||  (strcasecmp(parse_attr->value, "EUC-JP") == 0)
140               ||  (strcasecmp(parse_attr->value, "EUCJP" ) == 0)) {
141                 strcpy((char *)encoding, "EUC-JP");
142               }
143               break;
144
145             case 'u':
146             case 'U':
147               if ((strcasecmp(parse_attr->value, "UTF-8") == 0)
148               ||  (strcasecmp(parse_attr->value, "UTF8") == 0)) {
149                 strcpy((char *)encoding, "UTF-8");
150               }
151               break;
152
153             default:
154               strcpy((char *)encoding, "NONE");
155               break;
156             }
157           }
158         }
159         break;
160       }
161       break;
162     }
163   }
164
165   if (strcasecmp(encoding, "NONE") != 0 && strlen(encoding) != 0) {
166     char *sv_osrc;
167     olen = srclen * 4 + 1;
168     sv_osrc = osrc =(char *)apr_palloc(doc->pool, olen);
169     memset((char *)osrc, 0, olen);
170     if ((cd = iconv_open("CP932", encoding)) != (iconv_t) -1) {
171       ilen = srclen;
172       ibuf = apr_palloc(doc->pool, ilen+1);
173       memset(ibuf, 0, ilen+1);
174       memcpy(ibuf, src, ilen);
175       while (ilen > 0) {
176         size_t result = iconv(cd, &ibuf, &ilen, &osrc, &olen);
177         if (result == (size_t)(-1)) {
178           break;
179         }
180       }
181       srclen = olen;
182       src = sv_osrc;
183       iconv_close(cd);
184     }
185   }
186
187   /*
188    * Now, true parsing is done here. 
189    */
190   nl_cnt = 1;
191   for (ii=0; ii<srclen; ii++) {
192     if (src[ii] == '\n') {
193       nl_cnt++;
194       if (doc->now_parent_node != NULL) {
195         Node *node = qs_new_nl_node(doc);
196         qs_add_child_node(doc,node);
197       }
198     }
199     if (doc->parse_mode != PARSE_MODE_NO_PARSE 
200         && is_white_space(src[ii])
201         && (doc->now_parent_node == NULL || !STRCASEEQ('p','P',"pre",doc->now_parent_node->name))) {
202       continue;
203     }
204     if ((unsigned char)'<' == src[ii] && strncasecmp("<![CDATA[",&src[ii], sizeof("<![CDATA[")-1) != 0) {
205       int endpoint = s_cut_tag(&src[ii], srclen - ii);
206       Node *node   = qs_parse_tag(doc, &src[ii], endpoint);
207       if (! node) {
208         QX_LOGGER_FATAL("runtime exception: qs_parse_string(): Out of memory.");
209         return doc->root_node;
210       }
211       node->line = nl_cnt;
212
213       ii += endpoint;
214       if (node->name[0] == '/' ) {
215         if (doc->parse_mode == PARSE_MODE_CHTML) {
216           if (has_child(&(node->name[1]))) {
217             if (doc->now_parent_node->parent != NULL) {
218               doc->now_parent_node = doc->now_parent_node->parent;
219               doc->parse_mode = PARSE_MODE_CHTML;
220             }
221             if (STRCASEEQ('s','S',"script",&node->name[1])) {
222               script_flag = 0;
223             }
224             s_error_check(doc, &node->name[1], node->line, node_stack, err_stack);
225           }
226           else {
227             /* ignore */
228             continue;
229           }
230         }
231         else
232         if (doc->parse_mode == PARSE_MODE_NO_PARSE) {
233           if (STRCASEEQ('c','C',"chxj:if",&node->name[1]) || STRCASEEQ('p','P',"plaintext",&node->name[1]) || STRCASEEQ('c','C',"chxj:raw",&node->name[1])) {
234             if (doc->now_parent_node->parent != NULL) {
235               doc->now_parent_node = doc->now_parent_node->parent;
236               doc->parse_mode = PARSE_MODE_CHTML;
237               s_error_check(doc, &node->name[1], node->line, node_stack, err_stack);
238             }
239           }
240         }
241
242         if (doc->parse_mode != PARSE_MODE_NO_PARSE) {
243           continue;
244         }
245       }
246       if (*node->name == '!' && strncmp(node->name, "!--", 3) == 0) {
247         /* comment tag */
248         continue;
249       }
250       qs_add_child_node(doc,node);
251       if ((has_child(node->name) && doc->parse_mode != PARSE_MODE_NO_PARSE) || STRCASEEQ('p','P',"plaintext",node->name)) {
252         qs_push_node(doc, node, node_stack);
253       }
254
255       if (doc->parse_mode == PARSE_MODE_NO_PARSE) {
256         if (node->name[0] == '/')
257           continue;
258       }
259
260       if (doc->parse_mode == PARSE_MODE_CHTML && (STRCASEEQ('c','C',"chxj:if", node->name) || STRCASEEQ('c','C',"chxj:raw",node->name))) {
261         Attr *parse_attr;
262         doc->parse_mode = PARSE_MODE_NO_PARSE;
263         doc->now_parent_node = node;
264         for(parse_attr = node->attr;
265             parse_attr; 
266             parse_attr = parse_attr->next) {
267           if (STRCASEEQ('p','P',"parse",parse_attr->name)) {
268             if (STRCASEEQ('t','T',"true",parse_attr->value)) {
269               doc->parse_mode = PARSE_MODE_CHTML;
270             }
271           }
272         }
273       }
274       else if (doc->parse_mode == PARSE_MODE_CHTML && STRCASEEQ('p','P',"plaintext",node->name)) {
275         doc->parse_mode = PARSE_MODE_NO_PARSE;
276         doc->now_parent_node = node;
277       }
278
279       if (doc->parse_mode == PARSE_MODE_CHTML && has_child(node->name)) {
280         doc->now_parent_node = node;
281       }
282       if (STRCASEEQ('s','S',"script", node->name)) {
283         script_flag = 1;
284       }
285       if (doc->parse_mode == PARSE_MODE_CHTML && node->closed_by_itself) {
286         if (has_child(node->name)) {
287           if (doc->now_parent_node->parent != NULL) {
288             doc->now_parent_node = doc->now_parent_node->parent;
289             doc->parse_mode = PARSE_MODE_CHTML;
290           }
291           if (STRCASEEQ('s','S',"script",node->name)) {
292             script_flag = 0;
293           }
294           s_error_check(doc, node->name, node->line, node_stack, err_stack);
295         }
296         else {
297           /* ignore */
298           continue;
299         }
300       }
301     }
302     else if (strncasecmp("<![CDATA[", &src[ii], sizeof("<![CDATA[") - 1) == 0) {
303       /* TEXT */
304       int endpoint = s_cut_tag(&src[ii], srclen - ii);
305       Node *node = qs_new_tag(doc);
306       if (! node) {
307         QX_LOGGER_DEBUG("runtime exception: qs_parse_string(): Out of memory");
308         return doc->root_node;
309       }
310       node->value = (char *)apr_palloc(doc->pool,endpoint+1);
311       node->name  = (char *)apr_palloc(doc->pool,4+1);
312       node->otext = (char *)apr_palloc(doc->pool,endpoint+1);
313       node->size  = endpoint;
314       node->line  = nl_cnt;
315       memset(node->value, 0, endpoint+1);
316       memset(node->otext, 0, endpoint+1);
317       memset(node->name,  0, 4+1       );
318       memcpy(node->value, &src[ii+sizeof("<![CDATA[")-1], endpoint - (sizeof("<![CDATA[")-1) - (sizeof("]]")-1));
319       memcpy(node->name,  "text",   4);
320       memcpy(node->otext,node->value, endpoint);
321
322       qs_add_child_node(doc,node);
323       ii += (endpoint - 1);
324     }
325     else {
326       /* TEXT */
327       int endpoint = s_cut_text(&src[ii], srclen - ii, script_flag);
328       Node *node = qs_new_tag(doc);
329       if (! node) {
330         QX_LOGGER_DEBUG("runtime exception: qs_parse_string(): Out of memory");        
331         return doc->root_node;
332       }
333       node->value = (char *)apr_palloc(doc->pool,endpoint+1);
334       node->name  = (char *)apr_palloc(doc->pool,4+1);
335       node->otext = (char *)apr_palloc(doc->pool,endpoint+1);
336       node->size  = endpoint;
337       node->line  = nl_cnt;
338       memset(node->value, 0, endpoint+1);
339       memset(node->otext, 0, endpoint+1);
340       memset(node->name,  0, 4+1       );
341       memcpy(node->value, &src[ii], endpoint);
342       memcpy(node->name,  "text",   4);
343       memcpy(node->otext,node->value, endpoint);
344
345       qs_add_child_node(doc,node);
346       ii += (endpoint - 1);
347     }
348   }
349 #ifdef DEBUG
350   QX_LOGGER_DEBUG("parse_string end");
351 #endif
352 #ifdef DEBUG
353   if (doc->r != NULL) {
354     qs_dump_node(doc, doc->root_node, 0);
355   }
356 #endif
357 #ifdef DUMP_NODE_STACK
358   qs_dump_node_stack(doc, node_stack);
359 #endif
360   {
361     Node *prevNode;
362     for (prevNode = qs_pop_node(doc,node_stack);
363          prevNode;
364          prevNode = qs_pop_node(doc, node_stack)) {
365       if (has_child(prevNode->name)) {
366         if (doc->r)
367           ERR(doc->r, "tag parse error (perhaps, not close). tag_name:[%s] line:[%d]", prevNode->name, prevNode->line);
368         else
369           fprintf(stderr, "error :tag parse error (perhaps, not close). tag_name:[%s] line:[%d]\n", prevNode->name, prevNode->line);
370       }
371     }
372   }
373   qs_free_node_stack(doc, node_stack); node_stack = NULL;
374   qs_free_node_stack(doc, err_stack);  err_stack = NULL;
375   return doc->root_node;
376 }
377
378
379 static void
380 s_error_check(Doc *doc, const char *name, int line, NodeStack node_stack, NodeStack err_stack) 
381 {
382   Node *prevNode;
383   int err = 0;
384   for (prevNode = qs_pop_node(doc,node_stack);
385        prevNode;
386        prevNode = qs_pop_node(doc, node_stack)) {
387     if (prevNode && strcasecmp(prevNode->name, name) != 0) {
388       qs_push_node(doc, prevNode, err_stack);
389       err++;
390       continue;
391     }
392     break;
393   }
394   if (err) {
395     Node *tmpNode = qs_pop_node(doc,node_stack);
396     if (tmpNode == NULL && err != 1) {
397       if (doc->r) 
398         ERR(doc->r, "tag parse error (perhaps, miss spell). tag_name:[%s] line:[%d]", name, line);
399       else
400         fprintf(stderr, "error :tag parse error (perhaps, miss spell). tag_name:[%s] line:[%d]\n", name, line);
401       for (prevNode = qs_pop_node(doc,err_stack);
402            prevNode;
403            prevNode = qs_pop_node(doc, err_stack)) {
404         qs_push_node(doc, prevNode, node_stack);
405       }
406     }
407     else {
408       for (prevNode = qs_pop_node(doc,err_stack);
409            prevNode;
410            prevNode = qs_pop_node(doc, err_stack)) {
411         if (doc->r)
412           ERR(doc->r, "tag parse error (perhaps, not close). tag_name:[%s] line:[%d]", prevNode->name, prevNode->line);
413         else
414           fprintf(stderr, "error :tag parse error (perhaps, not close). tag_name:[%s] line:[%d]\n", prevNode->name, prevNode->line);
415       }
416       qs_push_node(doc, tmpNode, node_stack);
417     }
418     err = 0;
419   }
420 }
421
422
423 static void
424 qs_dump_node(Doc *doc, Node *node, int indent) 
425 {
426   Node *p = (Node *)qs_get_child_node(doc,node);
427
428   for (;p;p = (Node *)qs_get_next_node(doc,p)) {
429     Attr *attr;
430     if ((char *)qs_get_node_value(doc,p) != NULL) {
431       DBG(doc->r,"%*.*sNode:[%s][%s]\n", indent,indent," ",
432                       (char *)qs_get_node_name(doc,p),
433                       (char *)qs_get_node_value(doc,p));
434     }
435     else {
436       DBG(doc->r,"%*.*sNode:[%s]\n", indent,indent," ", qs_get_node_name(doc,p));
437     }
438     for (attr = (Attr *)qs_get_attr(doc,p); attr; attr = (Attr *)qs_get_next_attr(doc,attr)) {
439       DBG(doc->r,"%*.*s  ATTR:[%s]\n", indent,indent," ", (char *)qs_get_attr_name(doc,attr));
440       DBG(doc->r,"%*.*s  VAL :[%s]\n", indent,indent," ", (char *)qs_get_attr_value(doc,attr));
441     }
442     qs_dump_node(doc,p, indent+4);
443   }
444 }
445
446
447
448 int
449 chxj_cut_tag(const char *s, int len)
450 {
451   return s_cut_tag(s, len);
452 }
453
454
455 static int
456 s_cut_tag(const char *s, int len) 
457 {
458   int lv = 0;
459   int ii;
460   int comment = 0;
461   int cdata = 0;
462
463   if (strncmp("<!--", s, 4) == 0) {
464     comment = 1;
465   }
466   else if (strncasecmp("<![CDATA[", s, sizeof("<![CDATA[")-1) == 0) {
467     cdata = 1;
468   }
469
470   for (ii=0;ii<len; ii++) {
471     if (is_sjis_kanji(s[ii])) {
472       ii++;
473       continue;
474     }
475     if (is_sjis_kana(s[ii])) 
476       continue;
477
478     if (is_white_space(s[ii])) 
479       continue;
480
481     if (s[ii] == '<') {
482       lv++;
483       continue;
484     }
485     if (comment && s[ii] == '-') {
486       if (strncmp(&s[ii], "-->", 3) == 0) {
487         ii += 2;
488         break;
489       }
490     }
491     if (cdata && s[ii] == ']') {
492       if (strncmp(&s[ii], "]]>", 3) == 0) {
493         ii += 2;
494         break;
495       }
496     }
497
498     if (!cdata && !comment && s[ii] == '>') {
499       lv--;
500       if (lv == 0) 
501         break;
502       continue;
503     }
504   }
505   return ii;
506 }
507
508 static int
509 s_cut_text(const char *s, int len, int script)
510 {
511   register int ii;
512   register int sq = 0;
513   register int dq = 0;
514
515   for (ii=0;ii<len; ii++) {
516     if (is_sjis_kanji(s[ii])) {
517       ii++;
518       continue;
519     }
520     if (is_sjis_kana(s[ii])) 
521       continue;
522
523     if (is_white_space(s[ii])) 
524       continue;
525
526     if (script) {
527       if (s[ii] == '\\') {
528         ii++;
529         continue;
530       }
531       /* single quote */
532       if (s[ii] == '\'' && !dq) {
533         if (sq) sq--;
534         else    sq++;
535       }
536       /* double quote */
537       if (s[ii] == '"' && !sq) {
538         if (dq) dq--;
539         else    dq++;
540       }
541     }
542
543     if (!sq && !dq && s[ii] == '<') 
544       break;
545
546   }
547
548   return ii;
549 }
550
551
552 Node *
553 qs_init_root_node(Doc *doc) 
554 {
555   doc->root_node = (Node *)apr_palloc(doc->pool,sizeof(struct Node));
556   if (doc->root_node == NULL) {
557     QX_LOGGER_FATAL("Out Of Memory");
558   }
559
560   doc->root_node->next   = NULL;
561   doc->root_node->parent = NULL;
562   doc->root_node->child  = NULL;
563   doc->root_node->attr   = NULL;
564
565   doc->root_node->name   = (char *)apr_palloc(doc->pool, 5);
566   if (doc->root_node->name == NULL) {
567     QX_LOGGER_FATAL("Out Of Memory");
568   }
569   memset(doc->root_node->name, 0, 5);
570   strcpy(doc->root_node->name, "ROOT");
571
572   return doc->root_node;
573 }
574
575
576
577 void
578 qs_add_child_node(Doc *doc,Node *node) 
579 {
580   node->next       = NULL;
581   node->child      = NULL;
582   node->child_tail = NULL;
583   node->parent = doc->now_parent_node;
584   if (doc->now_parent_node->child == NULL) {
585     doc->now_parent_node->child      = node;
586     doc->now_parent_node->child_tail = node;
587   }
588   else {
589 #ifdef DEBUG
590     QX_LOGGER_DEBUG("search child free node");
591 #endif
592     doc->now_parent_node->child_tail->next = node;
593     doc->now_parent_node->child_tail       = node;
594   }
595 }
596
597
598
599
600 Node *
601 qs_get_root(Doc *doc) {
602   return doc->root_node;
603 }
604
605
606
607
608 char * 
609 qs_get_node_value(Doc *UNUSED(doc), Node *node) {
610   return node->value;
611 }
612
613
614
615
616 char *
617 qs_get_node_name(Doc *UNUSED(doc), Node *node) {
618   return node->name;
619 }
620
621
622
623 Node *
624 qs_get_child_node(Doc *UNUSED(doc), Node *node) {
625   return node->child;
626 }
627
628
629
630
631 Node *
632 qs_get_next_node(Doc *UNUSED(doc), Node *node) {
633   return node->next;
634 }
635
636
637
638 Attr *
639 qs_get_attr(Doc *UNUSED(doc), Node *node) {
640   return node->attr;
641 }
642
643
644
645
646 Attr *
647 qs_get_next_attr(Doc *UNUSED(doc), Attr *attr) {
648   return attr->next;
649 }
650
651
652
653 char *
654 qs_get_attr_name(Doc *UNUSED(doc), Attr *attr) {
655   return attr->name;
656 }
657
658
659
660 char *
661 qs_get_attr_value(Doc *UNUSED(doc), Attr *attr) {
662   return attr->value;
663 }
664
665 int 
666 qs_get_node_size(Doc *UNUSED(doc), Node *node) {
667   return node->size;
668 }
669
670
671 #define list_insert(node, point) do {           \
672     node->ref = point->ref;                     \
673     *node->ref = node;                          \
674     node->next = point;                         \
675     point->ref = &node->next;                   \
676 } while (0)
677
678 #define list_remove(node) do {                  \
679     *node->ref = node->next;                    \
680     node->next->ref = node->ref;                \
681 } while (0)
682
683
684 static void 
685 qs_push_node(Doc *doc, Node *node, NodeStack stack)
686 {
687   NodeStackElement elem;
688   if (doc->r != NULL) {
689     elem = apr_palloc(doc->r->pool, sizeof(struct node_stack_element));
690     memset(elem, 0, sizeof(struct node_stack_element));
691   }
692   else {
693     elem = malloc(sizeof(struct node_stack_element));
694     memset(elem, 0, sizeof(struct node_stack_element));
695   }
696   elem->node = node;
697   if (stack->head == NULL) {
698     /* add dummy head */
699     if (doc->r != NULL) {
700       stack->head = apr_palloc(doc->r->pool, sizeof(struct node_stack_element));
701       memset(stack->head, 0, sizeof(struct node_stack_element));
702     }
703     else {
704       stack->head = malloc(sizeof(struct node_stack_element));
705       memset(stack->head, 0, sizeof(struct node_stack_element));
706     }
707     stack->head->next = stack->head;
708     stack->head->ref = &stack->head->next;
709   }
710   list_insert(elem, stack->head);
711   stack->tail = elem;
712 }
713
714 #include "apr_ring.h"
715
716 static Node *
717 qs_pop_node(Doc *doc, NodeStack stack)
718 {
719   NodeStackElement tail = stack->tail;
720   Node *result;
721
722   if (tail == NULL) return NULL;
723   if (tail == stack->head) return NULL;
724   result = tail->node;
725
726
727   list_remove(tail);
728   stack->tail = (NodeStackElement)((apr_size_t)stack->head->ref - (apr_size_t)APR_OFFSETOF(struct node_stack_element, next));
729   if (doc->r == NULL)
730     free(tail);
731
732   return result;
733 }
734
735 #ifdef DUMP_NODE_STACK
736 static void
737 qs_dump_node_stack(Doc *doc, NodeStack stack)
738 {
739   NodeStackElement elm;
740   for (elm = stack->head->next;elm != stack->head; elm = elm->next) {
741     if (doc->r) DBG(doc->r, "name:[%s]", elm->node->name);
742      else       fprintf(stderr, "[%x] name:[%s] next:[%x]\n", (apr_size_t)elm, elm->node->name, (apr_size_t)elm->next);
743   }
744 }
745 #endif
746
747 static void
748 qs_free_node_stack(Doc *doc, NodeStack stack)
749 {
750   if (doc->r == NULL && stack != NULL) {
751     Node *elm;
752     for (elm = qs_pop_node(doc, stack);elm; elm = qs_pop_node(doc,stack))
753       ;
754     if (stack->head) 
755       free(stack->head);
756     free(stack);
757   }
758 }
759
760
761 static Node *
762 qs_new_nl_node(Doc *doc)
763 {
764   Node *node = (Node *)qs_new_tag(doc);
765   if (! node) {
766     QX_LOGGER_DEBUG("runtime exception: qs_parse_tag(): Out of memory.");
767     return NULL;
768   }
769   node->name = apr_pstrdup(doc->pool, QS_PARSE_NL_MARK);
770   node->otext = NULL;
771   return node;
772 }
773 /*
774  * vim:ts=2 et
775  */