* print name of context instead of number into the error message.
[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, 0, &pch, &len);
2179                 Comment = NewStrBufPlain(pch, len);
2180                 StrBufAppendBufPlain(Target, HKEY("<!--"), 0);
2181                 StrBufAppendTemplate(Target, TP, Comment, 1);
2182                 StrBufAppendBufPlain(Target, HKEY("-->"), 0);
2183                 FreeStrBuf(&Comment);
2184         }
2185 }
2186
2187 /*-----------------------------------------------------------------------------
2188  *                      Iterators
2189  */
2190 typedef struct _HashIterator {
2191         HashList *StaticList;
2192         int AdditionalParams;
2193         int ContextType;
2194         int XPectContextType;
2195         int Flags;
2196         RetrieveHashlistFunc GetHash;
2197         HashDestructorFunc Destructor;
2198         SubTemplFunc DoSubTemplate;
2199 } HashIterator;
2200
2201 void RegisterITERATOR(const char *Name, long len, 
2202                       int AdditionalParams, 
2203                       HashList *StaticList, 
2204                       RetrieveHashlistFunc GetHash, 
2205                       SubTemplFunc DoSubTempl,
2206                       HashDestructorFunc Destructor,
2207                       int ContextType, 
2208                       int XPectContextType, 
2209                       int Flags)
2210 {
2211         HashIterator *It;
2212
2213         It = (HashIterator*)malloc(sizeof(HashIterator));
2214         memset(It, 0, sizeof(HashIterator));
2215         It->StaticList = StaticList;
2216         It->AdditionalParams = AdditionalParams;
2217         It->GetHash = GetHash;
2218         It->DoSubTemplate = DoSubTempl;
2219         It->Destructor = Destructor;
2220         It->ContextType = ContextType;
2221         It->XPectContextType = XPectContextType;
2222         It->Flags = Flags;
2223         Put(Iterators, Name, len, It, NULL);
2224 }
2225
2226 typedef struct _iteratestruct {
2227         int GroupChange;
2228         int oddeven;
2229         const char *Key;
2230         long KeyLen;
2231         int n;
2232         int LastN;
2233         }IterateStruct; 
2234
2235 int preeval_iterate(WCTemplateToken *Token)
2236 {
2237         WCTemplputParams TPP;
2238         WCTemplputParams *TP;
2239         void *vTmpl;
2240         void *vIt;
2241         HashIterator *It;
2242
2243         memset(&TPP, 0, sizeof(WCTemplputParams));
2244         TP = &TPP;
2245         TP->Tokens = Token;
2246         if (!GetHash(Iterators, TKEY(0), &vIt)) {
2247                 LogTemplateError(
2248                         NULL, "Iterator", ERR_PARM1, TP,
2249                         "not found");
2250                 return 0;
2251         }
2252         if (TP->Tokens->Params[1]->Type != TYPE_SUBTEMPLATE) {
2253                 LogTemplateError(NULL, "Iterator", ERR_PARM1, TP,
2254                                  "Need token with type Subtemplate as param 1, have %s", 
2255                                  TP->Tokens->Params[1]->Start);
2256         }
2257         
2258         /* well, we don't check the mobile stuff here... */
2259         if (!GetHash(LocalTemplateCache, TKEY(1), &vTmpl) &&
2260             !GetHash(TemplateCache, TKEY(1), &vTmpl)) {
2261                 LogTemplateError(NULL, "SubTemplate", ERR_PARM1, TP,
2262                                  "referenced here doesn't exist");
2263         }
2264         Token->Preeval2 = vIt;
2265         It = (HashIterator *) vIt;
2266
2267         if (TP->Tokens->nParameters < It->AdditionalParams + 2) {
2268                 LogTemplateError(                               
2269                         NULL, "Iterator", ERR_PARM1, TP,
2270                         "doesn't work with %d params", 
2271                         TP->Tokens->nParameters);
2272         }
2273
2274
2275         return 1;
2276 }
2277
2278 void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
2279 {
2280         HashIterator *It;
2281         HashList *List;
2282         HashPos  *it;
2283         SortStruct *SortBy = NULL;
2284         void *vSortBy;
2285         int DetectGroupChange = 0;
2286         int nMembersUsed;
2287         void *vContext;
2288         void *vLastContext = NULL;
2289         StrBuf *SubBuf;
2290         WCTemplputParams SubTP;
2291         IterateStruct Status;
2292
2293         long StartAt = 0;
2294         long StepWidth = 0;
2295         long StopAt = -1;
2296
2297         memset(&Status, 0, sizeof(IterateStruct));
2298         memcpy (&SubTP, &TP, sizeof(WCTemplputParams));
2299         
2300         It = (HashIterator*) TP->Tokens->Preeval2;
2301         if (It == NULL) {
2302                 LogTemplateError(
2303                         Target, "Iterator", ERR_PARM1, TP, "Unknown!");
2304                 return;
2305         }
2306
2307         if (TP->Tokens->nParameters < It->AdditionalParams + 2) {
2308                 LogTemplateError(                               
2309                         Target, "Iterator", ERR_PARM1, TP,
2310                         "doesn't work with %d params", 
2311                         TP->Tokens->nParameters - 1);
2312                 return;
2313         }
2314
2315         if ((It->XPectContextType != CTX_NONE) &&
2316             (It->XPectContextType != TP->Filter.ContextType)) {
2317                 LogTemplateError(
2318                         Target, "Iterator", ERR_PARM1, TP,
2319                         "requires context of type %s, have %s", 
2320                         ContextName(It->XPectContextType), 
2321                         ContextName(TP->Filter.ContextType));
2322                 return ;
2323                 
2324         }
2325
2326         if (It->StaticList == NULL)
2327                 List = It->GetHash(Target, TP);
2328         else
2329                 List = It->StaticList;
2330
2331         DetectGroupChange = (It->Flags & IT_FLAG_DETECT_GROUPCHANGE) != 0;
2332         if (DetectGroupChange) {
2333                 const StrBuf *BSort;
2334                 DetectGroupChange = 0;
2335                 if (havebstr("SortBy")) {
2336                         BSort = sbstr("SortBy");
2337                         if (GetHash(SortHash, SKEY(BSort), &vSortBy) &&
2338                             (vSortBy != NULL)) {
2339                                 SortBy = (SortStruct*)vSortBy;
2340                                 /** Ok, its us, lets see in which direction we should sort... */
2341                                 if (havebstr("SortOrder")) {
2342                                         int SortOrder;
2343                                         SortOrder = LBSTR("SortOrder");
2344                                         if (SortOrder != 0)
2345                                                 DetectGroupChange = 1;
2346                                 }
2347                         }
2348                 }
2349         }
2350         nMembersUsed = GetCount(List);
2351         SubBuf = NewStrBuf();
2352         SubTP.Filter.ContextType = It->ContextType;
2353         SubTP.Filter.ControlContextType = CTX_ITERATE;
2354         SubTP.ControlContext = &Status;
2355         
2356         if (HAVE_PARAM(2)) {
2357                 StartAt = GetTemplateTokenNumber(Target, TP, 2, 0);
2358         }
2359         if (HAVE_PARAM(3)) {
2360                 StepWidth = GetTemplateTokenNumber(Target, TP, 3, 0);
2361         }
2362         if (HAVE_PARAM(4)) {
2363                 StopAt = GetTemplateTokenNumber(Target, TP, 4, -1);
2364         }
2365         it = GetNewHashPos(List, StepWidth);
2366         if (StopAt < 0) {
2367                 StopAt = GetCount(List);
2368         }
2369         while (GetNextHashPos(List, it, &Status.KeyLen, &Status.Key, &vContext)) {
2370                 if ((Status.n >= StartAt) && (Status.n <= StopAt)) {
2371                         if (DetectGroupChange && Status.n > 0) {
2372                                 Status.GroupChange = SortBy->GroupChange(vContext, vLastContext);
2373                         }
2374                         Status.LastN = (Status.n + 1) == nMembersUsed;
2375                         SubTP.Context = vContext;
2376                         if (It->DoSubTemplate != NULL)
2377                                 It->DoSubTemplate(SubBuf, &SubTP);
2378                         DoTemplate(TKEY(1), SubBuf, &SubTP);
2379                         
2380                         StrBufAppendBuf(Target, SubBuf, 0);
2381                         FlushStrBuf(SubBuf);
2382                         Status.oddeven = ! Status.oddeven;
2383                         vLastContext = vContext;
2384                 }
2385                 Status.n++;
2386         }
2387         FreeStrBuf(&SubBuf);
2388         DeleteHashPos(&it);
2389         if (It->Destructor != NULL)
2390                 It->Destructor(&List);
2391 }
2392
2393
2394 int conditional_ITERATE_ISGROUPCHANGE(StrBuf *Target, WCTemplputParams *TP)
2395 {
2396         IterateStruct *Ctx = CCTX;
2397         if (TP->Tokens->nParameters < 3)
2398                 return  Ctx->GroupChange;
2399
2400         return TP->Tokens->Params[2]->lvalue == Ctx->GroupChange;
2401 }
2402
2403 void tmplput_ITERATE_ODDEVEN(StrBuf *Target, WCTemplputParams *TP)
2404 {
2405         IterateStruct *Ctx = CCTX;
2406         if (Ctx->oddeven)
2407                 StrBufAppendBufPlain(Target, HKEY("odd"), 0);
2408         else
2409                 StrBufAppendBufPlain(Target, HKEY("even"), 0);
2410 }
2411
2412
2413 void tmplput_ITERATE_KEY(StrBuf *Target, WCTemplputParams *TP)
2414 {
2415         IterateStruct *Ctx = CCTX;
2416
2417         StrBufAppendBufPlain(Target, Ctx->Key, Ctx->KeyLen, 0);
2418 }
2419
2420
2421 void tmplput_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
2422 {
2423         IterateStruct *Ctx = CCTX;
2424         StrBufAppendPrintf(Target, "%d", Ctx->n);
2425 }
2426
2427 int conditional_ITERATE_FIRSTN(StrBuf *Target, WCTemplputParams *TP)
2428 {
2429         IterateStruct *Ctx = CCTX;
2430         return Ctx->n == 0;
2431 }
2432
2433 int conditional_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
2434 {
2435         IterateStruct *Ctx = CCTX;
2436         return Ctx->LastN;
2437 }
2438
2439
2440
2441 /*-----------------------------------------------------------------------------
2442  *                      Conditionals
2443  */
2444 int EvaluateConditional(StrBuf *Target, int Neg, int state, WCTemplputParams *TP)
2445 {
2446         ConditionalStruct *Cond;
2447
2448         if ((TP->Tokens->Params[0]->len == 1) &&
2449             (TP->Tokens->Params[0]->Start[0] == 'X'))
2450                 return (state != 0)?TP->Tokens->Params[1]->lvalue:0;
2451             
2452         Cond = (ConditionalStruct *) TP->Tokens->PreEval;
2453         if (Cond == NULL) {
2454                 LogTemplateError(
2455                         Target, "Conditional", ERR_PARM1, TP,
2456                         "unknown!");
2457                 return 1;
2458         }
2459
2460         if (!CheckContext(Target, &Cond->Filter, TP, "Conditional")) {
2461                 return 0;
2462         }
2463
2464         if (Cond->CondF(Target, TP) == Neg)
2465                 return TP->Tokens->Params[1]->lvalue;
2466         return 0;
2467 }
2468
2469 int ConditionalVar(StrBuf *Target, WCTemplputParams *TP)
2470 {
2471         void *vsubst;
2472         wcsubst *subst;
2473         
2474         if (!GetHash(WC->vars, TKEY(2), &vsubst))
2475                 return 0;
2476         subst = (wcsubst*) vsubst;
2477         
2478         switch(subst->wcs_type) {
2479         case WCS_FUNCTION:
2480                 return (subst->wcs_function!=NULL);
2481         case WCS_SERVCMD:
2482                 lprintf(1, "  -> Server [%s]\n", subst->wcs_value);/* TODO */
2483                 return 1;
2484         case WCS_STRING:
2485         case WCS_STRBUF:
2486         case WCS_STRBUF_REF:
2487                 if (TP->Tokens->nParameters < 4)
2488                         return 1;
2489                 return (strcmp(TP->Tokens->Params[3]->Start, ChrPtr(subst->wcs_value)) == 0);
2490         case WCS_LONG:
2491                 if (TP->Tokens->nParameters < 4)
2492                         return (subst->lvalue != 0);
2493                 return (subst->lvalue == TP->Tokens->Params[3]->lvalue);
2494         default:
2495                 lprintf(1,"  WARNING: invalid type: [%ld]!\n", subst->wcs_type);
2496                 return -1;
2497         }
2498         return 0;
2499 }
2500
2501 void RegisterConditional(const char *Name, long len, 
2502                          int nParams,
2503                          WCConditionalFunc CondF, 
2504                          int ContextRequired)
2505 {
2506         ConditionalStruct *Cond;
2507
2508         Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2509         memset(Cond, 0, sizeof(ConditionalStruct));
2510         Cond->PlainName = Name;
2511         Cond->Filter.nMaxArgs = nParams;
2512         Cond->Filter.nMinArgs = nParams;
2513         Cond->CondF = CondF;
2514         Cond->Filter.ContextType = ContextRequired;
2515         Cond->Filter.ControlContextType = CTX_NONE;
2516         Put(Conditionals, Name, len, Cond, NULL);
2517 }
2518
2519 void RegisterControlConditional(const char *Name, long len, 
2520                                 int nParams,
2521                                 WCConditionalFunc CondF, 
2522                                 int ControlContextRequired)
2523 {
2524         ConditionalStruct *Cond;
2525
2526         Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2527         memset(Cond, 0, sizeof(ConditionalStruct));
2528         Cond->PlainName = Name;
2529         Cond->Filter.nMaxArgs = nParams;
2530         Cond->Filter.nMinArgs = nParams;
2531         Cond->CondF = CondF;
2532         Cond->Filter.ContextType = CTX_NONE;
2533         Cond->Filter.ControlContextType = ControlContextRequired;
2534         Put(Conditionals, Name, len, Cond, NULL);
2535 }
2536
2537 void RegisterTokenParamDefine(const char *Name, long len, 
2538                               long Value)
2539 {
2540         long *PVal;
2541
2542         PVal = (long*)malloc(sizeof(long));
2543         *PVal = Value;
2544         Put(Defines, Name, len, PVal, NULL);
2545 }
2546
2547 long GetTokenDefine(const char *Name, long len, 
2548                     long DefValue)
2549 {
2550         void *vPVal;
2551
2552         if (GetHash(Defines, Name, len, &vPVal) &&
2553              (vPVal != NULL))
2554          {
2555                  return *(long*) vPVal;
2556          }
2557          else
2558          {
2559                  return DefValue;
2560          }
2561 }
2562
2563 void tmplput_DefStr(StrBuf *Target, WCTemplputParams *TP)
2564 {
2565         const char *Str;
2566         long len;
2567         GetTemplateTokenString(Target, TP, 2, &Str, &len);
2568         
2569         StrBufAppendBufPlain(Target, Str, len, 0);
2570 }
2571
2572 void tmplput_DefVal(StrBuf *Target, WCTemplputParams *TP)
2573 {
2574         int val;
2575
2576         val = GetTemplateTokenNumber(Target, TP, 0, 0);
2577         StrBufAppendPrintf(Target, "%d", val);
2578 }
2579
2580 HashList *Defines;
2581
2582 /*-----------------------------------------------------------------------------
2583  *                      Context Strings
2584  */
2585 void tmplput_ContextString(StrBuf *Target, WCTemplputParams *TP)
2586 {
2587         StrBufAppendTemplate(Target, TP, (StrBuf*)CTX, 0);
2588 }
2589 int ConditionalContextStr(StrBuf *Target, WCTemplputParams *TP)
2590 {
2591         StrBuf *TokenText = (StrBuf*) CTX;
2592         const char *CompareToken;
2593         long len;
2594
2595         GetTemplateTokenString(Target, TP, 2, &CompareToken, &len);
2596         return strcmp(ChrPtr(TokenText), CompareToken) == 0;
2597 }
2598
2599 void tmplput_ContextStringArray(StrBuf *Target, WCTemplputParams *TP)
2600 {
2601         HashList *Arr = (HashList*) CTX;
2602         void *pV;
2603         int val;
2604
2605         val = GetTemplateTokenNumber(Target, TP, 0, 0);
2606         if (GetHash(Arr, IKEY(val), &pV) && 
2607             (pV != NULL)) {
2608                 StrBufAppendTemplate(Target, TP, (StrBuf*)pV, 1);
2609         }
2610 }
2611 int ConditionalContextStrinArray(StrBuf *Target, WCTemplputParams *TP)
2612 {
2613         HashList *Arr = (HashList*) CTX;
2614         void *pV;
2615         int val;
2616         const char *CompareToken;
2617         long len;
2618
2619         GetTemplateTokenString(Target, TP, 2, &CompareToken, &len);
2620         val = GetTemplateTokenNumber(Target, TP, 0, 0);
2621         if (GetHash(Arr, IKEY(val), &pV) && 
2622             (pV != NULL)) {
2623                 return strcmp(ChrPtr((StrBuf*)pV), CompareToken) == 0;
2624         }
2625         else
2626                 return 0;
2627 }
2628
2629 /*-----------------------------------------------------------------------------
2630  *                      Boxed-API
2631  */
2632
2633 void tmpl_do_boxed(StrBuf *Target, WCTemplputParams *TP)
2634 {
2635         WCTemplputParams SubTP;
2636
2637         StrBuf *Headline = NULL;
2638         if (TP->Tokens->nParameters == 2) {
2639                 if (TP->Tokens->Params[1]->Type == TYPE_STR) {
2640                         Headline = NewStrBuf();
2641                         DoTemplate(TKEY(1), Headline, TP);
2642                 }
2643                 else {
2644                         const char *Ch;
2645                         long len;
2646                         GetTemplateTokenString(Target, 
2647                                                TP, 
2648                                                1,
2649                                                &Ch,
2650                                                &len);
2651                         Headline = NewStrBufPlain(Ch, len);
2652                 }
2653         }
2654         /* else TODO error? logging? */
2655         memcpy (&SubTP, TP, sizeof(WCTemplputParams));
2656         SubTP.Context = Headline;
2657         SubTP.Filter.ContextType = CTX_STRBUF;
2658         DoTemplate(HKEY("beginbox"), Target, &SubTP);
2659         DoTemplate(TKEY(0), Target, TP);
2660         DoTemplate(HKEY("endbox"), Target, TP);
2661         FreeStrBuf(&Headline);
2662 }
2663
2664 /*-----------------------------------------------------------------------------
2665  *                      Tabbed-API
2666  */
2667
2668 typedef struct _tab_struct {
2669         long CurrentTab;
2670         StrBuf *TabTitle;
2671 } tab_struct;
2672
2673 int preeval_do_tabbed(WCTemplateToken *Token)
2674 {
2675         WCTemplputParams TPP;
2676         WCTemplputParams *TP;
2677         const char *Ch;
2678         long len;
2679         int i, nTabs;
2680
2681
2682         memset(&TPP, 0, sizeof(WCTemplputParams));
2683         TP = &TPP;
2684         TP->Tokens = Token;
2685         nTabs = TP->Tokens->nParameters / 2 - 1;
2686         if (TP->Tokens->nParameters % 2 != 0)
2687         {
2688                 LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2689                                  "need even number of arguments");
2690                 return 0;
2691
2692         }
2693         else for (i = 0; i < nTabs; i++) {
2694                 if (!HaveTemplateTokenString(NULL, 
2695                                              TP, 
2696                                              i * 2,
2697                                              &Ch,
2698                                              &len) || 
2699                     (TP->Tokens->Params[i * 2]->len == 0))
2700                 {
2701                         LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2702                                          "Tab-Subject %d needs to be able to produce a string, have %s", 
2703                                          i, TP->Tokens->Params[i * 2]->Start);
2704                         return 0;
2705                 }
2706                 if (!HaveTemplateTokenString(NULL, 
2707                                              TP, 
2708                                              i * 2 + 1,
2709                                              &Ch,
2710                                              &len) || 
2711                     (TP->Tokens->Params[i * 2 + 1]->len == 0))
2712                 {
2713                         LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2714                                          "Tab-Content %d needs to be able to produce a string, have %s", 
2715                                          i, TP->Tokens->Params[i * 2 + 1]->Start);
2716                         return 0;
2717                 }
2718         }
2719
2720         if (!HaveTemplateTokenString(NULL, 
2721                                      TP, 
2722                                      i * 2 + 1,
2723                                      &Ch,
2724                                      &len) || 
2725             (TP->Tokens->Params[i * 2 + 1]->len == 0))
2726         {
2727                 LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2728                                  "Tab-Content %d needs to be able to produce a string, have %s", 
2729                                  i, TP->Tokens->Params[i * 2 + 1]->Start);
2730                 return 0;
2731         }
2732         return 1;
2733 }
2734
2735
2736 void tmpl_do_tabbed(StrBuf *Target, WCTemplputParams *TP)
2737 {
2738         StrBuf **TabNames;
2739         int i, ntabs, nTabs;
2740         tab_struct TS;
2741         WCTemplputParams SubTP;
2742
2743         memset(&TS, 0, sizeof(tab_struct));
2744         memcpy (&SubTP, &TP, sizeof(WCTemplputParams));
2745
2746         nTabs = ntabs = TP->Tokens->nParameters / 2;
2747         TabNames = (StrBuf **) malloc(ntabs * sizeof(StrBuf*));
2748         memset(TabNames, 0, ntabs * sizeof(StrBuf*));
2749
2750         for (i = 0; i < ntabs; i++) {
2751                 if ((TP->Tokens->Params[i * 2]->Type == TYPE_STR) &&
2752                     (TP->Tokens->Params[i * 2]->len > 0)) {
2753                         TabNames[i] = NewStrBuf();
2754                         DoTemplate(TKEY(i * 2), TabNames[i], TP);
2755                 }
2756                 else if (TP->Tokens->Params[i * 2]->Type == TYPE_GETTEXT) {
2757                         const char *Ch;
2758                         long len;
2759                         GetTemplateTokenString(Target, 
2760                                                TP, 
2761                                                i * 2,
2762                                                &Ch,
2763                                                &len);
2764                         TabNames[i] = NewStrBufPlain(Ch, -1);
2765                 }
2766                 else { 
2767                         /** A Tab without subject? we can't count that, add it as silent */
2768                         nTabs --;
2769                 }
2770         }
2771         memcpy (&SubTP, TP, sizeof(WCTemplputParams));
2772         SubTP.Filter.ControlContextType = CTX_TAB;
2773         SubTP.ControlContext = &TS;
2774
2775         StrTabbedDialog(Target, nTabs, TabNames);
2776         for (i = 0; i < ntabs; i++) {
2777                 memset(&TS, 0, sizeof(tab_struct));
2778                 TS.CurrentTab = i;
2779                 TS.TabTitle = TabNames[i];
2780                 StrBeginTab(Target, i, nTabs, TabNames);
2781                 DoTemplate(TKEY(i * 2 + 1), Target, &SubTP);
2782                 StrEndTab(Target, i, nTabs);
2783         }
2784         for (i = 0; i < ntabs; i++) 
2785                 FreeStrBuf(&TabNames[i]);
2786 }
2787
2788 void tmplput_TAB_N(StrBuf *Target, WCTemplputParams *TP)
2789 {
2790         tab_struct *Ctx = CCTX;
2791
2792         StrBufAppendPrintf(Target, "%d", Ctx->CurrentTab);
2793 }
2794
2795 void tmplput_TAB_TITLE(StrBuf *Target, WCTemplputParams *TP)
2796 {
2797         tab_struct *Ctx = CCTX;
2798         StrBufAppendTemplate(Target, TP, Ctx->TabTitle, 0);
2799 }
2800
2801 /*-----------------------------------------------------------------------------
2802  *                      Sorting-API
2803  */
2804
2805
2806 void RegisterSortFunc(const char *name, long len, 
2807                       const char *prepend, long preplen,
2808                       CompareFunc Forward, 
2809                       CompareFunc Reverse, 
2810                       CompareFunc GroupChange, 
2811                       long ContextType)
2812 {
2813         SortStruct *NewSort;
2814
2815         NewSort = (SortStruct*) malloc(sizeof(SortStruct));
2816         memset(NewSort, 0, sizeof(SortStruct));
2817         NewSort->Name = NewStrBufPlain(name, len);
2818         if (prepend != NULL)
2819                 NewSort->PrefPrepend = NewStrBufPlain(prepend, preplen);
2820         else
2821                 NewSort->PrefPrepend = NULL;
2822         NewSort->Forward = Forward;
2823         NewSort->Reverse = Reverse;
2824         NewSort->GroupChange = GroupChange;
2825         NewSort->ContextType = ContextType;
2826         if (ContextType == CTX_NONE) {
2827                 lprintf(1, "sorting requires a context. CTX_NONE won't make it.\n");
2828                 exit(1);
2829         }
2830                 
2831         Put(SortHash, name, len, NewSort, DestroySortStruct);
2832 }
2833
2834 CompareFunc RetrieveSort(WCTemplputParams *TP, 
2835                          const char *OtherPrefix, long OtherPrefixLen,
2836                          const char *Default, long ldefault, long DefaultDirection)
2837 {
2838         int isdefault = 0;
2839         const StrBuf *BSort = NULL;
2840         SortStruct *SortBy;
2841         void *vSortBy;
2842         long SortOrder = -1;
2843         
2844         if (havebstr("SortBy")) {
2845                 BSort = sbstr("SortBy");
2846                 if (OtherPrefix == NULL) {
2847                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2848                 }
2849                 else {
2850                         set_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen, NewStrBufDup(BSort), 0);
2851                 }
2852         }
2853         else { /** Try to fallback to our remembered values... */
2854                 if (OtherPrefix == NULL) {
2855                         BSort = get_room_pref("sort");
2856                 }
2857                 else {
2858                         BSort = get_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen);
2859                 }
2860                 if (BSort != NULL)
2861                         putbstr("SortBy", NewStrBufDup(BSort));
2862                 else {
2863                         StrBuf *Buf;
2864
2865                         BSort = Buf = NewStrBufPlain(Default, ldefault);
2866                         putbstr("SortBy", Buf);
2867                 }
2868         }
2869
2870         if (!GetHash(SortHash, SKEY(BSort), &vSortBy) || 
2871             (vSortBy == NULL)) {
2872                 isdefault = 1;
2873                 if (!GetHash(SortHash, Default, ldefault, &vSortBy) || 
2874                     (vSortBy == NULL)) {
2875                         LogTemplateError(
2876                                 NULL, "Sorting", ERR_PARM1, TP,
2877                                 "Illegal default sort: [%s]", Default);
2878                         wc_backtrace();
2879                 }
2880         }
2881         SortBy = (SortStruct*)vSortBy;
2882
2883         if (SortBy->ContextType != TP->Filter.ContextType)
2884                 return NULL;
2885
2886         /** Ok, its us, lets see in which direction we should sort... */
2887         if (havebstr("SortOrder")) {
2888                 SortOrder = LBSTR("SortOrder");
2889         }
2890         else { /** Try to fallback to our remembered values... */
2891                 StrBuf *Buf = NULL;
2892                 if (SortBy->PrefPrepend == NULL) {
2893                         Buf = get_room_pref("SortOrder");
2894                         SortOrder = StrTol(Buf);
2895                 }
2896                 else {
2897                         BSort = get_X_PREFS(HKEY("SortOrder"), OtherPrefix, OtherPrefixLen);
2898                 }
2899
2900                 if (Buf == NULL)
2901                         SortOrder = DefaultDirection;
2902
2903                 Buf = NewStrBufPlain(NULL, 64);
2904                 StrBufPrintf(Buf, "%ld", SortOrder);
2905                 putbstr("SortOrder", Buf);
2906         }
2907         switch (SortOrder) {
2908         default:
2909         case 0:
2910                 return NULL;
2911         case 1:
2912                 return SortBy->Forward;
2913         case 2:
2914                 return SortBy->Reverse;
2915         }
2916 }
2917
2918
2919 enum {
2920         eNO_SUCH_SORT, 
2921         eNOT_SPECIFIED,
2922         eINVALID_PARAM,
2923         eFOUND
2924 };
2925
2926 ConstStr SortIcons[] = {
2927         {HKEY("static/sort_none.gif")},
2928         {HKEY("static/up_pointer.gif")},
2929         {HKEY("static/down_pointer.gif")},
2930 };
2931
2932 ConstStr SortNextOrder[] = {
2933         {HKEY("1")},
2934         {HKEY("2")},
2935         {HKEY("0")},
2936 };
2937
2938
2939 int GetSortMetric(WCTemplputParams *TP, SortStruct **Next, SortStruct **Param, long *SortOrder, int N)
2940 {
2941         int bSortError = eNOT_SPECIFIED;
2942         const StrBuf *BSort;
2943         void *vSort;
2944         
2945         *SortOrder = 0;
2946         *Next = NULL;
2947         if (!GetHash(SortHash, TKEY(0), &vSort) || 
2948             (vSort == NULL))
2949                 return eNO_SUCH_SORT;
2950         *Param = (SortStruct*) vSort;
2951         
2952
2953         if (havebstr("SortBy")) {
2954                 BSort = sbstr("SortBy");
2955                 bSortError = eINVALID_PARAM;
2956                 if ((*Param)->PrefPrepend == NULL) {
2957                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2958                 }
2959                 else {
2960                         set_X_PREFS(HKEY("sort"), TKEY(N), NewStrBufDup(BSort), 0);
2961                 }
2962         }
2963         else { /** Try to fallback to our remembered values... */
2964                 if ((*Param)->PrefPrepend == NULL) {
2965                         BSort = get_room_pref("sort");
2966                 }
2967                 else {
2968                         BSort = get_X_PREFS(HKEY("sort"), TKEY(N));
2969                 }
2970         }
2971
2972         if (!GetHash(SortHash, SKEY(BSort), &vSort) || 
2973             (vSort == NULL))
2974                 return bSortError;
2975
2976         *Next = (SortStruct*) vSort;
2977
2978         /** Ok, its us, lets see in which direction we should sort... */
2979         if (havebstr("SortOrder")) {
2980                 *SortOrder = LBSTR("SortOrder");
2981         }
2982         else { /** Try to fallback to our remembered values... */
2983                 if ((*Param)->PrefPrepend == NULL) {
2984                         *SortOrder = StrTol(get_room_pref("SortOrder"));
2985                 }
2986                 else {
2987                         *SortOrder = StrTol(get_X_PREFS(HKEY("SortOrder"), TKEY(N)));
2988                 }
2989         }
2990         if (*SortOrder > 2)
2991                 *SortOrder = 0;
2992
2993         return eFOUND;
2994 }
2995
2996
2997 void tmplput_SORT_ICON(StrBuf *Target, WCTemplputParams *TP)
2998 {
2999         long SortOrder;
3000         SortStruct *Next;
3001         SortStruct *Param;
3002         const ConstStr *SortIcon;
3003
3004         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
3005         case eNO_SUCH_SORT:
3006                 LogTemplateError(
3007                         Target, "Sorter", ERR_PARM1, TP,
3008                         " Sorter [%s] unknown!", 
3009                         TP->Tokens->Params[0]->Start);
3010                 break;          
3011         case eINVALID_PARAM:
3012                 LogTemplateError(NULL, "Sorter", ERR_PARM1, TP,
3013                                  " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
3014                                  bstr("SortBy"));
3015         case eNOT_SPECIFIED:
3016         case eFOUND:
3017                 if (Next == Param) {
3018                         SortIcon = &SortIcons[SortOrder];
3019                 }
3020                 else { /** Not Us... */
3021                         SortIcon = &SortIcons[0];
3022                 }
3023                 StrBufAppendBufPlain(Target, SortIcon->Key, SortIcon->len, 0);
3024         }
3025 }
3026
3027 void tmplput_SORT_NEXT(StrBuf *Target, WCTemplputParams *TP)
3028 {
3029         long SortOrder;
3030         SortStruct *Next;
3031         SortStruct *Param;
3032
3033         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
3034         case eNO_SUCH_SORT:
3035                 LogTemplateError(
3036                         Target, "Sorter", ERR_PARM1, TP,                                  
3037                         " Sorter [%s] unknown!", 
3038                         TP->Tokens->Params[0]->Start);
3039                 break;          
3040         case eINVALID_PARAM:
3041                 LogTemplateError(
3042                         NULL, "Sorter", ERR_PARM1, TP,
3043                         " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
3044                         bstr("SortBy"));
3045         case eNOT_SPECIFIED:
3046         case eFOUND:
3047                 StrBufAppendBuf(Target, Param->Name, 0);
3048                 
3049         }
3050 }
3051
3052 void tmplput_SORT_ORDER(StrBuf *Target, WCTemplputParams *TP)
3053 {
3054         long SortOrder;
3055         const ConstStr *SortOrderStr;
3056         SortStruct *Next;
3057         SortStruct *Param;
3058
3059         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
3060         case eNO_SUCH_SORT:
3061                 LogTemplateError(
3062                         Target, "Sorter", ERR_PARM1, TP,
3063                         " Sorter [%s] unknown!",
3064                         TP->Tokens->Params[0]->Start);
3065                 break;          
3066         case eINVALID_PARAM:
3067                 LogTemplateError(
3068                         NULL, "Sorter", ERR_PARM1, TP,
3069                         " Sorter specified by BSTR 'SortBy' [%s] unknown!",
3070                         bstr("SortBy"));
3071         case eNOT_SPECIFIED:
3072         case eFOUND:
3073                 if (Next == Param) {
3074                         SortOrderStr = &SortNextOrder[SortOrder];
3075                 }
3076                 else { /** Not Us... */
3077                         SortOrderStr = &SortNextOrder[0];
3078                 }
3079                 StrBufAppendBufPlain(Target, SortOrderStr->Key, SortOrderStr->len, 0);
3080         }
3081 }
3082
3083
3084 void tmplput_long_vector(StrBuf *Target, WCTemplputParams *TP)
3085 {
3086         long *LongVector = (long*) CTX;
3087
3088         if ((TP->Tokens->Params[0]->Type == TYPE_LONG) && 
3089             (TP->Tokens->Params[0]->lvalue <= LongVector[0]))
3090         {
3091                 StrBufAppendPrintf(Target, "%ld", LongVector[TP->Tokens->Params[0]->lvalue]);
3092         }
3093         else
3094         {
3095                 if (TP->Tokens->Params[0]->Type != TYPE_LONG) {
3096                         LogTemplateError(
3097                                 Target, "Longvector", ERR_NAME, TP,
3098                                 "needs a numerical Parameter!");
3099                 }
3100                 else {
3101                         LogTemplateError(
3102                                 Target, "LongVector", ERR_PARM1, TP,
3103                                 "doesn't have %ld Parameters, its just the size of %ld!", 
3104                                 TP->Tokens->Params[0]->lvalue,
3105                                 LongVector[0]);
3106                 }
3107         }
3108 }
3109
3110 void dbg_print_longvector(long *LongVector)
3111 {
3112         StrBuf *Buf = NewStrBufPlain(HKEY("Longvector: ["));
3113         int nItems = LongVector[0];
3114         int i;
3115
3116         for (i = 0; i < nItems; i++) {
3117                 if (i + 1 < nItems)
3118                         StrBufAppendPrintf(Buf, "%d: %ld | ", i, LongVector[i]);
3119                 else
3120                         StrBufAppendPrintf(Buf, "%d: %ld]\n", i, LongVector[i]);
3121
3122         }
3123         lprintf(1, ChrPtr(Buf));
3124         FreeStrBuf(&Buf);
3125 }
3126
3127 int ConditionalLongVector(StrBuf *Target, WCTemplputParams *TP)
3128 {
3129         long *LongVector = (long*) CTX;
3130
3131         if ((TP->Tokens->Params[2]->Type == TYPE_LONG) && 
3132             (TP->Tokens->Params[2]->lvalue <= LongVector[0])&&
3133             (TP->Tokens->Params[3]->Type == TYPE_LONG) && 
3134             (TP->Tokens->Params[3]->lvalue <= LongVector[0]))
3135         {
3136                 return LongVector[TP->Tokens->Params[2]->lvalue] == 
3137                         LongVector[TP->Tokens->Params[3]->lvalue];
3138         }
3139         else
3140         {
3141                 if ((TP->Tokens->Params[2]->Type == TYPE_LONG) ||
3142                     (TP->Tokens->Params[2]->Type == TYPE_LONG)) {
3143                         LogTemplateError(
3144                                 Target, "ConditionalLongvector", ERR_PARM1, TP,
3145                                 "needs two long Parameter!");
3146                 }
3147                 else {
3148                         LogTemplateError(
3149                                 Target, "Longvector", ERR_PARM1, TP,
3150                                 "doesn't have %ld / %ld Parameters, its just the size of %ld!",
3151                                 TP->Tokens->Params[2]->lvalue,
3152                                 TP->Tokens->Params[3]->lvalue,
3153                                 LongVector[0]);
3154                 }
3155         }
3156         return 0;
3157 }
3158
3159
3160 void tmplput_CURRENT_FILE(StrBuf *Target, WCTemplputParams *TP)
3161 {
3162         StrBufAppendTemplate(Target, TP, TP->Tokens->FileName, 0);
3163 }
3164
3165 void 
3166 InitModule_SUBST
3167 (void)
3168 {
3169         memset(&NoCtx, 0, sizeof(WCTemplputParams));
3170         RegisterNamespace("--", 0, 2, tmplput_Comment, NULL, CTX_NONE);
3171         RegisterNamespace("SORT:ICON", 1, 2, tmplput_SORT_ICON, NULL, CTX_NONE);
3172         RegisterNamespace("SORT:ORDER", 1, 2, tmplput_SORT_ORDER, NULL, CTX_NONE);
3173         RegisterNamespace("SORT:NEXT", 1, 2, tmplput_SORT_NEXT, NULL, CTX_NONE);
3174         RegisterNamespace("CONTEXTSTR", 0, 1, tmplput_ContextString, NULL, CTX_STRBUF);
3175         RegisterNamespace("CONTEXTSTRARR", 1, 2, tmplput_ContextStringArray, NULL, CTX_STRBUFARR);
3176         RegisterNamespace("ITERATE", 2, 100, tmpl_iterate_subtmpl, preeval_iterate, CTX_NONE);
3177         RegisterNamespace("DOBOXED", 1, 2, tmpl_do_boxed, NULL, CTX_NONE);
3178         RegisterNamespace("DOTABBED", 2, 100, tmpl_do_tabbed, preeval_do_tabbed, CTX_NONE);
3179         RegisterControlNS(HKEY("TAB:N"), 0, 0, tmplput_TAB_N, CTX_TAB);
3180         RegisterControlNS(HKEY("TAB:SUBJECT"), 0, 1, tmplput_TAB_TITLE, CTX_TAB);
3181
3182
3183         RegisterNamespace("LONGVECTOR", 1, 1, tmplput_long_vector, NULL, CTX_LONGVECTOR);
3184
3185
3186         RegisterConditional(HKEY("COND:SUBST"), 3, ConditionalVar, CTX_NONE);
3187         RegisterConditional(HKEY("COND:CONTEXTSTR"), 3, ConditionalContextStr, CTX_STRBUF);
3188         RegisterConditional(HKEY("COND:CONTEXTSTRARR"), 4, ConditionalContextStrinArray, CTX_STRBUFARR);
3189         RegisterConditional(HKEY("COND:LONGVECTOR"), 4, ConditionalLongVector, CTX_LONGVECTOR);
3190
3191
3192         RegisterControlConditional(HKEY("COND:ITERATE:ISGROUPCHANGE"), 2, 
3193                                    conditional_ITERATE_ISGROUPCHANGE, 
3194                                    CTX_ITERATE);
3195         RegisterControlConditional(HKEY("COND:ITERATE:LASTN"), 2, 
3196                                    conditional_ITERATE_LASTN, 
3197                                    CTX_ITERATE);
3198         RegisterControlConditional(HKEY("COND:ITERATE:FIRSTN"), 2, 
3199                                    conditional_ITERATE_FIRSTN, 
3200                                    CTX_ITERATE);
3201
3202         RegisterControlNS(HKEY("ITERATE:ODDEVEN"), 0, 0, tmplput_ITERATE_ODDEVEN, CTX_ITERATE);
3203         RegisterControlNS(HKEY("ITERATE:KEY"), 0, 0, tmplput_ITERATE_KEY, CTX_ITERATE);
3204         RegisterControlNS(HKEY("ITERATE:N"), 0, 0, tmplput_ITERATE_LASTN, CTX_ITERATE);
3205         RegisterNamespace("CURRENTFILE", 0, 1, tmplput_CURRENT_FILE, NULL, CTX_NONE);
3206         RegisterNamespace("DEF:STR", 1, 1, tmplput_DefStr, NULL, CTX_NONE);
3207         RegisterNamespace("DEF:VAL", 1, 1, tmplput_DefVal, NULL, CTX_NONE);
3208
3209
3210
3211
3212 }
3213
3214 void
3215 ServerStartModule_SUBST
3216 (void)
3217 {
3218         WirelessTemplateCache = NewHash(1, NULL);
3219         WirelessLocalTemplateCache = NewHash(1, NULL);
3220         LocalTemplateCache = NewHash(1, NULL);
3221         TemplateCache = NewHash(1, NULL);
3222
3223         GlobalNS = NewHash(1, NULL);
3224         Iterators = NewHash(1, NULL);
3225         Conditionals = NewHash(1, NULL);
3226         SortHash = NewHash(1, NULL);
3227         Defines = NewHash(1, NULL);
3228 }
3229
3230 void
3231 FinalizeModule_SUBST
3232 (void)
3233 {
3234
3235 }
3236
3237 void 
3238 ServerShutdownModule_SUBST
3239 (void)
3240 {
3241         DeleteHash(&WirelessTemplateCache);
3242         DeleteHash(&WirelessLocalTemplateCache);
3243         DeleteHash(&TemplateCache);
3244         DeleteHash(&LocalTemplateCache);
3245
3246         DeleteHash(&GlobalNS);
3247         DeleteHash(&Iterators);
3248         DeleteHash(&Conditionals);
3249         DeleteHash(&SortHash);
3250         DeleteHash(&Defines);
3251 }
3252
3253
3254 void
3255 SessionNewModule_SUBST
3256 (wcsession *sess)
3257 {
3258
3259 }
3260
3261 void
3262 SessionAttachModule_SUBST
3263 (wcsession *sess)
3264 {
3265         sess->vars = NewHash(1,NULL);
3266 }
3267
3268 void
3269 SessionDetachModule_SUBST
3270 (wcsession *sess)
3271 {
3272         DeleteHash(&sess->vars);
3273         FreeStrBuf(&sess->WFBuf);
3274 }
3275
3276 void 
3277 SessionDestroyModule_SUBST  
3278 (wcsession *sess)
3279 {
3280
3281 }