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