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