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