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