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