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