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