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