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