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