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