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