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