* follow api-change in several places
[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(struct 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         struct 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         struct 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         struct 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         struct 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         struct 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         struct 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         struct 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(Tokens->Params[N]->Start, 
489                                       Tokens->Params[N]->len);
490                 *Value = ChrPtr(Buf);
491                 *len = StrLength(Buf);
492                 break;
493         case TYPE_PREFSTR:
494                 get_PREFERENCE(
495                         Tokens->Params[N]->Start, 
496                         Tokens->Params[N]->len, 
497                         &Buf);
498                 *Value = ChrPtr(Buf);
499                 *len = StrLength(Buf);
500                 break;
501         case TYPE_LONG:
502         case TYPE_PREFINT:
503                 break; ///todo: string to text?
504         case TYPE_GETTEXT:
505                 *Value = _(Tokens->Params[N]->Start);
506                 *len = strlen(*Value);
507                 break;
508         default:
509                 break;
510 //todo log error
511         }
512 }
513
514
515
516 /**
517  * \brief Print the value of a variable
518  * \param keyname get a key to print
519  */
520 void print_value_of(StrBuf *Target, WCTemplateToken *Tokens, void *Context, int ContextType) {
521         struct wcsession *WCC = WC;
522         wcsubst *ptr;
523         void *vVar;
524
525         /*if (WCC->vars != NULL) PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
526         /// TODO: debricated!
527         if (Tokens->pName[0] == '=') {
528                 DoTemplate(Tokens->pName+1, Tokens->NameEnd - 1, NULL, NULL, 0);
529         }
530
531 //////TODO: if param[1] == "U" -> urlescape
532 /// X -> escputs
533         /** Page-local variables */
534         if ((WCC->vars!= NULL) && GetHash(WCC->vars, Tokens->pName, Tokens->NameEnd, &vVar)) {
535                 ptr = (wcsubst*) vVar;
536                 switch(ptr->wcs_type) {
537                 case WCS_STRING:
538                         StrBufAppendBuf(Target, ptr->wcs_value, 0);
539                         break;
540                 case WCS_SERVCMD:
541                         pvo_do_cmd(Target, ptr->wcs_value);
542                         break;
543                 case WCS_FUNCTION:
544                         (*ptr->wcs_function) (Target, Tokens->nParameters, Tokens, Context, ContextType);
545                         break;
546                 case WCS_STRBUF:
547                 case WCS_STRBUF_REF:
548                         StrBufAppendBuf(Target, ptr->wcs_value, 0);
549                         break;
550                 case WCS_LONG:
551                         StrBufAppendPrintf(Target, "%ld", ptr->lvalue);
552                         break;
553                 default:
554                         lprintf(1,"WARNING: invalid value in SV-Hash at %s!\n", Tokens->pName);
555                         StrBufAppendPrintf(Target, "<pre>WARNING: \ninvalid value in SV-Hash at %s!\n</pre>", Tokens->pName);
556                 }
557         }
558         else {
559                 lprintf(1, "didn't find Handler [%s] (in '%s' line %ld); "
560                         " [%s]\n", 
561                         Tokens->pName,
562                         ChrPtr(Tokens->FileName),
563                         Tokens->Line,
564                         ChrPtr(Tokens->FlatToken));
565                 wc_backtrace();
566         }
567 }
568
569 int CompareSubstToToken(TemplateParam *ParamToCompare, TemplateParam *ParamToLookup)
570 {
571         struct wcsession *WCC = WC;
572         wcsubst *ptr;
573         void *vVar;
574
575         if ((WCC->vars!= NULL) && GetHash(WCC->vars, ParamToLookup->Start, 
576                                           ParamToLookup->len, &vVar)) {
577                 ptr = (wcsubst*) vVar;
578                 switch(ptr->wcs_type) {
579                 case WCS_STRING:
580                 case WCS_STRBUF:
581                 case WCS_STRBUF_REF:
582                         if (ParamToCompare->Type == TYPE_STR)
583                                 return ((ParamToCompare->len == StrLength(ptr->wcs_value)) &&
584                                         (strcmp(ParamToCompare->Start, ChrPtr(ptr->wcs_value)) == 0));
585                         else
586                                 return ParamToCompare->lvalue == StrTol(ptr->wcs_value);
587                         break;
588                 case WCS_SERVCMD:
589                         return 1; 
590                         break;
591                 case WCS_FUNCTION:
592                         return 1;
593                 case WCS_LONG:
594                         if (ParamToCompare->Type == TYPE_STR)
595                                 return 0;
596                         else 
597                                 return ParamToCompare->lvalue == ptr->lvalue;
598                         break;
599                 default:
600                         lprintf(1,"WARNING: invalid value in SV-Hash at %s!\n", 
601                                 ParamToLookup->Start);
602                 }
603         }
604         return 0;
605 }
606
607 int CompareSubstToStrBuf(StrBuf *Compare, TemplateParam *ParamToLookup)
608 {
609         struct wcsession *WCC = WC;
610         wcsubst *ptr;
611         void *vVar;
612
613         if ((WCC->vars!= NULL) && GetHash(WCC->vars, ParamToLookup->Start, 
614                                           ParamToLookup->len, &vVar)) {
615                 ptr = (wcsubst*) vVar;
616                 switch(ptr->wcs_type) {
617                 case WCS_STRING:
618                 case WCS_STRBUF:
619                 case WCS_STRBUF_REF:
620                         return ((StrLength(Compare) == StrLength(ptr->wcs_value)) &&
621                                 (strcmp(ChrPtr(Compare), ChrPtr(ptr->wcs_value)) == 0));
622                 case WCS_SERVCMD:
623                         return 1; 
624                         break;
625                 case WCS_FUNCTION:
626                         return 1;
627                 case WCS_LONG:
628                         return StrTol(Compare) == ptr->lvalue;
629                 default:
630                         lprintf(1,"WARNING: invalid value in SV-Hash at %s!\n", 
631                                 ParamToLookup->Start);
632                 }
633         }
634         return 0;
635 }
636
637
638
639 /**
640  * \brief puts string into the template and computes which escape methon we should use
641  * \param Source the string we should put into the template
642  * \param FormatTypeIndex where should we look for escape types if?
643  */
644 void StrBufAppendTemplate(StrBuf *Target, 
645                           int nArgs, 
646                           WCTemplateToken *Tokens,
647                           void *Context, int ContextType,
648                           const StrBuf *Source, int FormatTypeIndex)
649 {
650         struct wcsession *WCC;
651         StrBuf *Buf;
652         char EscapeAs = ' ';
653
654         if ((FormatTypeIndex < Tokens->nParameters) &&
655             (Tokens->Params[FormatTypeIndex]->Type == TYPE_STR) &&
656             (Tokens->Params[FormatTypeIndex]->len == 1)) {
657                 EscapeAs = *Tokens->Params[FormatTypeIndex]->Start;
658         }
659
660         switch(EscapeAs)
661         {
662         case 'H':
663                 WCC = WC;
664                 Buf = NewStrBufPlain(NULL, StrLength(Source));
665                 StrBuf_RFC822_to_Utf8(Buf, 
666                                       Source, 
667                                       (WCC!=NULL)? WCC->DefaultCharset : NULL, 
668                                       NULL);
669                 StrEscAppend(Target, Buf, NULL, 0, 0);
670                 FreeStrBuf(&Buf);
671                 break;
672         case 'X':
673                 StrEscAppend(Target, Source, NULL, 0, 0);
674                 break;
675         default:
676                 StrBufAppendBuf(Target, Source, 0);
677         }
678 }
679
680
681 void PutNewToken(WCTemplate *Template, WCTemplateToken *NewToken)
682 {
683         if (Template->nTokensUsed + 1 >= Template->TokenSpace) {
684                 if (Template->TokenSpace <= 0) {
685                         Template->Tokens = (WCTemplateToken**)malloc(
686                                 sizeof(WCTemplateToken*) * 10);
687                         Template->TokenSpace = 10;
688                 }
689                 else {
690                         WCTemplateToken **NewTokens;
691                         NewTokens= (WCTemplateToken**)malloc(
692                                 sizeof(WCTemplateToken*) * 
693                                 Template->TokenSpace * 2);
694                         memcpy(NewTokens, Template->Tokens, 
695                                sizeof(WCTemplateToken*) * Template->nTokensUsed);
696                         free(Template->Tokens);
697                         Template->TokenSpace *= 2;
698                         Template->Tokens = NewTokens;
699                 }
700         }
701         Template->Tokens[(Template->nTokensUsed)++] = NewToken;
702 }
703
704 TemplateParam *GetNextParameter(StrBuf *Buf, const char **pCh, const char *pe, WCTemplateToken *Tokens, WCTemplate *pTmpl)
705 {
706         const char *pch = *pCh;
707         const char *pchs, *pche;
708         TemplateParam *Parm = (TemplateParam *) malloc(sizeof(TemplateParam));
709         char quote = '\0';
710         
711
712         Parm->Type = TYPE_STR;
713
714         /* Skip leading whitespaces */
715         while ((*pch == ' ' )||
716                (*pch == '\t')||
717                (*pch == '\r')||
718                (*pch == '\n')) pch ++;
719
720         if (*pch == ':') {
721                 Parm->Type = TYPE_PREFSTR;
722                 pch ++;
723         }
724         else if (*pch == ';') {
725                 Parm->Type = TYPE_PREFINT;
726                 pch ++;
727         }
728         else if (*pch == '_') {
729                 Parm->Type = TYPE_GETTEXT;
730                 pch ++;
731         }
732         else if (*pch == 'B') {
733                 Parm->Type = TYPE_BSTR;
734                 pch ++;
735         }
736
737
738         if (*pch == '"')
739                 quote = '"';
740         else if (*pch == '\'')
741                 quote = '\'';
742         if (quote != '\0') {
743                 pch ++;
744                 pchs = pch;
745                 while (pch <= pe &&
746                        ((*pch != quote) ||
747                         ( (pch > pchs) && (*(pch - 1) == '\\'))
748                                )) {
749                         pch ++;
750                 }
751                 pche = pch;
752                 if (*pch != quote) {
753                         lprintf(1, "Error (in '%s' line %ld); "
754                                 "evaluating template param [%s] in Token [%s]\n",
755                                 ChrPtr(pTmpl->FileName),
756                                 Tokens->Line,
757                                 ChrPtr(Tokens->FlatToken),
758                                 *pCh);
759                         pch ++;
760                         free(Parm);
761                         return NULL;
762                 }
763                 else {
764                         StrBufPeek(Buf, pch, -1, '\0');         
765                         if (LoadTemplates > 1) {                        
766                                 lprintf(1, "DBG: got param [%s] %ld %ld\n", 
767                                         pchs, pche - pchs, strlen(pchs));
768                         }
769                         Parm->Start = pchs;
770                         Parm->len = pche - pchs;
771                         pch ++; /* move after trailing quote */
772                 }
773         }
774         else {
775                 Parm->Type = TYPE_LONG;
776                 pchs = pch;
777                 while ((pch <= pe) &&
778                        (isdigit(*pch) ||
779                         (*pch == '+') ||
780                         (*pch == '-')))
781                         pch ++;
782                 pch ++;
783                 if (pch - pchs > 1){
784                         StrBufPeek(Buf, pch, -1, '\0');
785                         Parm->lvalue = atol(pchs);
786                         Parm->Start = pchs;
787                         pch++;
788                 }
789                 else {
790                         Parm->lvalue = 0;
791                         lprintf(1, "Error (in '%s' line %ld); "
792                                 "evaluating long template param [%s] in Token [%s]\n",
793                                 ChrPtr(pTmpl->FileName),
794                                 Tokens->Line,
795                                 ChrPtr(Tokens->FlatToken),
796                                 *pCh);
797                         free(Parm);
798                         return NULL;
799                 }
800         }
801         while ((*pch == ' ' )||
802                (*pch == '\t')||
803                (*pch == '\r')||
804                (*pch == ',' )||
805                (*pch == '\n')) pch ++;
806
807         *pCh = pch;
808         return Parm;
809 }
810
811 WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf, 
812                                        const char *pStart, 
813                                        const char *pTmplStart, 
814                                        const char *pTmplEnd, 
815                                        long Line,
816                                        WCTemplate *pTmpl)
817 {
818         void *vVar;
819         const char *pch;
820         TemplateParam *Param;
821         WCTemplateToken *NewToken = (WCTemplateToken*)malloc(sizeof(WCTemplateToken));
822
823         NewToken->FileName = pTmpl->FileName; /* to print meaningfull log messages... */
824         NewToken->Flags = 0;
825         NewToken->Line = Line + 1;
826         NewToken->pTokenStart = pTmplStart;
827         NewToken->TokenStart = pTmplStart - pStart;
828         NewToken->TokenEnd =  (pTmplEnd - pStart) - NewToken->TokenStart;
829         NewToken->pTokenEnd = pTmplEnd;
830         NewToken->NameEnd = NewToken->TokenEnd - 2;
831         NewToken->PreEval = NULL;
832         NewToken->FlatToken = NewStrBufPlain(pTmplStart + 2, pTmplEnd - pTmplStart - 2);
833         
834         StrBufPeek(Buf, pTmplStart, + 1, '\0');
835         StrBufPeek(Buf, pTmplEnd, -1, '\0');
836         pch = NewToken->pName = pTmplStart + 2;
837
838         NewToken->HaveParameters = 0;;
839         NewToken->nParameters = 0;
840
841         while (pch < pTmplEnd - 1) {
842                 if (*pch == '(') {
843                         StrBufPeek(Buf, pch, -1, '\0');
844                         NewToken->NameEnd = pch - NewToken->pName;
845                         pch ++;
846                         if (*(pTmplEnd - 1) != ')') {
847                                 lprintf(1, "Warning, Non welformed Token; missing right parenthesis (in '%s' line %ld); "
848                                         "[%s]\n", 
849                                         ChrPtr(pTmpl->FileName),
850                                         NewToken->Line,
851                                         ChrPtr(NewToken->FlatToken));
852                         }
853                         while (pch < pTmplEnd - 1) {
854                                 Param = GetNextParameter(Buf, &pch, pTmplEnd - 1, NewToken, pTmpl);
855                                 if (Param != NULL) {
856                                         NewToken->HaveParameters = 1;
857                                         if (NewToken->nParameters > MAXPARAM) {
858                                                 lprintf(1, "Error (in '%s' line %ld); "
859                                                         "only [%ld] Params allowed in Tokens [%s]\n",
860                                                         ChrPtr(pTmpl->FileName),
861                                                         NewToken->Line,
862                                                         MAXPARAM,
863                                                         ChrPtr(NewToken->FlatToken));
864                                                 free(Param);
865                                                 FreeToken(&NewToken);
866                                                 return NULL;
867                                         }
868                                         NewToken->Params[NewToken->nParameters++] = Param;
869                                 }
870                                 else break;
871                         }
872                         if((NewToken->NameEnd == 1) &&
873                            (NewToken->HaveParameters == 1))
874                            
875                         {
876                                 if (*(NewToken->pName) == '_')
877                                         NewToken->Flags = SV_GETTEXT;
878                                 else if (*(NewToken->pName) == '=')
879                                         NewToken->Flags = SV_SUBTEMPL;
880                                 else if (*(NewToken->pName) == '%')
881                                         NewToken->Flags = SV_CUST_STR_CONDITIONAL;
882                                 else if (*(NewToken->pName) == '?')
883                                         NewToken->Flags = SV_CONDITIONAL;
884                                 else if (*(NewToken->pName) == '!')
885                                         NewToken->Flags = SV_NEG_CONDITIONAL;
886                         }
887                 }
888                 else pch ++;            
889         }
890         
891         switch (NewToken->Flags) {
892         case 0:
893                 /* If we're able to find out more about the token, do it now while its fresh. */
894                 if (GetHash(GlobalNS, NewToken->pName, NewToken->NameEnd, &vVar)) {
895                         HashHandler *Handler;
896                         Handler = (HashHandler*) vVar;
897                         if ((NewToken->nParameters < Handler->nMinArgs) || 
898                             (NewToken->nParameters > Handler->nMaxArgs)) {
899                                 lprintf(1, "Handler [%s] (in '%s' line %ld); "
900                                         "doesn't work with %ld params [%s]\n", 
901                                         NewToken->pName,
902                                         ChrPtr(pTmpl->FileName),
903                                         NewToken->Line,
904                                         NewToken->nParameters, 
905                                         ChrPtr(NewToken->FlatToken));
906                         }
907                         else {
908                                 NewToken->PreEval = Handler;
909                                 NewToken->Flags = SV_PREEVALUATED;              
910                         }
911                 }
912                 break;
913         case SV_GETTEXT:
914                 if (NewToken->nParameters !=1) {
915                         lprintf(1, "Gettext (in '%s' line %ld); "
916                                 "requires exactly 1 parameter, you gave %ld params [%s]\n", 
917                                 ChrPtr(pTmpl->FileName),
918                                 NewToken->Line,
919                                 NewToken->nParameters, 
920                                 ChrPtr(NewToken->FlatToken));
921                         NewToken->Flags = 0;
922                         break;
923                 }
924                 break;
925         case SV_SUBTEMPL:
926                 if (NewToken->nParameters != 1) {
927                         lprintf(1, "Subtemplates (in '%s' line %ld); "
928                                 "require exactly 1 parameter, you gave %ld params [%s]\n", 
929                                 ChrPtr(pTmpl->FileName),
930                                 NewToken->Line,
931                                 NewToken->nParameters, 
932                                 ChrPtr(NewToken->FlatToken));
933                         break;
934                 }
935                 break;
936         case SV_CUST_STR_CONDITIONAL:
937         case SV_CONDITIONAL:
938         case SV_NEG_CONDITIONAL:
939                 if (NewToken->Params[1]->lvalue == 0) {
940                         lprintf(1, "Conditional (in '%s' line %ld); "
941                                 "Conditional ID mustn't be 0! [%s]\n", 
942                                 ChrPtr(pTmpl->FileName),
943                                 NewToken->Line,
944                                 ChrPtr(NewToken->FlatToken));
945                         NewToken->Flags = 0;
946                         break;
947                 }
948                 if (NewToken->nParameters <2) {
949                         lprintf(1, "Conditional (in '%s' line %ld); "
950                                 "require at least 2 parameters, you gave %ld params [%s]\n", 
951                                 ChrPtr(pTmpl->FileName),
952                                 NewToken->Line,
953                                 NewToken->nParameters, 
954                                 ChrPtr(NewToken->FlatToken));
955                         NewToken->Flags = 0;
956                         break;
957                 }
958                 if (!GetHash(Conditionals, 
959                              NewToken->Params[0]->Start,
960                              NewToken->Params[0]->len,
961                              &vVar) || 
962                     (vVar == NULL)) {
963                         if ((NewToken->Params[0]->len == 1) &&
964                             (NewToken->Params[0]->Start[0] == 'X'))
965                                 break;
966                         lprintf(1, "Conditional [%s] (in '%s' line %ld); Not found![%s]\n", 
967                                 NewToken->Params[0]->Start,
968                                 ChrPtr(pTmpl->FileName),
969                                 NewToken->Line,
970                                 ChrPtr(NewToken->FlatToken));
971 /*
972                         NewToken->Error = NewStrBuf();
973                         StrBufAppendPrintf(
974                                 NewToken->Error, 
975                                 "<pre>\nConditional [%s] (in '%s' line %ld); Not found!\n[%s]\n</pre>\n", 
976                                 NewToken->Params[0]->Start,
977                                 ChrPtr(pTmpl->FileName),
978                                 NewToken->Line,
979                                 ChrPtr(NewToken->FlatToken));
980 */
981                 }
982                 else {
983                         NewToken->PreEval = vVar;
984                 }
985                 break;
986         }
987         return NewToken;
988 }
989
990
991
992
993
994 /**
995  * \brief Display a variable-substituted template
996  * \param templatename template file to load
997  */
998 void *prepare_template(StrBuf *filename, StrBuf *Key, HashList *PutThere)
999 {
1000         WCTemplate *NewTemplate;
1001         NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
1002         NewTemplate->Data = NULL;
1003         NewTemplate->FileName = NewStrBufDup(filename);
1004         NewTemplate->nTokensUsed = 0;
1005         NewTemplate->TokenSpace = 0;
1006         NewTemplate->Tokens = NULL;
1007
1008         Put(PutThere, ChrPtr(Key), StrLength(Key), NewTemplate, FreeWCTemplate);
1009         return NewTemplate;
1010 }
1011
1012 /**
1013  * \brief Display a variable-substituted template
1014  * \param templatename template file to load
1015  */
1016 void *load_template(StrBuf *filename, StrBuf *Key, HashList *PutThere)
1017 {
1018         int fd;
1019         struct stat statbuf;
1020         const char *pS, *pE, *pch, *Err;
1021         long Line;
1022         int pos;
1023         WCTemplate *NewTemplate;
1024
1025         fd = open(ChrPtr(filename), O_RDONLY);
1026         if (fd <= 0) {
1027                 lprintf(1, "ERROR: could not open template '%s' - %s\n",
1028                         ChrPtr(filename), strerror(errno));
1029                 return NULL;
1030         }
1031
1032         if (fstat(fd, &statbuf) == -1) {
1033                 lprintf(1, "ERROR: could not stat template '%s' - %s\n",
1034                         ChrPtr(filename), strerror(errno));
1035                 return NULL;
1036         }
1037
1038         NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
1039         NewTemplate->Data = NewStrBufPlain(NULL, statbuf.st_size);
1040         NewTemplate->FileName = NewStrBufDup(filename);
1041         NewTemplate->nTokensUsed = 0;
1042         NewTemplate->TokenSpace = 0;
1043         NewTemplate->Tokens = NULL;
1044         if (StrBufReadBLOB(NewTemplate->Data, &fd, 1, statbuf.st_size, &Err) < 0) {
1045                 close(fd);
1046                 FreeWCTemplate(NewTemplate);
1047                 lprintf(1, "ERROR: reading template '%s' - %s<br />\n",
1048                         ChrPtr(filename), strerror(errno));
1049                 return NULL;
1050         }
1051         close(fd);
1052
1053         Line = 0;
1054         pS = pch = ChrPtr(NewTemplate->Data);
1055         pE = pS + StrLength(NewTemplate->Data);
1056         while (pch < pE) {
1057                 const char *pts, *pte;
1058                 int InQuotes = 0;
1059                 int InDoubleQuotes = 0;
1060
1061                 /** Find one <? > */
1062                 pos = (-1);
1063                 for (; pch < pE; pch ++) {
1064                         if ((*pch=='<')&&(*(pch + 1)=='?'))
1065                                 break;
1066                         if (*pch=='\n') Line ++;
1067                 }
1068                 if (pch >= pE)
1069                         continue;
1070                 pts = pch;
1071
1072                 /** Found one? parse it. */
1073                 for (; pch < pE - 1; pch ++) {
1074                         if (*pch == '"')
1075                                 InDoubleQuotes = ! InDoubleQuotes;
1076                         else if (*pch == '\'')
1077                                 InQuotes = ! InQuotes;
1078                         else if ((!InQuotes  && !InDoubleQuotes) &&
1079                                  ((*pch!='\\')&&(*(pch + 1)=='>'))) {
1080                                 pch ++;
1081                                 break;
1082                         }
1083                 }
1084                 if (pch + 1 >= pE)
1085                         continue;
1086                 pte = pch;
1087                 PutNewToken(NewTemplate, 
1088                             NewTemplateSubstitute(NewTemplate->Data, pS, pts, pte, Line, NewTemplate));
1089                 pch ++;
1090         }
1091         if (LoadTemplates == 0)
1092                 Put(PutThere, ChrPtr(Key), StrLength(Key), NewTemplate, FreeWCTemplate);
1093         return NewTemplate;
1094 }
1095
1096
1097 ///void PrintTemplate(const char *Key, void *vSubst, int odd)
1098 const char* PrintTemplate(void *vSubst)
1099 {
1100         WCTemplate *Tmpl = vSubst;
1101
1102         return ChrPtr(Tmpl->FileName);
1103
1104 }
1105
1106 int LoadTemplateDir(const char *DirName, HashList *wireless, HashList *big)
1107 {
1108         StrBuf *FileName;
1109         StrBuf *Tag;
1110         StrBuf *Dir;
1111         DIR *filedir = NULL;
1112         struct dirent *filedir_entry;
1113         int d_namelen;
1114         int d_without_ext;
1115         int IsMobile;
1116         
1117         Dir = NewStrBuf();
1118         StrBufPrintf(Dir, "%s/t", DirName);
1119         filedir = opendir (ChrPtr(Dir));
1120         if (filedir == NULL) {
1121                 FreeStrBuf(&Dir);
1122                 return 0;
1123         }
1124
1125         FileName = NewStrBuf();
1126         Tag = NewStrBuf();
1127         while ((filedir_entry = readdir(filedir)))
1128         {
1129                 char *MinorPtr;
1130                 char *PStart;
1131 #ifdef _DIRENT_HAVE_D_NAMELEN
1132                 d_namelen = filedir_entry->d_namelen;
1133 #else
1134                 d_namelen = strlen(filedir_entry->d_name);
1135 #endif
1136                 d_without_ext = d_namelen;
1137                 while ((d_without_ext > 0) && (filedir_entry->d_name[d_without_ext] != '.'))
1138                         d_without_ext --;
1139                 if ((d_without_ext == 0) || (d_namelen < 3))
1140                         continue;
1141                 if ((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~')
1142                         continue; /* Ignore backup files... */
1143
1144                 IsMobile = (strstr(filedir_entry->d_name, ".m.html")!= NULL);
1145                 PStart = filedir_entry->d_name;
1146                 StrBufPrintf(FileName, "%s/%s", ChrPtr(Dir),  filedir_entry->d_name);
1147                 MinorPtr = strchr(filedir_entry->d_name, '.');
1148                 if (MinorPtr != NULL)
1149                         *MinorPtr = '\0';
1150                 StrBufPlain(Tag, filedir_entry->d_name, MinorPtr - filedir_entry->d_name);
1151
1152                 if (LoadTemplates > 1)
1153                         lprintf(1, "%s %d %s\n",ChrPtr(FileName), IsMobile, ChrPtr(Tag));
1154                 if (LoadTemplates == 0)
1155                         load_template(FileName, Tag, (IsMobile)?wireless:big);
1156                 else
1157                         prepare_template(FileName, Tag, (IsMobile)?wireless:big);
1158         }
1159         closedir(filedir);
1160         FreeStrBuf(&FileName);
1161         FreeStrBuf(&Tag);
1162         FreeStrBuf(&Dir);
1163         return 1;
1164 }
1165
1166 void InitTemplateCache(void)
1167 {
1168         LoadTemplateDir(static_dirs[0],
1169                         WirelessTemplateCache,
1170                         TemplateCache);
1171         LoadTemplateDir(static_dirs[1],
1172                         WirelessLocalTemplateCache,
1173                         LocalTemplateCache);
1174 }
1175
1176
1177
1178 /*-----------------------------------------------------------------------------
1179  *                      Filling & processing Templates
1180  */
1181 /**
1182  * \brief executes one token
1183  * \param Target buffer to append to
1184  * \param Token da to  process.
1185  * \param Template we're iterating
1186  * \param Context Contextpoointer to pass in
1187  * \param state are we in conditional state?
1188  * \param ContextType what type of information does context giv us?
1189  */
1190 int EvaluateToken(StrBuf *Target, WCTemplateToken *Tokens, WCTemplate *pTmpl, void *Context, int state, int ContextType)
1191 {
1192         HashHandler *Handler;
1193         void *vVar;
1194 // much output, since pName is not terminated...
1195 //      lprintf(1,"Doing token: %s\n",Token->pName);
1196
1197         switch (Tokens->Flags) {
1198         case SV_GETTEXT:
1199                 TmplGettext(Target, Tokens->nParameters, Tokens);
1200                 break;
1201         case SV_CONDITIONAL: /** Forward conditional evaluation */
1202                 return EvaluateConditional(Target, Tokens, pTmpl, Context, 1, state, ContextType);
1203                 break;
1204         case SV_NEG_CONDITIONAL: /** Reverse conditional evaluation */
1205                 return EvaluateConditional(Target, Tokens, pTmpl, Context, 0, state, ContextType);
1206                 break;
1207         case SV_CUST_STR_CONDITIONAL: /** Conditional put custom strings from params */
1208                 if (Tokens->nParameters >= 6) {
1209                         if (EvaluateConditional(Target, Tokens, pTmpl, Context, 0, state, ContextType))
1210                                 StrBufAppendBufPlain(Target, 
1211                                                      Tokens->Params[5]->Start,
1212                                                      Tokens->Params[5]->len,
1213                                                      0);
1214                         else
1215                                 StrBufAppendBufPlain(Target, 
1216                                                      Tokens->Params[4]->Start,
1217                                                      Tokens->Params[4]->len,
1218                                                      0);
1219                 }
1220                 else  {
1221                         lprintf(1, "Conditional [%s] (in '%s' line %ld); needs at least 6 Params![%s]\n", 
1222                                 Tokens->Params[0]->Start,
1223                                 ChrPtr(pTmpl->FileName),
1224                                 Tokens->Line,
1225                                 ChrPtr(Tokens->FlatToken));
1226                         StrBufAppendPrintf(
1227                                 Target, 
1228                                 "<pre>\nConditional [%s] (in '%s' line %ld); needs 6 Params!\n[%s]\n</pre>\n", 
1229                                 Tokens->Params[0]->Start,
1230                                 ChrPtr(pTmpl->FileName),
1231                                 Tokens->Line,
1232                                 ChrPtr(Tokens->FlatToken));
1233                 }
1234                 break;
1235         case SV_SUBTEMPL:
1236                 if (Tokens->nParameters == 1)
1237                         DoTemplate(Tokens->Params[0]->Start, Tokens->Params[0]->len, NULL, NULL, ContextType);
1238                 break;
1239         case SV_PREEVALUATED:
1240                 Handler = (HashHandler*) Tokens->PreEval;
1241                 if ((Handler->ContextRequired != CTX_NONE) &&
1242                     (Handler->ContextRequired != ContextType)) {
1243                         lprintf(1, "Handler [%s] (in '%s' line %ld); "
1244                                 "requires context of type %ld, have %ld [%s]\n", 
1245                                 Tokens->pName,
1246                                 ChrPtr(pTmpl->FileName),
1247                                 Tokens->Line,
1248                                 Handler->ContextRequired, 
1249                                 ContextType,
1250                                 ChrPtr(Tokens->FlatToken));
1251                         StrBufAppendPrintf(
1252                                 Target, 
1253                                 "<pre>\nHandler [%s] (in '%s' line %ld);"
1254                                 " requires context of type %ld, have %ld!\n[%s]\n</pre>\n", 
1255                                 Tokens->pName,
1256                                 ChrPtr(pTmpl->FileName),
1257                                 Tokens->Line,
1258                                 Handler->ContextRequired, 
1259                                 ContextType,
1260                                 ChrPtr(Tokens->FlatToken));
1261                         return -1;
1262
1263                 }
1264                 Handler->HandlerFunc(Target, 
1265                                      Tokens->nParameters,
1266                                      Tokens,
1267                                      Context, 
1268                                      ContextType); 
1269                 break;          
1270         default:
1271                 if (GetHash(GlobalNS, Tokens->pName, Tokens->NameEnd, &vVar)) {
1272                         Handler = (HashHandler*) vVar;
1273                         if ((Handler->ContextRequired != CTX_NONE) &&
1274                             (Handler->ContextRequired != ContextType)) {
1275                                 lprintf(1, "Handler [%s] (in '%s' line %ld); "
1276                                         "requires context of type %ld, have %ld [%s]\n", 
1277                                         Tokens->pName,
1278                                         ChrPtr(pTmpl->FileName),
1279                                         Tokens->Line,
1280                                         Handler->ContextRequired, 
1281                                         ContextType,
1282                                         ChrPtr(Tokens->FlatToken));
1283                                 StrBufAppendPrintf(
1284                                         Target, 
1285                                         "<pre>\nHandler [%s] (in '%s' line %ld);"
1286                                         " requires context of type %ld, have %ld!\n[%s]\n</pre>\n", 
1287                                         Tokens->pName,
1288                                         ChrPtr(pTmpl->FileName),
1289                                         Tokens->Line,
1290                                         Handler->ContextRequired, 
1291                                         ContextType,
1292                                         ChrPtr(Tokens->FlatToken));
1293                                 return -1;
1294                         }
1295                         else if ((Tokens->nParameters < Handler->nMinArgs) || 
1296                                  (Tokens->nParameters > Handler->nMaxArgs)) {
1297                                 lprintf(1, "Handler [%s] (in '%s' line %ld); "
1298                                         "doesn't work with %ld params [%s]\n", 
1299                                         Tokens->pName,
1300                                         ChrPtr(pTmpl->FileName),
1301                                         Tokens->Line,
1302                                         Tokens->nParameters, 
1303                                         ChrPtr(Tokens->FlatToken));
1304                                 StrBufAppendPrintf(
1305                                         Target, 
1306                                         "<pre>\nHandler [%s] (in '%s' line %ld);"
1307                                         " doesn't work with %ld params!\n[%s]\n</pre>\n", 
1308                                         Tokens->pName,
1309                                         ChrPtr(pTmpl->FileName),
1310                                         Tokens->Line,
1311                                         Tokens->nParameters,
1312                                         ChrPtr(Tokens->FlatToken));
1313                         }
1314                         else {
1315                                 Handler->HandlerFunc(Target, 
1316                                                      Tokens->nParameters,
1317                                                      Tokens,
1318                                                      Context, 
1319                                                      ContextType); /*TODO: subset of that */
1320                                 
1321                         }
1322                 }
1323                 else {
1324                         print_value_of(Target, Tokens, Context, ContextType);
1325                 }
1326         }
1327         return 0;
1328 }
1329
1330
1331
1332 void ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target, void *Context, int ContextType)
1333 {
1334         WCTemplate *pTmpl = Tmpl;
1335         int done = 0;
1336         int i, state;
1337         const char *pData, *pS;
1338         long len;
1339
1340         if (LoadTemplates != 0) {                       
1341                 if (LoadTemplates > 1)
1342                         lprintf(1, "DBG: ----- loading:  [%s] ------ \n", 
1343                                 ChrPtr(Tmpl->FileName));
1344
1345                 pTmpl = load_template(Tmpl->FileName, NULL, NULL);
1346                 if(pTmpl == NULL) {
1347                         StrBufAppendPrintf(
1348                                 Target, 
1349                                 "<pre>\nError loading Template [%s]\n See Logfile for details\n</pre>\n", 
1350                                 ChrPtr(Tmpl->FileName));
1351                         return;
1352                 }
1353
1354         }
1355
1356         pS = pData = ChrPtr(pTmpl->Data);
1357         len = StrLength(pTmpl->Data);
1358         i = 0;
1359         state = 0;
1360         while (!done) {
1361                 if (i >= pTmpl->nTokensUsed) {
1362                         StrBufAppendBufPlain(Target, 
1363                                              pData, 
1364                                              len - (pData - pS), 0);
1365                         done = 1;
1366                 }
1367                 else {
1368                         StrBufAppendBufPlain(
1369                                 Target, pData, 
1370                                 pTmpl->Tokens[i]->pTokenStart - pData, 0);
1371                         state = EvaluateToken(Target, pTmpl->Tokens[i], pTmpl, Context, state, ContextType);
1372                         while ((state != 0) && (i+1 < pTmpl->nTokensUsed)) {
1373                         /* condition told us to skip till its end condition */
1374                                 i++;
1375                                 if ((pTmpl->Tokens[i]->Flags == SV_CONDITIONAL) ||
1376                                     (pTmpl->Tokens[i]->Flags == SV_NEG_CONDITIONAL)) {
1377                                         if (state == EvaluateConditional(
1378                                                     Target,
1379                                                     pTmpl->Tokens[i], 
1380                                                     pTmpl,
1381                                                     Context, 
1382                                                     pTmpl->Tokens[i]->Flags,
1383                                                     state, 
1384                                                     ContextType))
1385                                                 state = 0;
1386                                 }
1387                         }
1388                         pData = pTmpl->Tokens[i++]->pTokenEnd + 1;
1389                         if (i > pTmpl->nTokensUsed)
1390                                 done = 1;
1391                 }
1392         }
1393         if (LoadTemplates != 0) {
1394                 FreeWCTemplate(pTmpl);
1395         }
1396 }
1397
1398 /**
1399  * \brief Display a variable-substituted template
1400  * \param templatename template file to load
1401  */
1402 void DoTemplate(const char *templatename, long len, StrBuf *Target, void *Context, int ContextType) 
1403 {
1404         HashList *Static;
1405         HashList *StaticLocal;
1406         void *vTmpl;
1407         
1408         if (Target == NULL)
1409                 Target = WC->WBuf;
1410         if (WC->is_mobile) {
1411                 Static = WirelessTemplateCache;
1412                 StaticLocal = WirelessLocalTemplateCache;
1413         }
1414         else {
1415                 Static = TemplateCache;
1416                 StaticLocal = LocalTemplateCache;
1417         }
1418
1419         if (len == 0)
1420         {
1421                 lprintf (1, "Can't to load a template with empty name!\n");
1422                 StrBufAppendPrintf(Target, "<pre>\nCan't to load a template with empty name!\n</pre>");
1423                 return;
1424         }
1425
1426         if (!GetHash(StaticLocal, templatename, len, &vTmpl) &&
1427             !GetHash(Static, templatename, len, &vTmpl)) {
1428                 lprintf (1, "didn't find Template [%s] %ld %ld\n", templatename, len , (long)strlen(templatename));
1429                 StrBufAppendPrintf(Target, "<pre>\ndidn't find Template [%s] %ld %ld\n</pre>", 
1430                                    templatename, len, 
1431                                    (long)strlen(templatename));
1432 ///             dbg_PrintHash(Static, PrintTemplate, NULL);
1433 //              PrintHash(Static, VarPrintTransition, PrintTemplate);
1434                 return;
1435         }
1436         if (vTmpl == NULL) 
1437                 return;
1438         ProcessTemplate(vTmpl, Target, Context, ContextType);
1439 }
1440
1441 /*-----------------------------------------------------------------------------
1442  *                      Iterators
1443  */
1444 typedef struct _HashIterator {
1445         HashList *StaticList;
1446         int AdditionalParams;
1447         int ContextType;
1448         int XPectContextType;
1449         RetrieveHashlistFunc GetHash;
1450         HashDestructorFunc Destructor;
1451         SubTemplFunc DoSubTemplate;
1452 } HashIterator;
1453
1454 void RegisterITERATOR(const char *Name, long len, 
1455                       int AdditionalParams, 
1456                       HashList *StaticList, 
1457                       RetrieveHashlistFunc GetHash, 
1458                       SubTemplFunc DoSubTempl,
1459                       HashDestructorFunc Destructor,
1460                       int ContextType, 
1461                       int XPectContextType)
1462 {
1463         HashIterator *It = (HashIterator*)malloc(sizeof(HashIterator));
1464         It->StaticList = StaticList;
1465         It->AdditionalParams = AdditionalParams;
1466         It->GetHash = GetHash;
1467         It->DoSubTemplate = DoSubTempl;
1468         It->Destructor = Destructor;
1469         It->ContextType = ContextType;
1470         It->XPectContextType = XPectContextType;
1471         Put(Iterators, Name, len, It, NULL);
1472 }
1473
1474 void tmpl_iterate_subtmpl(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
1475 {
1476         void *vIt;
1477         HashIterator *It;
1478         HashList *List;
1479         HashPos  *it;
1480         long len; 
1481         const char *Key;
1482         void *vContext;
1483         StrBuf *SubBuf;
1484         int oddeven = 0;
1485         
1486         if (!GetHash(Iterators, 
1487                      Tokens->Params[0]->Start,
1488                      Tokens->Params[0]->len,
1489                      &vIt)) {
1490                 lprintf(1, "unknown Iterator [%s] (in '%s' line %ld); "
1491                         " [%s]\n", 
1492                         Tokens->Params[0]->Start,
1493                         ChrPtr(Tokens->FileName),
1494                         Tokens->Line,
1495                         ChrPtr(Tokens->FlatToken));
1496                 StrBufAppendPrintf(
1497                         Target,
1498                         "<pre>\nunknown Iterator [%s] (in '%s' line %ld); \n"
1499                         " [%s]\n</pre>", 
1500                         Tokens->Params[0]->Start,
1501                         ChrPtr(Tokens->FileName),
1502                         Tokens->Line,
1503                         ChrPtr(Tokens->FlatToken));
1504                 return;
1505         }
1506
1507         It = (HashIterator*) vIt;
1508
1509         if (Tokens->nParameters < It->AdditionalParams + 2) {
1510                 lprintf(1, "Iterator [%s] (in '%s' line %ld); "
1511                         "doesn't work with %ld params [%s]\n", 
1512                         Tokens->Params[0]->Start,
1513                         ChrPtr(Tokens->FileName),
1514                         Tokens->Line,
1515                         Tokens->nParameters, 
1516                         ChrPtr(Tokens->FlatToken));
1517                 StrBufAppendPrintf(
1518                         Target,
1519                         "<pre>Iterator [%s] \n(in '%s' line %ld);\n"
1520                         "doesn't work with %ld params \n[%s]\n</pre>", 
1521                         Tokens->Params[0]->Start,
1522                         ChrPtr(Tokens->FileName),
1523                         Tokens->Line,
1524                         Tokens->nParameters, 
1525                         ChrPtr(Tokens->FlatToken));
1526                 return;
1527         }
1528
1529         if ((It->XPectContextType != CTX_NONE) &&
1530             (It->XPectContextType != ContextType)) {
1531                 lprintf(1, "Iterator [%s] (in '%s' line %ld); "
1532                         "requires context of type %ld, have %ld [%s]\n", 
1533                         Tokens->pName,
1534                         ChrPtr(Tokens->FileName),
1535                         Tokens->Line,
1536                         It->XPectContextType, 
1537                         ContextType,
1538                         ChrPtr(Tokens->FlatToken));
1539                 StrBufAppendPrintf(
1540                         Target, 
1541                         "<pre>\nIterator [%s] (in '%s' line %ld);"
1542                         " requires context of type %ld, have %ld!\n[%s]\n</pre>\n", 
1543                         Tokens->pName,
1544                         ChrPtr(Tokens->FileName),
1545                         Tokens->Line,
1546                         It->XPectContextType, 
1547                         ContextType,
1548                         ChrPtr(Tokens->FlatToken));
1549                 return ;
1550                 
1551         }
1552
1553         if (It->StaticList == NULL)
1554                 List = It->GetHash(Target, nArgs, Tokens, Context, ContextType);
1555         else
1556                 List = It->StaticList;
1557
1558         SubBuf = NewStrBuf();
1559         it = GetNewHashPos(List, 0);
1560         while (GetNextHashPos(List, it, &len, &Key, &vContext)) {
1561                 svprintf(HKEY("ITERATE:ODDEVEN"), WCS_STRING, "%s", 
1562                          (oddeven) ? "odd" : "even");
1563                 svprintf(HKEY("ITERATE:KEY"), WCS_STRING, "%s", Key);
1564
1565                 if (It->DoSubTemplate != NULL)
1566                         It->DoSubTemplate(SubBuf, vContext, Tokens);
1567                 DoTemplate(Tokens->Params[1]->Start,
1568                            Tokens->Params[1]->len,
1569                            SubBuf, vContext,
1570                            It->ContextType);
1571                         
1572                 StrBufAppendBuf(Target, SubBuf, 0);
1573                 FlushStrBuf(SubBuf);
1574                 oddeven = ~ oddeven;
1575         }
1576         FreeStrBuf(&SubBuf);
1577         DeleteHashPos(&it);
1578         if (It->Destructor != NULL)
1579                 It->Destructor(&List);
1580 }
1581
1582
1583
1584 /*-----------------------------------------------------------------------------
1585  *                      Conditionals
1586  */
1587 int EvaluateConditional(StrBuf *Target, WCTemplateToken *Tokens, WCTemplate *pTmpl, void *Context, int Neg, int state, int ContextType)
1588 {
1589         ConditionalStruct *Cond;
1590
1591         if ((Tokens->Params[0]->len == 1) &&
1592             (Tokens->Params[0]->Start[0] == 'X'))
1593                 return (state != 0)?Tokens->Params[1]->lvalue:0;
1594             
1595         Cond = (ConditionalStruct *) Tokens->PreEval;
1596         if (Cond == NULL) {
1597                 lprintf(1, "Conditional [%s] (in '%s' line %ld); unknown![%s]\n", 
1598                         Tokens->Params[0]->Start,
1599                         ChrPtr(pTmpl->FileName),
1600                         Tokens->Line,
1601                         ChrPtr(Tokens->FlatToken));
1602                 return 1;
1603         }
1604
1605         if (Tokens->nParameters < Cond->nParams) {
1606                 lprintf(1, "Conditional [%s] (in '%s' line %ld); needs %ld Params![%s]\n", 
1607                         Tokens->Params[0]->Start,
1608                         ChrPtr(pTmpl->FileName),
1609                         Tokens->Line,
1610                         Cond->nParams,
1611                         ChrPtr(Tokens->FlatToken));
1612                 StrBufAppendPrintf(
1613                         Target, 
1614                         "<pre>\nConditional [%s] (in '%s' line %ld); needs %ld Params!\n[%s]\n</pre>\n", 
1615                         Tokens->Params[0]->Start,
1616                         ChrPtr(pTmpl->FileName),
1617                         Tokens->Line,
1618                         Cond->nParams,
1619                         ChrPtr(Tokens->FlatToken));
1620                 return 0;
1621         }
1622         if (Cond->CondF(Tokens, Context, ContextType) == Neg)
1623                 return Tokens->Params[1]->lvalue;
1624         return 0;
1625 }
1626
1627 int ConditionalVar(WCTemplateToken *Tokens, void *Context, int ContextType)
1628 {
1629         void *vsubst;
1630         wcsubst *subst;
1631         
1632         if (!GetHash(WC->vars, 
1633                      Tokens->Params[2]->Start,
1634                      Tokens->Params[2]->len,
1635                      &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(Tokens->Params[1]->Start, 
1709                                    Tokens->Params[1]->len,
1710                                    Headline, 
1711                                    Context, 
1712                                    ContextType);
1713                 }
1714                 else {
1715                         const char *Ch;
1716                         long len;
1717                         GetTemplateTokenString(Tokens, 
1718                                                1,
1719                                                &Ch,
1720                                                &len);
1721                         Headline = NewStrBufPlain(Ch, len);
1722                 }
1723         }
1724         
1725         DoTemplate(HKEY("beginbox"), Target, Headline, CTX_STRBUF);
1726         DoTemplate(Tokens->Params[0]->Start, 
1727                    Tokens->Params[0]->len,
1728                    Target, 
1729                    Context, 
1730                    ContextType);
1731         DoTemplate(HKEY("endbox"), Target, Context, ContextType);
1732         FreeStrBuf(&Headline);
1733 }
1734
1735 /*-----------------------------------------------------------------------------
1736  *                      Tabbed-API
1737  */
1738
1739 void tmpl_do_tabbed(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
1740 {
1741         StrBuf **TabNames;
1742         int i, ntabs, nTabs;
1743
1744         nTabs = ntabs = Tokens->nParameters / 2;
1745         TabNames = (StrBuf **) malloc(ntabs * sizeof(StrBuf*));
1746
1747         for (i = 0; i < ntabs; i++) {
1748                 TabNames[i] = NewStrBuf();
1749                 if (Tokens->Params[i * 2]->len > 0) {
1750                         DoTemplate(Tokens->Params[i * 2]->Start, 
1751                                    Tokens->Params[i * 2]->len,
1752                                    TabNames[i],
1753                                    Context,
1754                                    ContextType);
1755                 }
1756                 else { 
1757                         /** A Tab without subject? we can't count that, add it as silent */
1758                         nTabs --;
1759                 }
1760         }
1761
1762         StrTabbedDialog(Target, nTabs, TabNames);
1763         for (i = 0; i < ntabs; i++) {
1764                 StrBeginTab(Target, i, nTabs);
1765
1766                 DoTemplate(Tokens->Params[i * 2 + 1]->Start, 
1767                            Tokens->Params[i * 2 + 1]->len,
1768                            Target,
1769                            Context, 
1770                            ContextType);
1771                 StrEndTab(Target, i, nTabs);
1772         }
1773 }
1774
1775
1776 /*-----------------------------------------------------------------------------
1777  *                      Sorting-API
1778  */
1779
1780 typedef struct _SortStruct {
1781         StrBuf *Name;
1782         StrBuf *PrefPrepend;
1783         CompareFunc Forward;
1784         CompareFunc Reverse;
1785
1786         long ContextType;
1787 }SortStruct;
1788
1789 void DestroySortStruct(void *vSort)
1790 {
1791         SortStruct *Sort = (SortStruct*) vSort;
1792         FreeStrBuf(&Sort->Name);
1793         FreeStrBuf(&Sort->PrefPrepend);
1794         free (Sort);
1795 }
1796
1797 void RegisterSortFunc(const char *name, long len, 
1798                       const char *prepend, long preplen,
1799                       CompareFunc Forward, 
1800                       CompareFunc Reverse, 
1801                       long ContextType)
1802 {
1803         SortStruct *NewSort = (SortStruct*) malloc(sizeof(SortStruct));
1804         NewSort->Name = NewStrBufPlain(name, len);
1805         if (prepend != NULL)
1806                 NewSort->PrefPrepend = NewStrBufPlain(prepend, preplen);
1807         else
1808                 NewSort->PrefPrepend = NULL;
1809         NewSort->Forward = Forward;
1810         NewSort->Reverse = Reverse;
1811         NewSort->ContextType = ContextType;
1812         Put(SortHash, name, len, NewSort, DestroySortStruct);
1813 }
1814
1815 CompareFunc RetrieveSort(long ContextType, const char *OtherPrefix, 
1816                          const char *Default, long ldefault, long DefaultDirection)
1817 {
1818         int isdefault = 0;
1819         const StrBuf *BSort;
1820         SortStruct *SortBy;
1821         void *vSortBy;
1822         long SortOrder;
1823         
1824         if (havebstr("SortBy")) {
1825                 BSort = sbstr("SortBy");
1826         }
1827         else { /** Try to fallback to our remembered values... */
1828                 if (OtherPrefix == NULL) {
1829                         BSort = get_room_pref("sort");
1830                 }
1831                 else {
1832                         ////todo: nail prefprepend to sort, and lookup this!
1833                 }
1834         }
1835
1836         if (!GetHash(SortHash, SKEY(BSort), &vSortBy) || 
1837             (vSortBy == NULL)) {
1838                 isdefault = 1;
1839                 if (!GetHash(SortHash, Default, ldefault, &vSortBy) || 
1840                     (vSortBy == NULL)) {
1841                         lprintf(1, "Illegal default sort: [%s]\n", Default);
1842                         wc_backtrace();
1843                 }
1844         }
1845         SortBy = (SortStruct*)vSortBy;
1846
1847         /** Ok, its us, lets see in which direction we should sort... */
1848         if (havebstr("SortOrder")) {
1849                 SortOrder = LBSTR("SortOrder");
1850         }
1851         else { /** Try to fallback to our remembered values... */
1852                 if (SortBy->PrefPrepend == NULL) {
1853                         SortOrder = StrTol(get_room_pref("SortOrder"));
1854                 }
1855                 else {
1856                         ////todo: nail prefprepend to sort, and lookup this!
1857                 }
1858         }
1859         switch (SortOrder) {
1860         default:
1861         case 0:
1862                 return NULL;
1863         case 1:
1864                 return SortBy->Forward;
1865         case 2:
1866                 return SortBy->Reverse;
1867         }
1868 }
1869
1870
1871 enum {
1872         eNO_SUCH_SORT, 
1873         eNOT_SPECIFIED,
1874         eINVALID_PARAM,
1875         eFOUND
1876 };
1877
1878 ConstStr SortIcons[] = {
1879         {HKEY("static/sort_none.gif")},
1880         {HKEY("static/up_pointer.gif")},
1881         {HKEY("static/down_pointer.gif")},
1882 };
1883
1884 ConstStr SortNextOrder[] = {
1885         {HKEY("1")},
1886         {HKEY("2")},
1887         {HKEY("0")},
1888 };
1889
1890
1891 int GetSortMetric(WCTemplateToken *Tokens, SortStruct **Next, SortStruct **Param, long *SortOrder)
1892 {
1893         int bSortError = eNOT_SPECIFIED;
1894         const StrBuf *BSort;
1895         void *vSort;
1896         
1897         *SortOrder = 0;
1898         *Next = NULL;
1899         if (!GetHash(SortHash, Tokens->Params[0]->Start, Tokens->Params[0]->len, &vSort) || 
1900             (vSort == NULL))
1901                 return eNO_SUCH_SORT;
1902         *Param = (SortStruct*) vSort;
1903         
1904
1905         if (havebstr("SortBy")) {
1906                 BSort = sbstr("SortBy");
1907                 bSortError = eINVALID_PARAM;
1908         }
1909         else { /** Try to fallback to our remembered values... */
1910                 if ((*Param)->PrefPrepend == NULL) {
1911                         BSort = get_room_pref("sort");
1912                 }
1913                 else {
1914                         ////todo: nail prefprepend to sort, and lookup this!
1915                 }
1916         }
1917
1918         if (!GetHash(SortHash, SKEY(BSort), &vSort) || 
1919             (vSort == NULL))
1920                 return bSortError;
1921
1922         *Next = (SortStruct*) vSort;
1923
1924         /** Ok, its us, lets see in which direction we should sort... */
1925         if (havebstr("SortOrder")) {
1926                 *SortOrder = LBSTR("SortOrder");
1927         }
1928         else { /** Try to fallback to our remembered values... */
1929                 if ((*Param)->PrefPrepend == NULL) {
1930                         *SortOrder = StrTol(get_room_pref("SortOrder"));
1931                 }
1932                 else {
1933                         ////todo: nail prefprepend to sort, and lookup this!
1934                 }
1935         }
1936         if (*SortOrder > 2)
1937                 *SortOrder = 0;
1938
1939         return eFOUND;
1940 }
1941
1942
1943 void tmplput_SORT_ICON(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
1944 {
1945         long SortOrder;
1946         SortStruct *Next;
1947         SortStruct *Param;
1948         const ConstStr *SortIcon;
1949
1950         switch (GetSortMetric(Tokens, &Next, &Param, &SortOrder)){
1951         case eNO_SUCH_SORT:
1952                 lprintf(1, "[%s] (in '%s' line %ld); "
1953                         " Sorter [%s] unknown! [%s]\n", 
1954                         Tokens->pName,
1955                         ChrPtr(Tokens->FileName),
1956                         Tokens->Line,
1957                         Tokens->Params[0]->Start,
1958                         ChrPtr(Tokens->FlatToken));
1959                 StrBufAppendPrintf(
1960                         Target, 
1961                         "<pre>\n [%s] (in '%s' line %ld);"
1962                         " Sorter [%s] unknown!\n[%s]\n</pre>\n", 
1963                         Tokens->pName,
1964                         ChrPtr(Tokens->FileName),
1965                         Tokens->Line,
1966                         Tokens->Params[0]->Start,
1967                         ChrPtr(Tokens->FlatToken));
1968                 break;          
1969         case eINVALID_PARAM:
1970                 lprintf(1, "[%s] (in '%s' line %ld); "
1971                         " Sorter specified by BSTR 'SortBy' [%s] unknown! [%s]\n", 
1972                         Tokens->pName,
1973                         ChrPtr(Tokens->FileName),
1974                         Tokens->Line,
1975                         bstr("SortBy"),
1976                         ChrPtr(Tokens->FlatToken));
1977         case eNOT_SPECIFIED:
1978         case eFOUND:
1979                 if (Next == Param) {
1980                         SortIcon = &SortIcons[SortOrder];
1981                 }
1982                 else { /** Not Us... */
1983                         SortIcon = &SortIcons[0];
1984                 }
1985                 StrBufAppendBufPlain(Target, SortIcon->Key, SortIcon->len, 0);
1986         }
1987 }
1988
1989 void tmplput_SORT_NEXT(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
1990 {
1991         long SortOrder;
1992         SortStruct *Next;
1993         SortStruct *Param;
1994
1995         switch (GetSortMetric(Tokens, &Next, &Param, &SortOrder)){
1996         case eNO_SUCH_SORT:
1997                 lprintf(1, "[%s] (in '%s' line %ld); "
1998                         " Sorter [%s] unknown! [%s]\n", 
1999                         Tokens->pName,
2000                         ChrPtr(Tokens->FileName),
2001                         Tokens->Line,
2002                         Tokens->Params[0]->Start,
2003                         ChrPtr(Tokens->FlatToken));
2004                 StrBufAppendPrintf(
2005                         Target, 
2006                         "<pre>\n [%s] (in '%s' line %ld);"
2007                         " Sorter [%s] unknown!\n[%s]\n</pre>\n", 
2008                         Tokens->pName,
2009                         ChrPtr(Tokens->FileName),
2010                         Tokens->Line,
2011                         Tokens->Params[0]->Start,
2012                         ChrPtr(Tokens->FlatToken));
2013                 break;          
2014         case eINVALID_PARAM:
2015                 lprintf(1, "[%s] (in '%s' line %ld); "
2016                         " Sorter specified by BSTR 'SortBy' [%s] unknown! [%s]\n", 
2017                         Tokens->pName,
2018                         ChrPtr(Tokens->FileName),
2019                         Tokens->Line,
2020                         bstr("SortBy"),
2021                         ChrPtr(Tokens->FlatToken));
2022
2023         case eNOT_SPECIFIED:
2024         case eFOUND:
2025                 StrBufAppendBuf(Target, Param->Name, 0);
2026                 
2027         }
2028 }
2029
2030 void tmplput_SORT_ORDER(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
2031 {
2032         long SortOrder;
2033         const ConstStr *SortOrderStr;
2034         SortStruct *Next;
2035         SortStruct *Param;
2036
2037         switch (GetSortMetric(Tokens, &Next, &Param, &SortOrder)){
2038         case eNO_SUCH_SORT:
2039                 lprintf(1, "[%s] (in '%s' line %ld); "
2040                         " Sorter [%s] unknown! [%s]\n", 
2041                         Tokens->pName,
2042                         ChrPtr(Tokens->FileName),
2043                         Tokens->Line,
2044                         Tokens->Params[0]->Start,
2045                         ChrPtr(Tokens->FlatToken));
2046                 StrBufAppendPrintf(
2047                         Target, 
2048                         "<pre>\n [%s] (in '%s' line %ld);"
2049                         " Sorter [%s] unknown!\n[%s]\n</pre>\n", 
2050                         Tokens->pName,
2051                         ChrPtr(Tokens->FileName),
2052                         Tokens->Line,
2053                         Tokens->Params[0]->Start,
2054                         ChrPtr(Tokens->FlatToken));
2055                 break;          
2056         case eINVALID_PARAM:
2057                 lprintf(1, "[%s] (in '%s' line %ld); "
2058                         " Sorter specified by BSTR 'SortBy' [%s] unknown! [%s]\n", 
2059                         Tokens->pName,
2060                         ChrPtr(Tokens->FileName),
2061                         Tokens->Line,
2062                         bstr("SortBy"),
2063                         ChrPtr(Tokens->FlatToken));
2064
2065         case eNOT_SPECIFIED:
2066         case eFOUND:
2067                 if (Next == Param) {
2068                         SortOrderStr = &SortNextOrder[SortOrder];
2069                 }
2070                 else { /** Not Us... */
2071                         SortOrderStr = &SortNextOrder[0];
2072                 }
2073                 StrBufAppendBufPlain(Target, SortOrderStr->Key, SortOrderStr->len, 0);
2074         }
2075 }
2076
2077
2078 void tmplput_long_vector(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
2079 {
2080         long *LongVector = (long*) Context;
2081
2082         if ((Tokens->Params[0]->Type == TYPE_LONG) && 
2083             (Tokens->Params[0]->lvalue <= LongVector[0]))
2084         {
2085                 StrBufAppendPrintf(Target, "%ld", LongVector[Tokens->Params[0]->lvalue]);
2086         }
2087         else
2088         {
2089                 if (Tokens->Params[0]->Type == TYPE_LONG) {
2090                         lprintf(1, "longvector [%s] (in '%s' line %ld); needs a long Parameter![%s]\n", 
2091                                 Tokens->Params[0]->Start,
2092                                 ChrPtr(Tokens->FileName),
2093                                 Tokens->Line,
2094                                 ChrPtr(Tokens->FlatToken));
2095                         StrBufAppendPrintf(
2096                                 Target, 
2097                                 "<pre>\nlongvector [%s] (in '%s' line %ld); needs a numerical Parameter!\n[%s]\n</pre>\n", 
2098                                 Tokens->Params[0]->Start,
2099                                 ChrPtr(Tokens->FileName),
2100                                 Tokens->Line,
2101                                 ChrPtr(Tokens->FlatToken));
2102                 }
2103                 else {
2104                         lprintf(1, "longvector [%s] (in '%s' line %ld); doesn't have %ld Parameters,"
2105                                 " its just the size of %ld![%s]\n", 
2106                                 Tokens->Params[0]->Start,
2107                                 ChrPtr(Tokens->FileName),
2108                                 Tokens->Line,
2109                                 Tokens->Params[0]->lvalue,
2110                                 LongVector[0],
2111                                 ChrPtr(Tokens->FlatToken));
2112                         StrBufAppendPrintf(
2113                                 Target, 
2114                                 "<pre>\nlongvector [%s] (in '%s' line %ld); doesn't have %ld Parameters,"
2115                                 " its just the size of %ld!\n[%s]\n</pre>\n", 
2116                                 Tokens->Params[0]->Start,
2117                                 ChrPtr(Tokens->FileName),
2118                                 Tokens->Line,
2119                                 Tokens->Params[0]->lvalue,
2120                                 LongVector[0],
2121                                 ChrPtr(Tokens->FlatToken));             
2122                 }
2123         }
2124 }
2125
2126
2127
2128 int ConditionalLongVector(WCTemplateToken *Tokens, void *Context, int ContextType)
2129 {
2130         long *LongVector = (long*) Context;
2131
2132         if ((Tokens->Params[2]->Type == TYPE_LONG) && 
2133             (Tokens->Params[2]->lvalue <= LongVector[0])&&
2134             (Tokens->Params[3]->Type == TYPE_LONG) && 
2135             (Tokens->Params[3]->lvalue <= LongVector[0]))
2136         {
2137                 return LongVector[Tokens->Params[2]->lvalue] == LongVector[Tokens->Params[3]->lvalue];
2138         }
2139         else
2140         {
2141                 if ((Tokens->Params[2]->Type == TYPE_LONG) ||
2142                     (Tokens->Params[2]->Type == TYPE_LONG)) {
2143                         lprintf(1, "ConditionalLongVector [%s] (in '%s' line %ld); needs two long Parameter![%s]\n", 
2144                                 Tokens->Params[0]->Start,
2145                                 ChrPtr(Tokens->FileName),
2146                                 Tokens->Line,
2147                                 ChrPtr(Tokens->FlatToken));
2148                 }
2149                 else {
2150                         lprintf(1, "longvector [%s] (in '%s' line %ld); doesn't have %ld / %ld Parameters,"
2151                                 " its just the size of %ld![%s]\n", 
2152                                 Tokens->Params[0]->Start,
2153                                 ChrPtr(Tokens->FileName),
2154                                 Tokens->Line,
2155                                 Tokens->Params[2]->lvalue,
2156                                 Tokens->Params[3]->lvalue,
2157                                 LongVector[0],
2158                                 ChrPtr(Tokens->FlatToken));
2159                 }
2160         }
2161         return 0;
2162 }
2163
2164 void 
2165 InitModule_SUBST
2166 (void)
2167 {
2168         RegisterNamespace("SORT:ICON", 1, 1, tmplput_SORT_ICON, CTX_NONE);
2169         RegisterNamespace("SORT:ORDER", 1, 1, tmplput_SORT_ORDER, CTX_NONE);
2170         RegisterNamespace("SORT:NEXT", 1, 1, tmplput_SORT_NEXT, CTX_NONE);
2171         RegisterNamespace("CONTEXTSTR", 0, 1, tmplput_ContextString, CTX_STRBUF);
2172         RegisterNamespace("ITERATE", 2, 100, tmpl_iterate_subtmpl, CTX_NONE);
2173         RegisterNamespace("DOBOXED", 1, 2, tmpl_do_boxed, CTX_NONE);
2174         RegisterNamespace("DOTABBED", 2, 100, tmpl_do_tabbed, CTX_NONE);
2175         RegisterNamespace("LONGVECTOR", 1, 1, tmplput_long_vector, CTX_LONGVECTOR);
2176         RegisterConditional(HKEY("COND:SUBST"), 3, ConditionalVar, CTX_NONE);
2177         RegisterConditional(HKEY("COND:CONTEXTSTR"), 3, ConditionalContextStr, CTX_STRBUF);
2178         RegisterConditional(HKEY("COND:LONGVECTOR"), 4, ConditionalLongVector, CTX_LONGVECTOR);
2179 }
2180
2181 /*@}*/