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