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