OSDN Git Service

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