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