* add a comment token '--', whichs content will show up in the output if -T is active
[citadel.git] / webcit / subst.c
1 /*
2  * $Id$
3  */
4 #include "sysdep.h"
5
6
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <dirent.h>
10 #include <errno.h>
11
12 #include <unistd.h>
13 #include <stdio.h>
14 #include <stdarg.h>
15 #include <stddef.h>
16
17 #define SHOW_ME_VAPPEND_PRINTF
18
19 #include "webcit.h"
20 #include "webserver.h"
21
22 extern char *static_dirs[PATH_MAX];  /* Disk representation */
23
24 HashList *WirelessTemplateCache;
25 HashList *WirelessLocalTemplateCache;
26 HashList *TemplateCache;
27 HashList *LocalTemplateCache;
28
29 HashList *GlobalNS;
30 HashList *Iterators;
31 HashList *Conditionals;
32 HashList *SortHash;
33 HashList *Defines;
34
35 int DumpTemplateI18NStrings = 0;
36 int LoadTemplates = 0;
37 int dbg_backtrace_template_errors = 0;
38 WCTemplputParams NoCtx;
39 StrBuf *I18nDump = NULL;
40
41 const char EmptyStr[]="";
42
43 #define SV_GETTEXT 1
44 #define SV_CONDITIONAL 2
45 #define SV_NEG_CONDITIONAL 3
46 #define SV_CUST_STR_CONDITIONAL 4
47 #define SV_SUBTEMPL 5
48 #define SV_PREEVALUATED 6
49
50
51 /*
52  * Dynamic content for variable substitution in templates
53  */
54 typedef struct _wcsubst {
55         ContextFilter Filter;
56         int wcs_type;                           /* which type of Substitution are we */
57         char wcs_key[32];                       /* copy of our hashkey for debugging */
58         StrBuf *wcs_value;                      /* if we're a string, keep it here */
59         long lvalue;                            /* type long? keep data here */
60         WCHandlerFunc wcs_function;             /* funcion hook ???*/
61 } wcsubst;
62
63
64 typedef struct _WCTemplate {
65         StrBuf *Data;
66         StrBuf *FileName;
67         int nTokensUsed;
68         int TokenSpace;
69         StrBuf *MimeType;
70         WCTemplateToken **Tokens;
71 } WCTemplate;
72
73 typedef struct _HashHandler {
74         ContextFilter Filter;
75         WCPreevalFunc PreEvalFunc;
76         WCHandlerFunc HandlerFunc;
77 }HashHandler;
78
79 void *load_template(WCTemplate *NewTemplate);
80 int EvaluateConditional(StrBuf *Target, int Neg, int state, WCTemplputParams *TP);
81
82
83
84 typedef struct _SortStruct {
85         StrBuf *Name;
86         StrBuf *PrefPrepend;
87         CompareFunc Forward;
88         CompareFunc Reverse;
89         CompareFunc GroupChange;
90
91         long ContextType;
92 }SortStruct;
93
94 const char *CtxNames[]  = {
95         "Context NONE",
96         "Context SITECFG",
97         "Context SESSION",
98         "Context INETCFG",
99         "Context VNOTE",
100         "Context WHO",
101         "Context PREF",
102         "Context NODECONF",
103         "Context USERLIST",
104         "Context MAILSUM",
105         "Context MIME_ATACH",
106         "Context FILELIST",
107         "Context STRBUF",
108         "Context STRBUFARR",
109         "Context LONGVECTOR",
110         "Context ROOMS",
111         "Context FLOORS",
112         "Context ITERATE",
113         "Context ICAL",
114         "Context DavNamespace",
115         "Context TAB",
116         "Context UNKNOWN"
117 };
118
119
120
121 void DestroySortStruct(void *vSort)
122 {
123         SortStruct *Sort = (SortStruct*) vSort;
124         FreeStrBuf(&Sort->Name);
125         FreeStrBuf(&Sort->PrefPrepend);
126         free (Sort);
127 }
128
129 const char *ContextName(int ContextType)
130 {
131         if (ContextType < CTX_UNKNOWN)
132                 return CtxNames[ContextType];
133         else
134                 return CtxNames[CTX_UNKNOWN];
135 }
136
137 void LogTemplateError (StrBuf *Target, const char *Type, int ErrorPos, WCTemplputParams *TP, const char *Format, ...)
138 {
139         wcsession *WCC;
140         StrBuf *Error;
141         StrBuf *Info;
142         va_list arg_ptr;
143         const char *Err = NULL;
144
145         Info = NewStrBuf();
146         Error = NewStrBuf();
147
148         va_start(arg_ptr, Format);
149         StrBufVAppendPrintf(Error, Format, arg_ptr);
150         va_end(arg_ptr);
151
152         switch (ErrorPos) {
153         case ERR_NAME: /* the main token name... */ 
154                 Err = (TP->Tokens!= NULL)? TP->Tokens->pName:"";
155                 break;
156         default:
157                 Err = ((TP->Tokens!= NULL) && 
158                        (TP->Tokens->nParameters > ErrorPos - 1))? 
159                         TP->Tokens->Params[ErrorPos - 1]->Start : "";
160                 break;
161         }
162         if (TP->Tokens != NULL) 
163         {
164                 lprintf(1, "%s [%s]  (in '%s' line %ld); %s; [%s]\n", 
165                         Type, 
166                         Err, 
167                         ChrPtr(TP->Tokens->FileName),
168                         TP->Tokens->Line, 
169                         ChrPtr(Error), 
170                         ChrPtr(TP->Tokens->FlatToken));
171         }
172         else 
173         {
174                 lprintf(1, "%s: %s;\n", 
175                         Type, 
176                         ChrPtr(Error));
177         }
178 /*
179         if (Target == NULL) 
180                 return;
181 */
182         WCC = WC;
183         if (WCC == NULL) {
184                 FreeStrBuf(&Info);
185                 FreeStrBuf(&Error);
186                 return; 
187         }
188
189         if (WCC->WFBuf == NULL) WCC->WFBuf = NewStrBuf();
190         if (TP->Tokens != NULL) 
191         {
192                 /* deprecated: 
193                 StrBufAppendPrintf(                                                          
194                         Target,                                                              
195                         "<pre>\n%s [%s] (in '%s' line %ld); %s\n[%s]\n</pre>\n",
196                         Type, 
197                         Err, 
198                         ChrPtr(TP->Tokens->FileName),
199                         TP->Tokens->Line,
200                         ChrPtr(Error),
201                         ChrPtr(TP->Tokens->FlatToken));
202                 */
203                 StrBufPrintf(Info, "%s [%s]  %s; [%s]", 
204                              Type, 
205                              Err, 
206                              ChrPtr(Error), 
207                              ChrPtr(TP->Tokens->FlatToken));
208
209
210                 SerializeJson(WCC->WFBuf, WildFireException(SKEY(TP->Tokens->FileName),
211                                                         TP->Tokens->Line,
212                                                         Info,
213                                                         1), 1);
214 /*
215                 SerializeJson(Header, WildFireMessage(SKEY(TP->Tokens->FileName),
216                                                       TP->Tokens->Line,
217                                                       Error,
218                                                       eERROR), 1);
219 */
220                 
221         }
222         else
223         {
224                 /* deprecated.
225                 StrBufAppendPrintf(                                                          
226                         Target,                                                              
227                         "<pre>\n%s: %s\n</pre>\n",
228                         Type, 
229                         ChrPtr(Error));
230                 */
231                 StrBufPrintf(Info, "%s [%s]  %s; [%s]", 
232                              Type, 
233                              Err, 
234                              ChrPtr(Error), 
235                              ChrPtr(TP->Tokens->FlatToken));
236                 SerializeJson(WCC->WFBuf, WildFireException(HKEY(__FILE__), __LINE__, Info, 1), 1);
237         }
238         FreeStrBuf(&Info);
239         FreeStrBuf(&Error);
240 /*
241         if (dbg_backtrace_template_errors)
242                 wc_backtrace(); 
243 */
244 }
245
246
247
248
249 void LogError (StrBuf *Target, const char *Type, const char *Format, ...)
250 {
251         wcsession *WCC;
252         StrBuf *Error;
253         StrBuf *Info;
254         va_list arg_ptr;
255
256         Info = NewStrBuf();
257         Error = NewStrBuf();
258
259         va_start(arg_ptr, Format);
260         StrBufVAppendPrintf(Error, Format, arg_ptr);
261         va_end(arg_ptr);
262
263         lprintf(1, ChrPtr(Error));
264
265         WCC = WC;
266         if (WCC->WFBuf == NULL) WCC->WFBuf = NewStrBuf();
267
268         SerializeJson(WCC->WFBuf, WildFireException(Type, strlen(Type),
269                                                     0,
270                                                     Info,
271                                                     1), 1);
272
273         FreeStrBuf(&Info);
274         FreeStrBuf(&Error);
275 /*
276         if (dbg_backtrace_template_errors)
277                 wc_backtrace(); 
278 */
279 }
280
281
282 void RegisterNS(const char *NSName, 
283                 long len, 
284                 int nMinArgs, 
285                 int nMaxArgs, 
286                 WCHandlerFunc HandlerFunc, 
287                 WCPreevalFunc PreevalFunc,
288                 int ContextRequired)
289 {
290         HashHandler *NewHandler;
291         
292         NewHandler = (HashHandler*) malloc(sizeof(HashHandler));
293         memset(NewHandler, 0, sizeof(HashHandler));
294         NewHandler->Filter.nMinArgs = nMinArgs;
295         NewHandler->Filter.nMaxArgs = nMaxArgs;
296         NewHandler->Filter.ContextType = ContextRequired;
297         NewHandler->Filter.ControlContextType = CTX_NONE;
298
299         NewHandler->PreEvalFunc = PreevalFunc;
300         NewHandler->HandlerFunc = HandlerFunc;  
301         Put(GlobalNS, NSName, len, NewHandler, NULL);
302 }
303
304 void RegisterControlNS(const char *NSName, 
305                        long len, 
306                        int nMinArgs, 
307                        int nMaxArgs, 
308                        WCHandlerFunc HandlerFunc, 
309                        int ControlContextRequired)
310 {
311         HashHandler *NewHandler;
312         
313         NewHandler = (HashHandler*) malloc(sizeof(HashHandler));
314         memset(NewHandler, 0, sizeof(HashHandler));
315         NewHandler->Filter.nMinArgs = nMinArgs;
316         NewHandler->Filter.nMaxArgs = nMaxArgs;
317         NewHandler->Filter.ContextType = CTX_NONE;
318         NewHandler->Filter.ControlContextType = ControlContextRequired;
319         NewHandler->HandlerFunc = HandlerFunc;  
320         Put(GlobalNS, NSName, len, NewHandler, NULL);
321 }
322
323
324
325 int CheckContext(StrBuf *Target, ContextFilter *Need, WCTemplputParams *TP, const char *ErrType)
326 {
327         if ((Need->ContextType != CTX_NONE) && 
328             (Need->ContextType != TP->Filter.ContextType)) {
329                 LogTemplateError(
330                         Target, ErrType, ERR_PARM1, TP,
331                         "  WARNING: requires Context: [%s], have [%s]!", 
332                         ContextName(Need->ContextType), 
333                         ContextName(TP->Filter.ContextType));
334                 return 0;
335         }
336
337         if ((Need->ControlContextType != CTX_NONE) && 
338             (Need->ControlContextType != TP->Filter.ControlContextType)) {
339                 LogTemplateError(
340                         Target, ErrType, ERR_PARM1, TP,
341                         "  WARNING: requires Control Context: [%s], have [%s]!", 
342                         ContextName(Need->ControlContextType), 
343                         ContextName(TP->Filter.ControlContextType));
344                 return 0;
345         }
346 /*                      
347         if (TP->Tokens->nParameters < Need->nMinArgs) {
348                 LogTemplateError(Target, ErrType, ERR_NAME, TP,
349                                  "needs at least %ld params, have %ld", 
350                                  Need->nMinArgs, 
351                                  TP->Tokens->nParameters);
352                 return 0;
353
354         }
355         else if (TP->Tokens->nParameters > Need->nMaxArgs) {
356                 LogTemplateError(Target, ErrType, ERR_NAME, TP,
357                                  "just needs %ld params, you gave %ld",
358                                  Need->nMaxArgs,
359                                  TP->Tokens->nParameters); 
360                 return 0;
361
362         }
363 */
364         return 1;
365 }
366
367 void FreeToken(WCTemplateToken **Token)
368 {
369         int i; 
370         FreeStrBuf(&(*Token)->FlatToken);
371         if ((*Token)->HaveParameters) 
372                 for (i = 0; i < (*Token)->nParameters; i++)
373                         free((*Token)->Params[i]);
374         free(*Token);
375         *Token = NULL;
376 }
377
378
379
380 void FreeWCTemplate(void *vFreeMe)
381 {
382         int i;
383         WCTemplate *FreeMe = (WCTemplate*)vFreeMe;
384
385         if (FreeMe->TokenSpace > 0) {
386                 for (i = 0; i < FreeMe->nTokensUsed; i ++) {
387                         FreeToken(&FreeMe->Tokens[i]);
388                 }
389                 free(FreeMe->Tokens);
390         }
391         FreeStrBuf(&FreeMe->FileName);
392         FreeStrBuf(&FreeMe->Data);
393         FreeStrBuf(&FreeMe->MimeType);
394         free(FreeMe);
395 }
396
397
398 /*
399  * debugging function to print array to log
400  */
401 void VarPrintTransition(void *vVar1, void *vVar2, int odd){}
402
403 /*
404  * debugging function to print array to log
405  */
406 void VarPrintEntry(const char *Key, void *vSubst, int odd)
407 {
408         wcsubst *ptr;
409         lprintf(1,"Subst[%s] : ", Key);
410         ptr = (wcsubst*) vSubst;
411
412         switch(ptr->wcs_type) {
413         case WCS_STRING:
414                 lprintf(1, "  -> %s\n", ChrPtr(ptr->wcs_value));
415                 break;
416         case WCS_SERVCMD:
417                 lprintf(1, "  -> Server [%s]\n", ChrPtr(ptr->wcs_value));
418                 break;
419         case WCS_FUNCTION:
420                 lprintf(1, "  -> function at [%0xd]\n", ptr->wcs_function);
421                 break;
422         default:
423                 lprintf(1,"  WARNING: invalid type: [%ld]!\n", ptr->wcs_type);
424         }
425 }
426
427
428
429 /*
430  * Clear out the list of substitution variables local to this session
431  */
432 void clear_substs(wcsession *wc) {
433
434         if (wc->vars != NULL) {
435                 DeleteHash(&wc->vars);
436         }
437 }
438
439 /*
440  * Clear out the list of substitution variables local to this session
441  */
442 void clear_local_substs(void) {
443         clear_substs (WC);
444 }
445
446 int NeedNewBuf(int type)
447 {
448         switch(type) {
449         case WCS_STRING:
450         case WCS_SERVCMD:
451         case WCS_STRBUF:
452                 return 1;
453         case WCS_FUNCTION:
454         case WCS_STRBUF_REF:
455         case WCS_LONG:
456         default:
457                 return 0;
458         }
459 }
460
461 void FlushPayload(wcsubst *ptr, int reusestrbuf, int type)
462 {
463         int NeedNew = NeedNewBuf(type);
464         switch(ptr->wcs_type) {
465         case WCS_STRING:
466         case WCS_SERVCMD:
467         case WCS_STRBUF:
468                 if (reusestrbuf && NeedNew) {
469                         FlushStrBuf(ptr->wcs_value);
470                 }
471                 else {
472                         
473                         FreeStrBuf(&ptr->wcs_value);
474                         ptr->wcs_value = NULL;
475                 }
476                 break;
477         case WCS_FUNCTION:
478                 ptr->wcs_function = NULL;
479                 if (reusestrbuf && NeedNew)
480                         ptr->wcs_value = NewStrBuf();
481                 break;
482         case WCS_STRBUF_REF:
483                 ptr->wcs_value = NULL;
484                 if (reusestrbuf && NeedNew)
485                         ptr->wcs_value = NewStrBuf();
486                 break;
487         case WCS_LONG:
488                 ptr->lvalue = 0;
489                 if (reusestrbuf && NeedNew)
490                         ptr->wcs_value = NewStrBuf();
491                 break;
492         default:
493                 if (reusestrbuf && NeedNew)
494                         ptr->wcs_value = NewStrBuf();
495                 break;
496         }
497 }
498
499
500 /*
501  * destructor; kill one entry.
502  */
503 void deletevar(void *data)
504 {
505         wcsubst *ptr = (wcsubst*)data;
506         FlushPayload(ptr, 0, ptr->wcs_type);
507         free(ptr);      
508 }
509
510
511 wcsubst *NewSubstVar(const char *keyname, int keylen, int type)
512 {
513         wcsubst* ptr;
514         wcsession *WCC = WC;
515
516         ptr = (wcsubst *) malloc(sizeof(wcsubst));
517         memset(ptr, 0, sizeof(wcsubst));
518
519         ptr->wcs_type = type;
520         safestrncpy(ptr->wcs_key, keyname, sizeof ptr->wcs_key);
521         Put(WCC->vars, keyname, keylen, ptr,  deletevar);
522
523         switch(ptr->wcs_type) {
524         case WCS_STRING:
525         case WCS_SERVCMD:
526                 ptr->wcs_value = NewStrBuf();
527                 break;
528         case WCS_STRBUF:
529         case WCS_FUNCTION:
530         case WCS_STRBUF_REF:
531         case WCS_LONG:
532         default:
533                 break;
534         }
535         return ptr;
536 }
537
538
539 /*
540  * Add a substitution variable (local to this session) (strlen version...)
541  * keyname the replacementstring to substitute
542  * keytype the kind of the key
543  * format the format string ala printf
544  * ... the arguments to substitute in the formatstring
545  */
546 void SVPRINTF(char *keyname, int keytype, const char *format,...)
547 {
548         va_list arg_ptr;
549         void *vPtr;
550         wcsubst *ptr = NULL;
551         size_t keylen;
552         wcsession *WCC = WC;
553         
554         keylen = strlen(keyname);
555         /*
556          * First look if we're doing a replacement of
557          * an existing key
558          */
559         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
560         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
561                 ptr = (wcsubst*)vPtr;
562                 FlushPayload(ptr, keytype, keytype);
563                 ptr->wcs_type = keytype;
564         }
565         else    /** Otherwise allocate a new one */
566         {
567                 ptr = NewSubstVar(keyname, keylen, keytype);
568         }
569
570         /* Format the string */
571         va_start(arg_ptr, format);
572         StrBufVAppendPrintf(ptr->wcs_value, format, arg_ptr);
573         va_end(arg_ptr);
574 }
575
576 /*
577  * Add a substitution variable (local to this session)
578  * keyname the replacementstring to substitute
579  * keytype the kind of the key
580  * format the format string ala printf
581  * ... the arguments to substitute in the formatstring
582  */
583 void svprintf(char *keyname, size_t keylen, int keytype, const char *format,...)
584 {
585         va_list arg_ptr;
586         void *vPtr;
587         wcsubst *ptr = NULL;
588         wcsession *WCC = WC;
589                 
590         /*
591          * First look if we're doing a replacement of
592          * an existing key
593          */
594         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
595         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
596                 ptr = (wcsubst*)vPtr;
597                 FlushPayload(ptr, 1, keytype);
598                 ptr->wcs_type = keytype;
599         }
600         else    /** Otherwise allocate a new one */
601         {
602                 ptr = NewSubstVar(keyname, keylen, keytype);
603         }
604
605         /** Format the string and save it */
606         va_start(arg_ptr, format);
607         StrBufVAppendPrintf(ptr->wcs_value, format, arg_ptr);
608         va_end(arg_ptr);
609 }
610
611 /*
612  * Add a substitution variable (local to this session)
613  * keyname the replacementstring to substitute
614  * keytype the kind of the key
615  * format the format string ala printf
616  * ... the arguments to substitute in the formatstring
617  */
618 void SVPut(char *keyname, size_t keylen, int keytype, char *Data)
619 {
620         void *vPtr;
621         wcsubst *ptr = NULL;
622         wcsession *WCC = WC;
623
624         
625         /*
626          * First look if we're doing a replacement of
627          * an existing key
628          */
629         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
630         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
631                 ptr = (wcsubst*)vPtr;
632                 FlushPayload(ptr, 1, keytype);
633                 ptr->wcs_type = keytype;
634         }
635         else    /** Otherwise allocate a new one */
636         {
637                 ptr = NewSubstVar(keyname, keylen, keytype);
638         }
639         StrBufAppendBufPlain(ptr->wcs_value, Data, -1, 0);
640 }
641
642 /**
643  * \brief Add a substitution variable (local to this session)
644  * \param keyname the replacementstring to substitute
645  * \param keytype the kind of the key
646  * \param format the format string ala printf
647  * \param ... the arguments to substitute in the formatstring
648  */
649 void SVPutLong(char *keyname, size_t keylen, long Data)
650 {
651         void *vPtr;
652         wcsubst *ptr = NULL;
653         wcsession *WCC = WC;
654
655         
656         /**
657          * First look if we're doing a replacement of
658          * an existing key
659          */
660         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
661         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
662                 ptr = (wcsubst*)vPtr;
663                 FlushPayload(ptr, 1, WCS_LONG);
664                 ptr->wcs_type = WCS_LONG;
665         }
666         else    /** Otherwise allocate a new one */
667         {
668                 ptr = NewSubstVar(keyname, keylen, WCS_LONG);
669         }
670         ptr->lvalue = Data;
671 }
672
673 /**
674  * \brief Add a substitution variable (local to this session) that does a callback
675  * \param keyname the keystring to substitute
676  * \param fcn_ptr the function callback to give the substitution string
677  */
678 void SVCallback(char *keyname, size_t keylen, WCHandlerFunc fcn_ptr)
679 {
680         wcsubst *ptr;
681         void *vPtr;
682         wcsession *WCC = WC;
683
684         /**
685          * First look if we're doing a replacement of
686          * an existing key
687          */
688         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
689         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
690                 ptr = (wcsubst*)vPtr;
691                 FlushPayload(ptr, 1, WCS_FUNCTION);
692                 ptr->wcs_type = WCS_FUNCTION;
693         }
694         else    /** Otherwise allocate a new one */
695         {
696                 ptr = NewSubstVar(keyname, keylen, WCS_FUNCTION);
697         }
698
699         ptr->wcs_function = fcn_ptr;
700 }
701 inline void SVCALLBACK(char *keyname, WCHandlerFunc fcn_ptr)
702 {
703         SVCallback(keyname, strlen(keyname), fcn_ptr);
704 }
705
706
707
708 void SVPUTBuf(const char *keyname, int keylen, const StrBuf *Buf, int ref)
709 {
710         wcsubst *ptr;
711         void *vPtr;
712         wcsession *WCC = WC;
713
714         /**
715          * First look if we're doing a replacement of
716          * an existing key
717          */
718         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
719         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
720                 ptr = (wcsubst*)vPtr;
721                 FlushPayload(ptr, 0, (ref)?WCS_STRBUF_REF:WCS_STRBUF);
722                 ptr->wcs_type = (ref)?WCS_STRBUF_REF:WCS_STRBUF;
723         }
724         else    /** Otherwise allocate a new one */
725         {
726                 ptr = NewSubstVar(keyname, keylen, (ref)?WCS_STRBUF_REF:WCS_STRBUF);
727         }
728         ptr->wcs_value = (StrBuf*)Buf;
729 }
730
731 /**
732  * \brief back end for print_value_of() ... does a server command
733  * \param servcmd server command to execute on the citadel server
734  */
735 void pvo_do_cmd(StrBuf *Target, StrBuf *servcmd) {
736         char buf[SIZ];
737         int len;
738
739         serv_puts(ChrPtr(servcmd));
740         len = serv_getln(buf, sizeof buf);
741
742         switch(buf[0]) {
743                 case '2':
744                 case '3':
745                 case '5':
746                         StrBufAppendPrintf(Target, "%s\n", &buf[4]);
747                         break;
748                 case '1':
749                         _fmout(Target, "CENTER");
750                         break;
751                 case '4':
752                         StrBufAppendPrintf(Target, "%s\n", &buf[4]);
753                         serv_puts("000");
754                         break;
755         }
756 }
757
758 int HaveTemplateTokenString(StrBuf *Target, 
759                             WCTemplputParams *TP,
760                             int N,
761                             const char **Value, 
762                             long *len)
763 {
764         if (N >= TP->Tokens->nParameters) {
765                 return 0;
766         }
767
768         switch (TP->Tokens->Params[N]->Type) {
769         case TYPE_INTDEFINE:
770         case TYPE_STR:
771         case TYPE_BSTR:
772         case TYPE_PREFSTR:
773         case TYPE_GETTEXT:
774         case TYPE_SUBTEMPLATE:
775                 return 1;
776         case TYPE_LONG:
777         case TYPE_PREFINT:
778         default:
779                 return 0;
780         }
781 }
782
783 void GetTemplateTokenString(StrBuf *Target, 
784                             WCTemplputParams *TP,
785                             int N,
786                             const char **Value, 
787                             long *len)
788 {
789         StrBuf *Buf;
790         WCTemplputParams SubTP;
791
792         if (N >= TP->Tokens->nParameters) {
793                 LogTemplateError(Target, 
794                                  "TokenParameter", N, TP, 
795                                  "invalid token %d. this shouldn't have come till here.\n", N);
796                 *Value = "";
797                 *len = 0;
798                 return;
799         }
800
801         switch (TP->Tokens->Params[N]->Type) {
802
803         case TYPE_INTDEFINE:
804         case TYPE_STR:
805                 *Value = TP->Tokens->Params[N]->Start;
806                 *len = TP->Tokens->Params[N]->len;
807                 break;
808         case TYPE_BSTR:
809                 if (TP->Tokens->Params[N]->len == 0) {
810                         LogTemplateError(Target, 
811                                          "TokenParameter", N, TP, 
812                                          "Requesting parameter %d; of type BSTR, empty lookup string not admitted.", N);
813                         *len = 0;
814                         *Value = EmptyStr;
815                         break;
816                 }
817                 Buf = (StrBuf*) SBstr(TKEY(N));
818                 *Value = ChrPtr(Buf);
819                 *len = StrLength(Buf);
820                 break;
821         case TYPE_PREFSTR:
822                 if (TP->Tokens->Params[N]->len == 0) {
823                         LogTemplateError(Target, 
824                                          "TokenParameter", N, TP, 
825                                          "Requesting parameter %d; of type PREFSTR, empty lookup string not admitted.", N);
826                         *len = 0;
827                         *Value = EmptyStr;
828                         break;
829                 }
830                 get_PREFERENCE(TKEY(N), &Buf);
831                 *Value = ChrPtr(Buf);
832                 *len = StrLength(Buf);
833                 break;
834         case TYPE_LONG:
835                 LogTemplateError(Target, 
836                                  "TokenParameter", N, TP, 
837                                  "Requesting parameter %d; of type LONG, want string.", N);
838                 break;
839         case TYPE_PREFINT:
840                 LogTemplateError(Target, 
841                                  "TokenParameter", N, TP, 
842                                  "Requesting parameter %d; of type PREFINT, want string.", N);
843                 break;
844         case TYPE_GETTEXT:
845                 *Value = _(TP->Tokens->Params[N]->Start);
846                 *len = strlen(*Value);
847                 break;
848         case TYPE_SUBTEMPLATE:
849                 if (TP->Tokens->Params[N]->len == 0) {
850                         LogTemplateError(Target, 
851                                          "TokenParameter", N, TP, 
852                                          "Requesting parameter %d; of type SUBTEMPLATE, empty lookup string not admitted.", N);
853                         *len = 0;
854                         *Value = EmptyStr;
855                         break;
856                 }
857
858                 memset(&SubTP, 0, sizeof(WCTemplputParams *));
859                 SubTP.Context = TP->Context;
860                 SubTP.Filter.ContextType = TP->Filter.ContextType;
861                 Buf = NewStrBuf();
862                 DoTemplate(TKEY(N), Buf, &SubTP);
863                 *Value = ChrPtr(Buf);
864                 *len = StrLength(Buf);
865                 /* we can't free it here, so we put it into the subst so its discarded later on. */
866                 SVPUTBuf(TKEY(N), Buf, 0);
867                 break;
868
869         default:
870                 LogTemplateError(Target, 
871                                  "TokenParameter", N, TP, 
872                                  "unknown param type %d; [%d]", N, TP->Tokens->Params[N]->Type);
873                 break;
874         }
875 }
876
877 long GetTemplateTokenNumber(StrBuf *Target, WCTemplputParams *TP, int N, long dflt)
878 {
879         long Ret;
880         if (N >= TP->Tokens->nParameters) {
881                 LogTemplateError(Target, 
882                                  "TokenParameter", N, TP, 
883                                  "invalid token %d. this shouldn't have come till here.\n", N);
884                 wc_backtrace(); 
885                 return 0;
886         }
887
888         switch (TP->Tokens->Params[N]->Type) {
889
890         case TYPE_STR:
891                 return atol(TP->Tokens->Params[N]->Start);
892                 break;
893         case TYPE_BSTR:
894                 if (TP->Tokens->Params[N]->len == 0) {
895                         LogTemplateError(Target, 
896                                          "TokenParameter", N, TP, 
897                                          "Requesting parameter %d; of type BSTR, empty lookup string not admitted.", N);
898                         return 0;
899                 }
900                 return  LBstr(TKEY(N));
901                 break;
902         case TYPE_PREFSTR:
903                 LogTemplateError(Target, 
904                                  "TokenParameter", N, TP, 
905                                  "requesting a prefstring in param %d want a number", N);
906                 if (TP->Tokens->Params[N]->len == 0) {
907                         LogTemplateError(Target, 
908                                          "TokenParameter", N, TP, 
909                                          "Requesting parameter %d; of type PREFSTR, empty lookup string not admitted.", N);
910                         return 0;
911                 }
912                 if (get_PREF_LONG(TKEY(N), &Ret, dflt))
913                         return Ret;
914                 return 0;
915         case TYPE_INTDEFINE:
916         case TYPE_LONG:
917                 return TP->Tokens->Params[N]->lvalue;
918         case TYPE_PREFINT:
919                 if (TP->Tokens->Params[N]->len == 0) {
920                         LogTemplateError(Target, 
921                                          "TokenParameter", N, TP, 
922                                          "Requesting parameter %d; of type PREFINT, empty lookup string not admitted.", N);
923                         return 0;
924                 }
925                 if (get_PREF_LONG(TKEY(N), &Ret, dflt))
926                         return Ret;
927                 return 0;               
928         case TYPE_GETTEXT:
929                 LogTemplateError(Target, 
930                                  "TokenParameter", N, TP, 
931                                  "requesting a I18N string in param %d; want a number", N);
932                 return 0;
933         case TYPE_SUBTEMPLATE:
934                 LogTemplateError(Target, 
935                                  "TokenParameter", N, TP, 
936                                  "requesting a subtemplate in param %d; not supported for numbers", N);
937                 return 0;
938         default:
939                 LogTemplateError(Target, 
940                                  "TokenParameter", N, TP, 
941                                  "unknown param type %d; [%d]", N, TP->Tokens->Params[N]->Type);
942                 return 0;
943         }
944 }
945
946
947
948 /**
949  * \brief Print the value of a variable
950  * \param keyname get a key to print
951  */
952 void print_value_of(StrBuf *Target, WCTemplputParams *TP)
953  
954 {
955         wcsession *WCC = WC;
956         wcsubst *ptr;
957         void *vVar;
958
959         /*if (WCC->vars != NULL) PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
960         /* TODO: depricated! */
961         if (TP->Tokens->pName[0] == '=') {
962                 DoTemplate(TP->Tokens->pName+1, TP->Tokens->NameEnd - 1, NULL, &NoCtx);
963         }
964 /*/////TODO: if param[1] == "U" -> urlescape
965 /// X -> escputs */
966         /** Page-local variables */
967         if ((WCC->vars!= NULL) && GetHash(WCC->vars, TP->Tokens->pName, TP->Tokens->NameEnd, &vVar)) {
968                 ptr = (wcsubst*) vVar;
969                 switch(ptr->wcs_type) {
970                 case WCS_STRING:
971                         StrBufAppendBuf(Target, ptr->wcs_value, 0);
972                         break;
973                 case WCS_SERVCMD:
974                         pvo_do_cmd(Target, ptr->wcs_value);
975                         break;
976                 case WCS_FUNCTION:
977                         (*ptr->wcs_function) (Target, TP);
978                         break;
979                 case WCS_STRBUF:
980                 case WCS_STRBUF_REF:
981                         StrBufAppendBuf(Target, ptr->wcs_value, 0);
982                         break;
983                 case WCS_LONG:
984                         StrBufAppendPrintf(Target, "%ld", ptr->lvalue);
985                         break;
986                 default:
987                         LogTemplateError(
988                                 Target, "Subst", ERR_NAME, TP,
989                                 "WARNING: invalid value in SV-Hash at %s!", TP->Tokens->pName);
990                 }
991         }
992         else {
993                 LogTemplateError(
994                         Target, "Token", ERR_NAME, TP,
995                         "didn't find Handler \"%s\"", TP->Tokens->pName);
996                 wc_backtrace();
997         }
998 }
999
1000 int CompareSubstToToken(TemplateParam *ParamToCompare, TemplateParam *ParamToLookup)
1001 {
1002         wcsession *WCC = WC;
1003         wcsubst *ptr;
1004         void *vVar;
1005
1006         if ((WCC->vars!= NULL) && GetHash(WCC->vars, ParamToLookup->Start, 
1007                                           ParamToLookup->len, &vVar)) {
1008                 ptr = (wcsubst*) vVar;
1009                 switch(ptr->wcs_type) {
1010                 case WCS_STRING:
1011                 case WCS_STRBUF:
1012                 case WCS_STRBUF_REF:
1013                         if (ParamToCompare->Type == TYPE_STR)
1014                                 return ((ParamToCompare->len == StrLength(ptr->wcs_value)) &&
1015                                         (strcmp(ParamToCompare->Start, ChrPtr(ptr->wcs_value)) == 0));
1016                         else
1017                                 return ParamToCompare->lvalue == StrTol(ptr->wcs_value);
1018                         break;
1019                 case WCS_SERVCMD:
1020                         return 1; 
1021                         break;
1022                 case WCS_FUNCTION:
1023                         return 1;
1024                 case WCS_LONG:
1025                         if (ParamToCompare->Type == TYPE_STR)
1026                                 return 0;
1027                         else 
1028                                 return ParamToCompare->lvalue == ptr->lvalue;
1029                         break;
1030                 default:
1031                         lprintf(1,"WARNING: invalid value in SV-Hash at %s!\n", 
1032                                 ParamToLookup->Start);
1033                 }
1034         }
1035         return 0;
1036 }
1037
1038 int CompareSubstToStrBuf(StrBuf *Compare, TemplateParam *ParamToLookup)
1039 {
1040         wcsession *WCC = WC;
1041         wcsubst *ptr;
1042         void *vVar;
1043
1044         if ((WCC->vars!= NULL) && GetHash(WCC->vars, ParamToLookup->Start, 
1045                                           ParamToLookup->len, &vVar)) {
1046                 ptr = (wcsubst*) vVar;
1047                 switch(ptr->wcs_type) {
1048                 case WCS_STRING:
1049                 case WCS_STRBUF:
1050                 case WCS_STRBUF_REF:
1051                         return ((StrLength(Compare) == StrLength(ptr->wcs_value)) &&
1052                                 (strcmp(ChrPtr(Compare), ChrPtr(ptr->wcs_value)) == 0));
1053                 case WCS_SERVCMD:
1054                         return 1; 
1055                         break;
1056                 case WCS_FUNCTION:
1057                         return 1;
1058                 case WCS_LONG:
1059                         return StrTol(Compare) == ptr->lvalue;
1060                 default:
1061                         lprintf(1,"WARNING: invalid value in SV-Hash at %s!\n", 
1062                                 ParamToLookup->Start);
1063                 }
1064         }
1065         return 0;
1066 }
1067
1068
1069
1070 /**
1071  * \brief puts string into the template and computes which escape methon we should use
1072  * \param Source the string we should put into the template
1073  * \param FormatTypeIndex where should we look for escape types if?
1074  */
1075 void StrBufAppendTemplate(StrBuf *Target, 
1076                           WCTemplputParams *TP,
1077                           const StrBuf *Source, int FormatTypeIndex)
1078 {
1079         wcsession *WCC;
1080         char EscapeAs = ' ';
1081
1082         if ((FormatTypeIndex < TP->Tokens->nParameters) &&
1083             (TP->Tokens->Params[FormatTypeIndex]->Type == TYPE_STR) &&
1084             (TP->Tokens->Params[FormatTypeIndex]->len == 1)) {
1085                 EscapeAs = *TP->Tokens->Params[FormatTypeIndex]->Start;
1086         }
1087
1088         switch(EscapeAs)
1089         {
1090         case 'H':
1091                 WCC = WC;
1092                 StrEscAppend(Target, Source, NULL, 0, 2);
1093                 break;
1094         case 'X':
1095                 StrEscAppend(Target, Source, NULL, 0, 0);
1096                 break;
1097         case 'J':
1098                 StrECMAEscAppend(Target, Source, NULL);
1099           break;
1100         case 'K':
1101                 StrHtmlEcmaEscAppend(Target, Source, NULL, 0, 0);
1102           break;
1103         case 'U':
1104                 StrBufUrlescAppend(Target, Source, NULL);
1105                 break;
1106         default:
1107                 StrBufAppendBuf(Target, Source, 0);
1108         }
1109 }
1110
1111
1112 void PutNewToken(WCTemplate *Template, WCTemplateToken *NewToken)
1113 {
1114         if (Template->nTokensUsed + 1 >= Template->TokenSpace) {
1115                 if (Template->TokenSpace <= 0) {
1116                         Template->Tokens = (WCTemplateToken**)malloc(
1117                                 sizeof(WCTemplateToken*) * 10);
1118                         memset(Template->Tokens, 0, sizeof(WCTemplateToken*) * 10);
1119                         Template->TokenSpace = 10;
1120                 }
1121                 else {
1122                         WCTemplateToken **NewTokens;
1123
1124                         NewTokens= (WCTemplateToken**) malloc(
1125                                 sizeof(WCTemplateToken*) * Template->TokenSpace * 2);
1126
1127                         memset(NewTokens, 
1128                                0, sizeof(WCTemplateToken*) * Template->TokenSpace * 2);
1129
1130                         memcpy(NewTokens, 
1131                                Template->Tokens, 
1132                                sizeof(WCTemplateToken*) * Template->nTokensUsed);
1133
1134                         free(Template->Tokens);
1135                         Template->TokenSpace *= 2;
1136                         Template->Tokens = NewTokens;
1137                 }
1138         }
1139         Template->Tokens[(Template->nTokensUsed)++] = NewToken;
1140 }
1141
1142 int GetNextParameter(StrBuf *Buf, 
1143                      const char **pCh, 
1144                      const char *pe, 
1145                      WCTemplateToken *Tokens, 
1146                      WCTemplate *pTmpl, 
1147                      WCTemplputParams *TP, 
1148                      TemplateParam **pParm)
1149 {
1150         const char *pch = *pCh;
1151         const char *pchs, *pche;
1152         TemplateParam *Parm;
1153         char quote = '\0';
1154         int ParamBrace = 0;
1155
1156         *pParm = Parm = (TemplateParam *) malloc(sizeof(TemplateParam));
1157         memset(Parm, 0, sizeof(TemplateParam));
1158         Parm->Type = TYPE_STR;
1159
1160         /* Skip leading whitespaces */
1161         while ((*pch == ' ' )||
1162                (*pch == '\t')||
1163                (*pch == '\r')||
1164                (*pch == '\n')) pch ++;
1165
1166         if (*pch == ':') {
1167                 Parm->Type = TYPE_PREFSTR;
1168                 pch ++;
1169                 if (*pch == '(') {
1170                         pch ++;
1171                         ParamBrace = 1;
1172                 }
1173         }
1174         else if (*pch == ';') {
1175                 Parm->Type = TYPE_PREFINT;
1176                 pch ++;
1177                 if (*pch == '(') {
1178                         pch ++;
1179                         ParamBrace = 1;
1180                 }
1181         }
1182         else if (*pch == '#') {
1183                 Parm->Type = TYPE_INTDEFINE;
1184                 pch ++;
1185         }
1186         else if (*pch == '_') {
1187                 Parm->Type = TYPE_GETTEXT;
1188                 pch ++;
1189                 if (*pch == '(') {
1190                         pch ++;
1191                         ParamBrace = 1;
1192                 }
1193         }
1194         else if (*pch == 'B') {
1195                 Parm->Type = TYPE_BSTR;
1196                 pch ++;
1197                 if (*pch == '(') {
1198                         pch ++;
1199                         ParamBrace = 1;
1200                 }
1201         }
1202         else if (*pch == '=') {
1203                 Parm->Type = TYPE_SUBTEMPLATE;
1204                 pch ++;
1205                 if (*pch == '(') {
1206                         pch ++;
1207                         ParamBrace = 1;
1208                 }
1209         }
1210
1211
1212         if (*pch == '"')
1213                 quote = '"';
1214         else if (*pch == '\'')
1215                 quote = '\'';
1216         if (quote != '\0') {
1217                 pch ++;
1218                 pchs = pch;
1219                 while (pch <= pe &&
1220                        ((*pch != quote) ||
1221                         ( (pch > pchs) && (*(pch - 1) == '\\'))
1222                                )) {
1223                         pch ++;
1224                 }
1225                 pche = pch;
1226                 if (*pch != quote) {
1227                         lprintf(1, "Error (in '%s' line %ld); "
1228                                 "evaluating template param [%s] in Token [%s]\n",
1229                                 ChrPtr(pTmpl->FileName),
1230                                 Tokens->Line,
1231                                 ChrPtr(Tokens->FlatToken),
1232                                 *pCh);
1233                         pch ++;
1234                         free(Parm);
1235                         *pParm = NULL;
1236                         return 0;
1237                 }
1238                 else {
1239                         StrBufPeek(Buf, pch, -1, '\0');         
1240                         if (LoadTemplates > 1) {                        
1241                                 lprintf(1, "DBG: got param [%s] %ld %ld\n", 
1242                                         pchs, pche - pchs, strlen(pchs));
1243                         }
1244                         Parm->Start = pchs;
1245                         Parm->len = pche - pchs;
1246                         pch ++; /* move after trailing quote */
1247                         if (ParamBrace && (*pch == ')')) {
1248                                 pch ++;
1249                         }
1250
1251                 }
1252         }
1253         else {
1254                 Parm->Type = TYPE_LONG;
1255                 pchs = pch;
1256                 while ((pch <= pe) &&
1257                        (isdigit(*pch) ||
1258                         (*pch == '+') ||
1259                         (*pch == '-')))
1260                         pch ++;
1261                 pch ++;
1262                 if (pch - pchs > 1){
1263                         StrBufPeek(Buf, pch, -1, '\0');
1264                         Parm->lvalue = atol(pchs);
1265                         Parm->Start = pchs;
1266                         pch++;
1267                 }
1268                 else {
1269                         Parm->lvalue = 0;
1270 /* TODO whUT?
1271                         lprintf(1, "Error (in '%s' line %ld); "
1272                                 "evaluating long template param [%s] in Token [%s]\n",
1273                                 ChrPtr(pTmpl->FileName),
1274                                 Tokens->Line,
1275                                 ChrPtr(Tokens->FlatToken),
1276                                 *pCh);
1277                                 */
1278                         free(Parm);
1279                         *pParm = NULL;
1280                         return 0;
1281                 }
1282         }
1283         while ((*pch == ' ' )||
1284                (*pch == '\t')||
1285                (*pch == '\r')||
1286                (*pch == ',' )||
1287                (*pch == '\n')) pch ++;
1288
1289         switch (Parm->Type)
1290         {
1291         case TYPE_GETTEXT:
1292                 if (DumpTemplateI18NStrings) {
1293                         StrBufAppendPrintf(I18nDump, "_(\"%s\");\n", Parm->Start);
1294                 }
1295                 break;
1296         case TYPE_INTDEFINE: {
1297                 void *vPVal;
1298                 
1299                 if (GetHash(Defines, Parm->Start, Parm->len, &vPVal) &&
1300                     (vPVal != NULL))
1301                 {
1302                         long *PVal;
1303                         PVal = (long*) vPVal;
1304                 
1305                         Parm->lvalue = *PVal;
1306                 }
1307                 else if (strchr(Parm->Start, '|') != NULL)
1308                 {
1309                         const char *Pos;
1310                         StrBuf *pToken;
1311                         StrBuf *Match;
1312
1313                         Parm->MaskBy = eOR;
1314                         pToken = NewStrBufPlain (Parm->Start, Parm->len);
1315                         Match = NewStrBufPlain (NULL, Parm->len);
1316                         Pos = ChrPtr(pToken);
1317                         
1318                         while ((Pos != NULL) && (Pos != StrBufNOTNULL))
1319                         {
1320                                 StrBufExtract_NextToken(Match, pToken, &Pos, '|');
1321                                 StrBufTrim(Match);
1322                                 if (StrLength (Match) > 0)
1323                                 {
1324                                         if (GetHash(Defines, SKEY(Match), &vPVal) &&
1325                                             (vPVal != NULL))
1326                                         {
1327                                                 long *PVal;
1328                                                 PVal = (long*) vPVal;
1329                                                 
1330                                                 Parm->lvalue |= *PVal;
1331                                         }
1332                                         else {
1333                                                 LogTemplateError(NULL, "Define", 
1334                                                                  Tokens->nParameters,
1335                                                                  TP,
1336                                                                  "%s isn't known!!",
1337                                                                  ChrPtr(Match));
1338
1339                                         }
1340                                 }
1341                         }
1342                 }
1343                 else if (strchr(Parm->Start, '&') != NULL)
1344                 {
1345                         const char *Pos;
1346                         StrBuf *pToken;
1347                         StrBuf *Match;
1348
1349                         Parm->MaskBy = eAND;
1350                         pToken = NewStrBufPlain (Parm->Start, Parm->len);
1351                         Match = NewStrBufPlain (NULL, Parm->len);
1352                         Pos = ChrPtr(pToken);
1353                         
1354                         while ((Pos != NULL) && (Pos != StrBufNOTNULL))
1355                         {
1356                                 StrBufExtract_NextToken(Match, pToken, &Pos, '&');
1357                                 StrBufTrim(Match);
1358                                 if (StrLength (Match) > 0)
1359                                 {
1360                                         if (GetHash(Defines, SKEY(Match), &vPVal) &&
1361                                             (vPVal != NULL))
1362                                         {
1363                                                 long *PVal;
1364                                                 PVal = (long*) vPVal;
1365                                                 
1366                                                 Parm->lvalue |= *PVal;
1367                                         }
1368                                         else {
1369                                                 LogTemplateError(NULL, "Define", 
1370                                                                  Tokens->nParameters,
1371                                                                  TP,
1372                                                                  "%s isn't known!!",
1373                                                                  ChrPtr(Match));
1374
1375                                         }
1376                                 }
1377                         }
1378                 }
1379                 else {
1380
1381
1382                         LogTemplateError(NULL, "Define", 
1383                                          Tokens->nParameters,
1384                                          TP,
1385                                          "%s isn't known!!",
1386                                          Parm->Start);
1387                 }}
1388                 break;
1389         case TYPE_SUBTEMPLATE:{
1390                 void *vTmpl;
1391                 /* well, we don't check the mobile stuff here... */
1392                 if (!GetHash(LocalTemplateCache, Parm->Start, Parm->len, &vTmpl) &&
1393                     !GetHash(TemplateCache, Parm->Start, Parm->len, &vTmpl)) {
1394                         LogTemplateError(NULL, 
1395                                          "SubTemplate", 
1396                                          Tokens->nParameters,
1397                                          TP,
1398                                          "referenced here doesn't exist");
1399                 }}
1400                 break;
1401         }
1402         *pCh = pch;
1403         return 1;
1404 }
1405
1406 WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf, 
1407                                        const char *pStart, 
1408                                        const char *pTokenStart, 
1409                                        const char *pTokenEnd, 
1410                                        long Line,
1411                                        WCTemplate *pTmpl)
1412 {
1413         void *vVar;
1414         const char *pch;
1415         WCTemplateToken *NewToken;
1416         WCTemplputParams TP;
1417
1418         NewToken = (WCTemplateToken*)malloc(sizeof(WCTemplateToken));
1419         memset(NewToken, 0, sizeof(WCTemplateToken));
1420         TP.Tokens = NewToken;
1421         NewToken->FileName = pTmpl->FileName; /* to print meaningfull log messages... */
1422         NewToken->Flags = 0;
1423         NewToken->Line = Line + 1;
1424         NewToken->pTokenStart = pTokenStart;
1425         NewToken->TokenStart = pTokenStart - pStart;
1426         NewToken->TokenEnd =  (pTokenEnd - pStart) - NewToken->TokenStart;
1427         NewToken->pTokenEnd = pTokenEnd;
1428         NewToken->NameEnd = NewToken->TokenEnd - 2;
1429         NewToken->PreEval = NULL;
1430         NewToken->FlatToken = NewStrBufPlain(pTokenStart + 2, pTokenEnd - pTokenStart - 2);
1431         StrBufShrinkToFit(NewToken->FlatToken, 1);
1432
1433         StrBufPeek(Buf, pTokenStart, + 1, '\0');
1434         StrBufPeek(Buf, pTokenEnd, -1, '\0');
1435         pch = NewToken->pName = pTokenStart + 2;
1436
1437         NewToken->HaveParameters = 0;;
1438         NewToken->nParameters = 0;
1439
1440         while (pch < pTokenEnd - 1) {
1441                 if (*pch == '(') {
1442                         StrBufPeek(Buf, pch, -1, '\0');
1443                         NewToken->NameEnd = pch - NewToken->pName;
1444                         pch ++;
1445                         if (*(pTokenEnd - 1) != ')') {
1446                                 LogTemplateError(
1447                                         NULL, "Parseerror", ERR_NAME, &TP, 
1448                                         "Warning, Non welformed Token; missing right parenthesis");
1449                         }
1450                         while (pch < pTokenEnd - 1) {
1451                                 NewToken->nParameters++;
1452                                 if (GetNextParameter(Buf, 
1453                                                      &pch, 
1454                                                      pTokenEnd - 1, 
1455                                                      NewToken, 
1456                                                      pTmpl, 
1457                                                      &TP, 
1458                                                      &NewToken->Params[NewToken->nParameters - 1]))
1459                                 {
1460                                         NewToken->HaveParameters = 1;
1461                                         if (NewToken->nParameters > MAXPARAM) {
1462                                                 LogTemplateError(
1463                                                         NULL, "Parseerror", ERR_NAME, &TP,
1464                                                         "only [%d] Params allowed in Tokens",
1465                                                         MAXPARAM);
1466
1467                                                 FreeToken(&NewToken);
1468                                                 return NULL;
1469                                         }
1470                                 }
1471                                 else break;
1472                         }
1473                         if((NewToken->NameEnd == 1) &&
1474                            (NewToken->HaveParameters == 1))
1475                            
1476                         {
1477                                 if (*(NewToken->pName) == '_')
1478                                         NewToken->Flags = SV_GETTEXT;
1479                                 else if (*(NewToken->pName) == '=')
1480                                         NewToken->Flags = SV_SUBTEMPL;
1481                                 else if (*(NewToken->pName) == '%')
1482                                         NewToken->Flags = SV_CUST_STR_CONDITIONAL;
1483                                 else if (*(NewToken->pName) == '?')
1484                                         NewToken->Flags = SV_CONDITIONAL;
1485                                 else if (*(NewToken->pName) == '!')
1486                                         NewToken->Flags = SV_NEG_CONDITIONAL;
1487                         }
1488                 }
1489                 else pch ++;            
1490         }
1491         
1492         switch (NewToken->Flags) {
1493         case 0:
1494                 /* If we're able to find out more about the token, do it now while its fresh. */
1495                 if (GetHash(GlobalNS, NewToken->pName, NewToken->NameEnd, &vVar)) {
1496                         HashHandler *Handler;
1497                         Handler = (HashHandler*) vVar;
1498                         if ((NewToken->nParameters < Handler->Filter.nMinArgs) || 
1499                             (NewToken->nParameters > Handler->Filter.nMaxArgs)) {
1500                                 LogTemplateError(
1501                                         NULL, "Token", ERR_NAME, &TP,
1502                                         "doesn't work with %d params", 
1503                                         NewToken->nParameters);
1504
1505                         }
1506                         else {
1507                                 NewToken->PreEval = Handler;
1508                                 NewToken->Flags = SV_PREEVALUATED;              
1509                                 if (Handler->PreEvalFunc != NULL)
1510                                         Handler->PreEvalFunc(NewToken);
1511                         }
1512                 }
1513                 break;
1514         case SV_GETTEXT:
1515                 if (NewToken->nParameters !=1) {
1516                         LogTemplateError(                               
1517                                 NULL, "Gettext", ERR_NAME, &TP,
1518                                 "requires exactly 1 parameter, you gave %d params", 
1519                                 NewToken->nParameters);
1520                         NewToken->Flags = 0;
1521                         break;
1522                 }
1523                 if (DumpTemplateI18NStrings) {
1524                         StrBufAppendPrintf(I18nDump, "_(\"%s\");\n", NewToken->Params[0]->Start);
1525                 }
1526                 break;
1527         case SV_SUBTEMPL:
1528                 if (NewToken->nParameters != 1) {
1529                         LogTemplateError(
1530                                 NULL, "Subtemplates", ERR_NAME, &TP,
1531                                 "require exactly 1 parameter, you gave %d params", 
1532                                 NewToken->nParameters);
1533                         break;
1534                 }
1535                 else {
1536                         void *vTmpl;
1537                         /* well, we don't check the mobile stuff here... */
1538                         if (!GetHash(LocalTemplateCache, 
1539                                      NewToken->Params[0]->Start, 
1540                                      NewToken->Params[0]->len, 
1541                                      &vTmpl) &&
1542                             !GetHash(TemplateCache, 
1543                                      NewToken->Params[0]->Start, 
1544                                      NewToken->Params[0]->len, 
1545                                      &vTmpl)) {
1546                                 LogTemplateError(
1547                                         NULL, "SubTemplate", ERR_PARM1, &TP,
1548                                         "doesn't exist");
1549                         }
1550                 }
1551                 break;
1552         case SV_CUST_STR_CONDITIONAL:
1553         case SV_CONDITIONAL:
1554         case SV_NEG_CONDITIONAL:
1555                 if (NewToken->nParameters <2) {
1556                         LogTemplateError(
1557                                 NULL, "Conditional", ERR_PARM1, &TP,
1558                                 "require at least 2 parameters, you gave %d params", 
1559                                 NewToken->nParameters);
1560                         NewToken->Flags = 0;
1561                         break;
1562                 }
1563                 if (NewToken->Params[1]->lvalue == 0) {
1564                         LogTemplateError(
1565                                 NULL, "Conditional", ERR_PARM1, &TP,
1566                                 "Conditional ID (Parameter 1) mustn't be 0!");
1567                         NewToken->Flags = 0;
1568                         break;
1569                 }
1570                 if (!GetHash(Conditionals, 
1571                              NewToken->Params[0]->Start, 
1572                              NewToken->Params[0]->len, 
1573                              &vVar) || 
1574                     (vVar == NULL)) {
1575                         if ((NewToken->Params[0]->len == 1) &&
1576                             (NewToken->Params[0]->Start[0] == 'X'))
1577                                 break;
1578                         LogTemplateError(
1579                                 NULL, "Conditional", ERR_PARM1, &TP,
1580                                 "Not found!");
1581 /*
1582                         NewToken->Error = NewStrBuf();
1583                         StrBufAppendPrintf(
1584                                 NewToken->Error, 
1585                                 "<pre>\nConditional [%s] (in '%s' line %ld); Not found!\n[%s]\n</pre>\n", 
1586                                 NewToken->Params[0]->Start,
1587                                 ChrPtr(pTmpl->FileName),
1588                                 NewToken->Line,
1589                                 ChrPtr(NewToken->FlatToken));
1590 */
1591                 }
1592                 else {
1593                         NewToken->PreEval = vVar;
1594                 }
1595                 break;
1596         }
1597         return NewToken;
1598 }
1599
1600
1601
1602
1603
1604 /**
1605  * \brief Display a variable-substituted template
1606  * \param templatename template file to load
1607  */
1608 void *prepare_template(StrBuf *filename, StrBuf *Key, HashList *PutThere)
1609 {
1610         WCTemplate *NewTemplate;
1611
1612         NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
1613         memset(NewTemplate, 0, sizeof(WCTemplate));
1614         NewTemplate->Data = NULL;
1615         NewTemplate->FileName = NewStrBufDup(filename);
1616         StrBufShrinkToFit(NewTemplate->FileName, 1);
1617         NewTemplate->nTokensUsed = 0;
1618         NewTemplate->TokenSpace = 0;
1619         NewTemplate->Tokens = NULL;
1620         NewTemplate->MimeType = NewStrBufPlain(GuessMimeByFilename (SKEY(NewTemplate->FileName)), -1);
1621         if (strstr(ChrPtr(NewTemplate->MimeType), "text") != NULL) {
1622                 StrBufAppendBufPlain(NewTemplate->MimeType, HKEY("; charset=utf-8"), 0);
1623         }
1624
1625         if (strstr(ChrPtr(NewTemplate->MimeType), "text") != NULL) {
1626                 StrBufAppendBufPlain(NewTemplate->MimeType, HKEY("; charset=utf-8"), 0);
1627         }
1628
1629         Put(PutThere, ChrPtr(Key), StrLength(Key), NewTemplate, FreeWCTemplate);
1630         return NewTemplate;
1631 }
1632
1633 /**
1634  * \brief Display a variable-substituted template
1635  * \param templatename template file to load
1636  */
1637 void *duplicate_template(WCTemplate *OldTemplate)
1638 {
1639         WCTemplate *NewTemplate;
1640
1641         NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
1642         memset(NewTemplate, 0, sizeof(WCTemplate));
1643         NewTemplate->Data = NULL;
1644         NewTemplate->FileName = NewStrBufDup(OldTemplate->FileName);
1645         StrBufShrinkToFit(NewTemplate->FileName, 1);
1646         NewTemplate->nTokensUsed = 0;
1647         NewTemplate->TokenSpace = 0;
1648         NewTemplate->Tokens = NULL;
1649         NewTemplate->MimeType = NewStrBufDup(OldTemplate->MimeType);
1650         return NewTemplate;
1651 }
1652
1653 /**
1654  * \brief Display a variable-substituted template
1655  * \param templatename template file to load
1656  */
1657 void *load_template(WCTemplate *NewTemplate)
1658 {
1659         int fd;
1660         struct stat statbuf;
1661         const char *pS, *pE, *pch, *Err;
1662         long Line;
1663         int pos;
1664
1665         fd = open(ChrPtr(NewTemplate->FileName), O_RDONLY);
1666         if (fd <= 0) {
1667                 lprintf(1, "ERROR: could not open template '%s' - %s\n",
1668                         ChrPtr(NewTemplate->FileName), strerror(errno));
1669                 return NULL;
1670         }
1671
1672         if (fstat(fd, &statbuf) == -1) {
1673                 lprintf(1, "ERROR: could not stat template '%s' - %s\n",
1674                         ChrPtr(NewTemplate->FileName), strerror(errno));
1675                 return NULL;
1676         }
1677
1678         NewTemplate->Data = NewStrBufPlain(NULL, statbuf.st_size + 1);
1679         if (StrBufReadBLOB(NewTemplate->Data, &fd, 1, statbuf.st_size, &Err) < 0) {
1680                 close(fd);
1681                 lprintf(1, "ERROR: reading template '%s' - %s<br />\n",
1682                         ChrPtr(NewTemplate->FileName), strerror(errno));
1683                 //FreeWCTemplate(NewTemplate);/////tODO
1684                 return NULL;
1685         }
1686         close(fd);
1687
1688         Line = 0;
1689         StrBufShrinkToFit(NewTemplate->Data, 1);
1690         StrBufShrinkToFit(NewTemplate->MimeType, 1);
1691         pS = pch = ChrPtr(NewTemplate->Data);
1692         pE = pS + StrLength(NewTemplate->Data);
1693         while (pch < pE) {
1694                 const char *pts, *pte;
1695                 int InQuotes = 0;
1696                 int InDoubleQuotes = 0;
1697
1698                 /** Find one <? > */
1699                 pos = (-1);
1700                 for (; pch < pE; pch ++) {
1701                         if ((*pch=='<')&&(*(pch + 1)=='?') &&
1702                             !((pch == pS) && /* we must ommit a <?xml */
1703                               (*(pch + 2) == 'x') && 
1704                               (*(pch + 3) == 'm') && 
1705                               (*(pch + 4) == 'l')))                          
1706                                 break;
1707                         if (*pch=='\n') Line ++;
1708                 }
1709                 if (pch >= pE)
1710                         continue;
1711                 pts = pch;
1712
1713                 /** Found one? parse it. */
1714                 for (; pch <= pE - 1; pch ++) {
1715                         if (*pch == '"')
1716                                 InDoubleQuotes = ! InDoubleQuotes;
1717                         else if (*pch == '\'')
1718                                 InQuotes = ! InQuotes;
1719                         else if ((!InQuotes  && !InDoubleQuotes) &&
1720                                  ((*pch!='\\')&&(*(pch + 1)=='>'))) {
1721                                 pch ++;
1722                                 break;
1723                         }
1724                 }
1725                 if (pch + 1 > pE)
1726                         continue;
1727                 pte = pch;
1728                 PutNewToken(NewTemplate, 
1729                             NewTemplateSubstitute(NewTemplate->Data, pS, pts, pte, Line, NewTemplate));
1730                 pch ++;
1731         }
1732         return NewTemplate;
1733 }
1734
1735
1736 const char* PrintTemplate(void *vSubst)
1737 {
1738         WCTemplate *Tmpl = vSubst;
1739
1740         return ChrPtr(Tmpl->FileName);
1741
1742 }
1743
1744 int LoadTemplateDir(const StrBuf *DirName, HashList *wireless, HashList *big, const StrBuf *BaseKey)
1745 {
1746         int Toplevel;
1747         StrBuf *FileName;
1748         StrBuf *Key;
1749         StrBuf *SubKey;
1750         StrBuf *SubDirectory;
1751         DIR *filedir = NULL;
1752         struct dirent *filedir_entry;
1753         struct dirent *d;
1754         int d_type = 0;
1755         int d_namelen;
1756         int d_without_ext;
1757         int IsMobile;
1758         
1759         d = (struct dirent *)malloc(offsetof(struct dirent, d_name) + PATH_MAX + 1);
1760         if (d == NULL) {
1761                 return 0;
1762         }
1763
1764         filedir = opendir (ChrPtr(DirName));
1765         if (filedir == NULL) {
1766                 free(d);
1767                 return 0;
1768         }
1769
1770         Toplevel = StrLength(BaseKey) == 0;
1771         SubDirectory = NewStrBuf();
1772         SubKey = NewStrBuf();
1773         FileName = NewStrBufPlain(NULL, PATH_MAX);
1774         Key = NewStrBuf();
1775         while ((readdir_r(filedir, d, &filedir_entry) == 0) &&
1776                (filedir_entry != NULL))
1777         {
1778                 char *MinorPtr;
1779                 char *PStart;
1780 #ifdef _DIRENT_HAVE_D_NAMELEN
1781                 d_namelen = filedir_entry->d_namelen;
1782                 d_type = filedir_entry->d_type;
1783 #else
1784
1785 #ifndef DT_UNKNOWN
1786 #define DT_UNKNOWN     0
1787 #define DT_DIR         4
1788 #define DT_REG         8
1789 #define DT_LNK         10
1790
1791 #define IFTODT(mode)   (((mode) & 0170000) >> 12)
1792 #define DTTOIF(dirtype)        ((dirtype) << 12)
1793 #endif
1794                 d_namelen = strlen(filedir_entry->d_name);
1795                 d_type = DT_UNKNOWN;
1796 #endif
1797                 d_without_ext = d_namelen;
1798
1799                 if ((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~')
1800                         continue; /* Ignore backup files... */
1801
1802                 if ((d_namelen == 1) && 
1803                     (filedir_entry->d_name[0] == '.'))
1804                         continue;
1805
1806                 if ((d_namelen == 2) && 
1807                     (filedir_entry->d_name[0] == '.') &&
1808                     (filedir_entry->d_name[1] == '.'))
1809                         continue;
1810
1811                 if (d_type == DT_UNKNOWN) {
1812                         struct stat s;
1813                         char path[PATH_MAX];
1814                         snprintf(path, PATH_MAX, "%s/%s", 
1815                                  ChrPtr(DirName), filedir_entry->d_name);
1816                         if (stat(path, &s) == 0) {
1817                                 d_type = IFTODT(s.st_mode);
1818                         }
1819                 }
1820                 switch (d_type)
1821                 {
1822                 case DT_DIR:
1823                         /* Skip directories we are not interested in... */
1824                         if (strcmp(filedir_entry->d_name, ".svn") == 0)
1825                                 continue;
1826
1827                         FlushStrBuf(SubKey);
1828                         if (!Toplevel) {
1829                                 /* If we're not toplevel, the upper dirs count as foo_bar_<local name>*/
1830                                 StrBufAppendBuf(SubKey, BaseKey, 0);
1831                                 StrBufAppendBufPlain(SubKey, HKEY("_"), 0);
1832                         }
1833                         StrBufAppendBufPlain(SubKey, filedir_entry->d_name, d_namelen, 0);
1834
1835                         FlushStrBuf(SubDirectory);
1836                         StrBufAppendBuf(SubDirectory, DirName, 0);
1837                         if (ChrPtr(SubDirectory)[StrLength(SubDirectory) - 1] != '/')
1838                                 StrBufAppendBufPlain(SubDirectory, HKEY("/"), 0);
1839                         StrBufAppendBufPlain(SubDirectory, filedir_entry->d_name, d_namelen, 0);
1840
1841                         LoadTemplateDir(SubDirectory, wireless, big, SubKey);
1842
1843                         break;
1844                 case DT_LNK: /* TODO: check whether its a file or a directory */
1845                 case DT_REG:
1846
1847
1848                         while ((d_without_ext > 0) && (filedir_entry->d_name[d_without_ext] != '.'))
1849                                 d_without_ext --;
1850                         if ((d_without_ext == 0) || (d_namelen < 3))
1851                                 continue;
1852                         if (((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~') ||
1853                             (strcmp(&filedir_entry->d_name[d_without_ext], ".orig") == 0) ||
1854                             (strcmp(&filedir_entry->d_name[d_without_ext], ".swp") == 0))
1855                                 continue; /* Ignore backup files... */
1856                         /* .m.xxx is for mobile useragents! */
1857                         IsMobile = 0;
1858                         if (d_without_ext > 2)
1859                                 IsMobile = (filedir_entry->d_name[d_without_ext - 1] == 'm') &&
1860                                         (filedir_entry->d_name[d_without_ext - 2] == '.');
1861                         PStart = filedir_entry->d_name;
1862                         StrBufPrintf(FileName, "%s/%s", ChrPtr(DirName),  filedir_entry->d_name);
1863                         MinorPtr = strchr(filedir_entry->d_name, '.');
1864                         if (MinorPtr != NULL)
1865                                 *MinorPtr = '\0';
1866                         FlushStrBuf(Key);
1867                         if (!Toplevel) {
1868                                 /* If we're not toplevel, the upper dirs count as foo_bar_<local name>*/
1869                                 StrBufAppendBuf(Key, BaseKey, 0);
1870                                 StrBufAppendBufPlain(Key, HKEY("_"), 0);
1871                         }
1872                         StrBufAppendBufPlain(Key, filedir_entry->d_name, MinorPtr - filedir_entry->d_name, 0);
1873
1874                         if (LoadTemplates >= 1)
1875                                 lprintf(1, "%s %d %s\n", ChrPtr(FileName), IsMobile, ChrPtr(Key));
1876                         prepare_template(FileName, Key, (IsMobile)?wireless:big);
1877                 default:
1878                         break;
1879                 }
1880         }
1881         free(d);
1882         closedir(filedir);
1883         FreeStrBuf(&FileName);
1884         FreeStrBuf(&Key);
1885         FreeStrBuf(&SubDirectory);
1886         FreeStrBuf(&SubKey);
1887         return 1;
1888 }
1889
1890 void InitTemplateCache(void)
1891 {
1892         int i;
1893         StrBuf *Key;
1894         StrBuf *Dir;
1895         HashList *Templates[4];
1896
1897         Dir = NewStrBuf();
1898         Key = NewStrBuf();
1899
1900         /* Primary Template set... */
1901         StrBufPrintf(Dir, "%s/t", static_dirs[0]);
1902         LoadTemplateDir(Dir,
1903                         WirelessTemplateCache,
1904                         TemplateCache, 
1905                         Key);
1906
1907         /* User local Template set */
1908         StrBufPrintf(Dir, "%s/t", static_dirs[1]);
1909         LoadTemplateDir(Dir,
1910                         WirelessLocalTemplateCache,
1911                         LocalTemplateCache, 
1912                         Key);
1913         
1914         /* Debug Templates, just to be loaded while debugging. */
1915         
1916         StrBufPrintf(Dir, "%s/dbg", static_dirs[0]);
1917         LoadTemplateDir(Dir,
1918                         WirelessTemplateCache,
1919                         TemplateCache, 
1920                         Key);
1921         Templates[0] = WirelessTemplateCache;
1922         Templates[1] = TemplateCache;
1923         Templates[2] = WirelessLocalTemplateCache;
1924         Templates[3] = LocalTemplateCache;
1925
1926
1927         if (LoadTemplates == 0) 
1928                 for (i=0; i < 4; i++) {
1929                         const char *Key;
1930                         long KLen;
1931                         HashPos *At;
1932                         void *vTemplate;
1933
1934                         At = GetNewHashPos(Templates[i], 0);
1935                         while (GetNextHashPos(Templates[i], 
1936                                               At, 
1937                                               &KLen,
1938                                               &Key, 
1939                                               &vTemplate) && 
1940                                (vTemplate != NULL))
1941                         {
1942                                 load_template((WCTemplate *)vTemplate);
1943                         }
1944                         DeleteHashPos(&At);
1945                 }
1946
1947
1948         FreeStrBuf(&Dir);
1949         FreeStrBuf(&Key);
1950 }
1951
1952
1953
1954 /*-----------------------------------------------------------------------------
1955  *                      Filling & processing Templates
1956  */
1957 /**
1958  * \brief executes one token
1959  * \param Target buffer to append to
1960  * \param Token da to  process.
1961  * \param Template we're iterating
1962  * \param Context Contextpoointer to pass in
1963  * \param state are we in conditional state?
1964  * \param ContextType what type of information does context giv us?
1965  */
1966 int EvaluateToken(StrBuf *Target, int state, WCTemplputParams *TP)
1967 {
1968         const char *AppendMe;
1969         long AppendMeLen;
1970         HashHandler *Handler;
1971         void *vVar;
1972         
1973 /* much output, since pName is not terminated...
1974         lprintf(1,"Doing token: %s\n",Token->pName);
1975 */
1976
1977         switch (TP->Tokens->Flags) {
1978         case SV_GETTEXT:
1979                 TmplGettext(Target, TP);
1980                 break;
1981         case SV_CONDITIONAL: /** Forward conditional evaluation */
1982                 return EvaluateConditional(Target, 1, state, TP);
1983                 break;
1984         case SV_NEG_CONDITIONAL: /** Reverse conditional evaluation */
1985                 return EvaluateConditional(Target, 0, state, TP);
1986                 break;
1987         case SV_CUST_STR_CONDITIONAL: /** Conditional put custom strings from params */
1988                 if (TP->Tokens->nParameters >= 6) {
1989                         if (EvaluateConditional(Target, 0, state, TP)) {
1990                                 GetTemplateTokenString(Target, TP, 5, &AppendMe, &AppendMeLen);
1991                                 StrBufAppendBufPlain(Target, 
1992                                                      AppendMe, 
1993                                                      AppendMeLen,
1994                                                      0);
1995                         }
1996                         else{
1997                                 GetTemplateTokenString(Target, TP, 4, &AppendMe, &AppendMeLen);
1998                                 StrBufAppendBufPlain(Target, 
1999                                                      AppendMe, 
2000                                                      AppendMeLen,
2001                                                      0);
2002                         }
2003                 }
2004                 else  {
2005                         LogTemplateError(
2006                                 Target, "Conditional", ERR_NAME, TP,
2007                                 "needs at least 6 Params!"); 
2008                 }
2009                 break;
2010         case SV_SUBTEMPL:
2011                 if (TP->Tokens->nParameters == 1)
2012                         DoTemplate(TKEY(0), Target, TP);
2013                 break;
2014         case SV_PREEVALUATED:
2015                 Handler = (HashHandler*) TP->Tokens->PreEval;
2016                 if (!CheckContext(Target, &Handler->Filter, TP, "Token")) {
2017                         return -1;
2018                 }
2019                 Handler->HandlerFunc(Target, TP);
2020                 break;          
2021         default:
2022                 if (GetHash(GlobalNS, TP->Tokens->pName, TP->Tokens->NameEnd, &vVar)) {
2023                         Handler = (HashHandler*) vVar;
2024                         if (!CheckContext(Target, &Handler->Filter, TP, "Token")) {
2025                                 return -1;
2026                         }
2027                         else {
2028                                 Handler->HandlerFunc(Target, TP);
2029                         }
2030                 }
2031                 else {
2032                         print_value_of(Target, TP);
2033                 }
2034         }
2035         return 0;
2036 }
2037
2038
2039
2040 const StrBuf *ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target, WCTemplputParams *CallingTP)
2041 {
2042         WCTemplate *pTmpl = Tmpl;
2043         int done = 0;
2044         int i, state;
2045         const char *pData, *pS;
2046         long len;
2047         WCTemplputParams TP;
2048
2049         memcpy(&TP.Filter, &CallingTP->Filter, sizeof(ContextFilter));
2050
2051         TP.Context = CallingTP->Context;
2052         TP.ControlContext = CallingTP->ControlContext;
2053
2054         if (LoadTemplates != 0) {                       
2055                 if (LoadTemplates > 1)
2056                         lprintf(1, "DBG: ----- loading:  [%s] ------ \n", 
2057                                 ChrPtr(Tmpl->FileName));
2058                 pTmpl = duplicate_template(Tmpl);
2059                 if(load_template(pTmpl) == NULL) {
2060                         StrBufAppendPrintf(
2061                                 Target, 
2062                                 "<pre>\nError loading Template [%s]\n See Logfile for details\n</pre>\n", 
2063                                 ChrPtr(Tmpl->FileName));
2064                         FreeWCTemplate(pTmpl);
2065                         return NULL;
2066                 }
2067
2068         }
2069
2070         pS = pData = ChrPtr(pTmpl->Data);
2071         len = StrLength(pTmpl->Data);
2072         i = 0;
2073         state = 0;
2074         while (!done) {
2075                 if (i >= pTmpl->nTokensUsed) {
2076                         StrBufAppendBufPlain(Target, 
2077                                              pData, 
2078                                              len - (pData - pS), 0);
2079                         done = 1;
2080                 }
2081                 else {
2082                         StrBufAppendBufPlain(
2083                                 Target, pData, 
2084                                 pTmpl->Tokens[i]->pTokenStart - pData, 0);
2085                         TP.Tokens = pTmpl->Tokens[i];
2086                         TP.nArgs = pTmpl->Tokens[i]->nParameters;
2087                         state = EvaluateToken(Target, state, &TP);
2088
2089                         while ((state != 0) && (i+1 < pTmpl->nTokensUsed)) {
2090                         /* condition told us to skip till its end condition */
2091                                 i++;
2092                                 TP.Tokens = pTmpl->Tokens[i];
2093                                 TP.nArgs = pTmpl->Tokens[i]->nParameters;
2094                                 if ((pTmpl->Tokens[i]->Flags == SV_CONDITIONAL) ||
2095                                     (pTmpl->Tokens[i]->Flags == SV_NEG_CONDITIONAL)) {
2096                                         if (state == EvaluateConditional(
2097                                                     Target, 
2098                                                     pTmpl->Tokens[i]->Flags, 
2099                                                     state, 
2100                                                     &TP))
2101                                                 state = 0;
2102                                 }
2103                         }
2104                         pData = pTmpl->Tokens[i++]->pTokenEnd + 1;
2105                         if (i > pTmpl->nTokensUsed)
2106                                 done = 1;
2107                 }
2108         }
2109         if (LoadTemplates != 0) {
2110                 FreeWCTemplate(pTmpl);
2111         }
2112         return Tmpl->MimeType;
2113
2114 }
2115
2116 /**
2117  * \brief Display a variable-substituted template
2118  * \param templatename template file to load
2119  * \returns the mimetype of the template its doing
2120  */
2121 const StrBuf *DoTemplate(const char *templatename, long len, StrBuf *Target, WCTemplputParams *TP) 
2122 {
2123         WCTemplputParams LocalTP;
2124         HashList *Static;
2125         HashList *StaticLocal;
2126         void *vTmpl;
2127         
2128         if (Target == NULL)
2129                 Target = WC->WBuf;
2130         if (TP == NULL) {
2131                 memset(&LocalTP, 0, sizeof(WCTemplputParams));
2132                 TP = &LocalTP;
2133         }
2134
2135         if (WC->is_mobile > 0) {
2136                 Static = WirelessTemplateCache;
2137                 StaticLocal = WirelessLocalTemplateCache;
2138         }
2139         else {
2140                 Static = TemplateCache;
2141                 StaticLocal = LocalTemplateCache;
2142         }
2143
2144         if (len == 0)
2145         {
2146                 lprintf (1, "Can't to load a template with empty name!\n");
2147                 StrBufAppendPrintf(Target, "<pre>\nCan't to load a template with empty name!\n</pre>");
2148                 return NULL;
2149         }
2150
2151         if (!GetHash(StaticLocal, templatename, len, &vTmpl) &&
2152             !GetHash(Static, templatename, len, &vTmpl)) {
2153                 lprintf (1, "didn't find Template [%s] %ld %ld\n", templatename, len , (long)strlen(templatename));
2154                 StrBufAppendPrintf(Target, "<pre>\ndidn't find Template [%s] %ld %ld\n</pre>", 
2155                                    templatename, len, 
2156                                    (long)strlen(templatename));
2157 #if 0
2158                 dbg_PrintHash(Static, PrintTemplate, NULL);
2159                 PrintHash(Static, VarPrintTransition, PrintTemplate);
2160 #endif
2161                 return NULL;
2162         }
2163         if (vTmpl == NULL) 
2164                 return NULL;
2165         return ProcessTemplate(vTmpl, Target, TP);
2166
2167 }
2168
2169
2170 void tmplput_Comment(StrBuf *Target, WCTemplputParams *TP)
2171 {
2172         if (LoadTemplates != 0)
2173         {
2174                 StrBuf *Comment;
2175                 const char *pch;
2176                 long len;
2177
2178                 GetTemplateTokenString(Target, TP, 2, &pch, &len);
2179                 Comment = NewStrBufPlain(pch, len);
2180                 StrBufAppendTemplate(Target, TP, Comment, 1);
2181
2182                 FreeStrBuf(&Comment);
2183         }
2184 }
2185
2186 /*-----------------------------------------------------------------------------
2187  *                      Iterators
2188  */
2189 typedef struct _HashIterator {
2190         HashList *StaticList;
2191         int AdditionalParams;
2192         int ContextType;
2193         int XPectContextType;
2194         int Flags;
2195         RetrieveHashlistFunc GetHash;
2196         HashDestructorFunc Destructor;
2197         SubTemplFunc DoSubTemplate;
2198 } HashIterator;
2199
2200 void RegisterITERATOR(const char *Name, long len, 
2201                       int AdditionalParams, 
2202                       HashList *StaticList, 
2203                       RetrieveHashlistFunc GetHash, 
2204                       SubTemplFunc DoSubTempl,
2205                       HashDestructorFunc Destructor,
2206                       int ContextType, 
2207                       int XPectContextType, 
2208                       int Flags)
2209 {
2210         HashIterator *It;
2211
2212         It = (HashIterator*)malloc(sizeof(HashIterator));
2213         memset(It, 0, sizeof(HashIterator));
2214         It->StaticList = StaticList;
2215         It->AdditionalParams = AdditionalParams;
2216         It->GetHash = GetHash;
2217         It->DoSubTemplate = DoSubTempl;
2218         It->Destructor = Destructor;
2219         It->ContextType = ContextType;
2220         It->XPectContextType = XPectContextType;
2221         It->Flags = Flags;
2222         Put(Iterators, Name, len, It, NULL);
2223 }
2224
2225 typedef struct _iteratestruct {
2226         int GroupChange;
2227         int oddeven;
2228         const char *Key;
2229         long KeyLen;
2230         int n;
2231         int LastN;
2232         }IterateStruct; 
2233
2234 int preeval_iterate(WCTemplateToken *Token)
2235 {
2236         WCTemplputParams TPP;
2237         WCTemplputParams *TP;
2238         void *vTmpl;
2239         void *vIt;
2240         HashIterator *It;
2241
2242         memset(&TPP, 0, sizeof(WCTemplputParams));
2243         TP = &TPP;
2244         TP->Tokens = Token;
2245         if (!GetHash(Iterators, TKEY(0), &vIt)) {
2246                 LogTemplateError(
2247                         NULL, "Iterator", ERR_PARM1, TP,
2248                         "not found");
2249                 return 0;
2250         }
2251         if (TP->Tokens->Params[1]->Type != TYPE_SUBTEMPLATE) {
2252                 LogTemplateError(NULL, "Iterator", ERR_PARM1, TP,
2253                                  "Need token with type Subtemplate as param 1, have %s", 
2254                                  TP->Tokens->Params[1]->Start);
2255         }
2256         
2257         /* well, we don't check the mobile stuff here... */
2258         if (!GetHash(LocalTemplateCache, TKEY(1), &vTmpl) &&
2259             !GetHash(TemplateCache, TKEY(1), &vTmpl)) {
2260                 LogTemplateError(NULL, "SubTemplate", ERR_PARM1, TP,
2261                                  "referenced here doesn't exist");
2262         }
2263         Token->Preeval2 = vIt;
2264         It = (HashIterator *) vIt;
2265
2266         if (TP->Tokens->nParameters < It->AdditionalParams + 2) {
2267                 LogTemplateError(                               
2268                         NULL, "Iterator", ERR_PARM1, TP,
2269                         "doesn't work with %d params", 
2270                         TP->Tokens->nParameters);
2271         }
2272
2273
2274         return 1;
2275 }
2276
2277 void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
2278 {
2279         HashIterator *It;
2280         HashList *List;
2281         HashPos  *it;
2282         SortStruct *SortBy = NULL;
2283         void *vSortBy;
2284         int DetectGroupChange = 0;
2285         int nMembersUsed;
2286         void *vContext;
2287         void *vLastContext = NULL;
2288         StrBuf *SubBuf;
2289         WCTemplputParams SubTP;
2290         IterateStruct Status;
2291
2292         long StartAt = 0;
2293         long StepWidth = 0;
2294         long StopAt = -1;
2295
2296         memset(&Status, 0, sizeof(IterateStruct));
2297         memcpy (&SubTP, &TP, sizeof(WCTemplputParams));
2298         
2299         It = (HashIterator*) TP->Tokens->Preeval2;
2300         if (It == NULL) {
2301                 LogTemplateError(
2302                         Target, "Iterator", ERR_PARM1, TP, "Unknown!");
2303                 return;
2304         }
2305
2306         if (TP->Tokens->nParameters < It->AdditionalParams + 2) {
2307                 LogTemplateError(                               
2308                         Target, "Iterator", ERR_PARM1, TP,
2309                         "doesn't work with %d params", 
2310                         TP->Tokens->nParameters - 1);
2311                 return;
2312         }
2313
2314         if ((It->XPectContextType != CTX_NONE) &&
2315             (It->XPectContextType != TP->Filter.ContextType)) {
2316                 LogTemplateError(
2317                         Target, "Iterator", ERR_PARM1, TP,
2318                         "requires context of type %d, have %d", 
2319                         It->XPectContextType, 
2320                         TP->Filter.ContextType);
2321                 return ;
2322                 
2323         }
2324
2325         if (It->StaticList == NULL)
2326                 List = It->GetHash(Target, TP);
2327         else
2328                 List = It->StaticList;
2329
2330         DetectGroupChange = (It->Flags & IT_FLAG_DETECT_GROUPCHANGE) != 0;
2331         if (DetectGroupChange) {
2332                 const StrBuf *BSort;
2333                 DetectGroupChange = 0;
2334                 if (havebstr("SortBy")) {
2335                         BSort = sbstr("SortBy");
2336                         if (GetHash(SortHash, SKEY(BSort), &vSortBy) &&
2337                             (vSortBy != NULL)) {
2338                                 SortBy = (SortStruct*)vSortBy;
2339                                 /** Ok, its us, lets see in which direction we should sort... */
2340                                 if (havebstr("SortOrder")) {
2341                                         int SortOrder;
2342                                         SortOrder = LBSTR("SortOrder");
2343                                         if (SortOrder != 0)
2344                                                 DetectGroupChange = 1;
2345                                 }
2346                         }
2347                 }
2348         }
2349         nMembersUsed = GetCount(List);
2350         SubBuf = NewStrBuf();
2351         SubTP.Filter.ContextType = It->ContextType;
2352         SubTP.Filter.ControlContextType = CTX_ITERATE;
2353         SubTP.ControlContext = &Status;
2354         
2355         if (HAVE_PARAM(2)) {
2356                 StartAt = GetTemplateTokenNumber(Target, TP, 2, 0);
2357         }
2358         if (HAVE_PARAM(3)) {
2359                 StepWidth = GetTemplateTokenNumber(Target, TP, 3, 0);
2360         }
2361         if (HAVE_PARAM(4)) {
2362                 StopAt = GetTemplateTokenNumber(Target, TP, 4, -1);
2363         }
2364         it = GetNewHashPos(List, StepWidth);
2365         if (StopAt < 0) {
2366                 StopAt = GetCount(List);
2367         }
2368         while (GetNextHashPos(List, it, &Status.KeyLen, &Status.Key, &vContext)) {
2369                 if ((Status.n >= StartAt) && (Status.n <= StopAt)) {
2370                         if (DetectGroupChange && Status.n > 0) {
2371                                 Status.GroupChange = SortBy->GroupChange(vContext, vLastContext);
2372                         }
2373                         Status.LastN = (Status.n + 1) == nMembersUsed;
2374                         SubTP.Context = vContext;
2375                         if (It->DoSubTemplate != NULL)
2376                                 It->DoSubTemplate(SubBuf, &SubTP);
2377                         DoTemplate(TKEY(1), SubBuf, &SubTP);
2378                         
2379                         StrBufAppendBuf(Target, SubBuf, 0);
2380                         FlushStrBuf(SubBuf);
2381                         Status.oddeven = ! Status.oddeven;
2382                         vLastContext = vContext;
2383                 }
2384                 Status.n++;
2385         }
2386         FreeStrBuf(&SubBuf);
2387         DeleteHashPos(&it);
2388         if (It->Destructor != NULL)
2389                 It->Destructor(&List);
2390 }
2391
2392
2393 int conditional_ITERATE_ISGROUPCHANGE(StrBuf *Target, WCTemplputParams *TP)
2394 {
2395         IterateStruct *Ctx = CCTX;
2396         if (TP->Tokens->nParameters < 3)
2397                 return  Ctx->GroupChange;
2398
2399         return TP->Tokens->Params[2]->lvalue == Ctx->GroupChange;
2400 }
2401
2402 void tmplput_ITERATE_ODDEVEN(StrBuf *Target, WCTemplputParams *TP)
2403 {
2404         IterateStruct *Ctx = CCTX;
2405         if (Ctx->oddeven)
2406                 StrBufAppendBufPlain(Target, HKEY("odd"), 0);
2407         else
2408                 StrBufAppendBufPlain(Target, HKEY("even"), 0);
2409 }
2410
2411
2412 void tmplput_ITERATE_KEY(StrBuf *Target, WCTemplputParams *TP)
2413 {
2414         IterateStruct *Ctx = CCTX;
2415
2416         StrBufAppendBufPlain(Target, Ctx->Key, Ctx->KeyLen, 0);
2417 }
2418
2419
2420 void tmplput_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
2421 {
2422         IterateStruct *Ctx = CCTX;
2423         StrBufAppendPrintf(Target, "%d", Ctx->n);
2424 }
2425
2426 int conditional_ITERATE_FIRSTN(StrBuf *Target, WCTemplputParams *TP)
2427 {
2428         IterateStruct *Ctx = CCTX;
2429         return Ctx->n == 0;
2430 }
2431
2432 int conditional_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
2433 {
2434         IterateStruct *Ctx = CCTX;
2435         return Ctx->LastN;
2436 }
2437
2438
2439
2440 /*-----------------------------------------------------------------------------
2441  *                      Conditionals
2442  */
2443 int EvaluateConditional(StrBuf *Target, int Neg, int state, WCTemplputParams *TP)
2444 {
2445         ConditionalStruct *Cond;
2446
2447         if ((TP->Tokens->Params[0]->len == 1) &&
2448             (TP->Tokens->Params[0]->Start[0] == 'X'))
2449                 return (state != 0)?TP->Tokens->Params[1]->lvalue:0;
2450             
2451         Cond = (ConditionalStruct *) TP->Tokens->PreEval;
2452         if (Cond == NULL) {
2453                 LogTemplateError(
2454                         Target, "Conditional", ERR_PARM1, TP,
2455                         "unknown!");
2456                 return 1;
2457         }
2458
2459         if (!CheckContext(Target, &Cond->Filter, TP, "Conditional")) {
2460                 return 0;
2461         }
2462
2463         if (Cond->CondF(Target, TP) == Neg)
2464                 return TP->Tokens->Params[1]->lvalue;
2465         return 0;
2466 }
2467
2468 int ConditionalVar(StrBuf *Target, WCTemplputParams *TP)
2469 {
2470         void *vsubst;
2471         wcsubst *subst;
2472         
2473         if (!GetHash(WC->vars, TKEY(2), &vsubst))
2474                 return 0;
2475         subst = (wcsubst*) vsubst;
2476         
2477         switch(subst->wcs_type) {
2478         case WCS_FUNCTION:
2479                 return (subst->wcs_function!=NULL);
2480         case WCS_SERVCMD:
2481                 lprintf(1, "  -> Server [%s]\n", subst->wcs_value);/* TODO */
2482                 return 1;
2483         case WCS_STRING:
2484         case WCS_STRBUF:
2485         case WCS_STRBUF_REF:
2486                 if (TP->Tokens->nParameters < 4)
2487                         return 1;
2488                 return (strcmp(TP->Tokens->Params[3]->Start, ChrPtr(subst->wcs_value)) == 0);
2489         case WCS_LONG:
2490                 if (TP->Tokens->nParameters < 4)
2491                         return (subst->lvalue != 0);
2492                 return (subst->lvalue == TP->Tokens->Params[3]->lvalue);
2493         default:
2494                 lprintf(1,"  WARNING: invalid type: [%ld]!\n", subst->wcs_type);
2495                 return -1;
2496         }
2497         return 0;
2498 }
2499
2500 void RegisterConditional(const char *Name, long len, 
2501                          int nParams,
2502                          WCConditionalFunc CondF, 
2503                          int ContextRequired)
2504 {
2505         ConditionalStruct *Cond;
2506
2507         Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2508         memset(Cond, 0, sizeof(ConditionalStruct));
2509         Cond->PlainName = Name;
2510         Cond->Filter.nMaxArgs = nParams;
2511         Cond->Filter.nMinArgs = nParams;
2512         Cond->CondF = CondF;
2513         Cond->Filter.ContextType = ContextRequired;
2514         Cond->Filter.ControlContextType = CTX_NONE;
2515         Put(Conditionals, Name, len, Cond, NULL);
2516 }
2517
2518 void RegisterControlConditional(const char *Name, long len, 
2519                                 int nParams,
2520                                 WCConditionalFunc CondF, 
2521                                 int ControlContextRequired)
2522 {
2523         ConditionalStruct *Cond;
2524
2525         Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2526         memset(Cond, 0, sizeof(ConditionalStruct));
2527         Cond->PlainName = Name;
2528         Cond->Filter.nMaxArgs = nParams;
2529         Cond->Filter.nMinArgs = nParams;
2530         Cond->CondF = CondF;
2531         Cond->Filter.ContextType = CTX_NONE;
2532         Cond->Filter.ControlContextType = ControlContextRequired;
2533         Put(Conditionals, Name, len, Cond, NULL);
2534 }
2535
2536 void RegisterTokenParamDefine(const char *Name, long len, 
2537                               long Value)
2538 {
2539         long *PVal;
2540
2541         PVal = (long*)malloc(sizeof(long));
2542         *PVal = Value;
2543         Put(Defines, Name, len, PVal, NULL);
2544 }
2545
2546 long GetTokenDefine(const char *Name, long len, 
2547                     long DefValue)
2548 {
2549         void *vPVal;
2550
2551         if (GetHash(Defines, Name, len, &vPVal) &&
2552              (vPVal != NULL))
2553          {
2554                  return *(long*) vPVal;
2555          }
2556          else
2557          {
2558                  return DefValue;
2559          }
2560 }
2561
2562 void tmplput_DefStr(StrBuf *Target, WCTemplputParams *TP)
2563 {
2564         const char *Str;
2565         long len;
2566         GetTemplateTokenString(Target, TP, 2, &Str, &len);
2567         
2568         StrBufAppendBufPlain(Target, Str, len, 0);
2569 }
2570
2571 void tmplput_DefVal(StrBuf *Target, WCTemplputParams *TP)
2572 {
2573         int val;
2574
2575         val = GetTemplateTokenNumber(Target, TP, 0, 0);
2576         StrBufAppendPrintf(Target, "%d", val);
2577 }
2578
2579 HashList *Defines;
2580
2581 /*-----------------------------------------------------------------------------
2582  *                      Context Strings
2583  */
2584 void tmplput_ContextString(StrBuf *Target, WCTemplputParams *TP)
2585 {
2586         StrBufAppendTemplate(Target, TP, (StrBuf*)CTX, 0);
2587 }
2588 int ConditionalContextStr(StrBuf *Target, WCTemplputParams *TP)
2589 {
2590         StrBuf *TokenText = (StrBuf*) CTX;
2591         const char *CompareToken;
2592         long len;
2593
2594         GetTemplateTokenString(Target, TP, 2, &CompareToken, &len);
2595         return strcmp(ChrPtr(TokenText), CompareToken) == 0;
2596 }
2597
2598 void tmplput_ContextStringArray(StrBuf *Target, WCTemplputParams *TP)
2599 {
2600         HashList *Arr = (HashList*) CTX;
2601         void *pV;
2602         int val;
2603
2604         val = GetTemplateTokenNumber(Target, TP, 0, 0);
2605         if (GetHash(Arr, IKEY(val), &pV) && 
2606             (pV != NULL)) {
2607                 StrBufAppendTemplate(Target, TP, (StrBuf*)pV, 1);
2608         }
2609 }
2610 int ConditionalContextStrinArray(StrBuf *Target, WCTemplputParams *TP)
2611 {
2612         HashList *Arr = (HashList*) CTX;
2613         void *pV;
2614         int val;
2615         const char *CompareToken;
2616         long len;
2617
2618         GetTemplateTokenString(Target, TP, 2, &CompareToken, &len);
2619         val = GetTemplateTokenNumber(Target, TP, 0, 0);
2620         if (GetHash(Arr, IKEY(val), &pV) && 
2621             (pV != NULL)) {
2622                 return strcmp(ChrPtr((StrBuf*)pV), CompareToken) == 0;
2623         }
2624         else
2625                 return 0;
2626 }
2627
2628 /*-----------------------------------------------------------------------------
2629  *                      Boxed-API
2630  */
2631
2632 void tmpl_do_boxed(StrBuf *Target, WCTemplputParams *TP)
2633 {
2634         WCTemplputParams SubTP;
2635
2636         StrBuf *Headline = NULL;
2637         if (TP->Tokens->nParameters == 2) {
2638                 if (TP->Tokens->Params[1]->Type == TYPE_STR) {
2639                         Headline = NewStrBuf();
2640                         DoTemplate(TKEY(1), Headline, TP);
2641                 }
2642                 else {
2643                         const char *Ch;
2644                         long len;
2645                         GetTemplateTokenString(Target, 
2646                                                TP, 
2647                                                1,
2648                                                &Ch,
2649                                                &len);
2650                         Headline = NewStrBufPlain(Ch, len);
2651                 }
2652         }
2653         /* else TODO error? logging? */
2654         memcpy (&SubTP, TP, sizeof(WCTemplputParams));
2655         SubTP.Context = Headline;
2656         SubTP.Filter.ContextType = CTX_STRBUF;
2657         DoTemplate(HKEY("beginbox"), Target, &SubTP);
2658         DoTemplate(TKEY(0), Target, TP);
2659         DoTemplate(HKEY("endbox"), Target, TP);
2660         FreeStrBuf(&Headline);
2661 }
2662
2663 /*-----------------------------------------------------------------------------
2664  *                      Tabbed-API
2665  */
2666
2667 typedef struct _tab_struct {
2668         long CurrentTab;
2669         StrBuf *TabTitle;
2670 } tab_struct;
2671
2672 int preeval_do_tabbed(WCTemplateToken *Token)
2673 {
2674         WCTemplputParams TPP;
2675         WCTemplputParams *TP;
2676         const char *Ch;
2677         long len;
2678         int i, nTabs;
2679
2680
2681         memset(&TPP, 0, sizeof(WCTemplputParams));
2682         TP = &TPP;
2683         TP->Tokens = Token;
2684         nTabs = TP->Tokens->nParameters / 2 - 1;
2685         if (TP->Tokens->nParameters % 2 != 0)
2686         {
2687                 LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2688                                  "need even number of arguments");
2689                 return 0;
2690
2691         }
2692         else for (i = 0; i < nTabs; i++) {
2693                 if (!HaveTemplateTokenString(NULL, 
2694                                              TP, 
2695                                              i * 2,
2696                                              &Ch,
2697                                              &len) || 
2698                     (TP->Tokens->Params[i * 2]->len == 0))
2699                 {
2700                         LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2701                                          "Tab-Subject %d needs to be able to produce a string, have %s", 
2702                                          i, TP->Tokens->Params[i * 2]->Start);
2703                         return 0;
2704                 }
2705                 if (!HaveTemplateTokenString(NULL, 
2706                                              TP, 
2707                                              i * 2 + 1,
2708                                              &Ch,
2709                                              &len) || 
2710                     (TP->Tokens->Params[i * 2 + 1]->len == 0))
2711                 {
2712                         LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2713                                          "Tab-Content %d needs to be able to produce a string, have %s", 
2714                                          i, TP->Tokens->Params[i * 2 + 1]->Start);
2715                         return 0;
2716                 }
2717         }
2718
2719         if (!HaveTemplateTokenString(NULL, 
2720                                      TP, 
2721                                      i * 2 + 1,
2722                                      &Ch,
2723                                      &len) || 
2724             (TP->Tokens->Params[i * 2 + 1]->len == 0))
2725         {
2726                 LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2727                                  "Tab-Content %d needs to be able to produce a string, have %s", 
2728                                  i, TP->Tokens->Params[i * 2 + 1]->Start);
2729                 return 0;
2730         }
2731         return 1;
2732 }
2733
2734
2735 void tmpl_do_tabbed(StrBuf *Target, WCTemplputParams *TP)
2736 {
2737         StrBuf **TabNames;
2738         int i, ntabs, nTabs;
2739         tab_struct TS;
2740         WCTemplputParams SubTP;
2741
2742         memset(&TS, 0, sizeof(tab_struct));
2743         memcpy (&SubTP, &TP, sizeof(WCTemplputParams));
2744
2745         nTabs = ntabs = TP->Tokens->nParameters / 2;
2746         TabNames = (StrBuf **) malloc(ntabs * sizeof(StrBuf*));
2747         memset(TabNames, 0, ntabs * sizeof(StrBuf*));
2748
2749         for (i = 0; i < ntabs; i++) {
2750                 if ((TP->Tokens->Params[i * 2]->Type == TYPE_STR) &&
2751                     (TP->Tokens->Params[i * 2]->len > 0)) {
2752                         TabNames[i] = NewStrBuf();
2753                         DoTemplate(TKEY(i * 2), TabNames[i], TP);
2754                 }
2755                 else if (TP->Tokens->Params[i * 2]->Type == TYPE_GETTEXT) {
2756                         const char *Ch;
2757                         long len;
2758                         GetTemplateTokenString(Target, 
2759                                                TP, 
2760                                                i * 2,
2761                                                &Ch,
2762                                                &len);
2763                         TabNames[i] = NewStrBufPlain(Ch, -1);
2764                 }
2765                 else { 
2766                         /** A Tab without subject? we can't count that, add it as silent */
2767                         nTabs --;
2768                 }
2769         }
2770         memcpy (&SubTP, TP, sizeof(WCTemplputParams));
2771         SubTP.Filter.ControlContextType = CTX_TAB;
2772         SubTP.ControlContext = &TS;
2773
2774         StrTabbedDialog(Target, nTabs, TabNames);
2775         for (i = 0; i < ntabs; i++) {
2776                 memset(&TS, 0, sizeof(tab_struct));
2777                 TS.CurrentTab = i;
2778                 TS.TabTitle = TabNames[i];
2779                 StrBeginTab(Target, i, nTabs, TabNames);
2780                 DoTemplate(TKEY(i * 2 + 1), Target, &SubTP);
2781                 StrEndTab(Target, i, nTabs);
2782         }
2783         for (i = 0; i < ntabs; i++) 
2784                 FreeStrBuf(&TabNames[i]);
2785 }
2786
2787 void tmplput_TAB_N(StrBuf *Target, WCTemplputParams *TP)
2788 {
2789         tab_struct *Ctx = CCTX;
2790
2791         StrBufAppendPrintf(Target, "%d", Ctx->CurrentTab);
2792 }
2793
2794 void tmplput_TAB_TITLE(StrBuf *Target, WCTemplputParams *TP)
2795 {
2796         tab_struct *Ctx = CCTX;
2797         StrBufAppendTemplate(Target, TP, Ctx->TabTitle, 0);
2798 }
2799
2800 /*-----------------------------------------------------------------------------
2801  *                      Sorting-API
2802  */
2803
2804
2805 void RegisterSortFunc(const char *name, long len, 
2806                       const char *prepend, long preplen,
2807                       CompareFunc Forward, 
2808                       CompareFunc Reverse, 
2809                       CompareFunc GroupChange, 
2810                       long ContextType)
2811 {
2812         SortStruct *NewSort;
2813
2814         NewSort = (SortStruct*) malloc(sizeof(SortStruct));
2815         memset(NewSort, 0, sizeof(SortStruct));
2816         NewSort->Name = NewStrBufPlain(name, len);
2817         if (prepend != NULL)
2818                 NewSort->PrefPrepend = NewStrBufPlain(prepend, preplen);
2819         else
2820                 NewSort->PrefPrepend = NULL;
2821         NewSort->Forward = Forward;
2822         NewSort->Reverse = Reverse;
2823         NewSort->GroupChange = GroupChange;
2824         NewSort->ContextType = ContextType;
2825         if (ContextType == CTX_NONE) {
2826                 lprintf(1, "sorting requires a context. CTX_NONE won't make it.\n");
2827                 exit(1);
2828         }
2829                 
2830         Put(SortHash, name, len, NewSort, DestroySortStruct);
2831 }
2832
2833 CompareFunc RetrieveSort(WCTemplputParams *TP, 
2834                          const char *OtherPrefix, long OtherPrefixLen,
2835                          const char *Default, long ldefault, long DefaultDirection)
2836 {
2837         int isdefault = 0;
2838         const StrBuf *BSort = NULL;
2839         SortStruct *SortBy;
2840         void *vSortBy;
2841         long SortOrder = -1;
2842         
2843         if (havebstr("SortBy")) {
2844                 BSort = sbstr("SortBy");
2845                 if (OtherPrefix == NULL) {
2846                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2847                 }
2848                 else {
2849                         set_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen, NewStrBufDup(BSort), 0);
2850                 }
2851         }
2852         else { /** Try to fallback to our remembered values... */
2853                 if (OtherPrefix == NULL) {
2854                         BSort = get_room_pref("sort");
2855                 }
2856                 else {
2857                         BSort = get_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen);
2858                 }
2859                 if (BSort != NULL)
2860                         putbstr("SortBy", NewStrBufDup(BSort));
2861                 else {
2862                         StrBuf *Buf;
2863
2864                         BSort = Buf = NewStrBufPlain(Default, ldefault);
2865                         putbstr("SortBy", Buf);
2866                 }
2867         }
2868
2869         if (!GetHash(SortHash, SKEY(BSort), &vSortBy) || 
2870             (vSortBy == NULL)) {
2871                 isdefault = 1;
2872                 if (!GetHash(SortHash, Default, ldefault, &vSortBy) || 
2873                     (vSortBy == NULL)) {
2874                         LogTemplateError(
2875                                 NULL, "Sorting", ERR_PARM1, TP,
2876                                 "Illegal default sort: [%s]", Default);
2877                         wc_backtrace();
2878                 }
2879         }
2880         SortBy = (SortStruct*)vSortBy;
2881
2882         if (SortBy->ContextType != TP->Filter.ContextType)
2883                 return NULL;
2884
2885         /** Ok, its us, lets see in which direction we should sort... */
2886         if (havebstr("SortOrder")) {
2887                 SortOrder = LBSTR("SortOrder");
2888         }
2889         else { /** Try to fallback to our remembered values... */
2890                 StrBuf *Buf = NULL;
2891                 if (SortBy->PrefPrepend == NULL) {
2892                         Buf = get_room_pref("SortOrder");
2893                         SortOrder = StrTol(Buf);
2894                 }
2895                 else {
2896                         BSort = get_X_PREFS(HKEY("SortOrder"), OtherPrefix, OtherPrefixLen);
2897                 }
2898
2899                 if (Buf == NULL)
2900                         SortOrder = DefaultDirection;
2901
2902                 Buf = NewStrBufPlain(NULL, 64);
2903                 StrBufPrintf(Buf, "%ld", SortOrder);
2904                 putbstr("SortOrder", Buf);
2905         }
2906         switch (SortOrder) {
2907         default:
2908         case 0:
2909                 return NULL;
2910         case 1:
2911                 return SortBy->Forward;
2912         case 2:
2913                 return SortBy->Reverse;
2914         }
2915 }
2916
2917
2918 enum {
2919         eNO_SUCH_SORT, 
2920         eNOT_SPECIFIED,
2921         eINVALID_PARAM,
2922         eFOUND
2923 };
2924
2925 ConstStr SortIcons[] = {
2926         {HKEY("static/sort_none.gif")},
2927         {HKEY("static/up_pointer.gif")},
2928         {HKEY("static/down_pointer.gif")},
2929 };
2930
2931 ConstStr SortNextOrder[] = {
2932         {HKEY("1")},
2933         {HKEY("2")},
2934         {HKEY("0")},
2935 };
2936
2937
2938 int GetSortMetric(WCTemplputParams *TP, SortStruct **Next, SortStruct **Param, long *SortOrder, int N)
2939 {
2940         int bSortError = eNOT_SPECIFIED;
2941         const StrBuf *BSort;
2942         void *vSort;
2943         
2944         *SortOrder = 0;
2945         *Next = NULL;
2946         if (!GetHash(SortHash, TKEY(0), &vSort) || 
2947             (vSort == NULL))
2948                 return eNO_SUCH_SORT;
2949         *Param = (SortStruct*) vSort;
2950         
2951
2952         if (havebstr("SortBy")) {
2953                 BSort = sbstr("SortBy");
2954                 bSortError = eINVALID_PARAM;
2955                 if ((*Param)->PrefPrepend == NULL) {
2956                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2957                 }
2958                 else {
2959                         set_X_PREFS(HKEY("sort"), TKEY(N), NewStrBufDup(BSort), 0);
2960                 }
2961         }
2962         else { /** Try to fallback to our remembered values... */
2963                 if ((*Param)->PrefPrepend == NULL) {
2964                         BSort = get_room_pref("sort");
2965                 }
2966                 else {
2967                         BSort = get_X_PREFS(HKEY("sort"), TKEY(N));
2968                 }
2969         }
2970
2971         if (!GetHash(SortHash, SKEY(BSort), &vSort) || 
2972             (vSort == NULL))
2973                 return bSortError;
2974
2975         *Next = (SortStruct*) vSort;
2976
2977         /** Ok, its us, lets see in which direction we should sort... */
2978         if (havebstr("SortOrder")) {
2979                 *SortOrder = LBSTR("SortOrder");
2980         }
2981         else { /** Try to fallback to our remembered values... */
2982                 if ((*Param)->PrefPrepend == NULL) {
2983                         *SortOrder = StrTol(get_room_pref("SortOrder"));
2984                 }
2985                 else {
2986                         *SortOrder = StrTol(get_X_PREFS(HKEY("SortOrder"), TKEY(N)));
2987                 }
2988         }
2989         if (*SortOrder > 2)
2990                 *SortOrder = 0;
2991
2992         return eFOUND;
2993 }
2994
2995
2996 void tmplput_SORT_ICON(StrBuf *Target, WCTemplputParams *TP)
2997 {
2998         long SortOrder;
2999         SortStruct *Next;
3000         SortStruct *Param;
3001         const ConstStr *SortIcon;
3002
3003         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
3004         case eNO_SUCH_SORT:
3005                 LogTemplateError(
3006                         Target, "Sorter", ERR_PARM1, TP,
3007                         " Sorter [%s] unknown!", 
3008                         TP->Tokens->Params[0]->Start);
3009                 break;          
3010         case eINVALID_PARAM:
3011                 LogTemplateError(NULL, "Sorter", ERR_PARM1, TP,
3012                                  " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
3013                                  bstr("SortBy"));
3014         case eNOT_SPECIFIED:
3015         case eFOUND:
3016                 if (Next == Param) {
3017                         SortIcon = &SortIcons[SortOrder];
3018                 }
3019                 else { /** Not Us... */
3020                         SortIcon = &SortIcons[0];
3021                 }
3022                 StrBufAppendBufPlain(Target, SortIcon->Key, SortIcon->len, 0);
3023         }
3024 }
3025
3026 void tmplput_SORT_NEXT(StrBuf *Target, WCTemplputParams *TP)
3027 {
3028         long SortOrder;
3029         SortStruct *Next;
3030         SortStruct *Param;
3031
3032         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
3033         case eNO_SUCH_SORT:
3034                 LogTemplateError(
3035                         Target, "Sorter", ERR_PARM1, TP,                                  
3036                         " Sorter [%s] unknown!", 
3037                         TP->Tokens->Params[0]->Start);
3038                 break;          
3039         case eINVALID_PARAM:
3040                 LogTemplateError(
3041                         NULL, "Sorter", ERR_PARM1, TP,
3042                         " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
3043                         bstr("SortBy"));
3044         case eNOT_SPECIFIED:
3045         case eFOUND:
3046                 StrBufAppendBuf(Target, Param->Name, 0);
3047                 
3048         }
3049 }
3050
3051 void tmplput_SORT_ORDER(StrBuf *Target, WCTemplputParams *TP)
3052 {
3053         long SortOrder;
3054         const ConstStr *SortOrderStr;
3055         SortStruct *Next;
3056         SortStruct *Param;
3057
3058         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
3059         case eNO_SUCH_SORT:
3060                 LogTemplateError(
3061                         Target, "Sorter", ERR_PARM1, TP,
3062                         " Sorter [%s] unknown!",
3063                         TP->Tokens->Params[0]->Start);
3064                 break;          
3065         case eINVALID_PARAM:
3066                 LogTemplateError(
3067                         NULL, "Sorter", ERR_PARM1, TP,
3068                         " Sorter specified by BSTR 'SortBy' [%s] unknown!",
3069                         bstr("SortBy"));
3070         case eNOT_SPECIFIED:
3071         case eFOUND:
3072                 if (Next == Param) {
3073                         SortOrderStr = &SortNextOrder[SortOrder];
3074                 }
3075                 else { /** Not Us... */
3076                         SortOrderStr = &SortNextOrder[0];
3077                 }
3078                 StrBufAppendBufPlain(Target, SortOrderStr->Key, SortOrderStr->len, 0);
3079         }
3080 }
3081
3082
3083 void tmplput_long_vector(StrBuf *Target, WCTemplputParams *TP)
3084 {
3085         long *LongVector = (long*) CTX;
3086
3087         if ((TP->Tokens->Params[0]->Type == TYPE_LONG) && 
3088             (TP->Tokens->Params[0]->lvalue <= LongVector[0]))
3089         {
3090                 StrBufAppendPrintf(Target, "%ld", LongVector[TP->Tokens->Params[0]->lvalue]);
3091         }
3092         else
3093         {
3094                 if (TP->Tokens->Params[0]->Type != TYPE_LONG) {
3095                         LogTemplateError(
3096                                 Target, "Longvector", ERR_NAME, TP,
3097                                 "needs a numerical Parameter!");
3098                 }
3099                 else {
3100                         LogTemplateError(
3101                                 Target, "LongVector", ERR_PARM1, TP,
3102                                 "doesn't have %ld Parameters, its just the size of %ld!", 
3103                                 TP->Tokens->Params[0]->lvalue,
3104                                 LongVector[0]);
3105                 }
3106         }
3107 }
3108
3109 void dbg_print_longvector(long *LongVector)
3110 {
3111         StrBuf *Buf = NewStrBufPlain(HKEY("Longvector: ["));
3112         int nItems = LongVector[0];
3113         int i;
3114
3115         for (i = 0; i < nItems; i++) {
3116                 if (i + 1 < nItems)
3117                         StrBufAppendPrintf(Buf, "%d: %ld | ", i, LongVector[i]);
3118                 else
3119                         StrBufAppendPrintf(Buf, "%d: %ld]\n", i, LongVector[i]);
3120
3121         }
3122         lprintf(1, ChrPtr(Buf));
3123         FreeStrBuf(&Buf);
3124 }
3125
3126 int ConditionalLongVector(StrBuf *Target, WCTemplputParams *TP)
3127 {
3128         long *LongVector = (long*) CTX;
3129
3130         if ((TP->Tokens->Params[2]->Type == TYPE_LONG) && 
3131             (TP->Tokens->Params[2]->lvalue <= LongVector[0])&&
3132             (TP->Tokens->Params[3]->Type == TYPE_LONG) && 
3133             (TP->Tokens->Params[3]->lvalue <= LongVector[0]))
3134         {
3135                 return LongVector[TP->Tokens->Params[2]->lvalue] == 
3136                         LongVector[TP->Tokens->Params[3]->lvalue];
3137         }
3138         else
3139         {
3140                 if ((TP->Tokens->Params[2]->Type == TYPE_LONG) ||
3141                     (TP->Tokens->Params[2]->Type == TYPE_LONG)) {
3142                         LogTemplateError(
3143                                 Target, "ConditionalLongvector", ERR_PARM1, TP,
3144                                 "needs two long Parameter!");
3145                 }
3146                 else {
3147                         LogTemplateError(
3148                                 Target, "Longvector", ERR_PARM1, TP,
3149                                 "doesn't have %ld / %ld Parameters, its just the size of %ld!",
3150                                 TP->Tokens->Params[2]->lvalue,
3151                                 TP->Tokens->Params[3]->lvalue,
3152                                 LongVector[0]);
3153                 }
3154         }
3155         return 0;
3156 }
3157
3158
3159 void tmplput_CURRENT_FILE(StrBuf *Target, WCTemplputParams *TP)
3160 {
3161         StrBufAppendTemplate(Target, TP, TP->Tokens->FileName, 0);
3162 }
3163
3164 void 
3165 InitModule_SUBST
3166 (void)
3167 {
3168         memset(&NoCtx, 0, sizeof(WCTemplputParams));
3169         RegisterNamespace("--", 0, 2, tmplput_Comment, NULL, CTX_NONE);
3170         RegisterNamespace("SORT:ICON", 1, 2, tmplput_SORT_ICON, NULL, CTX_NONE);
3171         RegisterNamespace("SORT:ORDER", 1, 2, tmplput_SORT_ORDER, NULL, CTX_NONE);
3172         RegisterNamespace("SORT:NEXT", 1, 2, tmplput_SORT_NEXT, NULL, CTX_NONE);
3173         RegisterNamespace("CONTEXTSTR", 0, 1, tmplput_ContextString, NULL, CTX_STRBUF);
3174         RegisterNamespace("CONTEXTSTRARR", 1, 2, tmplput_ContextStringArray, NULL, CTX_STRBUFARR);
3175         RegisterNamespace("ITERATE", 2, 100, tmpl_iterate_subtmpl, preeval_iterate, CTX_NONE);
3176         RegisterNamespace("DOBOXED", 1, 2, tmpl_do_boxed, NULL, CTX_NONE);
3177         RegisterNamespace("DOTABBED", 2, 100, tmpl_do_tabbed, preeval_do_tabbed, CTX_NONE);
3178         RegisterControlNS(HKEY("TAB:N"), 0, 0, tmplput_TAB_N, CTX_TAB);
3179         RegisterControlNS(HKEY("TAB:SUBJECT"), 0, 1, tmplput_TAB_TITLE, CTX_TAB);
3180
3181
3182         RegisterNamespace("LONGVECTOR", 1, 1, tmplput_long_vector, NULL, CTX_LONGVECTOR);
3183
3184
3185         RegisterConditional(HKEY("COND:SUBST"), 3, ConditionalVar, CTX_NONE);
3186         RegisterConditional(HKEY("COND:CONTEXTSTR"), 3, ConditionalContextStr, CTX_STRBUF);
3187         RegisterConditional(HKEY("COND:CONTEXTSTRARR"), 4, ConditionalContextStrinArray, CTX_STRBUFARR);
3188         RegisterConditional(HKEY("COND:LONGVECTOR"), 4, ConditionalLongVector, CTX_LONGVECTOR);
3189
3190
3191         RegisterControlConditional(HKEY("COND:ITERATE:ISGROUPCHANGE"), 2, 
3192                                    conditional_ITERATE_ISGROUPCHANGE, 
3193                                    CTX_ITERATE);
3194         RegisterControlConditional(HKEY("COND:ITERATE:LASTN"), 2, 
3195                                    conditional_ITERATE_LASTN, 
3196                                    CTX_ITERATE);
3197         RegisterControlConditional(HKEY("COND:ITERATE:FIRSTN"), 2, 
3198                                    conditional_ITERATE_FIRSTN, 
3199                                    CTX_ITERATE);
3200
3201         RegisterControlNS(HKEY("ITERATE:ODDEVEN"), 0, 0, tmplput_ITERATE_ODDEVEN, CTX_ITERATE);
3202         RegisterControlNS(HKEY("ITERATE:KEY"), 0, 0, tmplput_ITERATE_KEY, CTX_ITERATE);
3203         RegisterControlNS(HKEY("ITERATE:N"), 0, 0, tmplput_ITERATE_LASTN, CTX_ITERATE);
3204         RegisterNamespace("CURRENTFILE", 0, 1, tmplput_CURRENT_FILE, NULL, CTX_NONE);
3205         RegisterNamespace("DEF:STR", 1, 1, tmplput_DefStr, NULL, CTX_NONE);
3206         RegisterNamespace("DEF:VAL", 1, 1, tmplput_DefVal, NULL, CTX_NONE);
3207
3208
3209
3210
3211 }
3212
3213 void
3214 ServerStartModule_SUBST
3215 (void)
3216 {
3217         WirelessTemplateCache = NewHash(1, NULL);
3218         WirelessLocalTemplateCache = NewHash(1, NULL);
3219         LocalTemplateCache = NewHash(1, NULL);
3220         TemplateCache = NewHash(1, NULL);
3221
3222         GlobalNS = NewHash(1, NULL);
3223         Iterators = NewHash(1, NULL);
3224         Conditionals = NewHash(1, NULL);
3225         SortHash = NewHash(1, NULL);
3226         Defines = NewHash(1, NULL);
3227 }
3228
3229 void
3230 FinalizeModule_SUBST
3231 (void)
3232 {
3233
3234 }
3235
3236 void 
3237 ServerShutdownModule_SUBST
3238 (void)
3239 {
3240         DeleteHash(&WirelessTemplateCache);
3241         DeleteHash(&WirelessLocalTemplateCache);
3242         DeleteHash(&TemplateCache);
3243         DeleteHash(&LocalTemplateCache);
3244
3245         DeleteHash(&GlobalNS);
3246         DeleteHash(&Iterators);
3247         DeleteHash(&Conditionals);
3248         DeleteHash(&SortHash);
3249         DeleteHash(&Defines);
3250 }
3251
3252
3253 void
3254 SessionNewModule_SUBST
3255 (wcsession *sess)
3256 {
3257
3258 }
3259
3260 void
3261 SessionAttachModule_SUBST
3262 (wcsession *sess)
3263 {
3264         sess->vars = NewHash(1,NULL);
3265 }
3266
3267 void
3268 SessionDetachModule_SUBST
3269 (wcsession *sess)
3270 {
3271         DeleteHash(&sess->vars);
3272         FreeStrBuf(&sess->WFBuf);
3273 }
3274
3275 void 
3276 SessionDestroyModule_SUBST  
3277 (wcsession *sess)
3278 {
3279
3280 }