1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5 
6 #include "tmplpro.h"
7 #include "pconst.h"
8 #include "procore.h"
9 #include "prostate.h"
10 #include "provalue.h"
11 #include "tagstack.h"
12 #include "pbuffer.h"
13 #include "parse_expr.h"
14 #include "pparam.h"
15 #include "optint.h"
16 #include "proscope.h"
17 #include "proscope.inc"
18 #include "pstrutils.inc"
19 #include "pmiscdef.h" /*for snprintf */
20 /* for mmap_load_file & mmap_unload_file */
21 #include "loadfile.inc"
22 #include "loopvar.inc"
23 
24 #define HTML_TEMPLATE_NO_TAG     -1
25 #define HTML_TEMPLATE_BAD_TAG     0
26 #define HTML_TEMPLATE_FIRST_TAG_USED 1
27 #define HTML_TEMPLATE_TAG_VAR     1
28 #define HTML_TEMPLATE_TAG_INCLUDE 2
29 #define HTML_TEMPLATE_TAG_LOOP    3
30 #define HTML_TEMPLATE_TAG_IF      4
31 #define HTML_TEMPLATE_TAG_ELSE    5
32 #define HTML_TEMPLATE_TAG_UNLESS  6
33 #define HTML_TEMPLATE_TAG_ELSIF   7
34 #define HTML_TEMPLATE_LAST_TAG_USED  7
35 
36 static
37 const char* const tagname[]={
38     "Bad or unsupported tag", /* 0 */
39     "var", "include", "loop", "if", "else", "unless", "elsif"
40 };
41 
42 static
43 const char* const TAGNAME[]={
44     "Bad or unsupported tag", /* 0 */
45     "VAR", "INCLUDE", "LOOP", "IF", "ELSE", "UNLESS", "ELSIF"
46 };
47 
48 static int debuglevel=0;
49 
50 #define TAG_OPT_NAME 0
51 #define TAG_OPT_EXPR 1
52 #define TAG_OPT_ESCAPE 2
53 #define TAG_OPT_DEFAULT 3
54 #define MIN_TAG_OPT 0
55 #define MAX_TAG_OPT 3
56 
57 static const char* const tagopt[]={"name", "expr", "escape", "default" };
58 static const char* const TAGOPT[]={"NAME", "EXPR", "ESCAPE", "DEFAULT" };
59 
60 #include "prostate.inc"
61 #include "tags.inc"
62 
63 static const char const tag_can_be_closed[]={
64   1 /*Bad or unsupported tag*/,
65   0 /*VAR*/,
66   0 /*INCLUDE*/,
67   1 /*LOOP*/,
68   1 /*IF*/,
69   0 /*ELSE*/,
70   1 /*UNLESS*/,
71   1 /*ELSIF*/,
72   0 /**/,
73 };
74 
75 static const char const tag_has_opt[][6]={
76   /* "name", "expr", "escape", "default", todo, todo */
77 
78   { 0, 0, 0, 0, 0, 0 }, /*Bad or unsupported tag*/
79   { 1, 1, 1, 1, 0, 0 }, /*VAR*/
80   { 1, 1, 0, 1, 0, 0 }, /*INCLUDE*/
81   { 1, 0, 0, 0, 0, 0 }, /*LOOP*/
82   { 1, 1, 0, 0, 0, 0 }, /*IF*/
83   { 0, 0, 0, 0, 0, 0 }, /*ELSE*/
84   { 1, 1, 0, 0, 0, 0 }, /*UNLESS*/
85   { 1, 1, 0, 0, 0, 0 }, /*ELSIF*/
86   { 0, 0, 0, 0, 0, 0 }, /**/
87 };
88 
89 typedef void (*tag_handler_func)(struct tmplpro_state *state, const PSTRING* const TagOptVal);
90 
91 static const tag_handler_func const output_closetag_handler[]={
92   tag_handler_unknown,	/*Bad or unsupported tag*/
93   tag_handler_unknown,	/*VAR*/
94   tag_handler_unknown,	/*INCLUDE*/
95   tag_handler_closeloop,	/*LOOP*/
96   tag_handler_closeif,	/*IF*/
97   tag_handler_unknown,	/*ELSE*/
98   tag_handler_closeunless,	/*UNLESS*/
99   tag_handler_unknown,	/*ELSIF*/
100   tag_handler_unknown,	/**/
101 };
102 static const tag_handler_func const output_opentag_handler[]={
103   tag_handler_unknown,	/*Bad or unsupported tag*/
104   tag_handler_var,	/*VAR*/
105   tag_handler_include,	/*INCLUDE*/
106   tag_handler_loop,	/*LOOP*/
107   tag_handler_if,	/*IF*/
108   tag_handler_else,	/*ELSE*/
109   tag_handler_unless,	/*UNLESS*/
110   tag_handler_elsif,	/*ELSIF*/
111   tag_handler_unknown,	/**/
112 };
113 
114 static
115 int
is_string(struct tmplpro_state * state,const char * pattern,const char * PATTERN)116 is_string(struct tmplpro_state *state, const char* pattern,const char* PATTERN)
117 {
118   const char* cur_pos=state->cur_pos;
119   register const char* const next_to_end = state->next_to_end;
120   while (*pattern && cur_pos<next_to_end) {
121     if (*pattern == *cur_pos || *PATTERN == *cur_pos) {
122       pattern++;
123       PATTERN++;
124       cur_pos++;
125     } else {
126       return 0;
127     }
128   }
129   if (cur_pos>=next_to_end) return 0;
130   state->cur_pos=cur_pos;
131   return 1;
132 }
133 
134 static
135 INLINE
136 void
jump_over_space(struct tmplpro_state * state)137 jump_over_space(struct tmplpro_state *state)
138 {
139   register const char* const next_to_end = state->next_to_end;
140   while (isspace(*(state->cur_pos)) && state->cur_pos<next_to_end) {state->cur_pos++;};
141 }
142 
143 static
144 INLINE
145 void
jump_to_char(struct tmplpro_state * state,char c)146 jump_to_char(struct tmplpro_state *state, char c)
147 {
148   register const char* const next_to_end = state->next_to_end;
149   while (c!=*(state->cur_pos) && state->cur_pos<next_to_end) {state->cur_pos++;};
150 }
151 
152 static
153 PSTRING
read_tag_parameter_value(struct tmplpro_state * state)154 read_tag_parameter_value (struct tmplpro_state *state)
155 {
156   PSTRING modifier_value;
157   char cur_char;
158   char quote_char=0;
159   register const char* cur_pos;
160   const char* const next_to_end=state->next_to_end;
161   jump_over_space(state);
162   cur_pos=state->cur_pos;
163   cur_char=*cur_pos;
164   if (('"'==cur_char) || ('\''==cur_char)) {
165     quote_char=*cur_pos;
166     cur_pos++;
167   }
168   modifier_value.begin=cur_pos;
169   cur_char=*cur_pos;
170   if (quote_char) {
171     while (quote_char!=cur_char
172 #ifdef COMPAT_ON_BROKEN_QUOTE
173 /* compatibility mode; HTML::Template doesn't allow '>' inside quotes */
174 	   && ('>' != quote_char)
175 #endif
176 	   && cur_pos<next_to_end) {
177       cur_pos++;
178       cur_char=*cur_pos;
179     }
180   } else {
181     while ('>'!=cur_char && ! isspace(cur_char) && cur_pos<next_to_end) {
182       cur_pos++;
183       cur_char=*cur_pos;
184     }
185   }
186   if (cur_pos>=next_to_end) {
187     log_state(state,TMPL_LOG_ERROR,"quote char %c at pos " MOD_TD " is not terminated\n",
188 	     quote_char,TO_PTRDIFF_T(state->cur_pos - state->top));
189     modifier_value.endnext=modifier_value.begin;
190     jump_over_space(state);
191     return modifier_value;
192   }
193   modifier_value.endnext=cur_pos;
194   if (quote_char) {
195     if (quote_char==*cur_pos) {
196       cur_pos++;
197     } else {
198       log_state(state,TMPL_LOG_ERROR,"found %c instead of end quote %c at pos " MOD_TD "\n",
199 	       *cur_pos,quote_char,TO_PTRDIFF_T(cur_pos - state->top));
200     }
201   }
202   state->cur_pos=cur_pos;
203   /* if (debuglevel) log_state(state,TMPL_LOG_DEBUG2," at pos " MOD_TD "",TO_PTRDIFF_T(state->cur_pos-state->top)); */
204   jump_over_space(state);
205   return modifier_value;
206 }
207 
208 static
209 int
try_tag_parameter(struct tmplpro_state * state,const char * modifier,const char * MODIFIER)210 try_tag_parameter (struct tmplpro_state *state,const char *modifier,const char *MODIFIER)
211 {
212   const char* const initial_pos=state->cur_pos;
213   jump_over_space(state);
214   if (is_string(state, modifier, MODIFIER)) {
215     jump_over_space(state);
216     if ('='==*(state->cur_pos)) {
217       state->cur_pos++;
218       jump_over_space(state);
219       return 1;
220     }
221   }
222   state->cur_pos=initial_pos;
223   return 0;
224 }
225 
226 static
227 void
try_tmpl_var_options(struct tmplpro_state * state,int tag_type,PSTRING * TagOptVal)228 try_tmpl_var_options (struct tmplpro_state *state, int tag_type, PSTRING* TagOptVal)
229 {
230   int i;
231   int opt_found = 1;
232   /* reading parameter */
233   while (opt_found) {
234     int found_in_loop=0;
235     for (i=MIN_TAG_OPT; i<=MAX_TAG_OPT; i++) {
236       if (
237 	  /* we will complain about syntax errors later;
238 	     tag_has_opt[tag_type][i] && */
239 	  try_tag_parameter(state, tagopt[i], TAGOPT[i])) {
240 	TagOptVal[i] = read_tag_parameter_value(state);
241 	found_in_loop=1;
242 	if (debuglevel) log_state(state,TMPL_LOG_DEBUG,"in tag %s: found option %s=%.*s\n", TAGNAME[tag_type], TAGOPT[i],(int)(TagOptVal[i].endnext-TagOptVal[i].begin),TagOptVal[i].begin);
243       }
244     }
245     if (!found_in_loop) opt_found = 0;
246   }
247 }
248 
249 static
250 void
process_tmpl_tag(struct tmplpro_state * state)251 process_tmpl_tag(struct tmplpro_state *state)
252 {
253   const int is_tag_closed=state->is_tag_closed;
254 
255   int tag_type=HTML_TEMPLATE_BAD_TAG;
256   PSTRING TagOptVal[MAX_TAG_OPT+1];
257 
258   int i;
259   for (i=MIN_TAG_OPT; i<=MAX_TAG_OPT; i++) {
260     TagOptVal[i].begin = NULL;
261     TagOptVal[i].endnext = NULL;
262   }
263 
264   for (i=HTML_TEMPLATE_FIRST_TAG_USED; i<=HTML_TEMPLATE_LAST_TAG_USED; i++) {
265     if (is_string(state, tagname[i], TAGNAME[i])) {
266       tag_type=i;
267       state->tag=tag_type;
268       if (debuglevel) {
269 	if (is_tag_closed) {
270 	  tmpl_log(TMPL_LOG_DEBUG, "found </TMPL_%s> at pos " MOD_TD "\n",TAGNAME[i], TO_PTRDIFF_T(state->cur_pos-state->top));
271 	} else {
272 	  tmpl_log(TMPL_LOG_DEBUG, "found <TMPL_%s> at pos " MOD_TD "\n",TAGNAME[i], TO_PTRDIFF_T(state->cur_pos-state->top));
273 	}
274       }
275       break;
276     }
277   }
278   if (HTML_TEMPLATE_BAD_TAG==tag_type) {
279     state->param->found_syntax_error=1;
280     log_state(state,TMPL_LOG_ERROR, "found bad/unsupported tag at pos " MOD_TD "\n", TO_PTRDIFF_T(state->cur_pos-state->top));
281     /* TODO: flush its data ---  */
282     state->cur_pos++;
283     return;
284   }
285 
286   if (is_tag_closed && !tag_can_be_closed[tag_type]) {
287     state->param->found_syntax_error=1;
288     log_state(state,TMPL_LOG_ERROR, "incorrect closed tag </TMPL_%s> at pos " MOD_TD "\n",
289 	     TAGNAME[tag_type], TO_PTRDIFF_T(state->cur_pos-state->top));
290   }
291 
292   if (is_tag_closed || ! tag_has_opt[tag_type][TAG_OPT_NAME]) {
293     /* tag has no parameter */
294 #ifdef COMPAT_ALLOW_NAME_IN_CLOSING_TAG
295     /* requested compatibility mode
296        to try reading NAME inside </closing tags NAME="  ">
297        (useful for comments?) */
298     try_tag_parameter(state, tagopt[TAG_OPT_NAME], TAGOPT[TAG_OPT_NAME]);
299     read_tag_parameter_value(state);
300 #endif
301   } else {
302     try_tmpl_var_options(state, tag_type, TagOptVal);
303     /* suport for short syntax */
304     if (TagOptVal[TAG_OPT_NAME].begin == NULL &&
305 	tag_has_opt[tag_type][TAG_OPT_NAME] &&
306 	(!tag_has_opt[tag_type][TAG_OPT_EXPR] || TagOptVal[TAG_OPT_EXPR].begin == NULL )) {
307       TagOptVal[TAG_OPT_NAME]=read_tag_parameter_value(state);
308       try_tmpl_var_options(state, tag_type, TagOptVal);
309     }
310 
311     if (TagOptVal[TAG_OPT_NAME].begin == NULL &&
312 	tag_has_opt[tag_type][TAG_OPT_NAME] &&
313 	(!tag_has_opt[tag_type][TAG_OPT_EXPR] || TagOptVal[TAG_OPT_EXPR].begin == NULL )) {
314       state->param->found_syntax_error=1;
315       log_state(state,TMPL_LOG_ERROR,"NAME or EXPR is required for TMPL_%s\n", TAGNAME[tag_type]);
316     }
317     for (i=MIN_TAG_OPT; i<=MAX_TAG_OPT; i++) {
318       if (TagOptVal[i].begin!=NULL && ! tag_has_opt[tag_type][i]) {
319 	state->param->found_syntax_error=1;
320 	log_state(state,TMPL_LOG_ERROR,"TMPL_%s does not support %s= option\n", TAGNAME[tag_type], TAGOPT[i]);
321       }
322     }
323   }
324 
325   if (state->is_tag_commented) {
326     /* try read comment end */
327     /* jump_over_space(state); it should be already done :( */
328     jump_over_space(state);
329     if (state->cur_pos<state->next_to_end-2 && '-'==*(state->cur_pos) && '-'==*(state->cur_pos+1)) {
330       state->cur_pos+=2;
331     }
332   }
333   /* template tags could also be decorated as xml <tmpl_TAG /> */
334   if (!is_tag_closed && '/'==*(state->cur_pos)) state->cur_pos++;
335 
336   if ('>'==*(state->cur_pos)) {
337     state->cur_pos++;
338   } else {
339     state->param->found_syntax_error=1;
340     log_state(state,TMPL_LOG_ERROR,"end tag:found %c instead of > at pos " MOD_TD "\n",
341 	     *state->cur_pos, TO_PTRDIFF_T(state->cur_pos-state->top));
342   }
343   /* flush run chars (if in SHOW mode) */
344   if (state->is_visible) {
345     (state->param->WriterFuncPtr)(state->param->ext_writer_state,state->last_processed_pos,state->tag_start);
346     state->last_processed_pos=state->cur_pos;
347   }
348   if (is_tag_closed) {
349     output_closetag_handler[tag_type](state,TagOptVal);
350   } else {
351     output_opentag_handler[tag_type](state,TagOptVal);
352   }
353 }
354 
355 
356 /* max offset to ensure we are not out of file when try <!--/  */
357 #define TAG_WIDTH_OFFSET 4
358 static
359 void
process_state(struct tmplpro_state * state)360 process_state (struct tmplpro_state * state)
361 {
362   static const char* const metatag="tmpl_";
363   static const char* const METATAG="TMPL_";
364   int is_tag_closed;
365   int is_tag_commented;
366   register const char* const last_safe_pos=state->next_to_end-TAG_WIDTH_OFFSET;
367   /* constructor */
368   tagstack_init(&(state->tag_stack));
369   /* magic; 256 > 50 (50 is min.required for double to string conversion */
370   pbuffer_init_as(&(state->expr_left_pbuffer), 256);
371   pbuffer_init_as(&(state->expr_right_pbuffer), 256);
372 
373   if (debuglevel) tmpl_log(TMPL_LOG_DEBUG,"process_state:initiated at scope stack depth = %d\n",
374 			   curScopeLevel(&state->param->var_scope_stack));
375 
376   while (state->cur_pos < last_safe_pos) {
377     register const char* cur_pos=state->cur_pos;
378     while ('<'!=*(cur_pos++)) {
379       if (cur_pos >= last_safe_pos) {
380 	goto exit_mainloop;
381       }
382     };
383     state->tag_start=cur_pos-1;
384     is_tag_closed=0;
385     is_tag_commented=0;
386     state->cur_pos=cur_pos;
387     if (('!'==*(cur_pos)) && ('-'==*(cur_pos+1)) && ('-'==*(cur_pos+2))) {
388       state->cur_pos+=3;
389       jump_over_space(state);
390       is_tag_commented=1;
391     }
392     if ('/'==*(state->cur_pos)) {
393       state->cur_pos++;
394       is_tag_closed=1;
395     }
396     if (is_string(state,metatag,METATAG)) {
397       state->is_tag_commented=is_tag_commented;
398       state->is_tag_closed=is_tag_closed;
399       process_tmpl_tag(state);
400     }
401   }
402   exit_mainloop:;
403   (state->param->WriterFuncPtr)(state->param->ext_writer_state,state->last_processed_pos,state->next_to_end);
404 
405   /* destructor */
406   pbuffer_free(&(state->expr_right_pbuffer));
407   pbuffer_free(&(state->expr_left_pbuffer));
408   tagstack_free(&(state->tag_stack));
409   if (debuglevel) tmpl_log(TMPL_LOG_DEBUG,"process_state:finished\n");
410 }
411 
412 static
413 void
init_state(struct tmplpro_state * state,struct tmplpro_param * param)414 init_state (struct tmplpro_state *state, struct tmplpro_param *param)
415 {
416   /* initializing state */
417   state->param=param;
418   state->last_processed_pos=state->top;
419   state->cur_pos=state->top;
420   state->tag=HTML_TEMPLATE_NO_TAG;
421   state->is_visible=1;
422 }
423 
424 static
425 int
tmplpro_exec_tmpl_filename(struct tmplpro_param * param,const char * filename)426 tmplpro_exec_tmpl_filename (struct tmplpro_param *param, const char* filename)
427 {
428   struct tmplpro_state state;
429   int mmapstatus;
430   PSTRING memarea;
431   int retval = 0;
432   const char* saved_masterpath;
433   /*
434    * param->masterpath is path to upper level template
435    * (or NULL in toplevel) which called <include filename>.
436    * we use it to calculate filepath for filename.
437    * Then filename becames upper level template for its <include>.
438    */
439   const char* filepath=(param->FindFileFuncPtr)(param->ext_findfile_state,filename, param->masterpath);
440   if (NULL==filepath) return ERR_PRO_FILE_NOT_FOUND;
441   /* filepath should be alive for every nested template */
442   filepath = strdup(filepath);
443   if (NULL==filepath) return ERR_PRO_NOT_ENOUGH_MEMORY;
444   saved_masterpath=param->masterpath; /* saving current file name */
445   param->masterpath=filepath;
446   if (param->filters) memarea=(param->LoadFileFuncPtr)(param->ext_filter_state,filepath);
447   else memarea=mmap_load_file(filepath);
448   if (memarea.begin == NULL) {
449     retval = ERR_PRO_CANT_OPEN_FILE;
450     goto cleanup_filepath;
451   }
452   state.top =memarea.begin;
453   state.next_to_end=memarea.endnext;
454   if (memarea.begin < memarea.endnext) {
455     /* to avoid crash with empty file */
456     init_state(&state,param);
457     if (debuglevel) log_state(&state,TMPL_LOG_DEBUG, "exec_tmpl: loading %s\n",filename);
458     process_state(&state);
459   }
460   /* destroying */
461   if (param->filters) mmapstatus=(param->UnloadFileFuncPtr)(param->ext_filter_state,memarea);
462   else mmapstatus=mmap_unload_file(memarea);
463  cleanup_filepath:
464   if (filepath!=NULL) free((void*) filepath);
465   param->masterpath=saved_masterpath;
466   return retval;
467 }
468 
469 static
470 int
tmplpro_exec_tmpl_scalarref(struct tmplpro_param * param,PSTRING memarea)471 tmplpro_exec_tmpl_scalarref (struct tmplpro_param *param, PSTRING memarea)
472 {
473   struct tmplpro_state state;
474   const char* saved_masterpath=param->masterpath; /* saving current file name */
475   param->masterpath=NULL; /* no upper file */
476   state.top = memarea.begin;
477   state.next_to_end=memarea.endnext;
478   if (memarea.begin != memarea.endnext) {
479     init_state(&state,param);
480     process_state(&state);
481   }
482   /* exit cleanup code */
483   param->masterpath=saved_masterpath;
484   return 0;
485 }
486 
487 #include "builtin_findfile.inc"
488 #include "callback_stubs.inc"
489 
490 API_IMPL
491 int
492 APICALL
tmplpro_exec_tmpl(struct tmplpro_param * param)493 tmplpro_exec_tmpl (struct tmplpro_param *param)
494 {
495   int exitcode=0;
496   param->htp_errno=0;
497   if (param->GetAbstractValFuncPtr==NULL ||
498        param->AbstractVal2pstringFuncPtr==NULL ||
499        param->AbstractVal2abstractArrayFuncPtr==NULL ||
500        /*param->GetAbstractArrayLengthFuncPtr==NULL ||*/
501        param->GetAbstractMapFuncPtr==NULL ||
502       (param->IsExprUserfncFuncPtr!=NULL && param->IsExprUserfncFuncPtr != stub_is_expr_userfnc_func &&
503        (param->InitExprArglistFuncPtr==NULL ||
504 	param->PushExprArglistFuncPtr==NULL ||
505 	param->FreeExprArglistFuncPtr==NULL ||
506 	param->CallExprUserfncFuncPtr==NULL))
507       )
508     {
509       tmpl_log(TMPL_LOG_ERROR,"tmplpro_exec_tmpl: required callbacks are missing:");
510       if (param->GetAbstractValFuncPtr==NULL) tmpl_log(TMPL_LOG_ERROR," GetAbstractValFuncPtr");
511       if (param->AbstractVal2pstringFuncPtr==NULL) tmpl_log(TMPL_LOG_ERROR," AbstractVal2pstringFuncPtr");
512       if (param->AbstractVal2abstractArrayFuncPtr==NULL) tmpl_log(TMPL_LOG_ERROR," AbstractVal2abstractArrayFuncPtr");
513       if (param->GetAbstractMapFuncPtr==NULL) tmpl_log(TMPL_LOG_ERROR," GetAbstractMapFuncPtr");
514       if ((param->IsExprUserfncFuncPtr!=NULL &&
515 	   (param->InitExprArglistFuncPtr==NULL ||
516 	    param->PushExprArglistFuncPtr==NULL ||
517 	    param->FreeExprArglistFuncPtr==NULL ||
518 	    param->CallExprUserfncFuncPtr==NULL))
519 	  ) tmpl_log(TMPL_LOG_ERROR," one of the Expr callbacks");
520       tmpl_log(TMPL_LOG_ERROR,". The library is not initialized properly.\n");
521       return ERR_PRO_INVALID_ARGUMENT;
522   }
523   if (param->filters &&
524       (param->LoadFileFuncPtr==NULL ||
525        param->UnloadFileFuncPtr==NULL)) {
526     tmpl_log(TMPL_LOG_ERROR,"tmplpro_exec_tmpl: filters is set but filter callbacks are missing.\n");
527   }
528   /* set up stabs */
529   if (NULL==param->WriterFuncPtr) param->WriterFuncPtr = stub_write_chars_to_stdout;
530   if (NULL==param->ext_findfile_state) param->ext_findfile_state = param;
531   if (NULL==param->FindFileFuncPtr) {
532     param->FindFileFuncPtr = stub_find_file_func;
533     param->ext_findfile_state = param;
534     /*pbuffer_init(&param->builtin_findfile_buffer);*/
535   }
536   if (NULL==param->IsExprUserfncFuncPtr) param->IsExprUserfncFuncPtr = stub_is_expr_userfnc_func;
537   if (NULL==param->LoadFileFuncPtr) param->LoadFileFuncPtr = stub_load_file_func;
538   if (NULL==param->UnloadFileFuncPtr) param->UnloadFileFuncPtr = stub_unload_file_func;
539   if (NULL==param->GetAbstractArrayLengthFuncPtr) param->GetAbstractArrayLengthFuncPtr = stub_get_ABSTRACT_ARRAY_length_func;
540 
541   Scope_reset(&param->var_scope_stack, param->param_map_count);
542   /* reset other internals */
543   param->cur_includes=0; /* internal counter of include depth */
544   param->found_syntax_error=0;
545   /*masterpath=NULL;*/
546 
547 
548   /* TODO: hackaround;*/
549   debuglevel=param->debug;
550   tmpl_log_set_level(debuglevel);
551 
552   if (param->scalarref.begin) exitcode = tmplpro_exec_tmpl_scalarref(param, param->scalarref);
553   else if (param->filename) exitcode = tmplpro_exec_tmpl_filename(param, param->filename);
554   else {
555     tmpl_log(TMPL_LOG_ERROR,"tmplpro_exec_tmpl: neither scalarref nor filename was specified.\n");
556     exitcode = ERR_PRO_INVALID_ARGUMENT;
557   }
558   if (param->strict && param->found_syntax_error && 0==exitcode) exitcode = ERR_PRO_TEMPLATE_SYNTAX_ERROR;
559   param->htp_errno=exitcode;
560   return exitcode;
561 }
562 
563 API_IMPL
564 PSTRING
565 APICALL
tmplpro_tmpl2pstring(struct tmplpro_param * param,int * retvalptr)566 tmplpro_tmpl2pstring (struct tmplpro_param *param, int *retvalptr)
567 {
568   int exitcode;
569   PSTRING retval;
570   struct builtin_writer_state state;
571   writer_functype save_writer_func = param->WriterFuncPtr;
572   ABSTRACT_WRITER* save_writer_state = param->ext_writer_state;
573   param->WriterFuncPtr = stub_write_chars_to_pbuffer;
574   param->ext_writer_state = &state;
575   state.bufptr=&param->builtin_tmpl2string_buffer;
576   pbuffer_resize(state.bufptr, 4000);
577   state.size = 0;
578   exitcode = tmplpro_exec_tmpl (param);
579   param->WriterFuncPtr = save_writer_func;
580   param->ext_writer_state = save_writer_state;
581   if (NULL!=retvalptr) *retvalptr=exitcode;
582   retval.begin = pbuffer_string(state.bufptr);
583   retval.endnext = retval.begin+state.size;
584   *((char*) retval.endnext)='\0';
585   return retval;
586 }
587 
588 API_IMPL
589 void
590 APICALL
tmplpro_clear_option_param_map(struct tmplpro_param * param)591 tmplpro_clear_option_param_map(struct tmplpro_param *param)
592 {
593   param->param_map_count=0;
594   Scope_reset(&param->var_scope_stack,param->param_map_count);
595 }
596 
597 API_IMPL
598 int
599 APICALL
tmplpro_push_option_param_map(struct tmplpro_param * param,ABSTRACT_MAP * map,EXPR_int64 flags)600 tmplpro_push_option_param_map(struct tmplpro_param *param, ABSTRACT_MAP* map, EXPR_int64 flags)
601 {
602   pushScopeMap(&param->var_scope_stack, map, (int) flags);
603   return ++(param->param_map_count);
604 }
605 
606 API_IMPL
607 int
608 APICALL
tmplpro_count_option_param_map(struct tmplpro_param * param)609 tmplpro_count_option_param_map(struct tmplpro_param *param)
610 {
611   return param->param_map_count;
612 }
613 
614 
615 API_IMPL
616 void
617 APICALL
tmplpro_procore_init(void)618 tmplpro_procore_init(void)
619 {
620 }
621 
622 API_IMPL
623 void
624 APICALL
tmplpro_procore_done(void)625 tmplpro_procore_done(void)
626 {
627 }
628 
629 /* internal initialization of struct tmplpro_param */
630 API_IMPL
631 struct tmplpro_param*
632 APICALL
tmplpro_param_init(void)633 tmplpro_param_init(void)
634 {
635   struct tmplpro_param* param=(struct tmplpro_param*) malloc (sizeof(struct tmplpro_param));
636   if (param==NULL) return param;
637   /* filling initial struct tmplpro_param with 0 */
638   memset (param, 0, sizeof(struct tmplpro_param));
639   /* current level of inclusion */
640   /* param->cur_includes=0; */
641   /* not to use external file loader */
642   /* param->filters=0;
643      param->default_escape=HTML_TEMPLATE_OPT_ESCAPE_NO;
644      param->masterpath=NULL; *//* we are not included by something *//*
645      param->expr_func_map=NULL;
646      param->expr_func_arglist=NULL;
647   */
648   _reset_int_options_set_nonzero_defaults(param);
649   Scope_init(&param->var_scope_stack);
650   /* no need for them due to memset 0
651   pbuffer_preinit(&param->builtin_findfile_buffer);
652   pbuffer_preinit(&param->builtin_tmpl2string_buffer);
653   pbuffer_preinit(&param->lowercase_varname_buffer);
654   pbuffer_preinit(&param->uppercase_varname_buffer);
655   pbuffer_preinit(&param->escape_pstring_buffer);
656   */
657   return param;
658 }
659 
660 API_IMPL
661 void
662 APICALL
tmplpro_param_free(struct tmplpro_param * param)663 tmplpro_param_free(struct tmplpro_param* param)
664 {
665   pbuffer_free(&param->builtin_findfile_buffer);
666   pbuffer_free(&param->builtin_tmpl2string_buffer);
667   pbuffer_free(&param->lowercase_varname_buffer);
668   pbuffer_free(&param->uppercase_varname_buffer);
669   pbuffer_free(&param->escape_pstring_buffer);
670   Scope_free(&param->var_scope_stack);
671   free(param);
672 }
673 
674 API_IMPL
675 int
676 APICALL
tmplpro_errno(struct tmplpro_param * param)677 tmplpro_errno(struct tmplpro_param* param)
678 {
679   return param->htp_errno;
680 }
681 
682 API_IMPL
683 const char*
684 APICALL
tmplpro_errmsg(struct tmplpro_param * param)685 tmplpro_errmsg(struct tmplpro_param* param)
686 {
687   return  errlist[param->htp_errno];
688 }
689 
690 API_IMPL
691 int
692 APICALL
tmplpro_set_log_file(struct tmplpro_param * param,const char * logfilename)693 tmplpro_set_log_file(struct tmplpro_param* param, const char* logfilename)
694 {
695   FILE *file_p;
696   if (NULL==logfilename) {
697     if (tmpl_log_stream!=NULL) {
698       fclose(tmpl_log_stream);
699       tmpl_log_stream=NULL;
700     }
701     tmpl_log_set_callback(tmpl_log_default_callback);
702     return 0;
703   }
704   file_p = fopen(logfilename, "a");
705   if (!file_p) {
706     tmpl_log(TMPL_LOG_ERROR,"tmplpro_set_log_file: can't create log file [%s]\n",logfilename);
707     return ERR_PRO_FILE_NOT_FOUND;
708   } else {
709     if (tmpl_log_stream!=NULL) fclose(tmpl_log_stream);
710     tmpl_log_stream=file_p;
711     tmpl_log_set_callback(tmpl_log_stream_callback);
712     return 0;
713   }
714 }
715 
716 API_IMPL
717 size_t
718 APICALL
tmplpro_param_allocated_memory_info(struct tmplpro_param * param)719 tmplpro_param_allocated_memory_info(struct tmplpro_param* param)
720 {
721   return 0L +
722     pbuffer_size(&param->builtin_findfile_buffer) +
723     pbuffer_size(&param->builtin_tmpl2string_buffer) +
724     pbuffer_size(&param->lowercase_varname_buffer) +
725     pbuffer_size(&param->uppercase_varname_buffer) +
726     pbuffer_size(&param->escape_pstring_buffer) +
727     (1+curScopeLevel(&param->var_scope_stack)) * sizeof(struct scope_stack);
728 }
729 
730 #include "tagstack.inc"
731 
732 /*
733  * Local Variables:
734  * mode: c
735  * End:
736  */
737