* --pedantic cleanup.
[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: depricated! */
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 const char* PrintTemplate(void *vSubst)
1094 {
1095         WCTemplate *Tmpl = vSubst;
1096
1097         return ChrPtr(Tmpl->FileName);
1098
1099 }
1100
1101 int LoadTemplateDir(const char *DirName, HashList *wireless, HashList *big)
1102 {
1103         StrBuf *FileName;
1104         StrBuf *Tag;
1105         StrBuf *Dir;
1106         DIR *filedir = NULL;
1107         struct dirent *filedir_entry;
1108         int d_namelen;
1109         int d_without_ext;
1110         int IsMobile;
1111         
1112         Dir = NewStrBuf();
1113         StrBufPrintf(Dir, "%s/t", DirName);
1114         filedir = opendir (ChrPtr(Dir));
1115         if (filedir == NULL) {
1116                 FreeStrBuf(&Dir);
1117                 return 0;
1118         }
1119
1120         FileName = NewStrBuf();
1121         Tag = NewStrBuf();
1122         while ((filedir_entry = readdir(filedir)))
1123         {
1124                 char *MinorPtr;
1125                 char *PStart;
1126 #ifdef _DIRENT_HAVE_D_NAMELEN
1127                 d_namelen = filedir_entry->d_namelen;
1128 #else
1129                 d_namelen = strlen(filedir_entry->d_name);
1130 #endif
1131                 d_without_ext = d_namelen;
1132                 while ((d_without_ext > 0) && (filedir_entry->d_name[d_without_ext] != '.'))
1133                         d_without_ext --;
1134                 if ((d_without_ext == 0) || (d_namelen < 3))
1135                         continue;
1136                 if ((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~')
1137                         continue; /* Ignore backup files... */
1138
1139                 IsMobile = (strstr(filedir_entry->d_name, ".m.html")!= NULL);
1140                 PStart = filedir_entry->d_name;
1141                 StrBufPrintf(FileName, "%s/%s", ChrPtr(Dir),  filedir_entry->d_name);
1142                 MinorPtr = strchr(filedir_entry->d_name, '.');
1143                 if (MinorPtr != NULL)
1144                         *MinorPtr = '\0';
1145                 StrBufPlain(Tag, filedir_entry->d_name, MinorPtr - filedir_entry->d_name);
1146
1147                 if (LoadTemplates > 1)
1148                         lprintf(1, "%s %d %s\n",ChrPtr(FileName), IsMobile, ChrPtr(Tag));
1149                 if (LoadTemplates == 0)
1150                         load_template(FileName, Tag, (IsMobile)?wireless:big);
1151                 else
1152                         prepare_template(FileName, Tag, (IsMobile)?wireless:big);
1153         }
1154         closedir(filedir);
1155         FreeStrBuf(&FileName);
1156         FreeStrBuf(&Tag);
1157         FreeStrBuf(&Dir);
1158         return 1;
1159 }
1160
1161 void InitTemplateCache(void)
1162 {
1163         LoadTemplateDir(static_dirs[0],
1164                         WirelessTemplateCache,
1165                         TemplateCache);
1166         LoadTemplateDir(static_dirs[1],
1167                         WirelessLocalTemplateCache,
1168                         LocalTemplateCache);
1169 }
1170
1171
1172
1173 /*-----------------------------------------------------------------------------
1174  *                      Filling & processing Templates
1175  */
1176 /**
1177  * \brief executes one token
1178  * \param Target buffer to append to
1179  * \param Token da to  process.
1180  * \param Template we're iterating
1181  * \param Context Contextpoointer to pass in
1182  * \param state are we in conditional state?
1183  * \param ContextType what type of information does context giv us?
1184  */
1185 int EvaluateToken(StrBuf *Target, WCTemplateToken *Tokens, WCTemplate *pTmpl, void *Context, int state, int ContextType)
1186 {
1187         const char *AppendMe;
1188         long AppendMeLen;
1189         HashHandler *Handler;
1190         void *vVar;
1191 /* much output, since pName is not terminated...
1192         lprintf(1,"Doing token: %s\n",Token->pName);
1193 */
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 #if 0
1435                 dbg_PrintHash(Static, PrintTemplate, NULL);
1436                 PrintHash(Static, VarPrintTransition, PrintTemplate);
1437 #endif
1438                 return;
1439         }
1440         if (vTmpl == NULL) 
1441                 return;
1442         ProcessTemplate(vTmpl, Target, Context, ContextType);
1443 }
1444
1445 /*-----------------------------------------------------------------------------
1446  *                      Iterators
1447  */
1448 typedef struct _HashIterator {
1449         HashList *StaticList;
1450         int AdditionalParams;
1451         int ContextType;
1452         int XPectContextType;
1453         RetrieveHashlistFunc GetHash;
1454         HashDestructorFunc Destructor;
1455         SubTemplFunc DoSubTemplate;
1456 } HashIterator;
1457
1458 void RegisterITERATOR(const char *Name, long len, 
1459                       int AdditionalParams, 
1460                       HashList *StaticList, 
1461                       RetrieveHashlistFunc GetHash, 
1462                       SubTemplFunc DoSubTempl,
1463                       HashDestructorFunc Destructor,
1464                       int ContextType, 
1465                       int XPectContextType)
1466 {
1467         HashIterator *It = (HashIterator*)malloc(sizeof(HashIterator));
1468         It->StaticList = StaticList;
1469         It->AdditionalParams = AdditionalParams;
1470         It->GetHash = GetHash;
1471         It->DoSubTemplate = DoSubTempl;
1472         It->Destructor = Destructor;
1473         It->ContextType = ContextType;
1474         It->XPectContextType = XPectContextType;
1475         Put(Iterators, Name, len, It, NULL);
1476 }
1477
1478 void tmpl_iterate_subtmpl(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
1479 {
1480         void *vIt;
1481         HashIterator *It;
1482         HashList *List;
1483         HashPos  *it;
1484         int nMembersUsed;
1485         int nMembersCounted = 0;
1486         long len; 
1487         const char *Key;
1488         void *vContext;
1489         StrBuf *SubBuf;
1490         int oddeven = 0;
1491         
1492         if (!GetHash(Iterators, TKEY(0), &vIt)) {
1493                 lprintf(1, "unknown Iterator [%s] (in '%s' line %ld); "
1494                         " [%s]\n", 
1495                         Tokens->Params[0]->Start,
1496                         ChrPtr(Tokens->FileName),
1497                         Tokens->Line,
1498                         ChrPtr(Tokens->FlatToken));
1499                 StrBufAppendPrintf(
1500                         Target,
1501                         "<pre>\nunknown Iterator [%s] (in '%s' line %ld); \n"
1502                         " [%s]\n</pre>", 
1503                         Tokens->Params[0]->Start,
1504                         ChrPtr(Tokens->FileName),
1505                         Tokens->Line,
1506                         ChrPtr(Tokens->FlatToken));
1507                 return;
1508         }
1509
1510         It = (HashIterator*) vIt;
1511
1512         if (Tokens->nParameters < It->AdditionalParams + 2) {
1513                 lprintf(1, "Iterator [%s] (in '%s' line %ld); "
1514                         "doesn't work with %ld params [%s]\n", 
1515                         Tokens->Params[0]->Start,
1516                         ChrPtr(Tokens->FileName),
1517                         Tokens->Line,
1518                         Tokens->nParameters, 
1519                         ChrPtr(Tokens->FlatToken));
1520                 StrBufAppendPrintf(
1521                         Target,
1522                         "<pre>Iterator [%s] \n(in '%s' line %ld);\n"
1523                         "doesn't work with %ld params \n[%s]\n</pre>", 
1524                         Tokens->Params[0]->Start,
1525                         ChrPtr(Tokens->FileName),
1526                         Tokens->Line,
1527                         Tokens->nParameters, 
1528                         ChrPtr(Tokens->FlatToken));
1529                 return;
1530         }
1531
1532         if ((It->XPectContextType != CTX_NONE) &&
1533             (It->XPectContextType != ContextType)) {
1534                 lprintf(1, "Iterator [%s] (in '%s' line %ld); "
1535                         "requires context of type %ld, have %ld [%s]\n", 
1536                         Tokens->pName,
1537                         ChrPtr(Tokens->FileName),
1538                         Tokens->Line,
1539                         It->XPectContextType, 
1540                         ContextType,
1541                         ChrPtr(Tokens->FlatToken));
1542                 StrBufAppendPrintf(
1543                         Target, 
1544                         "<pre>\nIterator [%s] (in '%s' line %ld);"
1545                         " requires context of type %ld, have %ld!\n[%s]\n</pre>\n", 
1546                         Tokens->pName,
1547                         ChrPtr(Tokens->FileName),
1548                         Tokens->Line,
1549                         It->XPectContextType, 
1550                         ContextType,
1551                         ChrPtr(Tokens->FlatToken));
1552                 return ;
1553                 
1554         }
1555
1556         if (It->StaticList == NULL)
1557                 List = It->GetHash(Target, nArgs, Tokens, Context, ContextType);
1558         else
1559                 List = It->StaticList;
1560
1561         nMembersUsed = GetCount(List);
1562         SubBuf = NewStrBuf();
1563         it = GetNewHashPos(List, 0);
1564         while (GetNextHashPos(List, it, &len, &Key, &vContext)) {
1565                 svprintf(HKEY("ITERATE:ODDEVEN"), WCS_STRING, "%s", 
1566                          (oddeven) ? "odd" : "even");
1567                 svprintf(HKEY("ITERATE:KEY"), WCS_STRING, "%s", Key);
1568                 svputlong("ITERATE:N", nMembersCounted);
1569                 svputlong("ITERATE:LASTN", ++nMembersCounted == nMembersUsed);
1570
1571                 if (It->DoSubTemplate != NULL)
1572                         It->DoSubTemplate(SubBuf, vContext, Tokens);
1573                 DoTemplate(TKEY(1), SubBuf, vContext, It->ContextType);
1574                         
1575                 StrBufAppendBuf(Target, SubBuf, 0);
1576                 FlushStrBuf(SubBuf);
1577                 oddeven = ~ oddeven;
1578         }
1579         FreeStrBuf(&SubBuf);
1580         DeleteHashPos(&it);
1581         if (It->Destructor != NULL)
1582                 It->Destructor(&List);
1583 }
1584
1585
1586
1587 /*-----------------------------------------------------------------------------
1588  *                      Conditionals
1589  */
1590 int EvaluateConditional(StrBuf *Target, WCTemplateToken *Tokens, WCTemplate *pTmpl, void *Context, int Neg, int state, int ContextType)
1591 {
1592         ConditionalStruct *Cond;
1593
1594         if ((Tokens->Params[0]->len == 1) &&
1595             (Tokens->Params[0]->Start[0] == 'X'))
1596                 return (state != 0)?Tokens->Params[1]->lvalue:0;
1597             
1598         Cond = (ConditionalStruct *) Tokens->PreEval;
1599         if (Cond == NULL) {
1600                 lprintf(1, "Conditional [%s] (in '%s' line %ld); unknown![%s]\n", 
1601                         Tokens->Params[0]->Start,
1602                         ChrPtr(pTmpl->FileName),
1603                         Tokens->Line,
1604                         ChrPtr(Tokens->FlatToken));
1605                 return 1;
1606         }
1607
1608         if (Tokens->nParameters < Cond->nParams) {
1609                 lprintf(1, "Conditional [%s] (in '%s' line %ld); needs %ld Params![%s]\n", 
1610                         Tokens->Params[0]->Start,
1611                         ChrPtr(pTmpl->FileName),
1612                         Tokens->Line,
1613                         Cond->nParams,
1614                         ChrPtr(Tokens->FlatToken));
1615                 StrBufAppendPrintf(
1616                         Target, 
1617                         "<pre>\nConditional [%s] (in '%s' line %ld); needs %ld Params!\n[%s]\n</pre>\n", 
1618                         Tokens->Params[0]->Start,
1619                         ChrPtr(pTmpl->FileName),
1620                         Tokens->Line,
1621                         Cond->nParams,
1622                         ChrPtr(Tokens->FlatToken));
1623                 return 0;
1624         }
1625         if (Cond->CondF(Tokens, Context, ContextType) == Neg)
1626                 return Tokens->Params[1]->lvalue;
1627         return 0;
1628 }
1629
1630 int ConditionalVar(WCTemplateToken *Tokens, void *Context, int ContextType)
1631 {
1632         void *vsubst;
1633         wcsubst *subst;
1634         
1635         if (!GetHash(WC->vars, TKEY(2), &vsubst))
1636                 return 0;
1637         subst = (wcsubst*) vsubst;
1638         if ((subst->ContextRequired != CTX_NONE) &&
1639             (subst->ContextRequired != ContextType)) {
1640                 lprintf(1,"  WARNING: Conditional requires Context: [%ld]!\n", Tokens->Params[2]->Start);
1641                 return -1;
1642         }
1643
1644         switch(subst->wcs_type) {
1645         case WCS_FUNCTION:
1646                 return (subst->wcs_function!=NULL);
1647         case WCS_SERVCMD:
1648                 lprintf(1, "  -> Server [%s]\n", subst->wcs_value);/* TODO */
1649                 return 1;
1650         case WCS_STRING:
1651         case WCS_STRBUF:
1652         case WCS_STRBUF_REF:
1653                 if (Tokens->nParameters < 4)
1654                         return 1;
1655                 return (strcmp(Tokens->Params[3]->Start, ChrPtr(subst->wcs_value)) == 0);
1656         case WCS_LONG:
1657                 if (Tokens->nParameters < 4)
1658                         return (subst->lvalue != 0);
1659                 return (subst->lvalue == Tokens->Params[3]->lvalue);
1660         default:
1661                 lprintf(1,"  WARNING: invalid type: [%ld]!\n", subst->wcs_type);
1662                 return -1;
1663         }
1664         return 0;
1665 }
1666
1667
1668 void RegisterConditional(const char *Name, long len, 
1669                          int nParams,
1670                          WCConditionalFunc CondF, 
1671                          int ContextRequired)
1672 {
1673         ConditionalStruct *Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
1674         Cond->PlainName = Name;
1675         Cond->nParams = nParams;
1676         Cond->CondF = CondF;
1677         Cond->ContextRequired = ContextRequired;
1678         Put(Conditionals, Name, len, Cond, NULL);
1679 }
1680
1681 /*-----------------------------------------------------------------------------
1682  *                      Context Strings
1683  */
1684 void tmplput_ContextString(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
1685 {
1686         StrBufAppendTemplate(Target, nArgs, Tokens, Context, ContextType, (StrBuf*)Context, 0);
1687 }
1688 int ConditionalContextStr(WCTemplateToken *Tokens, void *Context, int ContextType)
1689 {
1690         StrBuf *TokenText = (StrBuf*) Context;
1691         const char *CompareToken;
1692         long len;
1693
1694         GetTemplateTokenString(Tokens, 2, &CompareToken, &len);
1695         return strcmp(ChrPtr(TokenText), CompareToken) == 0;
1696 }
1697
1698 /*-----------------------------------------------------------------------------
1699  *                      Boxed-API
1700  */
1701
1702 void tmpl_do_boxed(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
1703 {
1704         StrBuf *Headline;
1705         if (nArgs == 2) {
1706                 if (Tokens->Params[1]->Type == TYPE_STR) {
1707                         Headline = NewStrBuf();
1708                         DoTemplate(TKEY(1), Headline, Context, ContextType);
1709                 }
1710                 else {
1711                         const char *Ch;
1712                         long len;
1713                         GetTemplateTokenString(Tokens, 
1714                                                1,
1715                                                &Ch,
1716                                                &len);
1717                         Headline = NewStrBufPlain(Ch, len);
1718                 }
1719         }
1720         
1721         DoTemplate(HKEY("beginbox"), Target, Headline, CTX_STRBUF);
1722         DoTemplate(TKEY(0), Target, Context, ContextType);
1723         DoTemplate(HKEY("endbox"), Target, Context, ContextType);
1724         FreeStrBuf(&Headline);
1725 }
1726
1727 /*-----------------------------------------------------------------------------
1728  *                      Tabbed-API
1729  */
1730
1731 void tmpl_do_tabbed(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
1732 {
1733         StrBuf **TabNames;
1734         int i, ntabs, nTabs;
1735
1736         nTabs = ntabs = Tokens->nParameters / 2;
1737         TabNames = (StrBuf **) malloc(ntabs * sizeof(StrBuf*));
1738
1739         for (i = 0; i < ntabs; i++) {
1740                 TabNames[i] = NewStrBuf();
1741                 if (Tokens->Params[i * 2]->len > 0) {
1742                         DoTemplate(TKEY(i * 2), TabNames[i], Context, ContextType);
1743                 }
1744                 else { 
1745                         /** A Tab without subject? we can't count that, add it as silent */
1746                         nTabs --;
1747                 }
1748         }
1749
1750         StrTabbedDialog(Target, nTabs, TabNames);
1751         for (i = 0; i < ntabs; i++) {
1752                 StrBeginTab(Target, i, nTabs);
1753
1754                 DoTemplate(TKEY(i * 2 + 1), Target, Context, ContextType);
1755                 StrEndTab(Target, i, nTabs);
1756         }
1757 }
1758
1759
1760 /*-----------------------------------------------------------------------------
1761  *                      Sorting-API
1762  */
1763
1764 typedef struct _SortStruct {
1765         StrBuf *Name;
1766         StrBuf *PrefPrepend;
1767         CompareFunc Forward;
1768         CompareFunc Reverse;
1769
1770         long ContextType;
1771 }SortStruct;
1772
1773 void DestroySortStruct(void *vSort)
1774 {
1775         SortStruct *Sort = (SortStruct*) vSort;
1776         FreeStrBuf(&Sort->Name);
1777         FreeStrBuf(&Sort->PrefPrepend);
1778         free (Sort);
1779 }
1780
1781 void RegisterSortFunc(const char *name, long len, 
1782                       const char *prepend, long preplen,
1783                       CompareFunc Forward, 
1784                       CompareFunc Reverse, 
1785                       long ContextType)
1786 {
1787         SortStruct *NewSort = (SortStruct*) malloc(sizeof(SortStruct));
1788         NewSort->Name = NewStrBufPlain(name, len);
1789         if (prepend != NULL)
1790                 NewSort->PrefPrepend = NewStrBufPlain(prepend, preplen);
1791         else
1792                 NewSort->PrefPrepend = NULL;
1793         NewSort->Forward = Forward;
1794         NewSort->Reverse = Reverse;
1795         NewSort->ContextType = ContextType;
1796         Put(SortHash, name, len, NewSort, DestroySortStruct);
1797 }
1798
1799 CompareFunc RetrieveSort(long ContextType, const char *OtherPrefix, 
1800                          const char *Default, long ldefault, long DefaultDirection)
1801 {
1802         int isdefault = 0;
1803         const StrBuf *BSort;
1804         SortStruct *SortBy;
1805         void *vSortBy;
1806         long SortOrder = -1;
1807         
1808         if (havebstr("SortBy")) {
1809                 BSort = sbstr("SortBy");
1810         }
1811         else { /** Try to fallback to our remembered values... */
1812                 if (OtherPrefix == NULL) {
1813                         BSort = get_room_pref("sort");
1814                 }
1815                 else {
1816                         /*TODO: nail prefprepend to sort, and lookup this! */
1817                 }
1818                 if (BSort != NULL)
1819                         putbstr("SortBy", NewStrBufDup(BSort));
1820                 else {
1821                         StrBuf *Buf;
1822
1823                         BSort = Buf = NewStrBufPlain(Default, ldefault);
1824                         putbstr("SortBy", Buf);
1825                 }
1826         }
1827
1828         if (!GetHash(SortHash, SKEY(BSort), &vSortBy) || 
1829             (vSortBy == NULL)) {
1830                 isdefault = 1;
1831                 if (!GetHash(SortHash, Default, ldefault, &vSortBy) || 
1832                     (vSortBy == NULL)) {
1833                         lprintf(1, "Illegal default sort: [%s]\n", Default);
1834                         wc_backtrace();
1835                 }
1836         }
1837         SortBy = (SortStruct*)vSortBy;
1838
1839         /** Ok, its us, lets see in which direction we should sort... */
1840         if (havebstr("SortOrder")) {
1841                 SortOrder = LBSTR("SortOrder");
1842         }
1843         else { /** Try to fallback to our remembered values... */
1844                 StrBuf *Buf;
1845                 if (SortBy->PrefPrepend == NULL) {
1846                         Buf = get_room_pref("SortOrder");
1847                         SortOrder = StrTol(Buf);
1848                 }
1849                 else {
1850                         /* TODO: nail prefprepend to sort, and lookup this! */
1851                 }
1852
1853                 if (Buf == NULL)
1854                         SortOrder = DefaultDirection;
1855
1856                 Buf = NewStrBufPlain(NULL, 64);
1857                 StrBufPrintf(Buf, "%ld", SortOrder);
1858                 putbstr("SortOrder", Buf);
1859         }
1860         switch (SortOrder) {
1861         default:
1862         case 0:
1863                 return NULL;
1864         case 1:
1865                 return SortBy->Forward;
1866         case 2:
1867                 return SortBy->Reverse;
1868         }
1869 }
1870
1871
1872 enum {
1873         eNO_SUCH_SORT, 
1874         eNOT_SPECIFIED,
1875         eINVALID_PARAM,
1876         eFOUND
1877 };
1878
1879 ConstStr SortIcons[] = {
1880         {HKEY("static/sort_none.gif")},
1881         {HKEY("static/up_pointer.gif")},
1882         {HKEY("static/down_pointer.gif")},
1883 };
1884
1885 ConstStr SortNextOrder[] = {
1886         {HKEY("1")},
1887         {HKEY("2")},
1888         {HKEY("0")},
1889 };
1890
1891
1892 int GetSortMetric(WCTemplateToken *Tokens, SortStruct **Next, SortStruct **Param, long *SortOrder)
1893 {
1894         int bSortError = eNOT_SPECIFIED;
1895         const StrBuf *BSort;
1896         void *vSort;
1897         
1898         *SortOrder = 0;
1899         *Next = NULL;
1900         if (!GetHash(SortHash, TKEY(0), &vSort) || 
1901             (vSort == NULL))
1902                 return eNO_SUCH_SORT;
1903         *Param = (SortStruct*) vSort;
1904         
1905
1906         if (havebstr("SortBy")) {
1907                 BSort = sbstr("SortBy");
1908                 bSortError = eINVALID_PARAM;
1909         }
1910         else { /** Try to fallback to our remembered values... */
1911                 if ((*Param)->PrefPrepend == NULL) {
1912                         BSort = get_room_pref("sort");
1913                 }
1914                 else {
1915                         /* TODO: nail prefprepend to sort, and lookup this! */
1916                 }
1917         }
1918
1919         if (!GetHash(SortHash, SKEY(BSort), &vSort) || 
1920             (vSort == NULL))
1921                 return bSortError;
1922
1923         *Next = (SortStruct*) vSort;
1924
1925         /** Ok, its us, lets see in which direction we should sort... */
1926         if (havebstr("SortOrder")) {
1927                 *SortOrder = LBSTR("SortOrder");
1928         }
1929         else { /** Try to fallback to our remembered values... */
1930                 if ((*Param)->PrefPrepend == NULL) {
1931                         *SortOrder = StrTol(get_room_pref("SortOrder"));
1932                 }
1933                 else {
1934                         /* TODO: nail prefprepend to sort, and lookup this! */
1935                 }
1936         }
1937         if (*SortOrder > 2)
1938                 *SortOrder = 0;
1939
1940         return eFOUND;
1941 }
1942
1943
1944 void tmplput_SORT_ICON(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
1945 {
1946         long SortOrder;
1947         SortStruct *Next;
1948         SortStruct *Param;
1949         const ConstStr *SortIcon;
1950
1951         switch (GetSortMetric(Tokens, &Next, &Param, &SortOrder)){
1952         case eNO_SUCH_SORT:
1953                 lprintf(1, "[%s] (in '%s' line %ld); "
1954                         " Sorter [%s] unknown! [%s]\n", 
1955                         Tokens->pName,
1956                         ChrPtr(Tokens->FileName),
1957                         Tokens->Line,
1958                         Tokens->Params[0]->Start,
1959                         ChrPtr(Tokens->FlatToken));
1960                 StrBufAppendPrintf(
1961                         Target, 
1962                         "<pre>\n [%s] (in '%s' line %ld);"
1963                         " Sorter [%s] unknown!\n[%s]\n</pre>\n", 
1964                         Tokens->pName,
1965                         ChrPtr(Tokens->FileName),
1966                         Tokens->Line,
1967                         Tokens->Params[0]->Start,
1968                         ChrPtr(Tokens->FlatToken));
1969                 break;          
1970         case eINVALID_PARAM:
1971                 lprintf(1, "[%s] (in '%s' line %ld); "
1972                         " Sorter specified by BSTR 'SortBy' [%s] unknown! [%s]\n", 
1973                         Tokens->pName,
1974                         ChrPtr(Tokens->FileName),
1975                         Tokens->Line,
1976                         bstr("SortBy"),
1977                         ChrPtr(Tokens->FlatToken));
1978         case eNOT_SPECIFIED:
1979         case eFOUND:
1980                 if (Next == Param) {
1981                         SortIcon = &SortIcons[SortOrder];
1982                 }
1983                 else { /** Not Us... */
1984                         SortIcon = &SortIcons[0];
1985                 }
1986                 StrBufAppendBufPlain(Target, SortIcon->Key, SortIcon->len, 0);
1987         }
1988 }
1989
1990 void tmplput_SORT_NEXT(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
1991 {
1992         long SortOrder;
1993         SortStruct *Next;
1994         SortStruct *Param;
1995
1996         switch (GetSortMetric(Tokens, &Next, &Param, &SortOrder)){
1997         case eNO_SUCH_SORT:
1998                 lprintf(1, "[%s] (in '%s' line %ld); "
1999                         " Sorter [%s] unknown! [%s]\n", 
2000                         Tokens->pName,
2001                         ChrPtr(Tokens->FileName),
2002                         Tokens->Line,
2003                         Tokens->Params[0]->Start,
2004                         ChrPtr(Tokens->FlatToken));
2005                 StrBufAppendPrintf(
2006                         Target, 
2007                         "<pre>\n [%s] (in '%s' line %ld);"
2008                         " Sorter [%s] unknown!\n[%s]\n</pre>\n", 
2009                         Tokens->pName,
2010                         ChrPtr(Tokens->FileName),
2011                         Tokens->Line,
2012                         Tokens->Params[0]->Start,
2013                         ChrPtr(Tokens->FlatToken));
2014                 break;          
2015         case eINVALID_PARAM:
2016                 lprintf(1, "[%s] (in '%s' line %ld); "
2017                         " Sorter specified by BSTR 'SortBy' [%s] unknown! [%s]\n", 
2018                         Tokens->pName,
2019                         ChrPtr(Tokens->FileName),
2020                         Tokens->Line,
2021                         bstr("SortBy"),
2022                         ChrPtr(Tokens->FlatToken));
2023
2024         case eNOT_SPECIFIED:
2025         case eFOUND:
2026                 StrBufAppendBuf(Target, Param->Name, 0);
2027                 
2028         }
2029 }
2030
2031 void tmplput_SORT_ORDER(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
2032 {
2033         long SortOrder;
2034         const ConstStr *SortOrderStr;
2035         SortStruct *Next;
2036         SortStruct *Param;
2037
2038         switch (GetSortMetric(Tokens, &Next, &Param, &SortOrder)){
2039         case eNO_SUCH_SORT:
2040                 lprintf(1, "[%s] (in '%s' line %ld); "
2041                         " Sorter [%s] unknown! [%s]\n", 
2042                         Tokens->pName,
2043                         ChrPtr(Tokens->FileName),
2044                         Tokens->Line,
2045                         Tokens->Params[0]->Start,
2046                         ChrPtr(Tokens->FlatToken));
2047                 StrBufAppendPrintf(
2048                         Target, 
2049                         "<pre>\n [%s] (in '%s' line %ld);"
2050                         " Sorter [%s] unknown!\n[%s]\n</pre>\n", 
2051                         Tokens->pName,
2052                         ChrPtr(Tokens->FileName),
2053                         Tokens->Line,
2054                         Tokens->Params[0]->Start,
2055                         ChrPtr(Tokens->FlatToken));
2056                 break;          
2057         case eINVALID_PARAM:
2058                 lprintf(1, "[%s] (in '%s' line %ld); "
2059                         " Sorter specified by BSTR 'SortBy' [%s] unknown! [%s]\n", 
2060                         Tokens->pName,
2061                         ChrPtr(Tokens->FileName),
2062                         Tokens->Line,
2063                         bstr("SortBy"),
2064                         ChrPtr(Tokens->FlatToken));
2065
2066         case eNOT_SPECIFIED:
2067         case eFOUND:
2068                 if (Next == Param) {
2069                         SortOrderStr = &SortNextOrder[SortOrder];
2070                 }
2071                 else { /** Not Us... */
2072                         SortOrderStr = &SortNextOrder[0];
2073                 }
2074                 StrBufAppendBufPlain(Target, SortOrderStr->Key, SortOrderStr->len, 0);
2075         }
2076 }
2077
2078
2079 void tmplput_long_vector(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
2080 {
2081         long *LongVector = (long*) Context;
2082
2083         if ((Tokens->Params[0]->Type == TYPE_LONG) && 
2084             (Tokens->Params[0]->lvalue <= LongVector[0]))
2085         {
2086                 StrBufAppendPrintf(Target, "%ld", LongVector[Tokens->Params[0]->lvalue]);
2087         }
2088         else
2089         {
2090                 if (Tokens->Params[0]->Type == TYPE_LONG) {
2091                         lprintf(1, "longvector [%s] (in '%s' line %ld); needs a long Parameter![%s]\n", 
2092                                 Tokens->Params[0]->Start,
2093                                 ChrPtr(Tokens->FileName),
2094                                 Tokens->Line,
2095                                 ChrPtr(Tokens->FlatToken));
2096                         StrBufAppendPrintf(
2097                                 Target, 
2098                                 "<pre>\nlongvector [%s] (in '%s' line %ld); needs a numerical Parameter!\n[%s]\n</pre>\n", 
2099                                 Tokens->Params[0]->Start,
2100                                 ChrPtr(Tokens->FileName),
2101                                 Tokens->Line,
2102                                 ChrPtr(Tokens->FlatToken));
2103                 }
2104                 else {
2105                         lprintf(1, "longvector [%s] (in '%s' line %ld); doesn't have %ld Parameters,"
2106                                 " its just the size of %ld![%s]\n", 
2107                                 Tokens->Params[0]->Start,
2108                                 ChrPtr(Tokens->FileName),
2109                                 Tokens->Line,
2110                                 Tokens->Params[0]->lvalue,
2111                                 LongVector[0],
2112                                 ChrPtr(Tokens->FlatToken));
2113                         StrBufAppendPrintf(
2114                                 Target, 
2115                                 "<pre>\nlongvector [%s] (in '%s' line %ld); doesn't have %ld Parameters,"
2116                                 " its just the size of %ld!\n[%s]\n</pre>\n", 
2117                                 Tokens->Params[0]->Start,
2118                                 ChrPtr(Tokens->FileName),
2119                                 Tokens->Line,
2120                                 Tokens->Params[0]->lvalue,
2121                                 LongVector[0],
2122                                 ChrPtr(Tokens->FlatToken));             
2123                 }
2124         }
2125 }
2126
2127 void dbg_print_longvector(long *LongVector)
2128 {
2129         StrBuf *Buf = NewStrBufPlain(HKEY("Longvector: ["));
2130         int nItems = LongVector[0];
2131         int i;
2132
2133         for (i = 0; i < nItems; i++) {
2134                 if (i + 1 < nItems)
2135                         StrBufAppendPrintf(Buf, "%d: %ld | ", i, LongVector[i]);
2136                 else
2137                         StrBufAppendPrintf(Buf, "%d: %ld]\n", i, LongVector[i]);
2138
2139         }
2140         lprintf(1, ChrPtr(Buf));
2141         FreeStrBuf(&Buf);
2142 }
2143
2144 int ConditionalLongVector(WCTemplateToken *Tokens, void *Context, int ContextType)
2145 {
2146         long *LongVector = (long*) Context;
2147
2148         if ((Tokens->Params[2]->Type == TYPE_LONG) && 
2149             (Tokens->Params[2]->lvalue <= LongVector[0])&&
2150             (Tokens->Params[3]->Type == TYPE_LONG) && 
2151             (Tokens->Params[3]->lvalue <= LongVector[0]))
2152         {
2153                 return LongVector[Tokens->Params[2]->lvalue] == LongVector[Tokens->Params[3]->lvalue];
2154         }
2155         else
2156         {
2157                 if ((Tokens->Params[2]->Type == TYPE_LONG) ||
2158                     (Tokens->Params[2]->Type == TYPE_LONG)) {
2159                         lprintf(1, "ConditionalLongVector [%s] (in '%s' line %ld); needs two long Parameter![%s]\n", 
2160                                 Tokens->Params[0]->Start,
2161                                 ChrPtr(Tokens->FileName),
2162                                 Tokens->Line,
2163                                 ChrPtr(Tokens->FlatToken));
2164                 }
2165                 else {
2166                         lprintf(1, "longvector [%s] (in '%s' line %ld); doesn't have %ld / %ld Parameters,"
2167                                 " its just the size of %ld![%s]\n", 
2168                                 Tokens->Params[0]->Start,
2169                                 ChrPtr(Tokens->FileName),
2170                                 Tokens->Line,
2171                                 Tokens->Params[2]->lvalue,
2172                                 Tokens->Params[3]->lvalue,
2173                                 LongVector[0],
2174                                 ChrPtr(Tokens->FlatToken));
2175                 }
2176         }
2177         return 0;
2178 }
2179
2180 void 
2181 InitModule_SUBST
2182 (void)
2183 {
2184         RegisterNamespace("SORT:ICON", 1, 1, tmplput_SORT_ICON, CTX_NONE);
2185         RegisterNamespace("SORT:ORDER", 1, 1, tmplput_SORT_ORDER, CTX_NONE);
2186         RegisterNamespace("SORT:NEXT", 1, 1, tmplput_SORT_NEXT, CTX_NONE);
2187         RegisterNamespace("CONTEXTSTR", 0, 1, tmplput_ContextString, CTX_STRBUF);
2188         RegisterNamespace("ITERATE", 2, 100, tmpl_iterate_subtmpl, CTX_NONE);
2189         RegisterNamespace("DOBOXED", 1, 2, tmpl_do_boxed, CTX_NONE);
2190         RegisterNamespace("DOTABBED", 2, 100, tmpl_do_tabbed, CTX_NONE);
2191         RegisterNamespace("LONGVECTOR", 1, 1, tmplput_long_vector, CTX_LONGVECTOR);
2192         RegisterConditional(HKEY("COND:SUBST"), 3, ConditionalVar, CTX_NONE);
2193         RegisterConditional(HKEY("COND:CONTEXTSTR"), 3, ConditionalContextStr, CTX_STRBUF);
2194         RegisterConditional(HKEY("COND:LONGVECTOR"), 4, ConditionalLongVector, CTX_LONGVECTOR);
2195 }
2196
2197 /*@}*/