* scan for XML files while reading a template; xml shares the same notation as our...
[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                             !((pch == pS) && /* we must ommit a <?xml */
1510                               (*(pch + 2) == 'x') && 
1511                               (*(pch + 3) == 'm') && 
1512                               (*(pch + 4) == 'l')))                          
1513                                 break;
1514                         if (*pch=='\n') Line ++;
1515                 }
1516                 if (pch >= pE)
1517                         continue;
1518                 pts = pch;
1519
1520                 /** Found one? parse it. */
1521                 for (; pch <= pE - 1; pch ++) {
1522                         if (*pch == '"')
1523                                 InDoubleQuotes = ! InDoubleQuotes;
1524                         else if (*pch == '\'')
1525                                 InQuotes = ! InQuotes;
1526                         else if ((!InQuotes  && !InDoubleQuotes) &&
1527                                  ((*pch!='\\')&&(*(pch + 1)=='>'))) {
1528                                 pch ++;
1529                                 break;
1530                         }
1531                 }
1532                 if (pch + 1 > pE)
1533                         continue;
1534                 pte = pch;
1535                 PutNewToken(NewTemplate, 
1536                             NewTemplateSubstitute(NewTemplate->Data, pS, pts, pte, Line, NewTemplate));
1537                 pch ++;
1538         }
1539         if (LoadTemplates == 0)
1540                 Put(PutThere, ChrPtr(Key), StrLength(Key), NewTemplate, FreeWCTemplate);
1541         return NewTemplate;
1542 }
1543
1544
1545 const char* PrintTemplate(void *vSubst)
1546 {
1547         WCTemplate *Tmpl = vSubst;
1548
1549         return ChrPtr(Tmpl->FileName);
1550
1551 }
1552
1553 int LoadTemplateDir(const char *DirName, HashList *wireless, HashList *big)
1554 {
1555         StrBuf *FileName;
1556         StrBuf *Tag;
1557         StrBuf *Dir;
1558         DIR *filedir = NULL;
1559         struct dirent *filedir_entry;
1560         int d_namelen;
1561         int d_without_ext;
1562         int IsMobile;
1563         
1564         Dir = NewStrBuf();
1565         StrBufPrintf(Dir, "%s/t", DirName);
1566         filedir = opendir (ChrPtr(Dir));
1567         if (filedir == NULL) {
1568                 FreeStrBuf(&Dir);
1569                 return 0;
1570         }
1571
1572         FileName = NewStrBuf();
1573         Tag = NewStrBuf();
1574         while ((filedir_entry = readdir(filedir)))
1575         {
1576                 char *MinorPtr;
1577                 char *PStart;
1578 #ifdef _DIRENT_HAVE_D_NAMELEN
1579                 d_namelen = filedir_entry->d_namelen;
1580 #else
1581                 d_namelen = strlen(filedir_entry->d_name);
1582 #endif
1583                 d_without_ext = d_namelen;
1584                 while ((d_without_ext > 0) && (filedir_entry->d_name[d_without_ext] != '.'))
1585                         d_without_ext --;
1586                 if ((d_without_ext == 0) || (d_namelen < 3))
1587                         continue;
1588                 if ((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~')
1589                         continue; /* Ignore backup files... */
1590
1591                 IsMobile = (strstr(filedir_entry->d_name, ".m.html")!= NULL);
1592                 PStart = filedir_entry->d_name;
1593                 StrBufPrintf(FileName, "%s/%s", ChrPtr(Dir),  filedir_entry->d_name);
1594                 MinorPtr = strchr(filedir_entry->d_name, '.');
1595                 if (MinorPtr != NULL)
1596                         *MinorPtr = '\0';
1597                 StrBufPlain(Tag, filedir_entry->d_name, MinorPtr - filedir_entry->d_name);
1598
1599                 if (LoadTemplates > 1)
1600                         lprintf(1, "%s %d %s\n",ChrPtr(FileName), IsMobile, ChrPtr(Tag));
1601                 if (LoadTemplates == 0)
1602                         load_template(FileName, Tag, (IsMobile)?wireless:big);
1603                 else
1604                         prepare_template(FileName, Tag, (IsMobile)?wireless:big);
1605         }
1606         closedir(filedir);
1607         FreeStrBuf(&FileName);
1608         FreeStrBuf(&Tag);
1609         FreeStrBuf(&Dir);
1610         return 1;
1611 }
1612
1613 void InitTemplateCache(void)
1614 {
1615         LoadTemplateDir(static_dirs[0],
1616                         WirelessTemplateCache,
1617                         TemplateCache);
1618         LoadTemplateDir(static_dirs[1],
1619                         WirelessLocalTemplateCache,
1620                         LocalTemplateCache);
1621 }
1622
1623
1624
1625 /*-----------------------------------------------------------------------------
1626  *                      Filling & processing Templates
1627  */
1628 /**
1629  * \brief executes one token
1630  * \param Target buffer to append to
1631  * \param Token da to  process.
1632  * \param Template we're iterating
1633  * \param Context Contextpoointer to pass in
1634  * \param state are we in conditional state?
1635  * \param ContextType what type of information does context giv us?
1636  */
1637 int EvaluateToken(StrBuf *Target, int state, WCTemplputParams *TP)
1638 {
1639         const char *AppendMe;
1640         long AppendMeLen;
1641         HashHandler *Handler;
1642         void *vVar;
1643         
1644 /* much output, since pName is not terminated...
1645         lprintf(1,"Doing token: %s\n",Token->pName);
1646 */
1647
1648         switch (TP->Tokens->Flags) {
1649         case SV_GETTEXT:
1650                 TmplGettext(Target, TP);
1651                 break;
1652         case SV_CONDITIONAL: /** Forward conditional evaluation */
1653                 return EvaluateConditional(Target, 1, state, TP);
1654                 break;
1655         case SV_NEG_CONDITIONAL: /** Reverse conditional evaluation */
1656                 return EvaluateConditional(Target, 0, state, TP);
1657                 break;
1658         case SV_CUST_STR_CONDITIONAL: /** Conditional put custom strings from params */
1659                 if (TP->Tokens->nParameters >= 6) {
1660                         if (EvaluateConditional(Target, 0, state, TP)) {
1661                                 GetTemplateTokenString(Target, TP, 5, &AppendMe, &AppendMeLen);
1662                                 StrBufAppendBufPlain(Target, 
1663                                                      AppendMe, 
1664                                                      AppendMeLen,
1665                                                      0);
1666                         }
1667                         else{
1668                                 GetTemplateTokenString(Target, TP, 4, &AppendMe, &AppendMeLen);
1669                                 StrBufAppendBufPlain(Target, 
1670                                                      AppendMe, 
1671                                                      AppendMeLen,
1672                                                      0);
1673                         }
1674                 }
1675                 else  {
1676                         LogTemplateError(
1677                                 Target, "Conditional", ERR_NAME, TP,
1678                                 "needs at least 6 Params!"); 
1679                 }
1680                 break;
1681         case SV_SUBTEMPL:
1682                 if (TP->Tokens->nParameters == 1)
1683                         DoTemplate(TKEY(0), Target, TP);
1684                 break;
1685         case SV_PREEVALUATED:
1686                 Handler = (HashHandler*) TP->Tokens->PreEval;
1687                 if (!CheckContext(Target, &Handler->Filter, TP, "Token")) {
1688                         return -1;
1689                 }
1690                 Handler->HandlerFunc(Target, TP);
1691                 break;          
1692         default:
1693                 if (GetHash(GlobalNS, TP->Tokens->pName, TP->Tokens->NameEnd, &vVar)) {
1694                         Handler = (HashHandler*) vVar;
1695                         if (!CheckContext(Target, &Handler->Filter, TP, "Token")) {
1696                                 return -1;
1697                         }
1698                         else {
1699                                 Handler->HandlerFunc(Target, TP);
1700                         }
1701                 }
1702                 else {
1703                         print_value_of(Target, TP);
1704                 }
1705         }
1706         return 0;
1707 }
1708
1709
1710
1711 const StrBuf *ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target, WCTemplputParams *CallingTP)
1712 {
1713         WCTemplate *pTmpl = Tmpl;
1714         int done = 0;
1715         int i, state;
1716         const char *pData, *pS;
1717         long len;
1718         WCTemplputParams TP;
1719
1720         memcpy(&TP.Filter, &CallingTP->Filter, sizeof(ContextFilter));
1721
1722         TP.Context = CallingTP->Context;
1723         TP.ControlContext = CallingTP->ControlContext;
1724
1725         if (LoadTemplates != 0) {                       
1726                 if (LoadTemplates > 1)
1727                         lprintf(1, "DBG: ----- loading:  [%s] ------ \n", 
1728                                 ChrPtr(Tmpl->FileName));
1729
1730                 pTmpl = load_template(Tmpl->FileName, NULL, NULL);
1731                 if(pTmpl == NULL) {
1732                         StrBufAppendPrintf(
1733                                 Target, 
1734                                 "<pre>\nError loading Template [%s]\n See Logfile for details\n</pre>\n", 
1735                                 ChrPtr(Tmpl->FileName));
1736                         return NULL;
1737                 }
1738
1739         }
1740
1741         pS = pData = ChrPtr(pTmpl->Data);
1742         len = StrLength(pTmpl->Data);
1743         i = 0;
1744         state = 0;
1745         while (!done) {
1746                 if (i >= pTmpl->nTokensUsed) {
1747                         StrBufAppendBufPlain(Target, 
1748                                              pData, 
1749                                              len - (pData - pS), 0);
1750                         done = 1;
1751                 }
1752                 else {
1753                         StrBufAppendBufPlain(
1754                                 Target, pData, 
1755                                 pTmpl->Tokens[i]->pTokenStart - pData, 0);
1756                         TP.Tokens = pTmpl->Tokens[i];
1757                         TP.nArgs = pTmpl->Tokens[i]->nParameters;
1758                         state = EvaluateToken(Target, state, &TP);
1759
1760                         while ((state != 0) && (i+1 < pTmpl->nTokensUsed)) {
1761                         /* condition told us to skip till its end condition */
1762                                 i++;
1763                                 TP.Tokens = pTmpl->Tokens[i];
1764                                 TP.nArgs = pTmpl->Tokens[i]->nParameters;
1765                                 if ((pTmpl->Tokens[i]->Flags == SV_CONDITIONAL) ||
1766                                     (pTmpl->Tokens[i]->Flags == SV_NEG_CONDITIONAL)) {
1767                                         if (state == EvaluateConditional(
1768                                                     Target, 
1769                                                     pTmpl->Tokens[i]->Flags, 
1770                                                     state, 
1771                                                     &TP))
1772                                                 state = 0;
1773                                 }
1774                         }
1775                         pData = pTmpl->Tokens[i++]->pTokenEnd + 1;
1776                         if (i > pTmpl->nTokensUsed)
1777                                 done = 1;
1778                 }
1779         }
1780         if (LoadTemplates != 0) {
1781                 FreeWCTemplate(pTmpl);
1782         }
1783         return Tmpl->MimeType;
1784
1785 }
1786
1787 /**
1788  * \brief Display a variable-substituted template
1789  * \param templatename template file to load
1790  * \returns the mimetype of the template its doing
1791  */
1792 const StrBuf *DoTemplate(const char *templatename, long len, StrBuf *Target, WCTemplputParams *TP) 
1793 {
1794         WCTemplputParams LocalTP;
1795         HashList *Static;
1796         HashList *StaticLocal;
1797         void *vTmpl;
1798         
1799         if (Target == NULL)
1800                 Target = WC->WBuf;
1801         if (TP == NULL) {
1802                 memset(&LocalTP, 0, sizeof(WCTemplputParams));
1803                 TP = &LocalTP;
1804         }
1805
1806         if (WC->is_mobile) {
1807                 Static = WirelessTemplateCache;
1808                 StaticLocal = WirelessLocalTemplateCache;
1809         }
1810         else {
1811                 Static = TemplateCache;
1812                 StaticLocal = LocalTemplateCache;
1813         }
1814
1815         if (len == 0)
1816         {
1817                 lprintf (1, "Can't to load a template with empty name!\n");
1818                 StrBufAppendPrintf(Target, "<pre>\nCan't to load a template with empty name!\n</pre>");
1819                 return NULL;
1820         }
1821
1822         if (!GetHash(StaticLocal, templatename, len, &vTmpl) &&
1823             !GetHash(Static, templatename, len, &vTmpl)) {
1824                 lprintf (1, "didn't find Template [%s] %ld %ld\n", templatename, len , (long)strlen(templatename));
1825                 StrBufAppendPrintf(Target, "<pre>\ndidn't find Template [%s] %ld %ld\n</pre>", 
1826                                    templatename, len, 
1827                                    (long)strlen(templatename));
1828 #if 0
1829                 dbg_PrintHash(Static, PrintTemplate, NULL);
1830                 PrintHash(Static, VarPrintTransition, PrintTemplate);
1831 #endif
1832                 return NULL;
1833         }
1834         if (vTmpl == NULL) 
1835                 return NULL;
1836         return ProcessTemplate(vTmpl, Target, TP);
1837
1838 }
1839
1840 /*-----------------------------------------------------------------------------
1841  *                      Iterators
1842  */
1843 typedef struct _HashIterator {
1844         HashList *StaticList;
1845         int AdditionalParams;
1846         int ContextType;
1847         int XPectContextType;
1848         int Flags;
1849         RetrieveHashlistFunc GetHash;
1850         HashDestructorFunc Destructor;
1851         SubTemplFunc DoSubTemplate;
1852 } HashIterator;
1853
1854 void RegisterITERATOR(const char *Name, long len, 
1855                       int AdditionalParams, 
1856                       HashList *StaticList, 
1857                       RetrieveHashlistFunc GetHash, 
1858                       SubTemplFunc DoSubTempl,
1859                       HashDestructorFunc Destructor,
1860                       int ContextType, 
1861                       int XPectContextType, 
1862                       int Flags)
1863 {
1864         HashIterator *It;
1865
1866         It = (HashIterator*)malloc(sizeof(HashIterator));
1867         memset(It, 0, sizeof(HashIterator));
1868         It->StaticList = StaticList;
1869         It->AdditionalParams = AdditionalParams;
1870         It->GetHash = GetHash;
1871         It->DoSubTemplate = DoSubTempl;
1872         It->Destructor = Destructor;
1873         It->ContextType = ContextType;
1874         It->XPectContextType = XPectContextType;
1875         It->Flags = Flags;
1876         Put(Iterators, Name, len, It, NULL);
1877 }
1878
1879 typedef struct _iteratestruct {
1880         int GroupChange;
1881         int oddeven;
1882         const char *Key;
1883         long KeyLen;
1884         int n;
1885         int LastN;
1886         }IterateStruct; 
1887
1888 void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
1889 {
1890         void *vIt;
1891         HashIterator *It;
1892         HashList *List;
1893         HashPos  *it;
1894         SortStruct *SortBy = NULL;
1895         void *vSortBy;
1896         int DetectGroupChange = 0;
1897         int nMembersUsed;
1898         void *vContext;
1899         void *vLastContext = NULL;
1900         StrBuf *SubBuf;
1901         WCTemplputParams SubTP;
1902         IterateStruct Status;
1903
1904         long StartAt = 0;
1905         long StepWidth = 0;
1906         long StopAt = -1;
1907
1908         memset(&Status, 0, sizeof(IterateStruct));
1909         memcpy (&SubTP, &TP, sizeof(WCTemplputParams));
1910         
1911         if (!GetHash(Iterators, TKEY(0), &vIt)) {
1912                 LogTemplateError(
1913                         Target, "Iterator", ERR_PARM1, TP, "Unknown!");
1914                 return;
1915         }
1916
1917         It = (HashIterator*) vIt;
1918
1919         if (TP->Tokens->nParameters < It->AdditionalParams + 2) {
1920                 LogTemplateError(                               
1921                         Target, "Iterator", ERR_PARM1, TP,
1922                         "doesn't work with %d params", 
1923                         TP->Tokens->nParameters);
1924                 return;
1925         }
1926
1927         if ((It->XPectContextType != CTX_NONE) &&
1928             (It->XPectContextType != TP->Filter.ContextType)) {
1929                 LogTemplateError(
1930                         Target, "Iterator", ERR_PARM1, TP,
1931                         "requires context of type %d, have %d", 
1932                         It->XPectContextType, 
1933                         TP->Filter.ContextType);
1934                 return ;
1935                 
1936         }
1937
1938         if (It->StaticList == NULL)
1939                 List = It->GetHash(Target, TP);
1940         else
1941                 List = It->StaticList;
1942
1943         DetectGroupChange = (It->Flags & IT_FLAG_DETECT_GROUPCHANGE) != 0;
1944         if (DetectGroupChange) {
1945                 const StrBuf *BSort;
1946                 DetectGroupChange = 0;
1947                 if (havebstr("SortBy")) {
1948                         BSort = sbstr("SortBy");
1949                         if (GetHash(SortHash, SKEY(BSort), &vSortBy) &&
1950                             (vSortBy != NULL)) {
1951                                 SortBy = (SortStruct*)vSortBy;
1952                                 /** Ok, its us, lets see in which direction we should sort... */
1953                                 if (havebstr("SortOrder")) {
1954                                         int SortOrder;
1955                                         SortOrder = LBSTR("SortOrder");
1956                                         if (SortOrder != 0)
1957                                                 DetectGroupChange = 1;
1958                                 }
1959                         }
1960                 }
1961         }
1962         nMembersUsed = GetCount(List);
1963         SubBuf = NewStrBuf();
1964         SubTP.Filter.ContextType = It->ContextType;
1965         SubTP.Filter.ControlContextType = CTX_ITERATE;
1966         SubTP.ControlContext = &Status;
1967         
1968         if (HAVE_PARAM(2)) {
1969                 StartAt = GetTemplateTokenNumber(Target, TP, 2, 0);
1970         }
1971         if (HAVE_PARAM(3)) {
1972                 StepWidth = GetTemplateTokenNumber(Target, TP, 3, 0);
1973         }
1974         if (HAVE_PARAM(4)) {
1975                 StopAt = GetTemplateTokenNumber(Target, TP, 4, -1);
1976         }
1977         it = GetNewHashPos(List, StepWidth);
1978         if (StopAt < 0) {
1979                 StopAt = GetCount(List);
1980         }
1981         while (GetNextHashPos(List, it, &Status.KeyLen, &Status.Key, &vContext)) {
1982                 if ((Status.n >= StartAt) && (Status.n <= StopAt)) {
1983                         if (DetectGroupChange && Status.n > 0) {
1984                                 Status.GroupChange = SortBy->GroupChange(vContext, vLastContext);
1985                         }
1986                         Status.LastN = (Status.n + 1) == nMembersUsed;
1987                         SubTP.Context = vContext;
1988                         if (It->DoSubTemplate != NULL)
1989                                 It->DoSubTemplate(SubBuf, &SubTP);
1990                         DoTemplate(TKEY(1), SubBuf, &SubTP);
1991                         
1992                         StrBufAppendBuf(Target, SubBuf, 0);
1993                         FlushStrBuf(SubBuf);
1994                         Status.oddeven = ! Status.oddeven;
1995                         vLastContext = vContext;
1996                 }
1997                 Status.n++;
1998         }
1999         FreeStrBuf(&SubBuf);
2000         DeleteHashPos(&it);
2001         if (It->Destructor != NULL)
2002                 It->Destructor(&List);
2003 }
2004
2005
2006 int conditional_ITERATE_ISGROUPCHANGE(StrBuf *Target, WCTemplputParams *TP)
2007 {
2008         IterateStruct *Ctx = CCTX;
2009         if (TP->Tokens->nParameters < 3)
2010                 return  Ctx->GroupChange;
2011
2012         return TP->Tokens->Params[2]->lvalue == Ctx->GroupChange;
2013 }
2014
2015 void tmplput_ITERATE_ODDEVEN(StrBuf *Target, WCTemplputParams *TP)
2016 {
2017         IterateStruct *Ctx = CCTX;
2018         if (Ctx->oddeven)
2019                 StrBufAppendBufPlain(Target, HKEY("odd"), 0);
2020         else
2021                 StrBufAppendBufPlain(Target, HKEY("even"), 0);
2022 }
2023
2024
2025 void tmplput_ITERATE_KEY(StrBuf *Target, WCTemplputParams *TP)
2026 {
2027         IterateStruct *Ctx = CCTX;
2028
2029         StrBufAppendBufPlain(Target, Ctx->Key, Ctx->KeyLen, 0);
2030 }
2031
2032
2033 void tmplput_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
2034 {
2035         IterateStruct *Ctx = CCTX;
2036         StrBufAppendPrintf(Target, "%d", Ctx->n);
2037 }
2038
2039 int conditional_ITERATE_FIRSTN(StrBuf *Target, WCTemplputParams *TP)
2040 {
2041         IterateStruct *Ctx = CCTX;
2042         return Ctx->n == 0;
2043 }
2044
2045 int conditional_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
2046 {
2047         IterateStruct *Ctx = CCTX;
2048         return Ctx->LastN;
2049 }
2050
2051
2052
2053 /*-----------------------------------------------------------------------------
2054  *                      Conditionals
2055  */
2056 int EvaluateConditional(StrBuf *Target, int Neg, int state, WCTemplputParams *TP)
2057 {
2058         ConditionalStruct *Cond;
2059
2060         if ((TP->Tokens->Params[0]->len == 1) &&
2061             (TP->Tokens->Params[0]->Start[0] == 'X'))
2062                 return (state != 0)?TP->Tokens->Params[1]->lvalue:0;
2063             
2064         Cond = (ConditionalStruct *) TP->Tokens->PreEval;
2065         if (Cond == NULL) {
2066                 LogTemplateError(
2067                         Target, "Conditional", ERR_PARM1, TP,
2068                         "unknown!");
2069                 return 1;
2070         }
2071
2072         if (!CheckContext(Target, &Cond->Filter, TP, "Conditional")) {
2073                 return 0;
2074         }
2075
2076         if (Cond->CondF(Target, TP) == Neg)
2077                 return TP->Tokens->Params[1]->lvalue;
2078         return 0;
2079 }
2080
2081 int ConditionalVar(StrBuf *Target, WCTemplputParams *TP)
2082 {
2083         void *vsubst;
2084         wcsubst *subst;
2085         
2086         if (!GetHash(WC->vars, TKEY(2), &vsubst))
2087                 return 0;
2088         subst = (wcsubst*) vsubst;
2089         
2090         switch(subst->wcs_type) {
2091         case WCS_FUNCTION:
2092                 return (subst->wcs_function!=NULL);
2093         case WCS_SERVCMD:
2094                 lprintf(1, "  -> Server [%s]\n", subst->wcs_value);/* TODO */
2095                 return 1;
2096         case WCS_STRING:
2097         case WCS_STRBUF:
2098         case WCS_STRBUF_REF:
2099                 if (TP->Tokens->nParameters < 4)
2100                         return 1;
2101                 return (strcmp(TP->Tokens->Params[3]->Start, ChrPtr(subst->wcs_value)) == 0);
2102         case WCS_LONG:
2103                 if (TP->Tokens->nParameters < 4)
2104                         return (subst->lvalue != 0);
2105                 return (subst->lvalue == TP->Tokens->Params[3]->lvalue);
2106         default:
2107                 lprintf(1,"  WARNING: invalid type: [%ld]!\n", subst->wcs_type);
2108                 return -1;
2109         }
2110         return 0;
2111 }
2112
2113 void RegisterConditional(const char *Name, long len, 
2114                          int nParams,
2115                          WCConditionalFunc CondF, 
2116                          int ContextRequired)
2117 {
2118         ConditionalStruct *Cond;
2119
2120         Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2121         memset(Cond, 0, sizeof(ConditionalStruct));
2122         Cond->PlainName = Name;
2123         Cond->Filter.nMaxArgs = nParams;
2124         Cond->Filter.nMinArgs = nParams;
2125         Cond->CondF = CondF;
2126         Cond->Filter.ContextType = ContextRequired;
2127         Cond->Filter.ControlContextType = CTX_NONE;
2128         Put(Conditionals, Name, len, Cond, NULL);
2129 }
2130
2131 void RegisterControlConditional(const char *Name, long len, 
2132                                 int nParams,
2133                                 WCConditionalFunc CondF, 
2134                                 int ControlContextRequired)
2135 {
2136         ConditionalStruct *Cond;
2137
2138         Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2139         memset(Cond, 0, sizeof(ConditionalStruct));
2140         Cond->PlainName = Name;
2141         Cond->Filter.nMaxArgs = nParams;
2142         Cond->Filter.nMinArgs = nParams;
2143         Cond->CondF = CondF;
2144         Cond->Filter.ContextType = CTX_NONE;
2145         Cond->Filter.ControlContextType = ControlContextRequired;
2146         Put(Conditionals, Name, len, Cond, NULL);
2147 }
2148
2149 /*-----------------------------------------------------------------------------
2150  *                      Context Strings
2151  */
2152 void tmplput_ContextString(StrBuf *Target, WCTemplputParams *TP)
2153 {
2154         StrBufAppendTemplate(Target, TP, (StrBuf*)CTX, 0);
2155 }
2156 int ConditionalContextStr(StrBuf *Target, WCTemplputParams *TP)
2157 {
2158         StrBuf *TokenText = (StrBuf*) CTX;
2159         const char *CompareToken;
2160         long len;
2161
2162         GetTemplateTokenString(Target, TP, 2, &CompareToken, &len);
2163         return strcmp(ChrPtr(TokenText), CompareToken) == 0;
2164 }
2165
2166 /*-----------------------------------------------------------------------------
2167  *                      Boxed-API
2168  */
2169
2170 void tmpl_do_boxed(StrBuf *Target, WCTemplputParams *TP)
2171 {
2172         WCTemplputParams SubTP;
2173
2174         StrBuf *Headline;
2175         if (TP->Tokens->nParameters == 2) {
2176                 if (TP->Tokens->Params[1]->Type == TYPE_STR) {
2177                         Headline = NewStrBuf();
2178                         DoTemplate(TKEY(1), Headline, TP);
2179                 }
2180                 else {
2181                         const char *Ch;
2182                         long len;
2183                         GetTemplateTokenString(Target, 
2184                                                TP, 
2185                                                1,
2186                                                &Ch,
2187                                                &len);
2188                         Headline = NewStrBufPlain(Ch, len);
2189                 }
2190         }
2191         memcpy (&SubTP, TP, sizeof(WCTemplputParams));
2192         SubTP.Context = Headline;
2193         SubTP.Filter.ContextType = CTX_STRBUF;
2194         DoTemplate(HKEY("beginbox"), Target, &SubTP);
2195         DoTemplate(TKEY(0), Target, TP);
2196         DoTemplate(HKEY("endbox"), Target, TP);
2197         FreeStrBuf(&Headline);
2198 }
2199
2200 /*-----------------------------------------------------------------------------
2201  *                      Tabbed-API
2202  */
2203
2204 void tmpl_do_tabbed(StrBuf *Target, WCTemplputParams *TP)
2205 {
2206         StrBuf **TabNames;
2207         int i, ntabs, nTabs;
2208
2209         nTabs = ntabs = TP->Tokens->nParameters / 2;
2210         TabNames = (StrBuf **) malloc(ntabs * sizeof(StrBuf*));
2211         memset(TabNames, 0, ntabs * sizeof(StrBuf*));
2212
2213         for (i = 0; i < ntabs; i++) {
2214                 if ((TP->Tokens->Params[i * 2]->Type == TYPE_STR) &&
2215                     (TP->Tokens->Params[i * 2]->len > 0)) {
2216                         TabNames[i] = NewStrBuf();
2217                         DoTemplate(TKEY(i * 2), TabNames[i], TP);
2218                 }
2219                 else if (TP->Tokens->Params[i * 2]->Type == TYPE_GETTEXT) {
2220                         const char *Ch;
2221                         long len;
2222                         GetTemplateTokenString(Target, 
2223                                                TP, 
2224                                                i * 2,
2225                                                &Ch,
2226                                                &len);
2227                         TabNames[i] = NewStrBufPlain(Ch, -1);
2228                 }
2229                 else { 
2230                         /** A Tab without subject? we can't count that, add it as silent */
2231                         nTabs --;
2232                 }
2233         }
2234
2235         StrTabbedDialog(Target, nTabs, TabNames);
2236         for (i = 0; i < ntabs; i++) {
2237                 StrBeginTab(Target, i, nTabs);
2238                 DoTemplate(TKEY(i * 2 + 1), Target, TP);
2239                 StrEndTab(Target, i, nTabs);
2240         }
2241 }
2242
2243
2244 /*-----------------------------------------------------------------------------
2245  *                      Sorting-API
2246  */
2247
2248
2249 void RegisterSortFunc(const char *name, long len, 
2250                       const char *prepend, long preplen,
2251                       CompareFunc Forward, 
2252                       CompareFunc Reverse, 
2253                       CompareFunc GroupChange, 
2254                       long ContextType)
2255 {
2256         SortStruct *NewSort;
2257
2258         NewSort = (SortStruct*) malloc(sizeof(SortStruct));
2259         memset(NewSort, 0, sizeof(SortStruct));
2260         NewSort->Name = NewStrBufPlain(name, len);
2261         if (prepend != NULL)
2262                 NewSort->PrefPrepend = NewStrBufPlain(prepend, preplen);
2263         else
2264                 NewSort->PrefPrepend = NULL;
2265         NewSort->Forward = Forward;
2266         NewSort->Reverse = Reverse;
2267         NewSort->GroupChange = GroupChange;
2268         NewSort->ContextType = ContextType;
2269         Put(SortHash, name, len, NewSort, DestroySortStruct);
2270 }
2271
2272 CompareFunc RetrieveSort(WCTemplputParams *TP, 
2273                          const char *OtherPrefix, long OtherPrefixLen,
2274                          const char *Default, long ldefault, long DefaultDirection)
2275 {
2276         int isdefault = 0;
2277         const StrBuf *BSort = NULL;
2278         SortStruct *SortBy;
2279         void *vSortBy;
2280         long SortOrder = -1;
2281         
2282         if (havebstr("SortBy")) {
2283                 BSort = sbstr("SortBy");
2284                 if (OtherPrefix == NULL) {
2285                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2286                 }
2287                 else {
2288                         set_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen, NewStrBufDup(BSort), 0);
2289                 }
2290         }
2291         else { /** Try to fallback to our remembered values... */
2292                 if (OtherPrefix == NULL) {
2293                         BSort = get_room_pref("sort");
2294                 }
2295                 else {
2296                         BSort = get_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen);
2297                 }
2298                 if (BSort != NULL)
2299                         putbstr("SortBy", NewStrBufDup(BSort));
2300                 else {
2301                         StrBuf *Buf;
2302
2303                         BSort = Buf = NewStrBufPlain(Default, ldefault);
2304                         putbstr("SortBy", Buf);
2305                 }
2306         }
2307
2308         if (!GetHash(SortHash, SKEY(BSort), &vSortBy) || 
2309             (vSortBy == NULL)) {
2310                 isdefault = 1;
2311                 if (!GetHash(SortHash, Default, ldefault, &vSortBy) || 
2312                     (vSortBy == NULL)) {
2313                         LogTemplateError(
2314                                 NULL, "Sorting", ERR_PARM1, TP,
2315                                 "Illegal default sort: [%s]", Default);
2316                         wc_backtrace();
2317                 }
2318         }
2319         SortBy = (SortStruct*)vSortBy;
2320
2321         /** Ok, its us, lets see in which direction we should sort... */
2322         if (havebstr("SortOrder")) {
2323                 SortOrder = LBSTR("SortOrder");
2324         }
2325         else { /** Try to fallback to our remembered values... */
2326                 StrBuf *Buf = NULL;
2327                 if (SortBy->PrefPrepend == NULL) {
2328                         Buf = get_room_pref("SortOrder");
2329                         SortOrder = StrTol(Buf);
2330                 }
2331                 else {
2332                         BSort = get_X_PREFS(HKEY("SortOrder"), OtherPrefix, OtherPrefixLen);
2333                 }
2334
2335                 if (Buf == NULL)
2336                         SortOrder = DefaultDirection;
2337
2338                 Buf = NewStrBufPlain(NULL, 64);
2339                 StrBufPrintf(Buf, "%ld", SortOrder);
2340                 putbstr("SortOrder", Buf);
2341         }
2342         switch (SortOrder) {
2343         default:
2344         case 0:
2345                 return NULL;
2346         case 1:
2347                 return SortBy->Forward;
2348         case 2:
2349                 return SortBy->Reverse;
2350         }
2351 }
2352
2353
2354 enum {
2355         eNO_SUCH_SORT, 
2356         eNOT_SPECIFIED,
2357         eINVALID_PARAM,
2358         eFOUND
2359 };
2360
2361 ConstStr SortIcons[] = {
2362         {HKEY("static/sort_none.gif")},
2363         {HKEY("static/up_pointer.gif")},
2364         {HKEY("static/down_pointer.gif")},
2365 };
2366
2367 ConstStr SortNextOrder[] = {
2368         {HKEY("1")},
2369         {HKEY("2")},
2370         {HKEY("0")},
2371 };
2372
2373
2374 int GetSortMetric(WCTemplputParams *TP, SortStruct **Next, SortStruct **Param, long *SortOrder, int N)
2375 {
2376         int bSortError = eNOT_SPECIFIED;
2377         const StrBuf *BSort;
2378         void *vSort;
2379         
2380         *SortOrder = 0;
2381         *Next = NULL;
2382         if (!GetHash(SortHash, TKEY(0), &vSort) || 
2383             (vSort == NULL))
2384                 return eNO_SUCH_SORT;
2385         *Param = (SortStruct*) vSort;
2386         
2387
2388         if (havebstr("SortBy")) {
2389                 BSort = sbstr("SortBy");
2390                 bSortError = eINVALID_PARAM;
2391                 if ((*Param)->PrefPrepend == NULL) {
2392                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2393                 }
2394                 else {
2395                         set_X_PREFS(HKEY("sort"), TKEY(N), NewStrBufDup(BSort), 0);
2396                 }
2397         }
2398         else { /** Try to fallback to our remembered values... */
2399                 if ((*Param)->PrefPrepend == NULL) {
2400                         BSort = get_room_pref("sort");
2401                 }
2402                 else {
2403                         BSort = get_X_PREFS(HKEY("sort"), TKEY(N));
2404                 }
2405         }
2406
2407         if (!GetHash(SortHash, SKEY(BSort), &vSort) || 
2408             (vSort == NULL))
2409                 return bSortError;
2410
2411         *Next = (SortStruct*) vSort;
2412
2413         /** Ok, its us, lets see in which direction we should sort... */
2414         if (havebstr("SortOrder")) {
2415                 *SortOrder = LBSTR("SortOrder");
2416         }
2417         else { /** Try to fallback to our remembered values... */
2418                 if ((*Param)->PrefPrepend == NULL) {
2419                         *SortOrder = StrTol(get_room_pref("SortOrder"));
2420                 }
2421                 else {
2422                         *SortOrder = StrTol(get_X_PREFS(HKEY("SortOrder"), TKEY(N)));
2423                 }
2424         }
2425         if (*SortOrder > 2)
2426                 *SortOrder = 0;
2427
2428         return eFOUND;
2429 }
2430
2431
2432 void tmplput_SORT_ICON(StrBuf *Target, WCTemplputParams *TP)
2433 {
2434         long SortOrder;
2435         SortStruct *Next;
2436         SortStruct *Param;
2437         const ConstStr *SortIcon;
2438
2439         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2440         case eNO_SUCH_SORT:
2441                 LogTemplateError(
2442                         Target, "Sorter", ERR_PARM1, TP,
2443                         " Sorter [%s] unknown!", 
2444                         TP->Tokens->Params[0]->Start);
2445                 break;          
2446         case eINVALID_PARAM:
2447                 LogTemplateError(NULL, "Sorter", ERR_PARM1, TP,
2448                                  " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
2449                                  bstr("SortBy"));
2450         case eNOT_SPECIFIED:
2451         case eFOUND:
2452                 if (Next == Param) {
2453                         SortIcon = &SortIcons[SortOrder];
2454                 }
2455                 else { /** Not Us... */
2456                         SortIcon = &SortIcons[0];
2457                 }
2458                 StrBufAppendBufPlain(Target, SortIcon->Key, SortIcon->len, 0);
2459         }
2460 }
2461
2462 void tmplput_SORT_NEXT(StrBuf *Target, WCTemplputParams *TP)
2463 {
2464         long SortOrder;
2465         SortStruct *Next;
2466         SortStruct *Param;
2467
2468         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2469         case eNO_SUCH_SORT:
2470                 LogTemplateError(
2471                         Target, "Sorter", ERR_PARM1, TP,                                  
2472                         " Sorter [%s] unknown!", 
2473                         TP->Tokens->Params[0]->Start);
2474                 break;          
2475         case eINVALID_PARAM:
2476                 LogTemplateError(
2477                         NULL, "Sorter", ERR_PARM1, TP,
2478                         " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
2479                         bstr("SortBy"));
2480         case eNOT_SPECIFIED:
2481         case eFOUND:
2482                 StrBufAppendBuf(Target, Param->Name, 0);
2483                 
2484         }
2485 }
2486
2487 void tmplput_SORT_ORDER(StrBuf *Target, WCTemplputParams *TP)
2488 {
2489         long SortOrder;
2490         const ConstStr *SortOrderStr;
2491         SortStruct *Next;
2492         SortStruct *Param;
2493
2494         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2495         case eNO_SUCH_SORT:
2496                 LogTemplateError(
2497                         Target, "Sorter", ERR_PARM1, TP,
2498                         " Sorter [%s] unknown!",
2499                         TP->Tokens->Params[0]->Start);
2500                 break;          
2501         case eINVALID_PARAM:
2502                 LogTemplateError(
2503                         NULL, "Sorter", ERR_PARM1, TP,
2504                         " Sorter specified by BSTR 'SortBy' [%s] unknown!",
2505                         bstr("SortBy"));
2506         case eNOT_SPECIFIED:
2507         case eFOUND:
2508                 if (Next == Param) {
2509                         SortOrderStr = &SortNextOrder[SortOrder];
2510                 }
2511                 else { /** Not Us... */
2512                         SortOrderStr = &SortNextOrder[0];
2513                 }
2514                 StrBufAppendBufPlain(Target, SortOrderStr->Key, SortOrderStr->len, 0);
2515         }
2516 }
2517
2518
2519 void tmplput_long_vector(StrBuf *Target, WCTemplputParams *TP)
2520 {
2521         long *LongVector = (long*) CTX;
2522
2523         if ((TP->Tokens->Params[0]->Type == TYPE_LONG) && 
2524             (TP->Tokens->Params[0]->lvalue <= LongVector[0]))
2525         {
2526                 StrBufAppendPrintf(Target, "%ld", LongVector[TP->Tokens->Params[0]->lvalue]);
2527         }
2528         else
2529         {
2530                 if (TP->Tokens->Params[0]->Type != TYPE_LONG) {
2531                         LogTemplateError(
2532                                 Target, "Longvector", ERR_NAME, TP,
2533                                 "needs a numerical Parameter!");
2534                 }
2535                 else {
2536                         LogTemplateError(
2537                                 Target, "LongVector", ERR_PARM1, TP,
2538                                 "doesn't have %ld Parameters, its just the size of %ld!", 
2539                                 TP->Tokens->Params[0]->lvalue,
2540                                 LongVector[0]);
2541                 }
2542         }
2543 }
2544
2545 void dbg_print_longvector(long *LongVector)
2546 {
2547         StrBuf *Buf = NewStrBufPlain(HKEY("Longvector: ["));
2548         int nItems = LongVector[0];
2549         int i;
2550
2551         for (i = 0; i < nItems; i++) {
2552                 if (i + 1 < nItems)
2553                         StrBufAppendPrintf(Buf, "%d: %ld | ", i, LongVector[i]);
2554                 else
2555                         StrBufAppendPrintf(Buf, "%d: %ld]\n", i, LongVector[i]);
2556
2557         }
2558         lprintf(1, ChrPtr(Buf));
2559         FreeStrBuf(&Buf);
2560 }
2561
2562 int ConditionalLongVector(StrBuf *Target, WCTemplputParams *TP)
2563 {
2564         long *LongVector = (long*) CTX;
2565
2566         if ((TP->Tokens->Params[2]->Type == TYPE_LONG) && 
2567             (TP->Tokens->Params[2]->lvalue <= LongVector[0])&&
2568             (TP->Tokens->Params[3]->Type == TYPE_LONG) && 
2569             (TP->Tokens->Params[3]->lvalue <= LongVector[0]))
2570         {
2571                 return LongVector[TP->Tokens->Params[2]->lvalue] == 
2572                         LongVector[TP->Tokens->Params[3]->lvalue];
2573         }
2574         else
2575         {
2576                 if ((TP->Tokens->Params[2]->Type == TYPE_LONG) ||
2577                     (TP->Tokens->Params[2]->Type == TYPE_LONG)) {
2578                         LogTemplateError(
2579                                 Target, "ConditionalLongvector", ERR_PARM1, TP,
2580                                 "needs two long Parameter!");
2581                 }
2582                 else {
2583                         LogTemplateError(
2584                                 Target, "Longvector", ERR_PARM1, TP,
2585                                 "doesn't have %ld / %ld Parameters, its just the size of %ld!",
2586                                 TP->Tokens->Params[2]->lvalue,
2587                                 TP->Tokens->Params[3]->lvalue,
2588                                 LongVector[0]);
2589                 }
2590         }
2591         return 0;
2592 }
2593
2594 void 
2595 InitModule_SUBST
2596 (void)
2597 {
2598         memset(&NoCtx, 0, sizeof(WCTemplputParams));
2599         RegisterNamespace("SORT:ICON", 1, 2, tmplput_SORT_ICON, CTX_NONE);
2600         RegisterNamespace("SORT:ORDER", 1, 2, tmplput_SORT_ORDER, CTX_NONE);
2601         RegisterNamespace("SORT:NEXT", 1, 2, tmplput_SORT_NEXT, CTX_NONE);
2602         RegisterNamespace("CONTEXTSTR", 0, 1, tmplput_ContextString, CTX_STRBUF);
2603         RegisterNamespace("ITERATE", 2, 100, tmpl_iterate_subtmpl, CTX_NONE);
2604         RegisterNamespace("DOBOXED", 1, 2, tmpl_do_boxed, CTX_NONE);
2605         RegisterNamespace("DOTABBED", 2, 100, tmpl_do_tabbed, CTX_NONE);
2606         RegisterNamespace("LONGVECTOR", 1, 1, tmplput_long_vector, CTX_LONGVECTOR);
2607         RegisterConditional(HKEY("COND:SUBST"), 3, ConditionalVar, CTX_NONE);
2608         RegisterConditional(HKEY("COND:CONTEXTSTR"), 3, ConditionalContextStr, CTX_STRBUF);
2609         RegisterConditional(HKEY("COND:LONGVECTOR"), 4, ConditionalLongVector, CTX_LONGVECTOR);
2610
2611         RegisterControlConditional(HKEY("COND:ITERATE:ISGROUPCHANGE"), 2, 
2612                                    conditional_ITERATE_ISGROUPCHANGE, 
2613                                    CTX_ITERATE);
2614         RegisterControlConditional(HKEY("COND:ITERATE:LASTN"), 2, 
2615                                    conditional_ITERATE_LASTN, 
2616                                    CTX_ITERATE);
2617         RegisterControlConditional(HKEY("COND:ITERATE:FIRSTN"), 2, 
2618                                    conditional_ITERATE_FIRSTN, 
2619                                    CTX_ITERATE);
2620
2621         RegisterControlNS(HKEY("ITERATE:ODDEVEN"), 0, 0, tmplput_ITERATE_ODDEVEN, CTX_ITERATE);
2622         RegisterControlNS(HKEY("ITERATE:KEY"), 0, 0, tmplput_ITERATE_KEY, CTX_ITERATE);
2623         RegisterControlNS(HKEY("ITERATE:N"), 0, 0, tmplput_ITERATE_LASTN, CTX_ITERATE);
2624 }
2625
2626 void
2627 ServerStartModule_SUBST
2628 (void)
2629 {
2630         WirelessTemplateCache = NewHash(1, NULL);
2631         WirelessLocalTemplateCache = NewHash(1, NULL);
2632         LocalTemplateCache = NewHash(1, NULL);
2633         TemplateCache = NewHash(1, NULL);
2634
2635         GlobalNS = NewHash(1, NULL);
2636         Iterators = NewHash(1, NULL);
2637         Conditionals = NewHash(1, NULL);
2638         SortHash = NewHash(1, NULL);
2639 }
2640
2641 void
2642 FinalizeModule_SUBST
2643 (void)
2644 {
2645
2646 }
2647
2648 void 
2649 ServerShutdownModule_SUBST
2650 (void)
2651 {
2652         DeleteHash(&WirelessTemplateCache);
2653         DeleteHash(&WirelessLocalTemplateCache);
2654         DeleteHash(&TemplateCache);
2655         DeleteHash(&LocalTemplateCache);
2656
2657         DeleteHash(&GlobalNS);
2658         DeleteHash(&Iterators);
2659         DeleteHash(&Conditionals);
2660         DeleteHash(&SortHash);
2661
2662 }
2663
2664
2665 void
2666 SessionNewModule_SUBST
2667 (wcsession *sess)
2668 {
2669
2670 }
2671
2672 void
2673 SessionAttachModule_SUBST
2674 (wcsession *sess)
2675 {
2676         sess->vars = NewHash(1,NULL);
2677 }
2678
2679 void
2680 SessionDetachModule_SUBST
2681 (wcsession *sess)
2682 {
2683         DeleteHash(&sess->vars);
2684 }
2685
2686 void 
2687 SessionDestroyModule_SUBST  
2688 (wcsession *sess)
2689 {
2690
2691 }