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