1 /*
2  * Copyright (C) 2003-2005 Tommi Maekitalo
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * As a special exception, you may use this file as part of a free
10  * software library without restriction. Specifically, if other files
11  * instantiate templates or use macros or inline functions from this
12  * file, or you compile this file and link it with other files to
13  * produce an executable, this file does not by itself cause the
14  * resulting executable to be covered by the GNU General Public
15  * License. This exception does not however invalidate any other
16  * reasons why the executable file might be covered by the GNU Library
17  * General Public License.
18  *
19  * This library is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22  * Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public
25  * License along with this library; if not, write to the Free Software
26  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
27  */
28 
29 
30 #include "tnt/ecpp/parser.h"
31 #include "tnt/ecpp/parsehandler.h"
32 #include <sstream>
33 #include <fstream>
34 #include <cctype>
35 #include <cxxtools/log.h>
36 
37 log_define("tntnet.parser")
38 
39 namespace tnt
40 {
41   namespace
42   {
isVariableNameChar(char ch)43     inline bool isVariableNameChar(char ch)
44     {
45       return std::isalnum(ch) || ch == '_';
46     }
47   }
48 
49   namespace ecpp
50   {
51     static const char split_start = '{';
52     static const char split_end = '}';
53 
doInclude(const std::string & file)54     void Parser::doInclude(const std::string& file)
55     {
56       log_debug("include \"" << file << '"');
57 
58       std::string fullname = file;
59       std::ifstream inp(file.c_str());
60 
61       for (includes_type::const_iterator it = includes.begin();
62            !inp && it != includes.end(); ++it)
63       {
64         fullname = *it + '/' + file;
65         log_debug("try include \"" << fullname << '"');
66         inp.open(fullname.c_str());
67       }
68 
69       if (!inp)
70       {
71         std::ostringstream msg;
72         throw std::runtime_error("cannot open include file \"" + file + '"');
73       }
74 
75       std::string curfileSave = curfile;
76       unsigned curlineSave = curline;
77       curfile = fullname;
78       curline = 0;
79 
80       log_debug("onInclude(\"" << fullname << "\")");
81       handler.onInclude(fullname);
82 
83       parsePriv(inp);
84 
85       curfile = curfileSave;
86       curline = curlineSave;
87 
88       log_debug("onIncludeEnd(\"" << fullname << "\")");
89       handler.onIncludeEnd(fullname);
90     }
91 
parsePriv(std::istream & in)92     void Parser::parsePriv(std::istream& in)
93     {
94       enum state_type {
95         state_html0,  // 0
96         state_html,
97         state_htmlesc,
98         state_tagstart,
99         state_expr,
100         state_expre0,
101         state_tag,
102         state_tagarg0,
103         state_tagarg,
104         state_tagarge,
105         state_defarg0,  // 10
106         state_defarg,
107         state_defargdefval0,
108         state_defargdefval,
109         state_defarg_end,
110         state_cpp,
111         state_cppse,
112         state_cppe0,
113         state_cppe1,
114         state_cppetag,
115         state_cppstring,  // 20
116         state_cppstringesc,
117         state_cppchar,
118         state_cppcharesc,
119         state_cppchare,
120         state_cppcomment0,
121         state_cppcomment,
122         state_cppcommentc,
123         state_cppcommentce,
124         state_nl,
125         state_cpp1,
126         state_args0,
127         state_args0comment,
128         state_argsvar,  // 30
129         state_argsvare,
130         state_argsval,
131         state_argsvalstring,
132         state_argscomment0,
133         state_argscomment,
134         state_argsvalcomment0,
135         state_attr0,
136         state_attr0comment,
137         state_attrvar,
138         state_attrvare,  // 40
139         state_attrval,
140         state_attrvalstring,
141         state_attrcomment0,
142         state_attrcomment,
143         state_attrvalcomment0,
144         state_call0,
145         state_callname_expr,
146         state_callname_string,
147         state_callname,
148         state_call_cpparg0,  // 50
149         state_call_cpparg1,
150         state_call_cpparg_pe,
151         state_call_cpparg_sp,
152         state_call_cpparg_e,
153         state_callend,
154         state_callarg0,
155         state_callarg,
156         state_callarge,
157         state_callval_expr,
158         state_callval_string,  // 60
159         state_callval_word,
160         state_callval0,
161         state_callvale,
162         state_comment,
163         state_commente,
164         state_compe0,
165         state_compe,
166         state_cond0,
167         state_cond,
168         state_condexpr,
169         state_condexpre,  // 70
170         state_include0,
171         state_include1,
172         state_scopearg0,
173         state_scopearg,
174         state_scopeargeq,
175         state_scopeargval0,
176         state_scopeargval,
177         state_scopevale,
178         state_scope0,
179         state_scope,  // 80
180         state_scopeinit,
181         state_scopeiniteq,
182         state_scopee,
183         state_scopee0,
184         state_scopecomment0,
185         state_scopecomment,
186         state_endcall0,
187         state_endcall,
188         state_endcalle,
189         state_doc,  // 90
190         state_doce
191       };
192 
193       state_type state = state_nl;
194       std::string tag, etag, tagarg;
195       std::string html, code, arg, argtype, value;
196       std::string comp;
197       std::string cond, expr;
198       comp_args_type comp_args;
199       std::string pass_cgi;
200       std::string defarg, defval, paramname;
201       cppargs_type cppargs;
202       paramargs_type paramargs;
203       unsigned bracket_count = 0;
204       std::string scopetype, scopevar, scopeinit;
205       scope_container_type scope_container = application_container;
206       scope_type scope = global_scope;
207       bool inComp = false;
208       bool inClose = false;
209       bool htmlExpr = false;
210       bool splitBar = false;
211 
212       handler.start();
213 
214       char ch;
215       while (in.get(ch))
216       {
217         if (ch == '\n')
218           ++curline;
219 
220         switch(state)
221         {
222           case state_html0:
223             if (ch == ' ' || ch == '\t')
224               std::cerr << curfile << ':' << curline + 1 << ": warning: trailing white space after closing tag" << std::endl;
225             else if (ch == '\r')
226               std::cerr << curfile << ':' << curline + 1 << ": warning: trailing cr after closing tag (dos format?)" << std::endl;
227             // no break - continue with state_html
228 
229           case state_html:
230             if (ch == '<')
231             {
232               state = state_tagstart;
233             }
234             else if (ch == '\n')
235             {
236               if (state == state_html || !html.empty())
237                 html += ch;
238               state = state_nl;
239             }
240             else if (splitBar && (ch == split_start|| ch == split_end))
241             {
242               if (!html.empty() || ch == split_end)
243               {
244                 log_debug("onHtml(\"" << html << "\")");
245                 handler.onHtml(html);
246                 html.clear();
247               }
248               handler.tokenSplit(ch == split_start);
249             }
250             else if (ch == '\\')
251               state = state_htmlesc;
252             else
253             {
254               html += ch;
255               state = state_html;
256             }
257             break;
258 
259           case state_htmlesc:
260             if (ch != '\n')
261               html += ch;
262             state = state_html;
263             break;
264 
265           case state_tagstart:
266             if (ch == '%')
267             {
268               if (!html.empty())
269               {
270                 log_debug("onHtml(\"" << html << "\")");
271                 handler.onHtml(html);
272                 html.clear();
273               }
274               tag.clear();
275               handler.onLine(curline, curfile); //#
276               state = state_tag;
277             }
278             else if (ch == '$')
279             {
280               if (!html.empty())
281               {
282                 log_debug("onHtml(\"" << html << "\")");
283                 handler.onHtml(html);
284                 html.clear();
285               }
286               handler.onLine(curline, curfile); //#
287               state = state_expr;
288             }
289             else if (ch == '&')
290             {
291               if (!html.empty())
292               {
293                 log_debug("onHtml(\"" << html << "\")");
294                 handler.onHtml(html);
295                 html.clear();
296               }
297               handler.onLine(curline, curfile); //#
298               state = state_call0;
299             }
300             else if (ch == '#')
301             {
302               if (!html.empty())
303               {
304                 log_debug("onHtml(\"" << html << "\")");
305                 handler.onHtml(html);
306                 html.clear();
307               }
308               state = state_comment;
309             }
310             else if (ch == '{')
311             {
312               if (!html.empty())
313               {
314                 log_debug("onHtml(\"" << html << "\")");
315                 handler.onHtml(html);
316                 html.clear();
317               }
318               handler.onLine(curline, curfile); //#
319               state = state_cpp;
320             }
321             else if (ch == '?')
322             {
323               if (!html.empty())
324               {
325                 handler.onHtml(html);
326                 html.clear();
327               }
328               handler.onLine(curline, curfile); //#
329               state = state_cond0;
330             }
331             else if (ch == '/')
332             {
333               state = state_compe0;
334             }
335             else if (ch == '\\')
336             {
337               html += '<';
338               state = state_html;
339             }
340             else
341             {
342               html += '<';
343               html += ch;
344               state = state_html;
345             }
346             break;
347 
348           case state_expr:
349             if (ch == '$')
350             {
351               if (!htmlExpr && code.empty())
352                 // <$$ ... $>
353                 htmlExpr = true;
354               else
355                 // expression might end
356                 state = state_expre0;
357             }
358             else
359               code += ch;
360             break;
361 
362           case state_expre0:
363             if (ch == '>')
364             {
365               // expression ends, html continues
366               if (htmlExpr)
367               {
368                 log_debug("onHtmlExpression(\"" << code << "\")");
369                 handler.onHtmlExpression(code);
370                 htmlExpr = false;
371               }
372               else
373               {
374                 log_debug("onExpression(\"" << code << "\")");
375                 handler.onExpression(code);
376               }
377               code.clear();
378               state = state_html;
379             }
380             else
381             {
382               // expression does not end
383               code += '$';
384               code += ch;
385               if (ch != '$')
386                 state = state_expr;
387             }
388             break;
389 
390           case state_tag:
391             if (ch == '>')
392             {
393               if (tag == "args" || tag == "get" || tag == "post" || tag == "config")
394                 state = state_args0;
395               else if (tag == "attr")
396                 state = state_attr0;
397               else if (tag == "include")
398                 state = state_include0;
399               else if (tag == "application")
400               {
401                 scope_container = application_container;
402                 scope = component_scope;
403                 state = state_scope0;
404               }
405               else if (tag == "thread")
406               {
407                 scope_container = thread_container;
408                 scope = component_scope;
409                 state = state_scope0;
410               }
411               else if (tag == "session")
412               {
413                 scope_container = session_container;
414                 scope = component_scope;
415                 state = state_scope0;
416               }
417               else if (tag == "securesession")
418               {
419                 scope_container = secure_session_container;
420                 scope = component_scope;
421                 state = state_scope0;
422               }
423               else if (tag == "request")
424               {
425                 scope_container = request_container;
426                 scope = component_scope;
427                 state = state_scope0;
428               }
429               else if (tag == "param")
430               {
431                 scope_container = param_container;
432                 scope = component_scope;
433                 state = state_scope0;
434               }
435               else if (!inClose && tag == "close")
436               {
437                 handler.startClose();
438                 state = state_html0;
439                 inClose = true;
440               }
441               else if (tag == "i18n")
442               {
443                 splitBar = true;
444                 handler.startI18n();
445                 state = state_html0;
446               }
447               else if (tag == "doc")
448                 state = state_doc;
449               else
450                 state = state_cpp;
451             }
452             else if (!inComp && tag == "def" && std::isspace(ch))
453               state = state_tagarg0;
454             else if (std::isspace(ch))
455             {
456               if (tag == "application")
457               {
458                 scope_container = application_container;
459                 state = state_scopearg0;
460               }
461               else if (tag == "thread")
462               {
463                 scope_container = thread_container;
464                 state = state_scopearg0;
465               }
466               else if (tag == "session")
467               {
468                 scope_container = session_container;
469                 state = state_scopearg0;
470               }
471               else if (tag == "securesession")
472               {
473                 scope_container = secure_session_container;
474                 state = state_scopearg0;
475               }
476               else if (tag == "request")
477               {
478                 scope_container = request_container;
479                 state = state_scopearg0;
480               }
481               else if (tag == "param")
482               {
483                 scope_container = param_container;
484                 state = state_scopearg0;
485               }
486               else
487               {
488                 state_type s = state;
489                 state = state_html0;
490                 throw parse_error("unknown tag " + tag, s, curfile, curline);
491               }
492             }
493             else
494               tag += ch;
495             break;
496 
497           case state_tagarg0:
498             if (ch == '>')
499               state = state_cpp;
500             else if (!std::isspace(ch))
501             {
502               tagarg = ch;
503               state = state_tagarg;
504             }
505             break;
506 
507           case state_tagarg:
508             if (std::isspace(ch))
509               state = state_tagarge;
510             else if (tag == "def" && ch == '(')
511               state = state_defarg0;
512             else if (ch == '>')
513             {
514               if (tag == "args" || tag == "get" || tag == "post")
515                 state = state_args0;
516               else if (tag == "attr")
517                 state = state_attr0;
518               else if (tag == "def")
519               {
520                 handler.startComp(tagarg, cppargs);
521                 state = state_html0;
522                 inComp = true;
523               }
524               else if (tag == "close")
525               {
526                 handler.startClose();
527                 state = state_html0;
528                 inClose = true;
529               }
530               else if (tag == "i18n")
531               {
532                 splitBar = true;
533                 handler.startI18n();
534                 state = state_html0;
535               }
536               else
537                 state = state_cpp;
538             }
539             else
540               tagarg += ch;
541             break;
542 
543           case state_tagarge:
544             if (ch == '>')
545             {
546               if (tag == "args" || tag == "get" || tag == "post")
547                 state = state_args0;
548               else if (tag == "attr")
549                 state = state_attr0;
550               else if (tag == "def")
551               {
552                 handler.startComp(tagarg, cppargs);
553                 state = state_html0;
554                 inComp = true;
555               }
556               else if (tag == "close")
557               {
558                 handler.startClose();
559                 state = state_html0;
560                 inClose = true;
561               }
562               else if (tag == "i18n")
563               {
564                 splitBar = true;
565                 handler.startI18n();
566                 state = state_html0;
567               }
568               else
569                 state = state_cpp;
570             }
571             else if (tag == "def" && ch == '(')
572               state = state_defarg0;
573             break;
574 
575           case state_defarg0:
576             if (ch == ')')
577               state = state_defarg_end;
578             else if (!std::isspace(ch))
579             {
580               defarg += ch;
581               state = state_defarg;
582             }
583             break;
584 
585           case state_defarg:
586             if (ch == '=')
587               state = state_defargdefval0;
588             else if (ch == ')')
589             {
590               cppargs.push_back(std::make_pair(defarg, std::string()));
591               defarg.clear();
592               state = state_defarg_end;
593             }
594             else if (ch == ',')
595             {
596               cppargs.push_back(std::make_pair(defarg, std::string()));
597               defarg.clear();
598               state = state_defarg0;
599             }
600             else
601               defarg += ch;
602             break;
603 
604           case state_defargdefval0:
605             if (!std::isspace(ch))
606             {
607               defval = ch;
608               state = state_defargdefval;
609             }
610             break;
611 
612           case state_defargdefval:
613             if (ch == ')')
614             {
615               cppargs.push_back(std::make_pair(defarg, defval));
616               defarg.clear();
617               defval.clear();
618               state = state_defarg_end;
619             }
620             else if (ch == ',')
621             {
622               cppargs.push_back(std::make_pair(defarg, defval));
623               defarg.clear();
624               defval.clear();
625               state = state_defarg;
626             }
627             else
628               defval += ch;
629             break;
630 
631           case state_defarg_end:
632             if (ch == '>')
633             {
634               handler.startComp(tagarg, cppargs);
635               inComp = true;
636               cppargs.clear();
637               state = state_html0;
638             }
639             else if (!std::isspace(ch))
640             {
641               state_type s = state;
642               state = state_html0;
643               throw parse_error("def", s, curfile, curline);
644             }
645             break;
646 
647           case state_cppcomment0:
648             if (ch == '/')
649             {
650               code += "//";
651               state = state_cppcomment;
652               break;
653             }
654             else if (state == '*')
655             {
656               code += "/*";
657               state = state_cppcommentc;
658               break;
659             }
660 
661             code += '/';
662             state = state_cpp;
663 
664             // no break
665 
666           case state_cpp:
667             if (ch == '<')
668               state = state_cppe0;
669             else if (ch == '}')
670               state = state_cppse;
671             else if (ch == '/')
672               state = state_cppcomment0;
673             else
674             {
675               code += ch;
676               if (ch == '"')
677                 state = state_cppstring;
678               else if (ch == '\'')
679                 state = state_cppchar;
680             }
681             break;
682 
683           case state_cppstring:
684             code += ch;
685             if (ch == '"')
686               state = state_cpp;
687             else if (ch == '\\')
688               state = state_cppstringesc;
689             break;
690 
691           case state_cppstringesc:
692             code += ch;
693             state = state_cppstring;
694             break;
695 
696           case state_cppchar:
697             code += ch;
698             if (ch == '\\')
699               state = state_cppcharesc;
700             else
701               state = state_cppchare;
702             break;
703 
704           case state_cppcharesc:
705             code += ch;
706             state = state_cppchare;
707             break;
708 
709           case state_cppchare:
710             code += ch;
711             if (ch == '\'')
712               state = state_cpp;
713             break;
714 
715           case state_cppcomment:
716             code += ch;
717             if (ch == '\n')
718               state = state_cpp;
719             break;
720 
721           case state_cppcommentc:
722             code += ch;
723             if (ch == '*')
724               state = state_cppcommentce;
725             break;
726 
727           case state_cppcommentce:
728             code += ch;
729             if (ch == '/')
730               state = state_cpp;
731             break;
732 
733           case state_cppse:
734             if (ch == '>')
735             {
736               log_debug("onCpp(\"" << code << "\")");
737               handler.onCpp(code);
738               code.clear();
739               state = state_html;
740             }
741             else
742             {
743               code += '}';
744               code += ch;
745               state = state_cpp;
746             }
747             break;
748 
749           case state_cppe0:
750             if (ch == '/')
751             {
752               etag.clear();
753               state = state_cppe1;
754             }
755             else
756             {
757               code += '<';
758               code += ch;
759               state = state_cpp;
760             }
761             break;
762 
763           case state_cppe1:
764             if (ch == '%')
765               state = state_cppetag;
766             else
767             {
768               code += "</";
769               code += ch;
770               state = state_cpp;
771             }
772             break;
773 
774           case state_cppetag:
775             if (ch == '>')
776             {
777               if (tag != etag)
778               {
779                 state_type s = state;
780                 state = state_html0;
781                 throw parse_error("invalid end-tag - "
782                   + tag + " expected, "
783                   + etag + " found", s, curfile, curline);
784               }
785               else if (tag == "pre")
786               {
787                 log_debug("onPre(\"" << code << "\")");
788                 handler.onPre(code);
789               }
790               else if (tag == "init")
791               {
792                 log_debug("onInit(\"" << code << "\")");
793                 handler.onInit(code);
794               }
795               else if (tag == "cleanup")
796               {
797                 log_debug("onCleanup(\"" << code << "\")");
798                 handler.onCleanup(code);
799               }
800               else if (tag == "shared")
801               {
802                 log_debug("onShared(\"" << code << "\")");
803                 handler.onShared(code);
804               }
805               else if (tag == "cpp")
806               {
807                 log_debug("onCpp(\"" << code << "\")");
808                 handler.onCpp(code);
809               }
810               else if (tag == "out")
811               {
812                 handler.onHtmlExpression(code);
813               }
814               else if (tag == "sout")
815               {
816                 handler.onExpression(code);
817               }
818               else if (tag == "args"
819                 || tag == "get"
820                 || tag == "post"
821                 || tag == "attr"
822                 || tag == "config"
823                 || tag == "include"
824                 || tag == "session"
825                 || tag == "securesession"
826                 || tag == "application"
827                 || tag == "thread"
828                 || tag == "request"
829                 || tag == "param"
830                 || tag == "doc")
831                 ;
832               else
833               {
834                 state_type s = state;
835                 state = state_html0;
836                 throw parse_error("unknown tag " + tag, s, curfile, curline);
837               }
838               code.clear();
839               state = state_html0;
840             }
841             else
842             {
843               etag += ch;
844             }
845             break;
846 
847           case state_nl:
848             if (ch == '<')
849             {
850               state = state_tagstart;
851             }
852             else if (ch == '%')
853             {
854               // start of oneline-cpp
855               handler.onLine(curline, curfile); //#
856               if (!html.empty())
857               {
858                 log_debug("onHtml(\"" << html << "\")");
859                 handler.onHtml(html);
860                 html.clear();
861               }
862               state = state_cpp1;
863             }
864             else if (ch == '\\')
865               state = state_htmlesc;
866             else if (splitBar && (ch == split_start|| ch == split_end))
867             {
868               if (!html.empty())
869               {
870                 log_debug("onHtml(\"" << html << "\")");
871                 handler.onHtml(html);
872                 html.clear();
873               }
874               handler.tokenSplit(ch == split_start);
875               state = state_html;
876             }
877             else
878             {
879               html += ch;
880               if (ch != '\n')
881                 state = state_html;
882             }
883             break;
884 
885           case state_cpp1:
886             if (ch == '\n')
887             {
888               code += '\n';
889               log_debug("onCpp(\"" << code << "\")");
890               handler.onCpp(code);
891               code.clear();
892               state = state_nl;
893             }
894             else
895             {
896               code += ch;
897             }
898             break;
899 
900           case state_args0:
901             if (ch == '<')
902               state = state_cppe0;
903             else if (ch == '/')
904               state = state_args0comment;
905             else if (!std::isspace(ch))
906             {
907               arg = ch;
908               state = state_argsvar;
909             }
910             break;
911 
912           case state_args0comment:
913             if (ch == '/')
914               state = state_argscomment;
915             else
916               throw parse_error("7", state, curfile, curline);
917             break;
918 
919           case state_argsvar:
920             if (ch == '=')
921             {
922               value.clear();
923               state = state_argsval;
924             }
925             else if (ch == '/')
926             {
927               processNV(tag, arg, std::string());
928               arg.clear();
929               state = state_argscomment0;
930             }
931             else if (ch == ';')
932             {
933               processNV(tag, arg, std::string());
934               arg.clear();
935               state = state_args0;
936             }
937             else
938               arg += ch;
939             break;
940 
941           case state_argsvare:
942             if (ch == '=')
943             {
944               value.clear();
945               state = state_argsval;
946             }
947             else if (ch == '\n' || ch == ';')
948             {
949               processNV(tag, arg, std::string());
950               arg.clear();
951               state = state_args0;
952               if (ch == '\n')
953                 std::cerr << curfile << ':' << curline + 1 << ": warning: old syntax: ';' missing" << std::endl;
954             }
955             else if (!std::isspace(ch))
956             {
957               processNV(tag, arg, std::string());
958               arg.clear();
959 
960               if (ch == '<')
961                 state = state_cppe0;
962               else if (ch == '/')
963                 state = state_argscomment0;
964               else
965               {
966                 arg = ch;
967                 state = state_argsvar;
968               }
969             }
970             break;
971 
972           case state_argsval:
973             if (ch == '\n' || ch == ';')
974             {
975               processNV(tag, arg, value);
976               arg.clear();
977               value.clear();
978               state = state_args0;
979               if (ch == '\n')
980                 std::cerr << curfile << ':' << curline << ": warning: old syntax: ';' missing" << std::endl;
981             }
982             else if (ch == '"')
983             {
984               value += ch;
985               state = state_argsvalstring;
986             }
987             else if (ch == '/')
988               state = state_argsvalcomment0;
989             else
990               value += ch;
991             break;
992 
993           case state_argsvalstring:
994             if (ch == '"')
995               state = state_argsval;
996             value += ch;
997             break;
998 
999           case state_argscomment0:
1000             if (ch == '/')
1001               state = state_argscomment;
1002             else
1003               throw parse_error("6", state, curfile, curline);
1004             break;
1005 
1006           case state_argscomment:
1007             if (ch == '\n')
1008               state = state_args0;
1009             break;
1010 
1011           case state_argsvalcomment0:
1012             if (ch == '/')
1013             {
1014               processNV(tag, arg, value);
1015               arg.clear();
1016               value.clear();
1017               state = state_argscomment;
1018               if (ch == '\n')
1019                 std::cerr << curfile << ':' << curline << ": warning: old syntax: ';' missing" << std::endl;
1020             }
1021             else
1022             {
1023               value += '/';
1024               value += ch;
1025               state = state_argsval;
1026             }
1027             break;
1028 
1029           case state_attr0:
1030             if (ch == '<')
1031               state = state_cppe0;
1032             else if (ch == '/')
1033               state = state_attr0comment;
1034             else if (!std::isspace(ch))
1035             {
1036               arg = ch;
1037               state = state_attrvar;
1038             }
1039             break;
1040 
1041           case state_attr0comment:
1042             if (ch == '/')
1043               state = state_attrcomment;
1044             else
1045               throw parse_error("7", state, curfile, curline);
1046             break;
1047 
1048           case state_attrvar:
1049             if (ch == '=')
1050             {
1051               value.clear();
1052               state = state_attrval;
1053             }
1054             else if (std::isspace(ch))
1055               state = state_attrvare;
1056             else
1057               arg += ch;
1058             break;
1059 
1060           case state_attrvare:
1061             if (ch == '=')
1062             {
1063               if (arg.empty())
1064                 throw parse_error("name expected", state, curfile, curline);
1065               value.clear();
1066               state = state_attrval;
1067             }
1068             else if (!std::isspace(ch))
1069               throw parse_error("'=' expected", state, curfile, curline);
1070             break;
1071 
1072           case state_attrval:
1073             if (ch == '\n' || ch == ';')
1074             {
1075               if (value.empty())
1076                 throw parse_error("value expected", state, curfile, curline);
1077               log_debug("onAttr(\"" << arg << "\", \"" << value << "\")");
1078               handler.onAttr(arg, value);
1079               arg.clear();
1080               value.clear();
1081               state = state_attr0;
1082               if (ch == '\n')
1083                 std::cerr << curfile << ':' << curline << ": warning: old syntax: ';' missing" << std::endl;
1084             }
1085             else if (ch == '"')
1086             {
1087               value += ch;
1088               state = state_attrvalstring;
1089             }
1090             else if (ch == '/')
1091               state = state_attrvalcomment0;
1092             else
1093               value += ch;
1094             break;
1095 
1096           case state_attrvalstring:
1097             if (ch == '"')
1098               state = state_attrval;
1099             value += ch;
1100             break;
1101 
1102           case state_attrcomment0:
1103             if (ch == '/')
1104               state = state_attrcomment;
1105             else
1106               throw parse_error("6", state, curfile, curline);
1107             break;
1108 
1109           case state_attrcomment:
1110             if (ch == '\n')
1111               state = state_attr0;
1112             break;
1113 
1114           case state_attrvalcomment0:
1115             if (ch == '/')
1116             {
1117               if (value.empty())
1118                 throw parse_error("value expected", state, curfile, curline);
1119               log_debug("onAttr(\"" << arg << "\", \"" << value << "\")");
1120               handler.onAttr(arg, value);
1121               arg.clear();
1122               value.clear();
1123               state = state_attrcomment;
1124             }
1125             else
1126             {
1127               value += '/';
1128               value += ch;
1129               state = state_attrval;
1130             }
1131             break;
1132 
1133           case state_call0:
1134             if (ch == '(')
1135             {
1136               comp = ch;
1137               bracket_count = 1;
1138               state = state_callname_expr;
1139             }
1140             else if (ch == '"')
1141             {
1142               comp = ch;
1143               state = state_callname_string;
1144             }
1145             else if (!std::isspace(ch))
1146             {
1147               comp = ch;
1148               state = state_callname;
1149             }
1150             break;
1151 
1152           case state_callname_expr:
1153             comp += ch;
1154             if (ch == '(')
1155               ++bracket_count;
1156             else if (ch == ')' && --bracket_count == 0)
1157               state = state_callarg0;
1158             break;
1159 
1160           case state_callname_string:
1161             comp += ch;
1162             if (ch == '"')
1163               state = state_callarg0;
1164             else if (std::isspace(ch))
1165               throw parse_error("invalid componentname", state, curfile, curline);
1166             break;
1167 
1168           case state_callname:
1169             if (ch == '&' || ch == '>')
1170             {
1171               log_debug("onCall(\"" << comp << "comp_args (" << comp_args.size() << "), \"" << pass_cgi << "\", paramargs (" << paramargs.size() << "), defarg (" << defarg.size() << "))");
1172               handler.onCall(comp, comp_args, pass_cgi, paramargs, defarg);
1173               comp.clear();
1174               comp_args.clear();
1175               pass_cgi.clear();
1176               paramargs.clear();
1177               defarg.clear();
1178               state = ch == '>' ? state_html : state_callend;
1179             }
1180             else if (std::isspace(ch))
1181               state = state_callarg0;
1182             else if (ch == '(')
1183             {
1184               state = state_call_cpparg0;
1185               expr.clear();
1186               paramargs.clear();
1187             }
1188             else
1189               comp += ch;
1190             break;
1191 
1192           case state_call_cpparg0:
1193             if (ch == ')')
1194               state = state_callarg;
1195             else if (std::isalpha(ch) || ch == '_')
1196             {
1197               expr = ch;
1198               state = state_call_cpparg_pe;
1199             }
1200             else if (!std::isspace(ch))
1201             {
1202               expr = ch;
1203               if (ch == '(')
1204                 bracket_count = 1;
1205               state = state_call_cpparg_e;
1206             }
1207             break;
1208 
1209           case state_call_cpparg1:
1210             if (ch == ')')
1211               throw parse_error("')' unexpected", state, curfile, curline);
1212             else if (std::isalpha(ch) || ch == '_')
1213             {
1214               expr = ch;
1215               state = state_call_cpparg_pe;
1216             }
1217             else if (!std::isspace(ch))
1218             {
1219               expr = ch;
1220               if (ch == '(')
1221                 bracket_count = 1;
1222               state = state_call_cpparg_e;
1223             }
1224             break;
1225 
1226           case state_call_cpparg_pe:
1227             if (isVariableNameChar(ch))
1228               expr += ch;
1229             else if (std::isspace(ch))
1230             {
1231               paramname = expr;
1232               state = state_call_cpparg_sp;
1233             }
1234             else if (ch == '=')
1235             {
1236               paramname = expr;
1237               expr.clear();
1238               bracket_count = 0;
1239               state = state_call_cpparg_e;
1240             }
1241             else if (ch == ')')
1242             {
1243               if (!defarg.empty())
1244                 defarg += ',';
1245               defarg += expr;
1246               expr.clear();
1247               state = state_callarg0;
1248             }
1249             else
1250             {
1251               expr += ch;
1252               if (ch == '(')
1253                 bracket_count = 1;
1254               state = state_call_cpparg_e;
1255             }
1256             break;
1257 
1258           case state_call_cpparg_sp:
1259             if (std::isspace(ch))
1260               expr += ch;
1261             else if (ch == '=')
1262             {
1263               expr.clear();
1264               bracket_count = 0;
1265               state = state_call_cpparg_e;
1266             }
1267             else
1268             {
1269               expr += ch;
1270               paramname.clear();
1271               if (ch == '(')
1272                 bracket_count = 1;
1273               state = state_call_cpparg_e;
1274             }
1275             break;
1276 
1277           case state_call_cpparg_e:
1278             if (ch == ')' && bracket_count > 0)
1279             {
1280               --bracket_count;
1281               expr += ch;
1282             }
1283             else if (ch == ',' || ch == ')')
1284             {
1285               if (paramname.empty())
1286               {
1287                 if (!defarg.empty())
1288                   defarg += ',';
1289                 defarg += expr;
1290                 expr.clear();
1291                 state = ch == ',' ? state_call_cpparg1 : state_callarg0;
1292               }
1293               else
1294               {
1295                 if (paramargs.find(paramname) != paramargs.end())
1296                   throw parse_error("duplicate parameter " + paramname, state, curfile, curline);
1297                 paramargs[paramname] = expr;
1298                 paramname.clear();
1299                 expr.clear();
1300                 state = ch == ',' ? state_call_cpparg1 : state_callarg0;
1301               }
1302             }
1303             else
1304             {
1305               if (ch == '(')
1306                 ++bracket_count;
1307               expr += ch;
1308             }
1309             break;
1310 
1311           case state_callend:
1312             if (ch == '>')
1313               state = state_html;
1314             else
1315               throw parse_error("3", state, curfile, curline);
1316             break;
1317 
1318           case state_callarg0:
1319             if (std::isalpha(ch) || ch == '_')
1320             {
1321               arg = ch;
1322               state = state_callarg;
1323             }
1324             else if (ch == '&' || ch == '/' || ch == '>')
1325             {
1326               log_debug("onCall(\"" << comp << "comp_args (" << comp_args.size() << "), \"" << pass_cgi << "\", paramargs (" << paramargs.size() << "), defarg (" << defarg.size() << "))");
1327               handler.onCall(comp, comp_args, pass_cgi, paramargs, defarg);
1328               arg.clear();
1329               comp.clear();
1330               comp_args.clear();
1331               pass_cgi.clear();
1332               paramargs.clear();
1333               defarg.clear();
1334               state = ch == '>' ? state_html : state_callend;
1335             }
1336             else if (ch == '(' && arg.empty() && pass_cgi.empty())
1337               state = state_call_cpparg0;
1338             else if (!std::isspace(ch))
1339               throw parse_error(std::string("invalid argumentname (") + ch + ')', state, curfile, curline);
1340             break;
1341 
1342           case state_callarg:
1343             if (isVariableNameChar(ch))
1344               arg += ch;
1345             else if (std::isspace(ch))
1346               state = state_callarge;
1347             else if (ch == '=')
1348               state = state_callval0;
1349             else if ((pass_cgi.empty() && ch == '&') || ch == '/' || ch == '>')
1350             {
1351               log_debug("onCall(\"" << comp << "comp_args (" << comp_args.size() << "), \"" << pass_cgi << "\", paramargs (" << paramargs.size() << "), defarg (" << defarg.size() << "))");
1352               handler.onCall(comp, comp_args, arg, paramargs, defarg);
1353               arg.clear();
1354               comp.clear();
1355               comp_args.clear();
1356               paramargs.clear();
1357               defarg.clear();
1358               state = ch == '>' ? state_html : state_callend;
1359             }
1360             else
1361               throw parse_error(std::string("invalid argumentname (") + ch + ')', state, curfile, curline);
1362             break;
1363 
1364           case state_callarge:
1365             if (ch == '=')
1366               state = state_callval0;
1367             else if (pass_cgi.empty() && (isVariableNameChar(ch)))
1368             {
1369               pass_cgi = arg;
1370               arg = ch;
1371               state = state_callarg;
1372             }
1373             else if ((pass_cgi.empty() && ch == '&') || ch == '/' || ch == '>')
1374             {
1375               log_debug("onCall(\"" << comp << "comp_args (" << comp_args.size() << "), \"" << pass_cgi << "\", paramargs (" << paramargs.size() << "), defarg (" << defarg.size() << "))");
1376               handler.onCall(comp, comp_args, arg, paramargs, defarg);
1377               arg.clear();
1378               comp.clear();
1379               comp_args.clear();
1380               paramargs.clear();
1381               defarg.clear();
1382               state = ch == '>' ? state_html : state_callend;
1383             }
1384             else if (!std::isspace(ch))
1385               throw parse_error("5", state, curfile, curline);
1386             break;
1387 
1388           case state_callval0:
1389             if (ch == '(')
1390             {
1391               value = ch;
1392               state = state_callval_expr;
1393               bracket_count = 1;
1394             }
1395             else if (ch == '"')
1396             {
1397               value = ch;
1398               state = state_callval_string;
1399             }
1400             else if (!std::isspace(ch))
1401             {
1402               value = ch;
1403               state = state_callval_word;
1404             }
1405             break;
1406 
1407           case state_callval_expr:
1408             value += ch;
1409             if (ch == '(')
1410               ++bracket_count;
1411             else if (ch == ')' && --bracket_count == 0)
1412             {
1413               comp_args.insert(comp_args_type::value_type(arg, value));
1414               arg.clear();
1415               value.clear();
1416               state = state_callarg0;
1417             }
1418             break;
1419 
1420           case state_callval_string:
1421             value += ch;
1422             if (ch == '"')
1423             {
1424               comp_args.insert(comp_args_type::value_type(arg, value));
1425               arg.clear();
1426               value.clear();
1427               state = state_callarg0;
1428             }
1429             break;
1430 
1431           case state_callval_word:
1432             if (std::isspace(ch))
1433             {
1434               comp_args.insert(comp_args_type::value_type(arg, value));
1435               arg.clear();
1436               value.clear();
1437               state = state_callarg0;
1438             }
1439             else
1440               value += ch;
1441             break;
1442 
1443           case state_callvale:
1444             if (ch == '>')
1445             {
1446               comp_args.insert(comp_args_type::value_type(arg, value));
1447               arg.clear();
1448               value.clear();
1449 
1450               log_debug("onCall(\"" << comp << "comp_args (" << comp_args.size() << "), \"" << pass_cgi << "\", paramargs (" << paramargs.size() << "), defarg (" << defarg.size() << "))");
1451               handler.onCall(comp, comp_args, pass_cgi, paramargs, defarg);
1452               comp.clear();
1453               comp_args.clear();
1454               pass_cgi.clear();
1455               paramargs.clear();
1456               defarg.clear();
1457               state = state_html;
1458             }
1459             else
1460               throw parse_error("ivalid value", state, curfile, curline);
1461             break;
1462 
1463           case state_comment:
1464             if (ch == '#')
1465               state = state_commente;
1466             break;
1467 
1468           case state_commente:
1469             if (ch == '>')
1470               state = state_html;
1471             else if (ch != '#')
1472               state = state_comment;
1473             break;
1474 
1475           case state_compe0:
1476             if (ch == '%')
1477             {
1478               if (!html.empty())
1479               {
1480                 log_debug("onHtml(\"" << html << "\")");
1481                 handler.onHtml(html);
1482                 html.clear();
1483               }
1484               state = state_compe;
1485               tag.clear();
1486             }
1487             else if (ch == '&')
1488             {
1489               state = state_endcall0;
1490             }
1491             else
1492             {
1493               html += "</";
1494               html += ch;
1495               state = state_html;
1496             }
1497             break;
1498 
1499           case state_compe:
1500             if (ch == '>')
1501             {
1502               if (inComp && tag == "def")
1503               {
1504                 log_debug("onComp(\"" << code << "\")");
1505                 handler.onComp(code);
1506                 inComp = false;
1507                 state = state_html0;
1508               }
1509               else if (inClose && tag == "close")
1510               {
1511                 handler.endClose();
1512                 inClose = false;
1513                 state = state_html0;
1514               }
1515               else if (splitBar && tag == "i18n")
1516               {
1517                 splitBar = false;
1518                 handler.endI18n();
1519                 state = state_html0;
1520               }
1521               else
1522               {
1523                 html += "</%";
1524                 html += tag;
1525                 state = state_html;
1526               }
1527             }
1528             else
1529             {
1530               tag += ch;
1531               if (tag.size() > 5)
1532               {
1533                 html += "</%";
1534                 html += tag;
1535                 state = state_html;
1536               }
1537             }
1538             break;
1539 
1540           case state_cond0:
1541             if (ch == '?')
1542               htmlExpr = true;
1543             else
1544               cond += ch;
1545             state = state_cond;
1546             break;
1547 
1548           case state_cond:
1549             if (ch == '?')
1550               state = state_condexpr;
1551             else
1552               cond += ch;
1553             break;
1554 
1555           case state_condexpr:
1556             if (ch == '?')
1557               state = state_condexpre;
1558             else if (expr.empty() && ch == '>')
1559             {
1560               // special case for xml-header (<?xml ... ?>)
1561               handler.onHtml("<?" + cond + "?>");
1562               cond.clear();
1563               state = state_html;
1564             }
1565             else
1566               expr += ch;
1567             break;
1568 
1569           case state_condexpre:
1570             if (ch == '>')
1571             {
1572               log_debug("onCondExpression(\"" << cond << ", " << code << "\")");
1573               handler.onCondExpr(cond, expr, htmlExpr);
1574               htmlExpr = false;
1575               cond.clear();
1576               expr.clear();
1577               state = state_html;
1578             }
1579             else
1580             {
1581               expr += '?';
1582               expr += ch;
1583               state = state_condexpr;
1584             }
1585             break;
1586 
1587           case state_include0:
1588             if (ch == '<')
1589               state = state_cppe0;
1590             else if (!std::isspace(ch))
1591             {
1592               expr = ch;
1593               state = state_include1;
1594             }
1595             break;
1596 
1597           case state_include1:
1598             if (std::isspace(ch) || ch == '<')
1599             {
1600               doInclude(expr);
1601               expr.clear(),
1602               state = (ch == '<' ? state_cppe0 : state_include0);
1603             }
1604             else
1605               expr += ch;
1606             break;
1607 
1608           case state_scopearg0:
1609             if (ch == '>')
1610             {
1611               state = state_scope0;
1612               scope = component_scope;
1613             }
1614             else if (!std::isspace(ch))
1615             {
1616               tagarg = ch;
1617               state = state_scopearg;
1618             }
1619             break;
1620 
1621           case state_scopearg:
1622             if (ch == '=')
1623             {
1624               state = state_scopeargval0;
1625               if (tagarg != "scope")
1626                 throw parse_error("argument \"scope\" expected", state, curfile, curline);
1627               tagarg.clear();
1628             }
1629             else if (std::isspace(ch))
1630             {
1631               state = state_scopeargeq;
1632               if (tagarg != "scope")
1633                 throw parse_error("argument \"scope\" expected", state, curfile, curline);
1634               tagarg.clear();
1635             }
1636             else
1637               tagarg += ch;
1638             break;
1639 
1640           case state_scopeargeq:
1641             if (ch == '=')
1642               state = state_scopeargval0;
1643             else if (!std::isspace(ch))
1644               throw parse_error("\"=\" expected", state, curfile, curline);
1645             break;
1646 
1647           case state_scopeargval0:
1648             if (ch == '"')
1649               state = state_scopeargval;
1650             else if (!std::isspace(ch))
1651               throw parse_error("argument expected", state, curfile, curline);
1652             break;
1653 
1654           case state_scopeargval:
1655             if (ch == '"')
1656             {
1657               if (value == "global")
1658                 scope = global_scope;
1659               else if (value == "page")
1660                 scope = page_scope;
1661               else if (value == "component")
1662                 scope = component_scope;
1663               else
1664                 throw parse_error("global|page|component expected", state, curfile, curline);
1665 
1666               value.clear();
1667               state = state_scopevale;
1668             }
1669             else if (ch == '\n')
1670               throw parse_error("'\"' expected", state, curfile, curline);
1671             else
1672               value += ch;
1673             break;
1674 
1675           case state_scopevale:
1676             if (ch == '>')
1677               state = state_scope0;
1678             else if (!std::isspace(ch))
1679               throw parse_error("'>' expected", state, curfile, curline);
1680             break;
1681 
1682           case state_scope0:
1683             if (ch == '<')
1684               state = state_scopee;
1685             else if (ch == '/')
1686               state = state_scopecomment0;
1687             else if (!std::isspace(ch))
1688             {
1689               handler.onLine(curline, curfile); //#
1690               scopevar = ch;
1691               state = state_scope;
1692             }
1693             break;
1694 
1695           case state_scope:
1696             if (ch == ';')
1697             {
1698               // scopetype contains type-definition
1699               // scopevar contains variable-definition
1700               // scopeinit is empty
1701               if (scopetype.size() > 0
1702                 && std::isspace(scopetype.at(scopetype.size() - 1)))
1703                   scopetype.erase(scopetype.size() - 1);
1704               log_debug("onScope(" << scope_container << ", " << scope << ", "
1705                   << scopetype << ", " << scopevar << ", " << scopeinit << ')');
1706               handler.onScope(scope_container, scope, scopetype, scopevar, scopeinit);
1707 
1708               scopetype.clear();
1709               scopevar.clear();
1710               state = state_scope0;
1711             }
1712             else if (ch == '(')
1713             {
1714               state = state_scopeinit;
1715               bracket_count = 0;
1716             }
1717             else if (ch == '=')
1718             {
1719               state = state_scopeiniteq;
1720               bracket_count = 0;
1721             }
1722             else if (std::isspace(ch))
1723               scopevar += ch;
1724             else if (!isVariableNameChar(scopevar.at(scopevar.size() - 1)))
1725             {
1726               scopetype += scopevar;
1727               scopevar = ch;
1728             }
1729             else
1730               scopevar += ch;
1731             break;
1732 
1733           case state_scopee:
1734             if (ch == '/')
1735             {
1736               etag.clear();
1737               state = state_cppe1;
1738             }
1739             else
1740             {
1741               scopevar += '<';
1742               scopevar += ch;
1743               state = state_scope;
1744             }
1745             break;
1746 
1747           case state_scopeinit:
1748             if (bracket_count == 0 && ch == ')')
1749             {
1750               // scopevar contains variable-definition
1751               // scopeinit contains constructorparameter
1752               while (scopetype.size() > 0
1753                 && std::isspace(scopetype.at(scopetype.size() - 1)))
1754                   scopetype.erase(scopetype.size() - 1);
1755               while (scopevar.size() > 0
1756                 && std::isspace(scopevar.at(scopevar.size() - 1)))
1757                   scopevar.erase(scopevar.size() - 1);
1758               log_debug("onScope(" << scope_container << ", " << scope << ", "
1759                   << scopetype << ", " << scopevar << ", " << scopeinit << ')');
1760               handler.onScope(scope_container, scope, scopetype, scopevar, scopeinit);
1761 
1762               scopetype.clear();
1763               scopevar.clear();
1764               scopeinit.clear();
1765               state = state_scopee0;
1766             }
1767             else
1768             {
1769               scopeinit += ch;
1770               if (ch == '(')
1771                 ++bracket_count;
1772               else if (ch == ')')
1773               {
1774                 if (bracket_count == 0)
1775                   throw parse_error("unexpected ')'", state, curfile, curline);
1776                 --bracket_count;
1777               }
1778             }
1779             break;
1780 
1781           case state_scopeiniteq:
1782             if (bracket_count == 0 && ch == ';')
1783             {
1784               // scopevar contains variable-definition
1785               // scopeinit contains constructorparameter
1786               while (scopetype.size() > 0
1787                 && std::isspace(scopetype.at(scopetype.size() - 1)))
1788                   scopetype.erase(scopetype.size() - 1);
1789               while (scopevar.size() > 0
1790                 && std::isspace(scopevar.at(scopevar.size() - 1)))
1791                   scopevar.erase(scopevar.size() - 1);
1792               log_debug("onScope(" << scope_container << ", " << scope << ", "
1793                   << scopetype << ", " << scopevar << ", " << scopeinit << ')');
1794               handler.onScope(scope_container, scope, scopetype, scopevar, scopeinit);
1795 
1796               scopetype.clear();
1797               scopevar.clear();
1798               scopeinit.clear();
1799               state = state_scope0;
1800             }
1801             else
1802             {
1803               scopeinit += ch;
1804               if (ch == '(')
1805                 ++bracket_count;
1806               else if (ch == ')')
1807               {
1808                 if (bracket_count == 0)
1809                   throw parse_error("unexpected ')'", state, curfile, curline);
1810                 --bracket_count;
1811               }
1812             }
1813             break;
1814 
1815           case state_scopee0:
1816             if (ch == ';')
1817               state = state_scope0;
1818             else if (!std::isspace(ch))
1819               throw parse_error("invalid scopedefinition", state, curfile, curline);
1820             break;
1821 
1822           case state_scopecomment0:
1823             if (ch == '/')
1824               state = state_scopecomment;
1825             else
1826               throw parse_error("invalid scopedefinition - '/' expexted", state, curfile, curline);
1827             break;
1828 
1829           case state_scopecomment:
1830             if (ch == '\n')
1831               state = state_scope0;
1832             break;
1833 
1834           case state_endcall0:
1835             if (isVariableNameChar(ch))
1836             {
1837               comp = ch;
1838               state = state_endcall;
1839             }
1840             else if ('>')
1841             {
1842               if (!html.empty())
1843               {
1844                 log_debug("onHtml(\"" << html << "\")");
1845                 handler.onHtml(html);
1846                 html.clear();
1847               }
1848               log_debug("onEndCall(\"" << comp << "\")");
1849               handler.onEndCall(comp);
1850               comp.clear();
1851               state = state_html;
1852             }
1853             else if (!std::isspace(ch))
1854               throw parse_error(std::string("character expected, \'") + ch
1855                 + "\' found", state, curfile, curline);
1856             break;
1857 
1858           case state_endcall:
1859             if (ch == '>')
1860             {
1861               if (!html.empty())
1862               {
1863                 log_debug("onHtml(\"" << html << "\")");
1864                 handler.onHtml(html);
1865                 html.clear();
1866               }
1867               log_debug("onEndCall(\"" << comp << "\")");
1868               handler.onEndCall(comp);
1869               comp.clear();
1870               state = state_html;
1871             }
1872             else if (std::isspace(ch))
1873             {
1874               log_debug("onEndCall(\"" << comp << "\")");
1875               handler.onEndCall(comp);
1876               comp.clear();
1877               state = state_endcalle;
1878             }
1879             else if (isVariableNameChar(ch) || ch == '@')
1880               comp += ch;
1881             else
1882               throw parse_error("character expected", state, curfile, curline);
1883             break;
1884 
1885           case state_endcalle:
1886             if (ch == '>')
1887               state = state_html;
1888             else if (!std::isspace(ch))
1889               throw parse_error("'>' expected", state, curfile, curline);
1890             break;
1891 
1892           case state_doc:
1893             if (ch == '<')
1894             {
1895               etag.clear();
1896               state = state_doce;
1897             }
1898             break;
1899 
1900           case state_doce:
1901             if (ch == '>')
1902             {
1903               if (etag == "/%doc")
1904                 state = state_html0;
1905               else
1906                 state = state_doc;
1907             }
1908             else if (ch == '<')
1909               etag.clear();
1910             else
1911               etag += ch;
1912             break;
1913 
1914         }  // switch(state)
1915 
1916         log_debug("line " << curline << " char " << ch << " state " << state << " bc " << bracket_count);
1917       }  // while(in.get(ch))
1918 
1919       if (state != state_html && state != state_html0 && state != state_nl)
1920         throw parse_error("parse error", state, curfile, curline);
1921 
1922       if (inComp)
1923         throw parse_error("</%def> missing", state, curfile, curline);
1924 
1925       if (inClose)
1926         throw parse_error("</%close> missing", state, curfile, curline);
1927 
1928       if (!html.empty())
1929       {
1930         log_debug("onHtml(\"" << html << "\")");
1931         handler.onHtml(html);
1932       }
1933     }
1934 
parse(std::istream & in)1935     void Parser::parse(std::istream& in)
1936     {
1937       parsePriv(in);
1938       handler.end();
1939     }
1940 
processNV(const std::string & tag,const std::string & name,const std::string & value)1941     void Parser::processNV(const std::string& tag, const std::string& name,
1942       const std::string& value)
1943     {
1944       if (tag == "args")
1945         handler.onArg(name, value);
1946       if (tag == "get")
1947         handler.onGet(name, value);
1948       if (tag == "post")
1949         handler.onPost(name, value);
1950       else if (tag == "config")
1951         handler.onConfig(name, value);
1952     }
1953 
parse_error(const std::string & txt,int state,const std::string & file,unsigned curline)1954     parse_error::parse_error(const std::string& txt, int state, const std::string& file, unsigned curline)
1955       : runtime_error(std::string())
1956     {
1957       std::ostringstream m;
1958       m << file << ':' << (curline + 1) << ": error: " << txt << " (state " << state << ')';
1959       msg = m.str();
1960     }
1961 
what() const1962     const char* parse_error::what() const throw()
1963     {
1964       return msg.c_str();
1965     }
1966   }
1967 }
1968