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