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