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