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