* add K (after j...) escaper for combined html + json;
[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 'K':
1077                 StrHtmlEcmaEscAppend(Target, Source, NULL, 0, 0);
1078           break;
1079         case 'U':
1080                 StrBufUrlescAppend(Target, Source, NULL);
1081                 break;
1082         default:
1083                 StrBufAppendBuf(Target, Source, 0);
1084         }
1085 }
1086
1087
1088 void PutNewToken(WCTemplate *Template, WCTemplateToken *NewToken)
1089 {
1090         if (Template->nTokensUsed + 1 >= Template->TokenSpace) {
1091                 if (Template->TokenSpace <= 0) {
1092                         Template->Tokens = (WCTemplateToken**)malloc(
1093                                 sizeof(WCTemplateToken*) * 10);
1094                         memset(Template->Tokens, 0, sizeof(WCTemplateToken*));
1095                         Template->TokenSpace = 10;
1096                 }
1097                 else {
1098                         WCTemplateToken **NewTokens;
1099                         NewTokens= (WCTemplateToken**)malloc(
1100                                 sizeof(WCTemplateToken*) * 
1101                                 Template->TokenSpace * 2);
1102                         memcpy(NewTokens, Template->Tokens, 
1103                                sizeof(WCTemplateToken*) * Template->nTokensUsed);
1104                         free(Template->Tokens);
1105                         Template->TokenSpace *= 2;
1106                         Template->Tokens = NewTokens;
1107                 }
1108         }
1109         Template->Tokens[(Template->nTokensUsed)++] = NewToken;
1110 }
1111
1112 TemplateParam *GetNextParameter(StrBuf *Buf, const char **pCh, const char *pe, WCTemplateToken *Tokens, WCTemplate *pTmpl)
1113 {
1114         const char *pch = *pCh;
1115         const char *pchs, *pche;
1116         TemplateParam *Parm;
1117         char quote = '\0';
1118         int ParamBrace = 0;
1119
1120         Parm = (TemplateParam *) malloc(sizeof(TemplateParam));
1121         memset(Parm, 0, sizeof(TemplateParam));
1122         Parm->Type = TYPE_STR;
1123
1124         /* Skip leading whitespaces */
1125         while ((*pch == ' ' )||
1126                (*pch == '\t')||
1127                (*pch == '\r')||
1128                (*pch == '\n')) pch ++;
1129
1130         if (*pch == ':') {
1131                 Parm->Type = TYPE_PREFSTR;
1132                 pch ++;
1133                 if (*pch == '(') {
1134                         pch ++;
1135                         ParamBrace = 1;
1136                 }
1137         }
1138         else if (*pch == ';') {
1139                 Parm->Type = TYPE_PREFINT;
1140                 pch ++;
1141                 if (*pch == '(') {
1142                         pch ++;
1143                         ParamBrace = 1;
1144                 }
1145         }
1146         else if (*pch == '_') {
1147                 Parm->Type = TYPE_GETTEXT;
1148                 pch ++;
1149                 if (*pch == '(') {
1150                         pch ++;
1151                         ParamBrace = 1;
1152                 }
1153         }
1154         else if (*pch == 'B') {
1155                 Parm->Type = TYPE_BSTR;
1156                 pch ++;
1157                 if (*pch == '(') {
1158                         pch ++;
1159                         ParamBrace = 1;
1160                 }
1161         }
1162         else if (*pch == '=') {
1163                 Parm->Type = TYPE_SUBTEMPLATE;
1164                 pch ++;
1165                 if (*pch == '(') {
1166                         pch ++;
1167                         ParamBrace = 1;
1168                 }
1169         }
1170
1171
1172         if (*pch == '"')
1173                 quote = '"';
1174         else if (*pch == '\'')
1175                 quote = '\'';
1176         if (quote != '\0') {
1177                 pch ++;
1178                 pchs = pch;
1179                 while (pch <= pe &&
1180                        ((*pch != quote) ||
1181                         ( (pch > pchs) && (*(pch - 1) == '\\'))
1182                                )) {
1183                         pch ++;
1184                 }
1185                 pche = pch;
1186                 if (*pch != quote) {
1187                         lprintf(1, "Error (in '%s' line %ld); "
1188                                 "evaluating template param [%s] in Token [%s]\n",
1189                                 ChrPtr(pTmpl->FileName),
1190                                 Tokens->Line,
1191                                 ChrPtr(Tokens->FlatToken),
1192                                 *pCh);
1193                         pch ++;
1194                         free(Parm);
1195                         return NULL;
1196                 }
1197                 else {
1198                         StrBufPeek(Buf, pch, -1, '\0');         
1199                         if (LoadTemplates > 1) {                        
1200                                 lprintf(1, "DBG: got param [%s] %ld %ld\n", 
1201                                         pchs, pche - pchs, strlen(pchs));
1202                         }
1203                         Parm->Start = pchs;
1204                         Parm->len = pche - pchs;
1205                         pch ++; /* move after trailing quote */
1206                         if (ParamBrace && (*pch == ')')) {
1207                                 pch ++;
1208                         }
1209
1210                 }
1211         }
1212         else {
1213                 Parm->Type = TYPE_LONG;
1214                 pchs = pch;
1215                 while ((pch <= pe) &&
1216                        (isdigit(*pch) ||
1217                         (*pch == '+') ||
1218                         (*pch == '-')))
1219                         pch ++;
1220                 pch ++;
1221                 if (pch - pchs > 1){
1222                         StrBufPeek(Buf, pch, -1, '\0');
1223                         Parm->lvalue = atol(pchs);
1224                         Parm->Start = pchs;
1225                         pch++;
1226                 }
1227                 else {
1228                         Parm->lvalue = 0;
1229 /* TODO whUT?
1230                         lprintf(1, "Error (in '%s' line %ld); "
1231                                 "evaluating long template param [%s] in Token [%s]\n",
1232                                 ChrPtr(pTmpl->FileName),
1233                                 Tokens->Line,
1234                                 ChrPtr(Tokens->FlatToken),
1235                                 *pCh);
1236                                 */
1237                         free(Parm);
1238                         return NULL;
1239                 }
1240         }
1241         while ((*pch == ' ' )||
1242                (*pch == '\t')||
1243                (*pch == '\r')||
1244                (*pch == ',' )||
1245                (*pch == '\n')) pch ++;
1246
1247         if (DumpTemplateI18NStrings && (Parm->Type == TYPE_GETTEXT)) {
1248                 StrBufAppendPrintf(I18nDump, "_(\"%s\");\n", Parm->Start);
1249         }
1250         *pCh = pch;
1251         return Parm;
1252 }
1253
1254 WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf, 
1255                                        const char *pStart, 
1256                                        const char *pTmplStart, 
1257                                        const char *pTmplEnd, 
1258                                        long Line,
1259                                        WCTemplate *pTmpl)
1260 {
1261         void *vVar;
1262         const char *pch;
1263         TemplateParam *Param;
1264         WCTemplateToken *NewToken;
1265         WCTemplputParams TP;
1266
1267         NewToken = (WCTemplateToken*)malloc(sizeof(WCTemplateToken));
1268         memset(NewToken, 0, sizeof(WCTemplateToken));
1269         TP.Tokens = NewToken;
1270         NewToken->FileName = pTmpl->FileName; /* to print meaningfull log messages... */
1271         NewToken->Flags = 0;
1272         NewToken->Line = Line + 1;
1273         NewToken->pTokenStart = pTmplStart;
1274         NewToken->TokenStart = pTmplStart - pStart;
1275         NewToken->TokenEnd =  (pTmplEnd - pStart) - NewToken->TokenStart;
1276         NewToken->pTokenEnd = pTmplEnd;
1277         NewToken->NameEnd = NewToken->TokenEnd - 2;
1278         NewToken->PreEval = NULL;
1279         NewToken->FlatToken = NewStrBufPlain(pTmplStart + 2, pTmplEnd - pTmplStart - 2);
1280         StrBufShrinkToFit(NewToken->FlatToken, 1);
1281
1282         StrBufPeek(Buf, pTmplStart, + 1, '\0');
1283         StrBufPeek(Buf, pTmplEnd, -1, '\0');
1284         pch = NewToken->pName = pTmplStart + 2;
1285
1286         NewToken->HaveParameters = 0;;
1287         NewToken->nParameters = 0;
1288
1289         while (pch < pTmplEnd - 1) {
1290                 if (*pch == '(') {
1291                         StrBufPeek(Buf, pch, -1, '\0');
1292                         NewToken->NameEnd = pch - NewToken->pName;
1293                         pch ++;
1294                         if (*(pTmplEnd - 1) != ')') {
1295                                 LogTemplateError(
1296                                         NULL, "Parseerror", ERR_NAME, &TP, 
1297                                         "Warning, Non welformed Token; missing right parenthesis");
1298                         }
1299                         while (pch < pTmplEnd - 1) {
1300                                 Param = GetNextParameter(Buf, &pch, pTmplEnd - 1, NewToken, pTmpl);
1301                                 if (Param != NULL) {
1302                                         NewToken->HaveParameters = 1;
1303                                         if (NewToken->nParameters > MAXPARAM) {
1304                                                 LogTemplateError(
1305                                                         NULL, "Parseerror", ERR_NAME, &TP,
1306                                                         "only [%d] Params allowed in Tokens",
1307                                                         MAXPARAM);
1308
1309                                                 free(Param);
1310                                                 FreeToken(&NewToken);
1311                                                 return NULL;
1312                                         }
1313                                         NewToken->Params[NewToken->nParameters++] = Param;
1314                                 }
1315                                 else break;
1316                         }
1317                         if((NewToken->NameEnd == 1) &&
1318                            (NewToken->HaveParameters == 1))
1319                            
1320                         {
1321                                 if (*(NewToken->pName) == '_')
1322                                         NewToken->Flags = SV_GETTEXT;
1323                                 else if (*(NewToken->pName) == '=')
1324                                         NewToken->Flags = SV_SUBTEMPL;
1325                                 else if (*(NewToken->pName) == '%')
1326                                         NewToken->Flags = SV_CUST_STR_CONDITIONAL;
1327                                 else if (*(NewToken->pName) == '?')
1328                                         NewToken->Flags = SV_CONDITIONAL;
1329                                 else if (*(NewToken->pName) == '!')
1330                                         NewToken->Flags = SV_NEG_CONDITIONAL;
1331                         }
1332                 }
1333                 else pch ++;            
1334         }
1335         
1336         switch (NewToken->Flags) {
1337         case 0:
1338                 /* If we're able to find out more about the token, do it now while its fresh. */
1339                 if (GetHash(GlobalNS, NewToken->pName, NewToken->NameEnd, &vVar)) {
1340                         HashHandler *Handler;
1341                         Handler = (HashHandler*) vVar;
1342                         if ((NewToken->nParameters < Handler->Filter.nMinArgs) || 
1343                             (NewToken->nParameters > Handler->Filter.nMaxArgs)) {
1344                                 LogTemplateError(
1345                                         NULL, "Token", ERR_NAME, &TP,
1346                                         "doesn't work with %d params", 
1347                                         NewToken->nParameters);
1348
1349                         }
1350                         else {
1351                                 NewToken->PreEval = Handler;
1352                                 NewToken->Flags = SV_PREEVALUATED;              
1353                         }
1354                 }
1355                 break;
1356         case SV_GETTEXT:
1357                 if (NewToken->nParameters !=1) {
1358                         LogTemplateError(                               
1359                                 NULL, "Gettext", ERR_NAME, &TP,
1360                                 "requires exactly 1 parameter, you gave %d params", 
1361                                 NewToken->nParameters);
1362                         NewToken->Flags = 0;
1363                         break;
1364                 }
1365                 if (DumpTemplateI18NStrings) {
1366                         StrBufAppendPrintf(I18nDump, "_(\"%s\");\n", NewToken->Params[0]->Start);
1367                 }
1368                 break;
1369         case SV_SUBTEMPL:
1370                 if (NewToken->nParameters != 1) {
1371                         LogTemplateError(
1372                                 NULL, "Subtemplates", ERR_NAME, &TP,
1373                                 "require exactly 1 parameter, you gave %d params", 
1374                                 NewToken->nParameters);
1375                         break;
1376                 }
1377                 break;
1378         case SV_CUST_STR_CONDITIONAL:
1379         case SV_CONDITIONAL:
1380         case SV_NEG_CONDITIONAL:
1381                 if (NewToken->nParameters <2) {
1382                         LogTemplateError(
1383                                 NULL, "Conditional", ERR_NAME, &TP,
1384                                 "require at least 2 parameters, you gave %d params", 
1385                                 NewToken->nParameters);
1386                         NewToken->Flags = 0;
1387                         break;
1388                 }
1389                 if (NewToken->Params[1]->lvalue == 0) {
1390                         LogTemplateError(
1391                                 NULL, "Conditional", ERR_NAME, &TP,
1392                                 "Conditional ID (Parameter 1) mustn't be 0!");
1393                         NewToken->Flags = 0;
1394                         break;
1395                 }
1396                 if (!GetHash(Conditionals, 
1397                              NewToken->Params[0]->Start, 
1398                              NewToken->Params[0]->len, 
1399                              &vVar) || 
1400                     (vVar == NULL)) {
1401                         if ((NewToken->Params[0]->len == 1) &&
1402                             (NewToken->Params[0]->Start[0] == 'X'))
1403                                 break;
1404                         LogTemplateError(
1405                                 NULL, "Conditional", ERR_NAME, &TP,
1406                                 "Not found!");
1407 /*
1408                         NewToken->Error = NewStrBuf();
1409                         StrBufAppendPrintf(
1410                                 NewToken->Error, 
1411                                 "<pre>\nConditional [%s] (in '%s' line %ld); Not found!\n[%s]\n</pre>\n", 
1412                                 NewToken->Params[0]->Start,
1413                                 ChrPtr(pTmpl->FileName),
1414                                 NewToken->Line,
1415                                 ChrPtr(NewToken->FlatToken));
1416 */
1417                 }
1418                 else {
1419                         NewToken->PreEval = vVar;
1420                 }
1421                 break;
1422         }
1423         return NewToken;
1424 }
1425
1426
1427
1428
1429
1430 /**
1431  * \brief Display a variable-substituted template
1432  * \param templatename template file to load
1433  */
1434 void *prepare_template(StrBuf *filename, StrBuf *Key, HashList *PutThere)
1435 {
1436         WCTemplate *NewTemplate;
1437
1438         NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
1439         memset(NewTemplate, 0, sizeof(WCTemplate));
1440         NewTemplate->Data = NULL;
1441         NewTemplate->FileName = NewStrBufDup(filename);
1442         StrBufShrinkToFit(NewTemplate->FileName, 1);
1443         NewTemplate->nTokensUsed = 0;
1444         NewTemplate->TokenSpace = 0;
1445         NewTemplate->Tokens = NULL;
1446         NewTemplate->MimeType = NewStrBufPlain(GuessMimeByFilename (SKEY(NewTemplate->FileName)), -1);
1447         if (strstr(ChrPtr(NewTemplate->MimeType), "text") != NULL) {
1448                 StrBufAppendBufPlain(NewTemplate->MimeType, HKEY("; charset=utf-8"), 0);
1449         }
1450
1451         Put(PutThere, ChrPtr(Key), StrLength(Key), NewTemplate, FreeWCTemplate);
1452         return NewTemplate;
1453 }
1454
1455 /**
1456  * \brief Display a variable-substituted template
1457  * \param templatename template file to load
1458  */
1459 void *load_template(StrBuf *filename, StrBuf *Key, HashList *PutThere)
1460 {
1461         int fd;
1462         struct stat statbuf;
1463         const char *pS, *pE, *pch, *Err;
1464         long Line;
1465         int pos;
1466         WCTemplate *NewTemplate;
1467
1468         fd = open(ChrPtr(filename), O_RDONLY);
1469         if (fd <= 0) {
1470                 lprintf(1, "ERROR: could not open template '%s' - %s\n",
1471                         ChrPtr(filename), strerror(errno));
1472                 return NULL;
1473         }
1474
1475         if (fstat(fd, &statbuf) == -1) {
1476                 lprintf(1, "ERROR: could not stat template '%s' - %s\n",
1477                         ChrPtr(filename), strerror(errno));
1478                 return NULL;
1479         }
1480
1481         NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
1482         memset(NewTemplate, 0, sizeof(WCTemplate));
1483         NewTemplate->Data = NewStrBufPlain(NULL, statbuf.st_size);
1484         NewTemplate->FileName = NewStrBufDup(filename);
1485         NewTemplate->nTokensUsed = 0;
1486         NewTemplate->TokenSpace = 0;
1487         NewTemplate->Tokens = NULL;
1488         NewTemplate->MimeType = NewStrBufPlain(GuessMimeByFilename (SKEY(NewTemplate->FileName)), -1);
1489         if (strstr(ChrPtr(NewTemplate->MimeType), "text") != NULL) {
1490                 StrBufAppendBufPlain(NewTemplate->MimeType, HKEY("; charset=utf-8"), 0);
1491         }
1492
1493         if (StrBufReadBLOB(NewTemplate->Data, &fd, 1, statbuf.st_size, &Err) < 0) {
1494                 close(fd);
1495                 FreeWCTemplate(NewTemplate);
1496                 lprintf(1, "ERROR: reading template '%s' - %s<br />\n",
1497                         ChrPtr(filename), strerror(errno));
1498                 return NULL;
1499         }
1500         close(fd);
1501
1502         Line = 0;
1503         StrBufShrinkToFit(NewTemplate->Data, 1);
1504         StrBufShrinkToFit(NewTemplate->MimeType, 1);
1505         pS = pch = ChrPtr(NewTemplate->Data);
1506         pE = pS + StrLength(NewTemplate->Data);
1507         while (pch < pE) {
1508                 const char *pts, *pte;
1509                 int InQuotes = 0;
1510                 int InDoubleQuotes = 0;
1511
1512                 /** Find one <? > */
1513                 pos = (-1);
1514                 for (; pch < pE; pch ++) {
1515                         if ((*pch=='<')&&(*(pch + 1)=='?') &&
1516                             !((pch == pS) && /* we must ommit a <?xml */
1517                               (*(pch + 2) == 'x') && 
1518                               (*(pch + 3) == 'm') && 
1519                               (*(pch + 4) == 'l')))                          
1520                                 break;
1521                         if (*pch=='\n') Line ++;
1522                 }
1523                 if (pch >= pE)
1524                         continue;
1525                 pts = pch;
1526
1527                 /** Found one? parse it. */
1528                 for (; pch <= pE - 1; pch ++) {
1529                         if (*pch == '"')
1530                                 InDoubleQuotes = ! InDoubleQuotes;
1531                         else if (*pch == '\'')
1532                                 InQuotes = ! InQuotes;
1533                         else if ((!InQuotes  && !InDoubleQuotes) &&
1534                                  ((*pch!='\\')&&(*(pch + 1)=='>'))) {
1535                                 pch ++;
1536                                 break;
1537                         }
1538                 }
1539                 if (pch + 1 > pE)
1540                         continue;
1541                 pte = pch;
1542                 PutNewToken(NewTemplate, 
1543                             NewTemplateSubstitute(NewTemplate->Data, pS, pts, pte, Line, NewTemplate));
1544                 pch ++;
1545         }
1546         if (LoadTemplates == 0)
1547                 Put(PutThere, ChrPtr(Key), StrLength(Key), NewTemplate, FreeWCTemplate);
1548         return NewTemplate;
1549 }
1550
1551
1552 const char* PrintTemplate(void *vSubst)
1553 {
1554         WCTemplate *Tmpl = vSubst;
1555
1556         return ChrPtr(Tmpl->FileName);
1557
1558 }
1559
1560 int LoadTemplateDir(const char *DirName, HashList *wireless, HashList *big)
1561 {
1562         StrBuf *FileName;
1563         StrBuf *Tag;
1564         StrBuf *Dir;
1565         DIR *filedir = NULL;
1566         struct dirent *filedir_entry;
1567         int d_namelen;
1568         int d_without_ext;
1569         int IsMobile;
1570         
1571         Dir = NewStrBuf();
1572         StrBufPrintf(Dir, "%s/t", DirName);
1573         filedir = opendir (ChrPtr(Dir));
1574         if (filedir == NULL) {
1575                 FreeStrBuf(&Dir);
1576                 return 0;
1577         }
1578
1579         FileName = NewStrBuf();
1580         Tag = NewStrBuf();
1581         while ((filedir_entry = readdir(filedir)))
1582         {
1583                 char *MinorPtr;
1584                 char *PStart;
1585 #ifdef _DIRENT_HAVE_D_NAMELEN
1586                 d_namelen = filedir_entry->d_namelen;
1587 #else
1588                 d_namelen = strlen(filedir_entry->d_name);
1589 #endif
1590                 d_without_ext = d_namelen;
1591                 while ((d_without_ext > 0) && (filedir_entry->d_name[d_without_ext] != '.'))
1592                         d_without_ext --;
1593                 if ((d_without_ext == 0) || (d_namelen < 3))
1594                         continue;
1595                 if ((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~')
1596                         continue; /* Ignore backup files... */
1597
1598                 IsMobile = (strstr(filedir_entry->d_name, ".m.html")!= NULL);
1599                 PStart = filedir_entry->d_name;
1600                 StrBufPrintf(FileName, "%s/%s", ChrPtr(Dir),  filedir_entry->d_name);
1601                 MinorPtr = strchr(filedir_entry->d_name, '.');
1602                 if (MinorPtr != NULL)
1603                         *MinorPtr = '\0';
1604                 StrBufPlain(Tag, filedir_entry->d_name, MinorPtr - filedir_entry->d_name);
1605
1606                 if (LoadTemplates > 1)
1607                         lprintf(1, "%s %d %s\n",ChrPtr(FileName), IsMobile, ChrPtr(Tag));
1608                 if (LoadTemplates == 0)
1609                         load_template(FileName, Tag, (IsMobile)?wireless:big);
1610                 else
1611                         prepare_template(FileName, Tag, (IsMobile)?wireless:big);
1612         }
1613         closedir(filedir);
1614         FreeStrBuf(&FileName);
1615         FreeStrBuf(&Tag);
1616         FreeStrBuf(&Dir);
1617         return 1;
1618 }
1619
1620 void InitTemplateCache(void)
1621 {
1622         LoadTemplateDir(static_dirs[0],
1623                         WirelessTemplateCache,
1624                         TemplateCache);
1625         LoadTemplateDir(static_dirs[1],
1626                         WirelessLocalTemplateCache,
1627                         LocalTemplateCache);
1628 }
1629
1630
1631
1632 /*-----------------------------------------------------------------------------
1633  *                      Filling & processing Templates
1634  */
1635 /**
1636  * \brief executes one token
1637  * \param Target buffer to append to
1638  * \param Token da to  process.
1639  * \param Template we're iterating
1640  * \param Context Contextpoointer to pass in
1641  * \param state are we in conditional state?
1642  * \param ContextType what type of information does context giv us?
1643  */
1644 int EvaluateToken(StrBuf *Target, int state, WCTemplputParams *TP)
1645 {
1646         const char *AppendMe;
1647         long AppendMeLen;
1648         HashHandler *Handler;
1649         void *vVar;
1650         
1651 /* much output, since pName is not terminated...
1652         lprintf(1,"Doing token: %s\n",Token->pName);
1653 */
1654
1655         switch (TP->Tokens->Flags) {
1656         case SV_GETTEXT:
1657                 TmplGettext(Target, TP);
1658                 break;
1659         case SV_CONDITIONAL: /** Forward conditional evaluation */
1660                 return EvaluateConditional(Target, 1, state, TP);
1661                 break;
1662         case SV_NEG_CONDITIONAL: /** Reverse conditional evaluation */
1663                 return EvaluateConditional(Target, 0, state, TP);
1664                 break;
1665         case SV_CUST_STR_CONDITIONAL: /** Conditional put custom strings from params */
1666                 if (TP->Tokens->nParameters >= 6) {
1667                         if (EvaluateConditional(Target, 0, state, TP)) {
1668                                 GetTemplateTokenString(Target, TP, 5, &AppendMe, &AppendMeLen);
1669                                 StrBufAppendBufPlain(Target, 
1670                                                      AppendMe, 
1671                                                      AppendMeLen,
1672                                                      0);
1673                         }
1674                         else{
1675                                 GetTemplateTokenString(Target, TP, 4, &AppendMe, &AppendMeLen);
1676                                 StrBufAppendBufPlain(Target, 
1677                                                      AppendMe, 
1678                                                      AppendMeLen,
1679                                                      0);
1680                         }
1681                 }
1682                 else  {
1683                         LogTemplateError(
1684                                 Target, "Conditional", ERR_NAME, TP,
1685                                 "needs at least 6 Params!"); 
1686                 }
1687                 break;
1688         case SV_SUBTEMPL:
1689                 if (TP->Tokens->nParameters == 1)
1690                         DoTemplate(TKEY(0), Target, TP);
1691                 break;
1692         case SV_PREEVALUATED:
1693                 Handler = (HashHandler*) TP->Tokens->PreEval;
1694                 if (!CheckContext(Target, &Handler->Filter, TP, "Token")) {
1695                         return -1;
1696                 }
1697                 Handler->HandlerFunc(Target, TP);
1698                 break;          
1699         default:
1700                 if (GetHash(GlobalNS, TP->Tokens->pName, TP->Tokens->NameEnd, &vVar)) {
1701                         Handler = (HashHandler*) vVar;
1702                         if (!CheckContext(Target, &Handler->Filter, TP, "Token")) {
1703                                 return -1;
1704                         }
1705                         else {
1706                                 Handler->HandlerFunc(Target, TP);
1707                         }
1708                 }
1709                 else {
1710                         print_value_of(Target, TP);
1711                 }
1712         }
1713         return 0;
1714 }
1715
1716
1717
1718 const StrBuf *ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target, WCTemplputParams *CallingTP)
1719 {
1720         WCTemplate *pTmpl = Tmpl;
1721         int done = 0;
1722         int i, state;
1723         const char *pData, *pS;
1724         long len;
1725         WCTemplputParams TP;
1726
1727         memcpy(&TP.Filter, &CallingTP->Filter, sizeof(ContextFilter));
1728
1729         TP.Context = CallingTP->Context;
1730         TP.ControlContext = CallingTP->ControlContext;
1731
1732         if (LoadTemplates != 0) {                       
1733                 if (LoadTemplates > 1)
1734                         lprintf(1, "DBG: ----- loading:  [%s] ------ \n", 
1735                                 ChrPtr(Tmpl->FileName));
1736
1737                 pTmpl = load_template(Tmpl->FileName, NULL, NULL);
1738                 if(pTmpl == NULL) {
1739                         StrBufAppendPrintf(
1740                                 Target, 
1741                                 "<pre>\nError loading Template [%s]\n See Logfile for details\n</pre>\n", 
1742                                 ChrPtr(Tmpl->FileName));
1743                         return NULL;
1744                 }
1745
1746         }
1747
1748         pS = pData = ChrPtr(pTmpl->Data);
1749         len = StrLength(pTmpl->Data);
1750         i = 0;
1751         state = 0;
1752         while (!done) {
1753                 if (i >= pTmpl->nTokensUsed) {
1754                         StrBufAppendBufPlain(Target, 
1755                                              pData, 
1756                                              len - (pData - pS), 0);
1757                         done = 1;
1758                 }
1759                 else {
1760                         StrBufAppendBufPlain(
1761                                 Target, pData, 
1762                                 pTmpl->Tokens[i]->pTokenStart - pData, 0);
1763                         TP.Tokens = pTmpl->Tokens[i];
1764                         TP.nArgs = pTmpl->Tokens[i]->nParameters;
1765                         state = EvaluateToken(Target, state, &TP);
1766
1767                         while ((state != 0) && (i+1 < pTmpl->nTokensUsed)) {
1768                         /* condition told us to skip till its end condition */
1769                                 i++;
1770                                 TP.Tokens = pTmpl->Tokens[i];
1771                                 TP.nArgs = pTmpl->Tokens[i]->nParameters;
1772                                 if ((pTmpl->Tokens[i]->Flags == SV_CONDITIONAL) ||
1773                                     (pTmpl->Tokens[i]->Flags == SV_NEG_CONDITIONAL)) {
1774                                         if (state == EvaluateConditional(
1775                                                     Target, 
1776                                                     pTmpl->Tokens[i]->Flags, 
1777                                                     state, 
1778                                                     &TP))
1779                                                 state = 0;
1780                                 }
1781                         }
1782                         pData = pTmpl->Tokens[i++]->pTokenEnd + 1;
1783                         if (i > pTmpl->nTokensUsed)
1784                                 done = 1;
1785                 }
1786         }
1787         if (LoadTemplates != 0) {
1788                 FreeWCTemplate(pTmpl);
1789         }
1790         return Tmpl->MimeType;
1791
1792 }
1793
1794 /**
1795  * \brief Display a variable-substituted template
1796  * \param templatename template file to load
1797  * \returns the mimetype of the template its doing
1798  */
1799 const StrBuf *DoTemplate(const char *templatename, long len, StrBuf *Target, WCTemplputParams *TP) 
1800 {
1801         WCTemplputParams LocalTP;
1802         HashList *Static;
1803         HashList *StaticLocal;
1804         void *vTmpl;
1805         
1806         if (Target == NULL)
1807                 Target = WC->WBuf;
1808         if (TP == NULL) {
1809                 memset(&LocalTP, 0, sizeof(WCTemplputParams));
1810                 TP = &LocalTP;
1811         }
1812
1813         if (WC->is_mobile) {
1814                 Static = WirelessTemplateCache;
1815                 StaticLocal = WirelessLocalTemplateCache;
1816         }
1817         else {
1818                 Static = TemplateCache;
1819                 StaticLocal = LocalTemplateCache;
1820         }
1821
1822         if (len == 0)
1823         {
1824                 lprintf (1, "Can't to load a template with empty name!\n");
1825                 StrBufAppendPrintf(Target, "<pre>\nCan't to load a template with empty name!\n</pre>");
1826                 return NULL;
1827         }
1828
1829         if (!GetHash(StaticLocal, templatename, len, &vTmpl) &&
1830             !GetHash(Static, templatename, len, &vTmpl)) {
1831                 lprintf (1, "didn't find Template [%s] %ld %ld\n", templatename, len , (long)strlen(templatename));
1832                 StrBufAppendPrintf(Target, "<pre>\ndidn't find Template [%s] %ld %ld\n</pre>", 
1833                                    templatename, len, 
1834                                    (long)strlen(templatename));
1835 #if 0
1836                 dbg_PrintHash(Static, PrintTemplate, NULL);
1837                 PrintHash(Static, VarPrintTransition, PrintTemplate);
1838 #endif
1839                 return NULL;
1840         }
1841         if (vTmpl == NULL) 
1842                 return NULL;
1843         return ProcessTemplate(vTmpl, Target, TP);
1844
1845 }
1846
1847 /*-----------------------------------------------------------------------------
1848  *                      Iterators
1849  */
1850 typedef struct _HashIterator {
1851         HashList *StaticList;
1852         int AdditionalParams;
1853         int ContextType;
1854         int XPectContextType;
1855         int Flags;
1856         RetrieveHashlistFunc GetHash;
1857         HashDestructorFunc Destructor;
1858         SubTemplFunc DoSubTemplate;
1859 } HashIterator;
1860
1861 void RegisterITERATOR(const char *Name, long len, 
1862                       int AdditionalParams, 
1863                       HashList *StaticList, 
1864                       RetrieveHashlistFunc GetHash, 
1865                       SubTemplFunc DoSubTempl,
1866                       HashDestructorFunc Destructor,
1867                       int ContextType, 
1868                       int XPectContextType, 
1869                       int Flags)
1870 {
1871         HashIterator *It;
1872
1873         It = (HashIterator*)malloc(sizeof(HashIterator));
1874         memset(It, 0, sizeof(HashIterator));
1875         It->StaticList = StaticList;
1876         It->AdditionalParams = AdditionalParams;
1877         It->GetHash = GetHash;
1878         It->DoSubTemplate = DoSubTempl;
1879         It->Destructor = Destructor;
1880         It->ContextType = ContextType;
1881         It->XPectContextType = XPectContextType;
1882         It->Flags = Flags;
1883         Put(Iterators, Name, len, It, NULL);
1884 }
1885
1886 typedef struct _iteratestruct {
1887         int GroupChange;
1888         int oddeven;
1889         const char *Key;
1890         long KeyLen;
1891         int n;
1892         int LastN;
1893         }IterateStruct; 
1894
1895 void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
1896 {
1897         void *vIt;
1898         HashIterator *It;
1899         HashList *List;
1900         HashPos  *it;
1901         SortStruct *SortBy = NULL;
1902         void *vSortBy;
1903         int DetectGroupChange = 0;
1904         int nMembersUsed;
1905         void *vContext;
1906         void *vLastContext = NULL;
1907         StrBuf *SubBuf;
1908         WCTemplputParams SubTP;
1909         IterateStruct Status;
1910
1911         long StartAt = 0;
1912         long StepWidth = 0;
1913         long StopAt = -1;
1914
1915         memset(&Status, 0, sizeof(IterateStruct));
1916         memcpy (&SubTP, &TP, sizeof(WCTemplputParams));
1917         
1918         if (!GetHash(Iterators, TKEY(0), &vIt)) {
1919                 LogTemplateError(
1920                         Target, "Iterator", ERR_PARM1, TP, "Unknown!");
1921                 return;
1922         }
1923
1924         It = (HashIterator*) vIt;
1925
1926         if (TP->Tokens->nParameters < It->AdditionalParams + 2) {
1927                 LogTemplateError(                               
1928                         Target, "Iterator", ERR_PARM1, TP,
1929                         "doesn't work with %d params", 
1930                         TP->Tokens->nParameters);
1931                 return;
1932         }
1933
1934         if ((It->XPectContextType != CTX_NONE) &&
1935             (It->XPectContextType != TP->Filter.ContextType)) {
1936                 LogTemplateError(
1937                         Target, "Iterator", ERR_PARM1, TP,
1938                         "requires context of type %d, have %d", 
1939                         It->XPectContextType, 
1940                         TP->Filter.ContextType);
1941                 return ;
1942                 
1943         }
1944
1945         if (It->StaticList == NULL)
1946                 List = It->GetHash(Target, TP);
1947         else
1948                 List = It->StaticList;
1949
1950         DetectGroupChange = (It->Flags & IT_FLAG_DETECT_GROUPCHANGE) != 0;
1951         if (DetectGroupChange) {
1952                 const StrBuf *BSort;
1953                 DetectGroupChange = 0;
1954                 if (havebstr("SortBy")) {
1955                         BSort = sbstr("SortBy");
1956                         if (GetHash(SortHash, SKEY(BSort), &vSortBy) &&
1957                             (vSortBy != NULL)) {
1958                                 SortBy = (SortStruct*)vSortBy;
1959                                 /** Ok, its us, lets see in which direction we should sort... */
1960                                 if (havebstr("SortOrder")) {
1961                                         int SortOrder;
1962                                         SortOrder = LBSTR("SortOrder");
1963                                         if (SortOrder != 0)
1964                                                 DetectGroupChange = 1;
1965                                 }
1966                         }
1967                 }
1968         }
1969         nMembersUsed = GetCount(List);
1970         SubBuf = NewStrBuf();
1971         SubTP.Filter.ContextType = It->ContextType;
1972         SubTP.Filter.ControlContextType = CTX_ITERATE;
1973         SubTP.ControlContext = &Status;
1974         
1975         if (HAVE_PARAM(2)) {
1976                 StartAt = GetTemplateTokenNumber(Target, TP, 2, 0);
1977         }
1978         if (HAVE_PARAM(3)) {
1979                 StepWidth = GetTemplateTokenNumber(Target, TP, 3, 0);
1980         }
1981         if (HAVE_PARAM(4)) {
1982                 StopAt = GetTemplateTokenNumber(Target, TP, 4, -1);
1983         }
1984         it = GetNewHashPos(List, StepWidth);
1985         if (StopAt < 0) {
1986                 StopAt = GetCount(List);
1987         }
1988         while (GetNextHashPos(List, it, &Status.KeyLen, &Status.Key, &vContext)) {
1989                 if ((Status.n >= StartAt) && (Status.n <= StopAt)) {
1990                         if (DetectGroupChange && Status.n > 0) {
1991                                 Status.GroupChange = SortBy->GroupChange(vContext, vLastContext);
1992                         }
1993                         Status.LastN = (Status.n + 1) == nMembersUsed;
1994                         SubTP.Context = vContext;
1995                         if (It->DoSubTemplate != NULL)
1996                                 It->DoSubTemplate(SubBuf, &SubTP);
1997                         DoTemplate(TKEY(1), SubBuf, &SubTP);
1998                         
1999                         StrBufAppendBuf(Target, SubBuf, 0);
2000                         FlushStrBuf(SubBuf);
2001                         Status.oddeven = ! Status.oddeven;
2002                         vLastContext = vContext;
2003                 }
2004                 Status.n++;
2005         }
2006         FreeStrBuf(&SubBuf);
2007         DeleteHashPos(&it);
2008         if (It->Destructor != NULL)
2009                 It->Destructor(&List);
2010 }
2011
2012
2013 int conditional_ITERATE_ISGROUPCHANGE(StrBuf *Target, WCTemplputParams *TP)
2014 {
2015         IterateStruct *Ctx = CCTX;
2016         if (TP->Tokens->nParameters < 3)
2017                 return  Ctx->GroupChange;
2018
2019         return TP->Tokens->Params[2]->lvalue == Ctx->GroupChange;
2020 }
2021
2022 void tmplput_ITERATE_ODDEVEN(StrBuf *Target, WCTemplputParams *TP)
2023 {
2024         IterateStruct *Ctx = CCTX;
2025         if (Ctx->oddeven)
2026                 StrBufAppendBufPlain(Target, HKEY("odd"), 0);
2027         else
2028                 StrBufAppendBufPlain(Target, HKEY("even"), 0);
2029 }
2030
2031
2032 void tmplput_ITERATE_KEY(StrBuf *Target, WCTemplputParams *TP)
2033 {
2034         IterateStruct *Ctx = CCTX;
2035
2036         StrBufAppendBufPlain(Target, Ctx->Key, Ctx->KeyLen, 0);
2037 }
2038
2039
2040 void tmplput_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
2041 {
2042         IterateStruct *Ctx = CCTX;
2043         StrBufAppendPrintf(Target, "%d", Ctx->n);
2044 }
2045
2046 int conditional_ITERATE_FIRSTN(StrBuf *Target, WCTemplputParams *TP)
2047 {
2048         IterateStruct *Ctx = CCTX;
2049         return Ctx->n == 0;
2050 }
2051
2052 int conditional_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
2053 {
2054         IterateStruct *Ctx = CCTX;
2055         return Ctx->LastN;
2056 }
2057
2058
2059
2060 /*-----------------------------------------------------------------------------
2061  *                      Conditionals
2062  */
2063 int EvaluateConditional(StrBuf *Target, int Neg, int state, WCTemplputParams *TP)
2064 {
2065         ConditionalStruct *Cond;
2066
2067         if ((TP->Tokens->Params[0]->len == 1) &&
2068             (TP->Tokens->Params[0]->Start[0] == 'X'))
2069                 return (state != 0)?TP->Tokens->Params[1]->lvalue:0;
2070             
2071         Cond = (ConditionalStruct *) TP->Tokens->PreEval;
2072         if (Cond == NULL) {
2073                 LogTemplateError(
2074                         Target, "Conditional", ERR_PARM1, TP,
2075                         "unknown!");
2076                 return 1;
2077         }
2078
2079         if (!CheckContext(Target, &Cond->Filter, TP, "Conditional")) {
2080                 return 0;
2081         }
2082
2083         if (Cond->CondF(Target, TP) == Neg)
2084                 return TP->Tokens->Params[1]->lvalue;
2085         return 0;
2086 }
2087
2088 int ConditionalVar(StrBuf *Target, WCTemplputParams *TP)
2089 {
2090         void *vsubst;
2091         wcsubst *subst;
2092         
2093         if (!GetHash(WC->vars, TKEY(2), &vsubst))
2094                 return 0;
2095         subst = (wcsubst*) vsubst;
2096         
2097         switch(subst->wcs_type) {
2098         case WCS_FUNCTION:
2099                 return (subst->wcs_function!=NULL);
2100         case WCS_SERVCMD:
2101                 lprintf(1, "  -> Server [%s]\n", subst->wcs_value);/* TODO */
2102                 return 1;
2103         case WCS_STRING:
2104         case WCS_STRBUF:
2105         case WCS_STRBUF_REF:
2106                 if (TP->Tokens->nParameters < 4)
2107                         return 1;
2108                 return (strcmp(TP->Tokens->Params[3]->Start, ChrPtr(subst->wcs_value)) == 0);
2109         case WCS_LONG:
2110                 if (TP->Tokens->nParameters < 4)
2111                         return (subst->lvalue != 0);
2112                 return (subst->lvalue == TP->Tokens->Params[3]->lvalue);
2113         default:
2114                 lprintf(1,"  WARNING: invalid type: [%ld]!\n", subst->wcs_type);
2115                 return -1;
2116         }
2117         return 0;
2118 }
2119
2120 void RegisterConditional(const char *Name, long len, 
2121                          int nParams,
2122                          WCConditionalFunc CondF, 
2123                          int ContextRequired)
2124 {
2125         ConditionalStruct *Cond;
2126
2127         Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2128         memset(Cond, 0, sizeof(ConditionalStruct));
2129         Cond->PlainName = Name;
2130         Cond->Filter.nMaxArgs = nParams;
2131         Cond->Filter.nMinArgs = nParams;
2132         Cond->CondF = CondF;
2133         Cond->Filter.ContextType = ContextRequired;
2134         Cond->Filter.ControlContextType = CTX_NONE;
2135         Put(Conditionals, Name, len, Cond, NULL);
2136 }
2137
2138 void RegisterControlConditional(const char *Name, long len, 
2139                                 int nParams,
2140                                 WCConditionalFunc CondF, 
2141                                 int ControlContextRequired)
2142 {
2143         ConditionalStruct *Cond;
2144
2145         Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2146         memset(Cond, 0, sizeof(ConditionalStruct));
2147         Cond->PlainName = Name;
2148         Cond->Filter.nMaxArgs = nParams;
2149         Cond->Filter.nMinArgs = nParams;
2150         Cond->CondF = CondF;
2151         Cond->Filter.ContextType = CTX_NONE;
2152         Cond->Filter.ControlContextType = ControlContextRequired;
2153         Put(Conditionals, Name, len, Cond, NULL);
2154 }
2155
2156 /*-----------------------------------------------------------------------------
2157  *                      Context Strings
2158  */
2159 void tmplput_ContextString(StrBuf *Target, WCTemplputParams *TP)
2160 {
2161         StrBufAppendTemplate(Target, TP, (StrBuf*)CTX, 0);
2162 }
2163 int ConditionalContextStr(StrBuf *Target, WCTemplputParams *TP)
2164 {
2165         StrBuf *TokenText = (StrBuf*) CTX;
2166         const char *CompareToken;
2167         long len;
2168
2169         GetTemplateTokenString(Target, TP, 2, &CompareToken, &len);
2170         return strcmp(ChrPtr(TokenText), CompareToken) == 0;
2171 }
2172
2173 /*-----------------------------------------------------------------------------
2174  *                      Boxed-API
2175  */
2176
2177 void tmpl_do_boxed(StrBuf *Target, WCTemplputParams *TP)
2178 {
2179         WCTemplputParams SubTP;
2180
2181         StrBuf *Headline;
2182         if (TP->Tokens->nParameters == 2) {
2183                 if (TP->Tokens->Params[1]->Type == TYPE_STR) {
2184                         Headline = NewStrBuf();
2185                         DoTemplate(TKEY(1), Headline, TP);
2186                 }
2187                 else {
2188                         const char *Ch;
2189                         long len;
2190                         GetTemplateTokenString(Target, 
2191                                                TP, 
2192                                                1,
2193                                                &Ch,
2194                                                &len);
2195                         Headline = NewStrBufPlain(Ch, len);
2196                 }
2197         }
2198         memcpy (&SubTP, TP, sizeof(WCTemplputParams));
2199         SubTP.Context = Headline;
2200         SubTP.Filter.ContextType = CTX_STRBUF;
2201         DoTemplate(HKEY("beginbox"), Target, &SubTP);
2202         DoTemplate(TKEY(0), Target, TP);
2203         DoTemplate(HKEY("endbox"), Target, TP);
2204         FreeStrBuf(&Headline);
2205 }
2206
2207 /*-----------------------------------------------------------------------------
2208  *                      Tabbed-API
2209  */
2210
2211 void tmpl_do_tabbed(StrBuf *Target, WCTemplputParams *TP)
2212 {
2213         StrBuf **TabNames;
2214         int i, ntabs, nTabs;
2215
2216         nTabs = ntabs = TP->Tokens->nParameters / 2;
2217         TabNames = (StrBuf **) malloc(ntabs * sizeof(StrBuf*));
2218         memset(TabNames, 0, ntabs * sizeof(StrBuf*));
2219
2220         for (i = 0; i < ntabs; i++) {
2221                 if ((TP->Tokens->Params[i * 2]->Type == TYPE_STR) &&
2222                     (TP->Tokens->Params[i * 2]->len > 0)) {
2223                         TabNames[i] = NewStrBuf();
2224                         DoTemplate(TKEY(i * 2), TabNames[i], TP);
2225                 }
2226                 else if (TP->Tokens->Params[i * 2]->Type == TYPE_GETTEXT) {
2227                         const char *Ch;
2228                         long len;
2229                         GetTemplateTokenString(Target, 
2230                                                TP, 
2231                                                i * 2,
2232                                                &Ch,
2233                                                &len);
2234                         TabNames[i] = NewStrBufPlain(Ch, -1);
2235                 }
2236                 else { 
2237                         /** A Tab without subject? we can't count that, add it as silent */
2238                         nTabs --;
2239                 }
2240         }
2241
2242         StrTabbedDialog(Target, nTabs, TabNames);
2243         for (i = 0; i < ntabs; i++) {
2244                 StrBeginTab(Target, i, nTabs);
2245                 DoTemplate(TKEY(i * 2 + 1), Target, TP);
2246                 StrEndTab(Target, i, nTabs);
2247         }
2248 }
2249
2250
2251 /*-----------------------------------------------------------------------------
2252  *                      Sorting-API
2253  */
2254
2255
2256 void RegisterSortFunc(const char *name, long len, 
2257                       const char *prepend, long preplen,
2258                       CompareFunc Forward, 
2259                       CompareFunc Reverse, 
2260                       CompareFunc GroupChange, 
2261                       long ContextType)
2262 {
2263         SortStruct *NewSort;
2264
2265         NewSort = (SortStruct*) malloc(sizeof(SortStruct));
2266         memset(NewSort, 0, sizeof(SortStruct));
2267         NewSort->Name = NewStrBufPlain(name, len);
2268         if (prepend != NULL)
2269                 NewSort->PrefPrepend = NewStrBufPlain(prepend, preplen);
2270         else
2271                 NewSort->PrefPrepend = NULL;
2272         NewSort->Forward = Forward;
2273         NewSort->Reverse = Reverse;
2274         NewSort->GroupChange = GroupChange;
2275         NewSort->ContextType = ContextType;
2276         Put(SortHash, name, len, NewSort, DestroySortStruct);
2277 }
2278
2279 CompareFunc RetrieveSort(WCTemplputParams *TP, 
2280                          const char *OtherPrefix, long OtherPrefixLen,
2281                          const char *Default, long ldefault, long DefaultDirection)
2282 {
2283         int isdefault = 0;
2284         const StrBuf *BSort = NULL;
2285         SortStruct *SortBy;
2286         void *vSortBy;
2287         long SortOrder = -1;
2288         
2289         if (havebstr("SortBy")) {
2290                 BSort = sbstr("SortBy");
2291                 if (OtherPrefix == NULL) {
2292                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2293                 }
2294                 else {
2295                         set_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen, NewStrBufDup(BSort), 0);
2296                 }
2297         }
2298         else { /** Try to fallback to our remembered values... */
2299                 if (OtherPrefix == NULL) {
2300                         BSort = get_room_pref("sort");
2301                 }
2302                 else {
2303                         BSort = get_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen);
2304                 }
2305                 if (BSort != NULL)
2306                         putbstr("SortBy", NewStrBufDup(BSort));
2307                 else {
2308                         StrBuf *Buf;
2309
2310                         BSort = Buf = NewStrBufPlain(Default, ldefault);
2311                         putbstr("SortBy", Buf);
2312                 }
2313         }
2314
2315         if (!GetHash(SortHash, SKEY(BSort), &vSortBy) || 
2316             (vSortBy == NULL)) {
2317                 isdefault = 1;
2318                 if (!GetHash(SortHash, Default, ldefault, &vSortBy) || 
2319                     (vSortBy == NULL)) {
2320                         LogTemplateError(
2321                                 NULL, "Sorting", ERR_PARM1, TP,
2322                                 "Illegal default sort: [%s]", Default);
2323                         wc_backtrace();
2324                 }
2325         }
2326         SortBy = (SortStruct*)vSortBy;
2327
2328         /** Ok, its us, lets see in which direction we should sort... */
2329         if (havebstr("SortOrder")) {
2330                 SortOrder = LBSTR("SortOrder");
2331         }
2332         else { /** Try to fallback to our remembered values... */
2333                 StrBuf *Buf = NULL;
2334                 if (SortBy->PrefPrepend == NULL) {
2335                         Buf = get_room_pref("SortOrder");
2336                         SortOrder = StrTol(Buf);
2337                 }
2338                 else {
2339                         BSort = get_X_PREFS(HKEY("SortOrder"), OtherPrefix, OtherPrefixLen);
2340                 }
2341
2342                 if (Buf == NULL)
2343                         SortOrder = DefaultDirection;
2344
2345                 Buf = NewStrBufPlain(NULL, 64);
2346                 StrBufPrintf(Buf, "%ld", SortOrder);
2347                 putbstr("SortOrder", Buf);
2348         }
2349         switch (SortOrder) {
2350         default:
2351         case 0:
2352                 return NULL;
2353         case 1:
2354                 return SortBy->Forward;
2355         case 2:
2356                 return SortBy->Reverse;
2357         }
2358 }
2359
2360
2361 enum {
2362         eNO_SUCH_SORT, 
2363         eNOT_SPECIFIED,
2364         eINVALID_PARAM,
2365         eFOUND
2366 };
2367
2368 ConstStr SortIcons[] = {
2369         {HKEY("static/sort_none.gif")},
2370         {HKEY("static/up_pointer.gif")},
2371         {HKEY("static/down_pointer.gif")},
2372 };
2373
2374 ConstStr SortNextOrder[] = {
2375         {HKEY("1")},
2376         {HKEY("2")},
2377         {HKEY("0")},
2378 };
2379
2380
2381 int GetSortMetric(WCTemplputParams *TP, SortStruct **Next, SortStruct **Param, long *SortOrder, int N)
2382 {
2383         int bSortError = eNOT_SPECIFIED;
2384         const StrBuf *BSort;
2385         void *vSort;
2386         
2387         *SortOrder = 0;
2388         *Next = NULL;
2389         if (!GetHash(SortHash, TKEY(0), &vSort) || 
2390             (vSort == NULL))
2391                 return eNO_SUCH_SORT;
2392         *Param = (SortStruct*) vSort;
2393         
2394
2395         if (havebstr("SortBy")) {
2396                 BSort = sbstr("SortBy");
2397                 bSortError = eINVALID_PARAM;
2398                 if ((*Param)->PrefPrepend == NULL) {
2399                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2400                 }
2401                 else {
2402                         set_X_PREFS(HKEY("sort"), TKEY(N), NewStrBufDup(BSort), 0);
2403                 }
2404         }
2405         else { /** Try to fallback to our remembered values... */
2406                 if ((*Param)->PrefPrepend == NULL) {
2407                         BSort = get_room_pref("sort");
2408                 }
2409                 else {
2410                         BSort = get_X_PREFS(HKEY("sort"), TKEY(N));
2411                 }
2412         }
2413
2414         if (!GetHash(SortHash, SKEY(BSort), &vSort) || 
2415             (vSort == NULL))
2416                 return bSortError;
2417
2418         *Next = (SortStruct*) vSort;
2419
2420         /** Ok, its us, lets see in which direction we should sort... */
2421         if (havebstr("SortOrder")) {
2422                 *SortOrder = LBSTR("SortOrder");
2423         }
2424         else { /** Try to fallback to our remembered values... */
2425                 if ((*Param)->PrefPrepend == NULL) {
2426                         *SortOrder = StrTol(get_room_pref("SortOrder"));
2427                 }
2428                 else {
2429                         *SortOrder = StrTol(get_X_PREFS(HKEY("SortOrder"), TKEY(N)));
2430                 }
2431         }
2432         if (*SortOrder > 2)
2433                 *SortOrder = 0;
2434
2435         return eFOUND;
2436 }
2437
2438
2439 void tmplput_SORT_ICON(StrBuf *Target, WCTemplputParams *TP)
2440 {
2441         long SortOrder;
2442         SortStruct *Next;
2443         SortStruct *Param;
2444         const ConstStr *SortIcon;
2445
2446         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2447         case eNO_SUCH_SORT:
2448                 LogTemplateError(
2449                         Target, "Sorter", ERR_PARM1, TP,
2450                         " Sorter [%s] unknown!", 
2451                         TP->Tokens->Params[0]->Start);
2452                 break;          
2453         case eINVALID_PARAM:
2454                 LogTemplateError(NULL, "Sorter", ERR_PARM1, TP,
2455                                  " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
2456                                  bstr("SortBy"));
2457         case eNOT_SPECIFIED:
2458         case eFOUND:
2459                 if (Next == Param) {
2460                         SortIcon = &SortIcons[SortOrder];
2461                 }
2462                 else { /** Not Us... */
2463                         SortIcon = &SortIcons[0];
2464                 }
2465                 StrBufAppendBufPlain(Target, SortIcon->Key, SortIcon->len, 0);
2466         }
2467 }
2468
2469 void tmplput_SORT_NEXT(StrBuf *Target, WCTemplputParams *TP)
2470 {
2471         long SortOrder;
2472         SortStruct *Next;
2473         SortStruct *Param;
2474
2475         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2476         case eNO_SUCH_SORT:
2477                 LogTemplateError(
2478                         Target, "Sorter", ERR_PARM1, TP,                                  
2479                         " Sorter [%s] unknown!", 
2480                         TP->Tokens->Params[0]->Start);
2481                 break;          
2482         case eINVALID_PARAM:
2483                 LogTemplateError(
2484                         NULL, "Sorter", ERR_PARM1, TP,
2485                         " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
2486                         bstr("SortBy"));
2487         case eNOT_SPECIFIED:
2488         case eFOUND:
2489                 StrBufAppendBuf(Target, Param->Name, 0);
2490                 
2491         }
2492 }
2493
2494 void tmplput_SORT_ORDER(StrBuf *Target, WCTemplputParams *TP)
2495 {
2496         long SortOrder;
2497         const ConstStr *SortOrderStr;
2498         SortStruct *Next;
2499         SortStruct *Param;
2500
2501         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2502         case eNO_SUCH_SORT:
2503                 LogTemplateError(
2504                         Target, "Sorter", ERR_PARM1, TP,
2505                         " Sorter [%s] unknown!",
2506                         TP->Tokens->Params[0]->Start);
2507                 break;          
2508         case eINVALID_PARAM:
2509                 LogTemplateError(
2510                         NULL, "Sorter", ERR_PARM1, TP,
2511                         " Sorter specified by BSTR 'SortBy' [%s] unknown!",
2512                         bstr("SortBy"));
2513         case eNOT_SPECIFIED:
2514         case eFOUND:
2515                 if (Next == Param) {
2516                         SortOrderStr = &SortNextOrder[SortOrder];
2517                 }
2518                 else { /** Not Us... */
2519                         SortOrderStr = &SortNextOrder[0];
2520                 }
2521                 StrBufAppendBufPlain(Target, SortOrderStr->Key, SortOrderStr->len, 0);
2522         }
2523 }
2524
2525
2526 void tmplput_long_vector(StrBuf *Target, WCTemplputParams *TP)
2527 {
2528         long *LongVector = (long*) CTX;
2529
2530         if ((TP->Tokens->Params[0]->Type == TYPE_LONG) && 
2531             (TP->Tokens->Params[0]->lvalue <= LongVector[0]))
2532         {
2533                 StrBufAppendPrintf(Target, "%ld", LongVector[TP->Tokens->Params[0]->lvalue]);
2534         }
2535         else
2536         {
2537                 if (TP->Tokens->Params[0]->Type != TYPE_LONG) {
2538                         LogTemplateError(
2539                                 Target, "Longvector", ERR_NAME, TP,
2540                                 "needs a numerical Parameter!");
2541                 }
2542                 else {
2543                         LogTemplateError(
2544                                 Target, "LongVector", ERR_PARM1, TP,
2545                                 "doesn't have %ld Parameters, its just the size of %ld!", 
2546                                 TP->Tokens->Params[0]->lvalue,
2547                                 LongVector[0]);
2548                 }
2549         }
2550 }
2551
2552 void dbg_print_longvector(long *LongVector)
2553 {
2554         StrBuf *Buf = NewStrBufPlain(HKEY("Longvector: ["));
2555         int nItems = LongVector[0];
2556         int i;
2557
2558         for (i = 0; i < nItems; i++) {
2559                 if (i + 1 < nItems)
2560                         StrBufAppendPrintf(Buf, "%d: %ld | ", i, LongVector[i]);
2561                 else
2562                         StrBufAppendPrintf(Buf, "%d: %ld]\n", i, LongVector[i]);
2563
2564         }
2565         lprintf(1, ChrPtr(Buf));
2566         FreeStrBuf(&Buf);
2567 }
2568
2569 int ConditionalLongVector(StrBuf *Target, WCTemplputParams *TP)
2570 {
2571         long *LongVector = (long*) CTX;
2572
2573         if ((TP->Tokens->Params[2]->Type == TYPE_LONG) && 
2574             (TP->Tokens->Params[2]->lvalue <= LongVector[0])&&
2575             (TP->Tokens->Params[3]->Type == TYPE_LONG) && 
2576             (TP->Tokens->Params[3]->lvalue <= LongVector[0]))
2577         {
2578                 return LongVector[TP->Tokens->Params[2]->lvalue] == 
2579                         LongVector[TP->Tokens->Params[3]->lvalue];
2580         }
2581         else
2582         {
2583                 if ((TP->Tokens->Params[2]->Type == TYPE_LONG) ||
2584                     (TP->Tokens->Params[2]->Type == TYPE_LONG)) {
2585                         LogTemplateError(
2586                                 Target, "ConditionalLongvector", ERR_PARM1, TP,
2587                                 "needs two long Parameter!");
2588                 }
2589                 else {
2590                         LogTemplateError(
2591                                 Target, "Longvector", ERR_PARM1, TP,
2592                                 "doesn't have %ld / %ld Parameters, its just the size of %ld!",
2593                                 TP->Tokens->Params[2]->lvalue,
2594                                 TP->Tokens->Params[3]->lvalue,
2595                                 LongVector[0]);
2596                 }
2597         }
2598         return 0;
2599 }
2600
2601 void 
2602 InitModule_SUBST
2603 (void)
2604 {
2605         memset(&NoCtx, 0, sizeof(WCTemplputParams));
2606         RegisterNamespace("SORT:ICON", 1, 2, tmplput_SORT_ICON, CTX_NONE);
2607         RegisterNamespace("SORT:ORDER", 1, 2, tmplput_SORT_ORDER, CTX_NONE);
2608         RegisterNamespace("SORT:NEXT", 1, 2, tmplput_SORT_NEXT, CTX_NONE);
2609         RegisterNamespace("CONTEXTSTR", 0, 1, tmplput_ContextString, CTX_STRBUF);
2610         RegisterNamespace("ITERATE", 2, 100, tmpl_iterate_subtmpl, CTX_NONE);
2611         RegisterNamespace("DOBOXED", 1, 2, tmpl_do_boxed, CTX_NONE);
2612         RegisterNamespace("DOTABBED", 2, 100, tmpl_do_tabbed, CTX_NONE);
2613         RegisterNamespace("LONGVECTOR", 1, 1, tmplput_long_vector, CTX_LONGVECTOR);
2614         RegisterConditional(HKEY("COND:SUBST"), 3, ConditionalVar, CTX_NONE);
2615         RegisterConditional(HKEY("COND:CONTEXTSTR"), 3, ConditionalContextStr, CTX_STRBUF);
2616         RegisterConditional(HKEY("COND:LONGVECTOR"), 4, ConditionalLongVector, CTX_LONGVECTOR);
2617
2618         RegisterControlConditional(HKEY("COND:ITERATE:ISGROUPCHANGE"), 2, 
2619                                    conditional_ITERATE_ISGROUPCHANGE, 
2620                                    CTX_ITERATE);
2621         RegisterControlConditional(HKEY("COND:ITERATE:LASTN"), 2, 
2622                                    conditional_ITERATE_LASTN, 
2623                                    CTX_ITERATE);
2624         RegisterControlConditional(HKEY("COND:ITERATE:FIRSTN"), 2, 
2625                                    conditional_ITERATE_FIRSTN, 
2626                                    CTX_ITERATE);
2627
2628         RegisterControlNS(HKEY("ITERATE:ODDEVEN"), 0, 0, tmplput_ITERATE_ODDEVEN, CTX_ITERATE);
2629         RegisterControlNS(HKEY("ITERATE:KEY"), 0, 0, tmplput_ITERATE_KEY, CTX_ITERATE);
2630         RegisterControlNS(HKEY("ITERATE:N"), 0, 0, tmplput_ITERATE_LASTN, CTX_ITERATE);
2631 }
2632
2633 void
2634 ServerStartModule_SUBST
2635 (void)
2636 {
2637         WirelessTemplateCache = NewHash(1, NULL);
2638         WirelessLocalTemplateCache = NewHash(1, NULL);
2639         LocalTemplateCache = NewHash(1, NULL);
2640         TemplateCache = NewHash(1, NULL);
2641
2642         GlobalNS = NewHash(1, NULL);
2643         Iterators = NewHash(1, NULL);
2644         Conditionals = NewHash(1, NULL);
2645         SortHash = NewHash(1, NULL);
2646 }
2647
2648 void
2649 FinalizeModule_SUBST
2650 (void)
2651 {
2652
2653 }
2654
2655 void 
2656 ServerShutdownModule_SUBST
2657 (void)
2658 {
2659         DeleteHash(&WirelessTemplateCache);
2660         DeleteHash(&WirelessLocalTemplateCache);
2661         DeleteHash(&TemplateCache);
2662         DeleteHash(&LocalTemplateCache);
2663
2664         DeleteHash(&GlobalNS);
2665         DeleteHash(&Iterators);
2666         DeleteHash(&Conditionals);
2667         DeleteHash(&SortHash);
2668
2669 }
2670
2671
2672 void
2673 SessionNewModule_SUBST
2674 (wcsession *sess)
2675 {
2676
2677 }
2678
2679 void
2680 SessionAttachModule_SUBST
2681 (wcsession *sess)
2682 {
2683         sess->vars = NewHash(1,NULL);
2684 }
2685
2686 void
2687 SessionDetachModule_SUBST
2688 (wcsession *sess)
2689 {
2690         DeleteHash(&sess->vars);
2691 }
2692
2693 void 
2694 SessionDestroyModule_SUBST  
2695 (wcsession *sess)
2696 {
2697
2698 }