1 /*
2 Text-ClearSilver.xs - The template processor class
3
4 Copyright(c) 2010 Craftworks. All rights reserved.
5
6 See lib/Text/ClearSilver.pm for details.
7 */
8
9 #define NEED_newSVpvn_flags_GLOBAL
10 #define NO_XSLOCKS
11 #include "Text-ClearSilver.h"
12
13 #define MY_CXT_KEY "Text::ClearSilver::_guts" XS_VERSION
14 /* my_cxt_t is defined in Text-ClearSilver.h */
15 START_MY_CXT
16
17 /* my_cxt accessor for HDF.xs */
18 my_cxt_t*
tcs_get_my_cxtp(pTHX)19 tcs_get_my_cxtp(pTHX) {
20 dMY_CXT;
21 return &MY_CXT;
22 }
23
24 /* ClearSilver can access ibuf out of range of memory :(
25 so extra some memory must be allocated for cs_parse_string().
26 */
27 static const size_t extra_bytes = 8;
28
29 /*
30 NOTE: Currently, file_cache is always enabled, although it can be disabled.
31 */
32
33 static NEOERR*
tcs_fileload(void * vcsparse,HDF * const hdf,const char * filename,char ** const contents)34 tcs_fileload(void* vcsparse, HDF* const hdf, const char* filename, char** const contents) {
35 dTHX;
36 dMY_CXT;
37 I32 filename_len;
38 NEOERR* err = STATUS_OK;
39 char fpath[_POSIX_PATH_MAX];
40 Stat_t st;
41 bool stat_ok = FALSE;
42
43 /* find file */
44 if (filename[0] != '/') {
45 err = hdf_search_path (hdf, filename, fpath);
46 if (((CSPARSE*)vcsparse)->global_hdf && nerr_handle(&err, NERR_NOT_FOUND)) {
47 err = hdf_search_path(((CSPARSE*)vcsparse)->global_hdf, filename, fpath);
48 }
49 if (err != STATUS_OK) return nerr_pass(err);
50
51 filename = fpath;
52 }
53 filename_len = strlen(filename);
54
55 /* check cache */
56 if(MY_CXT.file_cache){
57 SV** const svp = hv_fetch(MY_CXT.file_cache, filename, filename_len, FALSE);
58
59 if(svp){
60 SV* const mtime_cache = AvARRAY(SvRV(*svp))[0];
61 SV* const contents_cache = AvARRAY(SvRV(*svp))[1];
62
63 if(PerlLIO_stat(filename, &st) < 0) {
64 return nerr_raise(NERR_IO, "Failed to stat(%s): %s", filename, Strerror(errno));
65 }
66 stat_ok = TRUE;
67
68 assert(SvIOK(mtime_cache));
69 assert(SvPOK(contents_cache));
70
71 if(st.st_mtime == SvIVX(mtime_cache)) {
72 *contents = (char*)malloc(st.st_size + extra_bytes);
73 Copy(SvPVX(contents_cache), *contents, st.st_size + 1, char);
74 return STATUS_OK;
75 }
76 }
77 }
78
79 /* load file normally */
80 if(!(stat_ok || PerlLIO_stat(filename, &st) >= 0)) {
81 return nerr_raise(NERR_IO, "Failed to stat(%s): %s", filename, Strerror(errno));
82 }
83
84 ENTER;
85 SAVETMPS;
86 {
87 SV* namesv = newSVpvn_flags(filename, filename_len, SVs_TEMP);
88 SV* file_buf;
89 PerlIO* const ifp = PerlIO_openn(aTHX_
90 MY_CXT.input_layer, "r", -1, O_RDONLY, 0, NULL, 1, &namesv);
91
92 if(!ifp){
93 err = nerr_raise(NERR_IO, "Failed to open %s: %s", filename, Strerror(errno));
94 goto cleanup;
95 }
96
97 file_buf = sv_2mortal(newSV(st.st_size));
98
99 /* local $/ = undef */
100 SAVESPTR(PL_rs);
101 PL_rs = &PL_sv_undef;
102
103 sv_gets(file_buf, ifp, FALSE);
104
105 if(PerlIO_error(ifp)) {
106 PerlIO_close(ifp);
107 err = nerr_raise(NERR_IO, "Failed to gets");
108 goto cleanup;
109 }
110
111 PerlIO_close(ifp);
112
113 *contents = (char*)malloc(SvCUR(file_buf) + extra_bytes);
114 Copy(SvPVX(file_buf), *contents, SvCUR(file_buf) + 1, char);
115
116 if(MY_CXT.file_cache){
117 SV* cache_entry[2]; /* mtime, contents */
118
119 cache_entry[0] = newSViv(st.st_mtime);
120 cache_entry[1] = SvREFCNT_inc_simple_NN(file_buf);
121
122 (void)hv_store(MY_CXT.file_cache, filename, filename_len,
123 newRV_noinc((SV*)av_make(2, cache_entry)), 0U);
124 }
125 }
126
127 cleanup:
128 FREETMPS;
129 LEAVE;
130 return err;
131 }
132
133 /* in csparse.c */
134 NEOERR*
135 tcs_eval_expr(CSPARSE* parse, CSARG* arg, CSARG* result);
136 const char*
137 tcs_var_lookup(CSPARSE* parse, const char* name);
138 long
139 tcs_var_int_lookup(CSPARSE* parse, const char* name);
140 HDF*
141 tcs_var_lookup_obj(CSPARSE* parse, const char* name);
142
143 static NEOERR*
tcs_push_args(pTHX_ CSPARSE * const parse,CSARG * args,const bool utf8)144 tcs_push_args(pTHX_ CSPARSE* const parse, CSARG* args, const bool utf8) {
145 dSP;
146
147 PUSHMARK(SP);
148
149 while(args) {
150 const char* str;
151 CSARG val;
152 NEOERR* err;
153 SV* sv;
154
155 err = tcs_eval_expr(parse, args, &val);
156
157 if(err){
158 (void)POPMARK;
159
160 return nerr_pass(err);
161 }
162
163 sv = sv_newmortal();
164 XPUSHs(sv);
165
166 switch(val.op_type & CS_TYPES){
167 case CS_TYPE_STRING:
168 assert(val.s);
169 sv_setpv(sv, val.s);
170 if(utf8) {
171 sv_utf8_decode(sv);
172 }
173 break;
174
175 case CS_TYPE_VAR:
176 assert(val.s);
177 str = tcs_var_lookup(parse, val.s);
178 if(str) {
179 sv_setpv(sv, str);
180 if(utf8) {
181 sv_utf8_decode(sv);
182 }
183 }
184 else { /* HDF node */
185 HDF* const hdf = tcs_var_lookup_obj(parse, val.s);
186 if(hdf) {
187 sv_setref_pv(sv, C_HDF, hdf);
188 }
189 }
190 break;
191
192 case CS_TYPE_NUM:
193 sv_setiv(sv, val.n);
194 break;
195
196 case CS_TYPE_VAR_NUM:
197 assert(val.s);
198 sv_setiv(sv, tcs_var_int_lookup(parse, val.s));
199 break;
200
201 default:
202 /* something's wrong? */
203 break;
204 }
205
206 if(val.alloc){
207 free(val.s);
208 }
209 args = args->next;
210 }
211 PUTBACK;
212 return STATUS_OK;
213 }
214
215 /* general cs function wrapper */
216 static NEOERR*
tcs_function_wrapper(CSPARSE * const parse,CS_FUNCTION * const csf,CSARG * const args,CSARG * const result)217 tcs_function_wrapper(CSPARSE* const parse, CS_FUNCTION* const csf, CSARG* const args, CSARG* const result) {
218 dTHX;
219 dMY_CXT;
220 SV** svp;
221 SV* retval;
222 NEOERR* err;
223
224 assert(MY_CXT.functions);
225
226 /* XXX: Hey! csf->name_len is not set!! */
227 //svp = hv_fetch(MY_CXT.functions, csf->name, csf->name_len, FALSE);
228 svp = hv_fetch(MY_CXT.functions, csf->name, strlen(csf->name), FALSE);
229 if(!( svp && SvROK(*svp) && SvTYPE(SvRV(*svp)) == SVt_PVAV
230 && (svp = av_fetch((AV*)SvRV(*svp), 0, FALSE)) )){
231 return nerr_raise(NERR_ASSERT, "Function entry for %s() is broken", csf->name);
232 }
233
234 ENTER;
235 SAVETMPS;
236
237 err = tcs_push_args(aTHX_ parse, args, MY_CXT.utf8); /* PUSHMARK & PUSH & PUTBACK */
238 if(err != STATUS_OK) {
239 err = nerr_pass(err);
240 goto cleanup;
241 }
242
243 call_sv(*svp, G_SCALAR | G_EVAL);
244
245 {
246 dSP;
247 SPAGAIN;
248 retval = POPs;
249 PUTBACK;
250 }
251
252 if(sv_true(ERRSV)){
253 err = nerr_raise(NERR_ASSERT,
254 "Function %s() died: %s", csf->name, SvPV_nolen_const(ERRSV));
255 goto cleanup;
256 }
257
258 if(!((SvTYPE(retval) & SVf_OK) == SVf_IOK && PERL_ABS(SvIVX(retval)) <= PERL_LONG_MAX)) {
259 STRLEN len;
260 const char* const pv = SvPV_const(retval, len);
261 len++; /* '\0' */
262
263 result->op_type = CS_TYPE_STRING;
264 result->s = (char*)malloc(len);
265 result->alloc = TRUE;
266 Copy(pv, result->s, len, char);
267 }
268 else { /* SvIOK */
269 result->op_type = CS_TYPE_NUM;
270 result->n = (long)SvIVX(retval);
271 }
272
273 cleanup:
274 FREETMPS;
275 LEAVE;
276
277 return err;
278 }
279
280 NEOERR*
tcs_parse_sv(pTHX_ CSPARSE * const parse,SV * const sv)281 tcs_parse_sv(pTHX_ CSPARSE* const parse, SV* const sv) {
282 STRLEN str_len;
283 const char* const str = SvPV_const(sv, str_len);
284
285 char* const ibuf = (char*)malloc(str_len + extra_bytes);
286 if(ibuf == NULL){
287 return nerr_raise (NERR_NOMEM,
288 "Unable to allocate memory");
289 }
290
291 Copy(str, ibuf, str_len + 1, char); /* with '\0' */
292 return cs_parse_string(parse, ibuf, str_len);
293 }
294
295 void
tcs_throw_error(pTHX_ NEOERR * const err)296 tcs_throw_error(pTHX_ NEOERR* const err) {
297 SV* sv;
298 STRING errstr;
299 string_init(&errstr);
300 nerr_error_string(err, &errstr);
301 sv = newSVpvn_flags(errstr.buf, errstr.len, SVs_TEMP);
302 string_clear(&errstr);
303
304 Perl_croak(aTHX_ "ClearSilver: %"SVf, sv);
305 }
306
307
308 static CV*
tcs_sv2cv(pTHX_ SV * const func)309 tcs_sv2cv(pTHX_ SV* const func) {
310 HV* stash; /* unused */
311 GV* gv; /* unused */
312 CV* const cv = sv_2cv(func, &stash, &gv, 0);
313 if(!cv){
314 croak("Not a CODE reference");
315 }
316 return cv;
317 }
318
319 static HV*
tcs_deref_hv(pTHX_ SV * const hvref)320 tcs_deref_hv(pTHX_ SV* const hvref) {
321 if(!(SvROK(hvref) && SvTYPE(SvRV(hvref)) == SVt_PVHV)) {
322 croak("Not a HASH reference");
323 }
324 return (HV*)SvRV(hvref);
325 }
326
327
328 static const char*
tcs_get_class_name(pTHX_ SV * const self)329 tcs_get_class_name(pTHX_ SV* const self) {
330 if(SvROK(self) && SvOBJECT(SvRV(self))){
331 HV* const stash = SvSTASH(SvRV(self));
332 return HvNAME_get(stash);
333 }
334 else {
335 return SvPV_nolen_const(self);
336 }
337 }
338
339 static bool
tcs_is_utf8(pTHX_ SV * const tcs)340 tcs_is_utf8(pTHX_ SV* const tcs) {
341 SV** const svp = hv_fetchs(tcs_deref_hv(aTHX_ tcs), "utf8", FALSE);
342 return svp ? sv_true(*svp) : FALSE;
343 }
344
345
346 static void
tcs_register_function(pTHX_ SV * const self,SV * const name,SV * const func,IV const n_args)347 tcs_register_function(pTHX_ SV* const self, SV* const name, SV* const func, IV const n_args) {
348 SV** const svp = hv_fetchs(tcs_deref_hv(aTHX_ self), "functions", FALSE);
349 HV* hv;
350 SV* pair[2];
351 if(svp) {
352 hv = tcs_deref_hv(aTHX_ *svp);
353 }
354 else {
355 hv = newHV();
356 (void)hv_stores(tcs_deref_hv(aTHX_ self), "functions", newRV_noinc((SV*)hv));
357 }
358
359 pair[0] = newRV_inc((SV*)tcs_sv2cv(aTHX_ func));
360 pair[1] = newSViv(n_args);
361
362 (void)hv_store_ent(hv, name, newRV_noinc((SV*)av_make(2, pair)), 0U);
363 }
364 static void
tcs_load_function_set(pTHX_ SV * const self,SV * const val)365 tcs_load_function_set(pTHX_ SV* const self, SV* const val) {
366 dMY_CXT;
367
368 ENTER;
369 SAVETMPS;
370
371 if(!MY_CXT.function_set_is_loaded){
372 require_pv("Text/ClearSilver/FunctionSet.pm");
373 if(sv_true(ERRSV)){
374 croak("ClearSilver: panic: %"SVf, ERRSV);
375 }
376 MY_CXT.function_set_is_loaded = TRUE;
377 }
378
379 SAVESPTR(ERRSV);
380 ERRSV = sv_newmortal();
381 {
382 dSP;
383 HV* set;
384 HE* he;
385
386 PUSHMARK(SP);
387 EXTEND(SP, 2);
388 PUSHs(newSVpvs_flags("Text::ClearSilver::FunctionSet", SVs_TEMP));
389 PUSHs(val);
390 PUTBACK;
391 call_method("load", G_SCALAR | G_EVAL);
392
393 if(sv_true(ERRSV)){
394 croak("ClearSilver: cannot load a function set: %"SVf, ERRSV);
395 }
396
397 SPAGAIN;
398 set = tcs_deref_hv(aTHX_ POPs);
399 PUTBACK;
400
401 hv_iterinit(set);
402 while((he = hv_iternext(set))){
403 tcs_register_function(aTHX_ self, hv_iterkeysv(he), hv_iterval(set, he), -1);
404 }
405 }
406 FREETMPS;
407 LEAVE;
408 }
409
410 static void
tcs_set_config(pTHX_ SV * const self,HV * const hv,HDF * const hdf,SV * const key,SV * const val)411 tcs_set_config(pTHX_ SV* const self, HV* const hv, HDF* const hdf, SV* const key, SV* const val) {
412 const char* const keypv = SvPV_nolen_const(key);
413 if(isUPPER(*keypv)){ /* builtin config */
414 HDF* config;
415 CHECK_ERR( hdf_get_node(hdf, "Config", &config) );
416 CHECK_ERR( hdf_set_value(config, keypv, SvPV_nolen_const(val)) );
417 }
418 else { /* extended config */
419 if(strEQ(keypv, "encoding")) {
420 const char* const valpv = SvPV_nolen_const(val);
421 bool utf8;
422 if(strEQ(valpv, "utf8")){
423 utf8 = TRUE;
424 }
425 else if(strEQ(valpv, "bytes")){
426 utf8 = FALSE;
427 }
428 else {
429 croak("ClearSilver: encoding must be 'utf8' or 'bytes', not '%s'", valpv);
430 }
431 (void)hv_stores(hv, "utf8", newSViv(utf8));
432 }
433 else if(strEQ(keypv, "input_layer")){
434 (void)hv_stores(hv, "input_layer", newSVsv(val));
435 }
436 else if(strEQ(keypv, "dataset")) {
437 tcs_hdf_add(aTHX_ hdf, val, tcs_is_utf8(aTHX_ self));
438 }
439 else if(strEQ(keypv, "load_path")) {
440 HDF* loadpaths;
441 CHECK_ERR( hdf_get_node(hdf, "hdf.loadpaths", &loadpaths) );
442
443 tcs_hdf_add(aTHX_ loadpaths, val, tcs_is_utf8(aTHX_ self));
444 }
445 else if(strEQ(keypv, "functions")) {
446 tcs_load_function_set(aTHX_ self, val);
447 }
448 else if(ckWARN(WARN_MISC)) {
449 Perl_warner(aTHX_ packWARN(WARN_MISC), "%s: unknown configuration variable '%s'",
450 tcs_get_class_name(aTHX_ self), keypv);
451 (void)hv_store_ent(hv, key, newSVsv(val), 0U);
452 }
453 }
454 }
455
456 static void
tcs_configure(pTHX_ SV * const self,HV * const hv,HDF * const hdf,I32 const ax,I32 const items)457 tcs_configure(pTHX_ SV* const self, HV* const hv, HDF* const hdf, I32 const ax, I32 const items) {
458 if(items == 1) {
459 SV* const args_ref = ST(0);
460 HV* args;
461 HE* he;
462
463 SvGETMAGIC(args_ref);
464
465 if(!(SvROK(args_ref) && SvTYPE(SvRV(args_ref)) == SVt_PVHV
466 && !SvOBJECT(SvRV(args_ref)) )){
467 croak("%s: single parameters to configure must be a HASH ref",
468 tcs_get_class_name(aTHX_ self));
469 }
470 args = (HV*)SvRV(args_ref);
471
472 hv_iterinit(args);
473 while((he = hv_iternext(args))) {
474 tcs_set_config(aTHX_ self, hv, hdf, hv_iterkeysv(he), hv_iterval(args, he));
475 }
476 }
477 else {
478 I32 i;
479
480 if( (items % 2) != 0 ){
481 croak("%s: odd number of parameters to configure",
482 tcs_get_class_name(aTHX_ self));
483 }
484
485 for(i = 0; i < items; i += 2){
486 tcs_set_config(aTHX_ self, hv, hdf, ST(i), ST(i+1));
487 }
488 }
489 }
490
491 static PerlIO*
tcs_sv2io(pTHX_ SV * sv,const char * const mode,int const imode,bool * const need_closep)492 tcs_sv2io(pTHX_ SV* sv, const char* const mode, int const imode, bool* const need_closep) {
493 if(isGV(sv) || (SvROK(sv) && (isGV(SvRV(sv)) || SvTYPE(SvRV(sv)) == SVt_PVIO))){
494 return IoIFP(sv_2io(sv));
495 }
496 else {
497 PerlIO* const fp = PerlIO_openn(aTHX_
498 NULL, mode, -1, imode, 0, NULL, 1, &sv);
499 if(!fp){
500 croak("Cannot open %"SVf": %"SVf, sv, get_sv("!", GV_ADD));
501 }
502 *need_closep = TRUE;
503 return fp;
504 }
505 }
506
507 void
tcs_register_funcs(pTHX_ CSPARSE * const cs,HV * const funcs)508 tcs_register_funcs(pTHX_ CSPARSE* const cs, HV* const funcs) {
509
510 /* functions registered by users */
511 if(funcs) {
512 dMY_CXT;
513 char* key;
514 I32 keylen;
515 SV* val;
516
517 SAVEVPTR(MY_CXT.functions);
518 MY_CXT.functions = funcs;
519
520 hv_iterinit(funcs);
521 while((val = hv_iternextsv(funcs, &key, &keylen))) {
522 AV* pair;
523 if(!(SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVAV)){
524 croak("Function entry for %s() is broken", key);
525 }
526 pair = (AV*)SvRV(val);
527
528 CHECK_ERR( cs_register_function(cs, key,
529 SvIVx(*av_fetch(pair, 1, TRUE)), tcs_function_wrapper) );
530 }
531 }
532
533 /* functions from cgi_register_strfuncs() */
534 CHECK_ERR( cgi_register_strfuncs(cs) );
535 }
536
537 void*
tcs_get_struct_ptr(pTHX_ SV * const arg,const char * const klass,const char * const func_fq_name,const char * var_name)538 tcs_get_struct_ptr(pTHX_ SV* const arg, const char* const klass,
539 const char* const func_fq_name, const char* var_name) {
540 if(SvROK(arg) && sv_derived_from(arg, klass) && SvIOK(SvRV(arg))){
541 return INT2PTR(void*, SvIVX(SvRV(arg)));
542 }
543
544 croak("%s: %s is not of type %s", func_fq_name, var_name, klass);
545 return NULL; /* NOT REACHED */
546 }
547
548 #define register_function(self, name, cb, nargs) tcs_register_function(aTHX_ self, name, cb, nargs)
549
550 MODULE = Text::ClearSilver PACKAGE = Text::ClearSilver
551
552 PROTOTYPES: DISABLE
553
554 BOOT:
555 {
556 XS(boot_Text__ClearSilver__HDF);
557 XS(boot_Text__ClearSilver__CS);
558 MY_CXT_INIT;
559 MY_CXT.sort_cmp_cb = NULL;
560 MY_CXT.functions = NULL;
561 MY_CXT.input_layer = NULL;
562 MY_CXT.file_cache = newHV();
563
564 PUSHMARK(SP);
565 boot_Text__ClearSilver__HDF(aTHX_ cv);
566 SPAGAIN;
567
568 PUSHMARK(SP);
569 boot_Text__ClearSilver__CS(aTHX_ cv);
570 SPAGAIN;
571 }
572
573 #ifdef USE_ITHREADS
574
575 void
CLONE(...)576 CLONE(...)
577 CODE:
578 {
579 MY_CXT_CLONE;
580 MY_CXT.sort_cmp_cb = NULL;
581 MY_CXT.functions = NULL;
582 MY_CXT.input_layer = NULL;
583 MY_CXT.file_cache = newHV();
584 PERL_UNUSED_VAR(items);
585 }
586
587 #endif
588
589 void
new(SV * klass,...)590 new(SV* klass, ...)
591 CODE:
592 {
593 HDF* hdf;
594 SV* self;
595 HV* hv;
596 if(SvROK(klass)){
597 croak("Cannot %s->new as an instance method", "Text::ClearSilver");
598 }
599 hv = newHV();
600 self = sv_2mortal( newRV_noinc((SV*)hv) );
601 ST(0) = sv_bless(self, gv_stashsv(klass, GV_ADD));
602
603 CHECK_ERR( hdf_init(&hdf) );
604 sv_setref_pv(*hv_fetchs(hv, "dataset", TRUE), C_HDF, hdf);
605
606 /* ax+1 && items-1 for shift @_ */
607 tcs_configure(aTHX_ self, hv, hdf, ax + 1, items - 1);
608 XSRETURN(1);
609 }
610
611 void
612 register_function(SV* self, SV* name, SV* func, int n_args = -1)
613
614
615 void
dataset(SV * self)616 dataset(SV* self)
617 CODE:
618 {
619 ST(0) = *hv_fetchs(tcs_deref_hv(aTHX_ self), "dataset", TRUE);
620 XSRETURN(1);
621 }
622
623
624 #define DEFAULT_OUT ((SV*)PL_defoutgv)
625
626 void
627 process(SV* self, SV* src, SV* vars, SV* volatile dest = DEFAULT_OUT, ...)
628 CODE:
629 {
630 dMY_CXT;
631 dXCPT;
632 CSPARSE* cs = NULL;
633 HDF* hdf = NULL;
634 bool need_ifp_close = FALSE;
635 bool need_ofp_close = FALSE;
636 PerlIO* volatile ifp = NULL;
637 PerlIO* volatile ofp = NULL;
638 bool save_utf8;
639 const char* save_input_layer;
640
641 if(!( SvROK(self) && SvOBJECT(SvRV(self)) )){
642 croak("Cannot %s->process as a class method", "Text::ClearSilver");
643 }
644
645 SvGETMAGIC(src);
646 SvGETMAGIC(dest);
647
648 save_utf8 = MY_CXT.utf8;
649 MY_CXT.utf8 = FALSE;
650
651 save_input_layer = MY_CXT.input_layer;
652 MY_CXT.input_layer = NULL;
653
654 XCPT_TRY_START {
655 HV* const hv = tcs_deref_hv(aTHX_ self);
656 const char* input_layer;
657 SV** svp;
658
659 CHECK_ERR( hdf_init(&hdf) );
660
661 CHECK_ERR( hdf_copy(hdf, "", (HDF*)tcs_get_struct_ptr(aTHX_
662 *hv_fetchs(hv, "dataset", TRUE), C_HDF, "Text::ClearSilver::process", "dataset")) );
663
664 if(!(SvROK(dest) && SvTYPE(SvRV(dest)) <= SVt_PVMG)) { /* not a scalar ref */
665 ofp = tcs_sv2io(aTHX_ dest, "w", O_WRONLY|O_CREAT|O_TRUNC, &need_ofp_close);
666 }
667
668 MY_CXT.utf8 = tcs_is_utf8(aTHX_ self);
669
670 svp = NULL;
671 if(items > 4){
672 HV* const local_hv = newHV();
673 sv_2mortal((SV*)local_hv);
674 tcs_configure(aTHX_ self, local_hv, hdf, ax + 4, items - 4);
675
676 svp = hv_fetchs(local_hv, "utf8", FALSE);
677 if(svp) {
678 MY_CXT.utf8 = sv_true(*svp);
679 }
680
681 svp = hv_fetchs(local_hv, "input_layer", FALSE);
682 }
683 if(!svp){
684 svp = hv_fetchs(hv, "input_layer", FALSE);
685 }
686
687 if(svp) {
688 input_layer = SvPV_nolen_const(*svp);
689 }
690 else if(MY_CXT.utf8) {
691 input_layer = ":utf8";
692 }
693 else {
694 input_layer = NULL;
695 }
696
697 tcs_hdf_add(aTHX_ hdf, vars, MY_CXT.utf8);
698
699 CHECK_ERR( cs_init(&cs, hdf) );
700
701 svp = hv_fetchs(hv, "functions", FALSE);
702 tcs_register_funcs(aTHX_ cs, svp ? tcs_deref_hv(aTHX_ *svp) : NULL);
703
704 cs_register_fileload(cs, cs, tcs_fileload);
705
706 MY_CXT.input_layer = input_layer;
707
708 /* parse CS template */
709 if(SvROK(src)){
710 if(SvTYPE(SvRV(src)) > SVt_PVMG){
711 croak("Source must be a scalar reference or a filename, not %"SVf, src);
712 }
713 CHECK_ERR( tcs_parse_sv(aTHX_ cs, SvRV(src)) );
714 }
715 else {
716 CHECK_ERR( cs_parse_file(cs, SvPV_nolen_const(src)) );
717 }
718
719 /* render */
720 if(ofp) {
721 if(MY_CXT.utf8 && !PerlIO_isutf8(ofp)) {
722 PerlIO_binmode(aTHX_ ofp, '>', O_TEXT, ":utf8");
723 }
724 CHECK_ERR( cs_render(cs, ofp, tcs_output_to_io) );
725 }
726 else {
727 sv_setpvs(SvRV(dest), "");
728 if(MY_CXT.utf8) {
729 SvUTF8_on(SvRV(dest));
730 }
731 CHECK_ERR( cs_render(cs, SvRV(dest), tcs_output_to_sv) );
732 }
733 }
734 XCPT_TRY_END
735
736 MY_CXT.utf8 = save_utf8;
737 MY_CXT.input_layer = save_input_layer;
738
739 if(need_ifp_close) PerlIO_close(ifp);
740 if(need_ofp_close) PerlIO_close(ofp);
741
742 cs_destroy(&cs);
743 hdf_destroy(&hdf);
744
745 XCPT_CATCH {
746 XCPT_RETHROW;
747 }
748 }
749
750
751 void
clear_cache(self)752 clear_cache(self)
753 CODE:
754 {
755 dMY_CXT;
756 if(MY_CXT.file_cache){
757 ST(0) = sv_2mortal(newRV_noinc((SV*)MY_CXT.file_cache));
758 MY_CXT.file_cache = newHV();
759 }
760 else {
761 ST(0) = &PL_sv_undef;
762 }
763 XSRETURN(1);
764 }
765