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