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