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