#include #include #include #include #include "tmplpro.h" #include "pconst.h" #include "procore.h" #include "prostate.h" #include "provalue.h" #include "tagstack.h" #include "pbuffer.h" #include "parse_expr.h" #include "pparam.h" #include "proscope.h" #include "proscope.inc" #include "pstrutils.inc" #include "pmiscdef.h" /*for snprintf */ /* for mmap_load_file & mmap_unload_file */ #include "loadfile.inc" #include "loopvar.inc" #define HTML_TEMPLATE_NO_TAG -1 #define HTML_TEMPLATE_BAD_TAG 0 #define HTML_TEMPLATE_FIRST_TAG_USED 1 #define HTML_TEMPLATE_TAG_VAR 1 #define HTML_TEMPLATE_TAG_INCLUDE 2 #define HTML_TEMPLATE_TAG_LOOP 3 #define HTML_TEMPLATE_TAG_IF 4 #define HTML_TEMPLATE_TAG_ELSE 5 #define HTML_TEMPLATE_TAG_UNLESS 6 #define HTML_TEMPLATE_TAG_ELSIF 7 #define HTML_TEMPLATE_LAST_TAG_USED 7 static const char* const tagname[]={ "Bad or unsupported tag", /* 0 */ "var", "include", "loop", "if", "else", "unless", "elsif" }; static const char* const TAGNAME[]={ "Bad or unsupported tag", /* 0 */ "VAR", "INCLUDE", "LOOP", "IF", "ELSE", "UNLESS", "ELSIF" }; static int debuglevel=0; #define TAG_OPT_NAME 0 #define TAG_OPT_EXPR 1 #define TAG_OPT_ESCAPE 2 #define TAG_OPT_DEFAULT 3 #define MIN_TAG_OPT 0 #define MAX_TAG_OPT 3 static const char* const tagopt[]={"name", "expr", "escape", "default" }; static const char* const TAGOPT[]={"NAME", "EXPR", "ESCAPE", "DEFAULT" }; #include "prostate.inc" #include "tags.inc" static const char const tag_can_be_closed[]={ 1 /*Bad or unsupported tag*/, 0 /*VAR*/, 0 /*INCLUDE*/, 1 /*LOOP*/, 1 /*IF*/, 0 /*ELSE*/, 1 /*UNLESS*/, 1 /*ELSIF*/, 0 /**/, }; static const char const tag_has_opt[][6]={ /* "name", "expr", "escape", "default", todo, todo */ { 0, 0, 0, 0, 0, 0 }, /*Bad or unsupported tag*/ { 1, 1, 1, 1, 0, 0 }, /*VAR*/ { 1, 1, 0, 1, 0, 0 }, /*INCLUDE*/ { 1, 0, 0, 0, 0, 0 }, /*LOOP*/ { 1, 1, 0, 0, 0, 0 }, /*IF*/ { 0, 0, 0, 0, 0, 0 }, /*ELSE*/ { 1, 1, 0, 0, 0, 0 }, /*UNLESS*/ { 1, 1, 0, 0, 0, 0 }, /*ELSIF*/ { 0, 0, 0, 0, 0, 0 }, /**/ }; typedef void (*tag_handler_func)(struct tmplpro_state *state, const PSTRING* const TagOptVal); static const tag_handler_func const output_closetag_handler[]={ tag_handler_unknown, /*Bad or unsupported tag*/ tag_handler_unknown, /*VAR*/ tag_handler_unknown, /*INCLUDE*/ tag_handler_closeloop, /*LOOP*/ tag_handler_closeif, /*IF*/ tag_handler_unknown, /*ELSE*/ tag_handler_closeunless, /*UNLESS*/ tag_handler_unknown, /*ELSIF*/ tag_handler_unknown, /**/ }; static const tag_handler_func const output_opentag_handler[]={ tag_handler_unknown, /*Bad or unsupported tag*/ tag_handler_var, /*VAR*/ tag_handler_include, /*INCLUDE*/ tag_handler_loop, /*LOOP*/ tag_handler_if, /*IF*/ tag_handler_else, /*ELSE*/ tag_handler_unless, /*UNLESS*/ tag_handler_elsif, /*ELSIF*/ tag_handler_unknown, /**/ }; static int is_string(struct tmplpro_state *state, const char* pattern,const char* PATTERN) { const char* cur_pos=state->cur_pos; register const char* const next_to_end = state->next_to_end; while (*pattern && cur_pos=next_to_end) return 0; state->cur_pos=cur_pos; return 1; } static INLINE void jump_over_space(struct tmplpro_state *state) { register const char* const next_to_end = state->next_to_end; while (isspace(*(state->cur_pos)) && state->cur_poscur_pos++;}; } static INLINE void jump_to_char(struct tmplpro_state *state, char c) { register const char* const next_to_end = state->next_to_end; while (c!=*(state->cur_pos) && state->cur_poscur_pos++;}; } static PSTRING read_tag_parameter_value (struct tmplpro_state *state) { PSTRING modifier_value; char cur_char; char quote_char=0; register const char* cur_pos; const char* const next_to_end=state->next_to_end; jump_over_space(state); cur_pos=state->cur_pos; cur_char=*cur_pos; if (('"'==cur_char) || ('\''==cur_char)) { quote_char=*cur_pos; cur_pos++; } modifier_value.begin=cur_pos; cur_char=*cur_pos; if (quote_char) { while (quote_char!=cur_char #ifdef COMPAT_ON_BROKEN_QUOTE /* compatibility mode; HTML::Template doesn't allow '>' inside quotes */ && ('>' != quote_char) #endif && cur_pos'!=cur_char && ! isspace(cur_char) && cur_pos=next_to_end) { log_state(state,TMPL_LOG_ERROR,"quote char %c at pos " MOD_TD " is not terminated\n", quote_char,TO_PTRDIFF_T(state->cur_pos - state->top)); modifier_value.endnext=modifier_value.begin; jump_over_space(state); return modifier_value; } modifier_value.endnext=cur_pos; if (quote_char) { if (quote_char==*cur_pos) { cur_pos++; } else { log_state(state,TMPL_LOG_ERROR,"found %c instead of end quote %c at pos " MOD_TD "\n", *cur_pos,quote_char,TO_PTRDIFF_T(cur_pos - state->top)); } } state->cur_pos=cur_pos; /* if (debuglevel) log_state(state,TMPL_LOG_DEBUG2," at pos " MOD_TD "",TO_PTRDIFF_T(state->cur_pos-state->top)); */ jump_over_space(state); return modifier_value; } static int try_tag_parameter (struct tmplpro_state *state,const char *modifier,const char *MODIFIER) { const char* const initial_pos=state->cur_pos; jump_over_space(state); if (is_string(state, modifier, MODIFIER)) { jump_over_space(state); if ('='==*(state->cur_pos)) { state->cur_pos++; jump_over_space(state); return 1; } } state->cur_pos=initial_pos; return 0; } static void try_tmpl_var_options (struct tmplpro_state *state, int tag_type, PSTRING* TagOptVal) { int i; int opt_found = 1; /* reading parameter */ while (opt_found) { int found_in_loop=0; for (i=MIN_TAG_OPT; i<=MAX_TAG_OPT; i++) { if ( /* we will complain about syntax errors later; tag_has_opt[tag_type][i] && */ try_tag_parameter(state, tagopt[i], TAGOPT[i])) { TagOptVal[i] = read_tag_parameter_value(state); found_in_loop=1; if (debuglevel) log_state(state,TMPL_LOG_DEBUG,"in tag %s: found option %s\n", TAGNAME[tag_type], TAGOPT[i]); } } if (!found_in_loop) opt_found = 0; } } static void process_tmpl_tag(struct tmplpro_state *state) { const int is_tag_closed=state->is_tag_closed; int tag_type=HTML_TEMPLATE_BAD_TAG; PSTRING TagOptVal[MAX_TAG_OPT+1]; int i; for (i=MIN_TAG_OPT; i<=MAX_TAG_OPT; i++) { TagOptVal[i].begin = NULL; TagOptVal[i].endnext = NULL; } for (i=HTML_TEMPLATE_FIRST_TAG_USED; i<=HTML_TEMPLATE_LAST_TAG_USED; i++) { if (is_string(state, tagname[i], TAGNAME[i])) { tag_type=i; state->tag=tag_type; if (debuglevel) { if (is_tag_closed) { tmpl_log(TMPL_LOG_DEBUG, "found at pos " MOD_TD "\n",TAGNAME[i], TO_PTRDIFF_T(state->cur_pos-state->top)); } else { tmpl_log(TMPL_LOG_DEBUG, "found at pos " MOD_TD "\n",TAGNAME[i], TO_PTRDIFF_T(state->cur_pos-state->top)); } } break; } } if (HTML_TEMPLATE_BAD_TAG==tag_type) { state->param->found_syntax_error=1; log_state(state,TMPL_LOG_ERROR, "found bad/unsupported tag at pos " MOD_TD "\n", TO_PTRDIFF_T(state->cur_pos-state->top)); /* TODO: flush its data --- */ state->cur_pos++; return; } if (is_tag_closed && !tag_can_be_closed[tag_type]) { state->param->found_syntax_error=1; log_state(state,TMPL_LOG_ERROR, "incorrect closed tag at pos " MOD_TD "\n", TAGNAME[tag_type], TO_PTRDIFF_T(state->cur_pos-state->top)); } if (is_tag_closed || ! tag_has_opt[tag_type][TAG_OPT_NAME]) { /* tag has no parameter */ #ifdef COMPAT_ALLOW_NAME_IN_CLOSING_TAG /* requested compatibility mode to try reading NAME inside (useful for comments?) */ try_tag_parameter(state, tagopt[TAG_OPT_NAME], TAGOPT[TAG_OPT_NAME]); read_tag_parameter_value(state); #endif } else { try_tmpl_var_options(state, tag_type, TagOptVal); /* suport for short syntax */ if (TagOptVal[TAG_OPT_NAME].begin == NULL && tag_has_opt[tag_type][TAG_OPT_NAME] && (!tag_has_opt[tag_type][TAG_OPT_EXPR] || TagOptVal[TAG_OPT_EXPR].begin == NULL )) { TagOptVal[TAG_OPT_NAME]=read_tag_parameter_value(state); try_tmpl_var_options(state, tag_type, TagOptVal); } if (TagOptVal[TAG_OPT_NAME].begin == NULL && tag_has_opt[tag_type][TAG_OPT_NAME] && (!tag_has_opt[tag_type][TAG_OPT_EXPR] || TagOptVal[TAG_OPT_EXPR].begin == NULL )) { state->param->found_syntax_error=1; log_state(state,TMPL_LOG_ERROR,"NAME or EXPR is required for TMPL_%s\n", TAGNAME[tag_type]); } for (i=MIN_TAG_OPT; i<=MAX_TAG_OPT; i++) { if (TagOptVal[i].begin!=NULL && ! tag_has_opt[tag_type][i]) { state->param->found_syntax_error=1; log_state(state,TMPL_LOG_ERROR,"TMPL_%s does not support %s= option\n", TAGNAME[tag_type], TAGOPT[i]); } } } if (state->is_tag_commented) { /* try read comment end */ /* jump_over_space(state); it should be already done :( */ jump_over_space(state); if (state->cur_posnext_to_end-2 && '-'==*(state->cur_pos) && '-'==*(state->cur_pos+1)) { state->cur_pos+=2; } } /* template tags could also be decorated as xml */ if (!is_tag_closed && '/'==*(state->cur_pos)) state->cur_pos++; if ('>'==*(state->cur_pos)) { state->cur_pos++; } else { state->param->found_syntax_error=1; log_state(state,TMPL_LOG_ERROR,"end tag:found %c instead of > at pos " MOD_TD "\n", *state->cur_pos, TO_PTRDIFF_T(state->cur_pos-state->top)); } /* flush run chars (if in SHOW mode) */ if (state->is_visible) { (state->param->WriterFuncPtr)(state->param->ext_writer_state,state->last_processed_pos,state->tag_start); state->last_processed_pos=state->cur_pos; } if (is_tag_closed) { output_closetag_handler[tag_type](state,TagOptVal); } else { output_opentag_handler[tag_type](state,TagOptVal); } } /* max offset to ensure we are not out of file when try