Templatize sieve rules editor; return success/fail state of serverwrite
[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         char EscapeAs = ' ';
624
625         if ((FormatTypeIndex < TP->Tokens->nParameters) &&
626             (TP->Tokens->Params[FormatTypeIndex]->Type == TYPE_STR) &&
627             (TP->Tokens->Params[FormatTypeIndex]->len == 1)) {
628                 EscapeAs = *TP->Tokens->Params[FormatTypeIndex]->Start;
629         }
630
631         switch(EscapeAs)
632         {
633         case 'H':
634                 WCC = WC;
635                 StrEscAppend(Target, Source, NULL, 0, 2);
636                 break;
637         case 'X':
638                 StrEscAppend(Target, Source, NULL, 0, 0);
639                 break;
640         case 'J':
641                 StrECMAEscAppend(Target, Source, NULL);
642           break;
643         case 'K':
644                 StrHtmlEcmaEscAppend(Target, Source, NULL, 0, 0);
645           break;
646         case 'U':
647                 StrBufUrlescAppend(Target, Source, NULL);
648                 break;
649         default:
650                 StrBufAppendBuf(Target, Source, 0);
651         }
652 }
653
654
655 void PutNewToken(WCTemplate *Template, WCTemplateToken *NewToken)
656 {
657         if (Template->nTokensUsed + 1 >= Template->TokenSpace) {
658                 if (Template->TokenSpace <= 0) {
659                         Template->Tokens = (WCTemplateToken**)malloc(
660                                 sizeof(WCTemplateToken*) * 10);
661                         memset(Template->Tokens, 0, sizeof(WCTemplateToken*) * 10);
662                         Template->TokenSpace = 10;
663                 }
664                 else {
665                         WCTemplateToken **NewTokens;
666
667                         NewTokens= (WCTemplateToken**) malloc(
668                                 sizeof(WCTemplateToken*) * Template->TokenSpace * 2);
669
670                         memset(NewTokens, 
671                                0, sizeof(WCTemplateToken*) * Template->TokenSpace * 2);
672
673                         memcpy(NewTokens, 
674                                Template->Tokens, 
675                                sizeof(WCTemplateToken*) * Template->nTokensUsed);
676
677                         free(Template->Tokens);
678                         Template->TokenSpace *= 2;
679                         Template->Tokens = NewTokens;
680                 }
681         }
682         Template->Tokens[(Template->nTokensUsed)++] = NewToken;
683 }
684
685 int GetNextParameter(StrBuf *Buf, 
686                      const char **pCh, 
687                      const char *pe, 
688                      WCTemplateToken *Tokens, 
689                      WCTemplate *pTmpl, 
690                      WCTemplputParams *TP, 
691                      TemplateParam **pParm)
692 {
693         const char *pch = *pCh;
694         const char *pchs, *pche;
695         TemplateParam *Parm;
696         char quote = '\0';
697         int ParamBrace = 0;
698
699         *pParm = Parm = (TemplateParam *) malloc(sizeof(TemplateParam));
700         memset(Parm, 0, sizeof(TemplateParam));
701         Parm->Type = TYPE_STR;
702
703         /* Skip leading whitespaces */
704         while ((*pch == ' ' )||
705                (*pch == '\t')||
706                (*pch == '\r')||
707                (*pch == '\n')) pch ++;
708
709         if (*pch == ':') {
710                 Parm->Type = TYPE_PREFSTR;
711                 pch ++;
712                 if (*pch == '(') {
713                         pch ++;
714                         ParamBrace = 1;
715                 }
716         }
717         else if (*pch == ';') {
718                 Parm->Type = TYPE_PREFINT;
719                 pch ++;
720                 if (*pch == '(') {
721                         pch ++;
722                         ParamBrace = 1;
723                 }
724         }
725         else if (*pch == '#') {
726                 Parm->Type = TYPE_INTDEFINE;
727                 pch ++;
728         }
729         else if (*pch == '_') {
730                 Parm->Type = TYPE_GETTEXT;
731                 pch ++;
732                 if (*pch == '(') {
733                         pch ++;
734                         ParamBrace = 1;
735                 }
736         }
737         else if (*pch == 'B') {
738                 Parm->Type = TYPE_BSTR;
739                 pch ++;
740                 if (*pch == '(') {
741                         pch ++;
742                         ParamBrace = 1;
743                 }
744         }
745         else if (*pch == '=') {
746                 Parm->Type = TYPE_SUBTEMPLATE;
747                 pch ++;
748                 if (*pch == '(') {
749                         pch ++;
750                         ParamBrace = 1;
751                 }
752         }
753
754
755         if (*pch == '"')
756                 quote = '"';
757         else if (*pch == '\'')
758                 quote = '\'';
759         if (quote != '\0') {
760                 pch ++;
761                 pchs = pch;
762                 while (pch <= pe &&
763                        ((*pch != quote) ||
764                         ( (pch > pchs) && (*(pch - 1) == '\\'))
765                                )) {
766                         pch ++;
767                 }
768                 pche = pch;
769                 if (*pch != quote) {
770                         syslog(1, "Error (in '%s' line %ld); "
771                                 "evaluating template param [%s] in Token [%s]\n",
772                                 ChrPtr(pTmpl->FileName),
773                                 Tokens->Line,
774                                 ChrPtr(Tokens->FlatToken),
775                                 *pCh);
776                         pch ++;
777                         free(Parm);
778                         *pParm = NULL;
779                         return 0;
780                 }
781                 else {
782                         StrBufPeek(Buf, pch, -1, '\0');         
783                         if (LoadTemplates > 1) {                        
784                                 syslog(1, "DBG: got param [%s] %ld %ld\n", 
785                                         pchs, pche - pchs, strlen(pchs));
786                         }
787                         Parm->Start = pchs;
788                         Parm->len = pche - pchs;
789                         pch ++; /* move after trailing quote */
790                         if (ParamBrace && (*pch == ')')) {
791                                 pch ++;
792                         }
793
794                 }
795         }
796         else {
797                 Parm->Type = TYPE_LONG;
798                 pchs = pch;
799                 while ((pch <= pe) &&
800                        (isdigit(*pch) ||
801                         (*pch == '+') ||
802                         (*pch == '-')))
803                         pch ++;
804                 pch ++;
805                 if (pch - pchs > 1){
806                         StrBufPeek(Buf, pch, -1, '\0');
807                         Parm->lvalue = atol(pchs);
808                         Parm->Start = pchs;
809                         pch++;
810                 }
811                 else {
812                         Parm->lvalue = 0;
813 /* TODO whUT?
814                         syslog(1, "Error (in '%s' line %ld); "
815                                 "evaluating long template param [%s] in Token [%s]\n",
816                                 ChrPtr(pTmpl->FileName),
817                                 Tokens->Line,
818                                 ChrPtr(Tokens->FlatToken),
819                                 *pCh);
820                                 */
821                         free(Parm);
822                         *pParm = NULL;
823                         return 0;
824                 }
825         }
826         while ((*pch == ' ' )||
827                (*pch == '\t')||
828                (*pch == '\r')||
829                (*pch == ',' )||
830                (*pch == '\n')) pch ++;
831
832         switch (Parm->Type)
833         {
834         case TYPE_GETTEXT:
835                 if (DumpTemplateI18NStrings) {
836                         StrBufAppendPrintf(I18nDump, "_(\"%s\");\n", Parm->Start);
837                 }
838                 break;
839         case TYPE_INTDEFINE: {
840                 void *vPVal;
841                 
842                 if (GetHash(Defines, Parm->Start, Parm->len, &vPVal) &&
843                     (vPVal != NULL))
844                 {
845                         long *PVal;
846                         PVal = (long*) vPVal;
847                 
848                         Parm->lvalue = *PVal;
849                 }
850                 else if (strchr(Parm->Start, '|') != NULL)
851                 {
852                         const char *Pos;
853                         StrBuf *pToken;
854                         StrBuf *Match;
855
856                         Parm->MaskBy = eOR;
857                         pToken = NewStrBufPlain (Parm->Start, Parm->len);
858                         Match = NewStrBufPlain (NULL, Parm->len);
859                         Pos = ChrPtr(pToken);
860                         
861                         while ((Pos != NULL) && (Pos != StrBufNOTNULL))
862                         {
863                                 StrBufExtract_NextToken(Match, pToken, &Pos, '|');
864                                 StrBufTrim(Match);
865                                 if (StrLength (Match) > 0)
866                                 {
867                                         if (GetHash(Defines, SKEY(Match), &vPVal) &&
868                                             (vPVal != NULL))
869                                         {
870                                                 long *PVal;
871                                                 PVal = (long*) vPVal;
872                                                 
873                                                 Parm->lvalue |= *PVal;
874                                         }
875                                         else {
876                                                 LogTemplateError(NULL, "Define", 
877                                                                  Tokens->nParameters,
878                                                                  TP,
879                                                                  "%s isn't known!!",
880                                                                  ChrPtr(Match));
881
882                                         }
883                                 }
884                         }
885                         FreeStrBuf(&pToken);
886                         FreeStrBuf(&Match);
887                 }
888                 else if (strchr(Parm->Start, '&') != NULL)
889                 {
890                         const char *Pos;
891                         StrBuf *pToken;
892                         StrBuf *Match;
893
894                         Parm->MaskBy = eAND;
895                         pToken = NewStrBufPlain (Parm->Start, Parm->len);
896                         Match = NewStrBufPlain (NULL, Parm->len);
897                         Pos = ChrPtr(pToken);
898                         
899                         while ((Pos != NULL) && (Pos != StrBufNOTNULL))
900                         {
901                                 StrBufExtract_NextToken(Match, pToken, &Pos, '&');
902                                 StrBufTrim(Match);
903                                 if (StrLength (Match) > 0)
904                                 {
905                                         if (GetHash(Defines, SKEY(Match), &vPVal) &&
906                                             (vPVal != NULL))
907                                         {
908                                                 long *PVal;
909                                                 PVal = (long*) vPVal;
910                                                 
911                                                 Parm->lvalue |= *PVal;
912                                         }
913                                         else {
914                                                 LogTemplateError(NULL, "Define", 
915                                                                  Tokens->nParameters,
916                                                                  TP,
917                                                                  "%s isn't known!!",
918                                                                  ChrPtr(Match));
919
920                                         }
921                                 }
922                         }
923                         FreeStrBuf(&Match);
924                         FreeStrBuf(&pToken);
925                 }
926                 else {
927
928
929                         LogTemplateError(NULL, "Define", 
930                                          Tokens->nParameters,
931                                          TP,
932                                          "%s isn't known!!",
933                                          Parm->Start);
934                 }}
935                 break;
936         case TYPE_SUBTEMPLATE:{
937                 void *vTmpl;
938                 /* well, we don't check the mobile stuff here... */
939                 if (!GetHash(LocalTemplateCache, Parm->Start, Parm->len, &vTmpl) &&
940                     !GetHash(TemplateCache, Parm->Start, Parm->len, &vTmpl)) {
941                         LogTemplateError(NULL, 
942                                          "SubTemplate", 
943                                          Tokens->nParameters,
944                                          TP,
945                                          "referenced here doesn't exist");
946                 }}
947                 break;
948         }
949         *pCh = pch;
950         return 1;
951 }
952
953 WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf, 
954                                        const char *pStart, 
955                                        const char *pTokenStart, 
956                                        const char *pTokenEnd, 
957                                        long Line,
958                                        WCTemplate *pTmpl)
959 {
960         void *vVar;
961         const char *pch;
962         WCTemplateToken *NewToken;
963         WCTemplputParams TP;
964
965         NewToken = (WCTemplateToken*)malloc(sizeof(WCTemplateToken));
966         memset(NewToken, 0, sizeof(WCTemplateToken));
967         TP.Tokens = NewToken;
968         NewToken->FileName = pTmpl->FileName; /* to print meaningfull log messages... */
969         NewToken->Flags = 0;
970         NewToken->Line = Line + 1;
971         NewToken->pTokenStart = pTokenStart;
972         NewToken->TokenStart = pTokenStart - pStart;
973         NewToken->TokenEnd =  (pTokenEnd - pStart) - NewToken->TokenStart;
974         NewToken->pTokenEnd = pTokenEnd;
975         NewToken->NameEnd = NewToken->TokenEnd - 2;
976         NewToken->PreEval = NULL;
977         NewToken->FlatToken = NewStrBufPlain(pTokenStart + 2, pTokenEnd - pTokenStart - 2);
978         StrBufShrinkToFit(NewToken->FlatToken, 1);
979
980         StrBufPeek(Buf, pTokenStart, + 1, '\0');
981         StrBufPeek(Buf, pTokenEnd, -1, '\0');
982         pch = NewToken->pName = pTokenStart + 2;
983
984         NewToken->HaveParameters = 0;;
985         NewToken->nParameters = 0;
986
987         while (pch < pTokenEnd - 1) {
988                 if (*pch == '(') {
989                         StrBufPeek(Buf, pch, -1, '\0');
990                         NewToken->NameEnd = pch - NewToken->pName;
991                         pch ++;
992                         if (*(pTokenEnd - 1) != ')') {
993                                 LogTemplateError(
994                                         NULL, "Parseerror", ERR_NAME, &TP, 
995                                         "Warning, Non welformed Token; missing right parenthesis");
996                         }
997                         while (pch < pTokenEnd - 1) {
998                                 NewToken->nParameters++;
999                                 if (GetNextParameter(Buf, 
1000                                                      &pch, 
1001                                                      pTokenEnd - 1, 
1002                                                      NewToken, 
1003                                                      pTmpl, 
1004                                                      &TP, 
1005                                                      &NewToken->Params[NewToken->nParameters - 1]))
1006                                 {
1007                                         NewToken->HaveParameters = 1;
1008                                         if (NewToken->nParameters >= MAXPARAM) {
1009                                                 LogTemplateError(
1010                                                         NULL, "Parseerror", ERR_NAME, &TP,
1011                                                         "only [%d] Params allowed in Tokens",
1012                                                         MAXPARAM);
1013
1014                                                 FreeToken(&NewToken);
1015                                                 return NULL;
1016                                         }
1017                                 }
1018                                 else break;
1019                         }
1020                         if((NewToken->NameEnd == 1) &&
1021                            (NewToken->HaveParameters == 1))
1022                            
1023                         {
1024                                 if (*(NewToken->pName) == '_')
1025                                         NewToken->Flags = SV_GETTEXT;
1026                                 else if (*(NewToken->pName) == '=')
1027                                         NewToken->Flags = SV_SUBTEMPL;
1028                                 else if (*(NewToken->pName) == '%')
1029                                         NewToken->Flags = SV_CUST_STR_CONDITIONAL;
1030                                 else if (*(NewToken->pName) == '?')
1031                                         NewToken->Flags = SV_CONDITIONAL;
1032                                 else if (*(NewToken->pName) == '!')
1033                                         NewToken->Flags = SV_NEG_CONDITIONAL;
1034                         }
1035                 }
1036                 else pch ++;            
1037         }
1038         
1039         switch (NewToken->Flags) {
1040         case 0:
1041                 /* If we're able to find out more about the token, do it now while its fresh. */
1042                 if (GetHash(GlobalNS, NewToken->pName, NewToken->NameEnd, &vVar)) {
1043                         HashHandler *Handler;
1044                         Handler = (HashHandler*) vVar;
1045                         if ((NewToken->nParameters < Handler->Filter.nMinArgs) || 
1046                             (NewToken->nParameters > Handler->Filter.nMaxArgs)) {
1047                                 LogTemplateError(
1048                                         NULL, "Token", ERR_NAME, &TP,
1049                                         "doesn't work with %d params", 
1050                                         NewToken->nParameters);
1051
1052                         }
1053                         else {
1054                                 NewToken->PreEval = Handler;
1055                                 NewToken->Flags = SV_PREEVALUATED;              
1056                                 if (Handler->PreEvalFunc != NULL)
1057                                         Handler->PreEvalFunc(NewToken);
1058                         }
1059                 }
1060                 break;
1061         case SV_GETTEXT:
1062                 if (NewToken->nParameters !=1) {
1063                         LogTemplateError(                               
1064                                 NULL, "Gettext", ERR_NAME, &TP,
1065                                 "requires exactly 1 parameter, you gave %d params", 
1066                                 NewToken->nParameters);
1067                         NewToken->Flags = 0;
1068                         break;
1069                 }
1070                 if (DumpTemplateI18NStrings) {
1071                         StrBufAppendPrintf(I18nDump, "_(\"%s\");\n", NewToken->Params[0]->Start);
1072                 }
1073                 break;
1074         case SV_SUBTEMPL:
1075                 if (NewToken->nParameters != 1) {
1076                         LogTemplateError(
1077                                 NULL, "Subtemplates", ERR_NAME, &TP,
1078                                 "require exactly 1 parameter, you gave %d params", 
1079                                 NewToken->nParameters);
1080                         break;
1081                 }
1082                 else {
1083                         void *vTmpl;
1084                         /* well, we don't check the mobile stuff here... */
1085                         if (!GetHash(LocalTemplateCache, 
1086                                      NewToken->Params[0]->Start, 
1087                                      NewToken->Params[0]->len, 
1088                                      &vTmpl) &&
1089                             !GetHash(TemplateCache, 
1090                                      NewToken->Params[0]->Start, 
1091                                      NewToken->Params[0]->len, 
1092                                      &vTmpl)) {
1093                                 LogTemplateError(
1094                                         NULL, "SubTemplate", ERR_PARM1, &TP,
1095                                         "doesn't exist");
1096                         }
1097                 }
1098                 break;
1099         case SV_CUST_STR_CONDITIONAL:
1100         case SV_CONDITIONAL:
1101         case SV_NEG_CONDITIONAL:
1102                 if (NewToken->nParameters <2) {
1103                         LogTemplateError(
1104                                 NULL, "Conditional", ERR_PARM1, &TP,
1105                                 "require at least 2 parameters, you gave %d params", 
1106                                 NewToken->nParameters);
1107                         NewToken->Flags = 0;
1108                         break;
1109                 }
1110                 if (NewToken->Params[1]->lvalue == 0) {
1111                         LogTemplateError(
1112                                 NULL, "Conditional", ERR_PARM1, &TP,
1113                                 "Conditional ID (Parameter 1) mustn't be 0!");
1114                         NewToken->Flags = 0;
1115                         break;
1116                 }
1117                 if (!GetHash(Conditionals, 
1118                              NewToken->Params[0]->Start, 
1119                              NewToken->Params[0]->len, 
1120                              &vVar) || 
1121                     (vVar == NULL)) {
1122                         if ((NewToken->Params[0]->len == 1) &&
1123                             (NewToken->Params[0]->Start[0] == 'X'))
1124                                 break;
1125                         LogTemplateError(
1126                                 NULL, "Conditional", ERR_PARM1, &TP,
1127                                 "Not found!");
1128 /*
1129                         NewToken->Error = NewStrBuf();
1130                         StrBufAppendPrintf(
1131                                 NewToken->Error, 
1132                                 "<pre>\nConditional [%s] (in '%s' line %ld); Not found!\n[%s]\n</pre>\n", 
1133                                 NewToken->Params[0]->Start,
1134                                 ChrPtr(pTmpl->FileName),
1135                                 NewToken->Line,
1136                                 ChrPtr(NewToken->FlatToken));
1137 */
1138                 }
1139                 else {
1140                         NewToken->PreEval = vVar;
1141                 }
1142                 break;
1143         }
1144         return NewToken;
1145 }
1146
1147
1148
1149
1150
1151 /**
1152  * \brief Display a variable-substituted template
1153  * \param templatename template file to load
1154  */
1155 void *prepare_template(StrBuf *filename, StrBuf *Key, HashList *PutThere)
1156 {
1157         WCTemplate *NewTemplate;
1158
1159         NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
1160         memset(NewTemplate, 0, sizeof(WCTemplate));
1161         NewTemplate->Data = NULL;
1162         NewTemplate->FileName = NewStrBufDup(filename);
1163         StrBufShrinkToFit(NewTemplate->FileName, 1);
1164         NewTemplate->nTokensUsed = 0;
1165         NewTemplate->TokenSpace = 0;
1166         NewTemplate->Tokens = NULL;
1167         NewTemplate->MimeType = NewStrBufPlain(GuessMimeByFilename (SKEY(NewTemplate->FileName)), -1);
1168         if (strstr(ChrPtr(NewTemplate->MimeType), "text") != NULL) {
1169                 StrBufAppendBufPlain(NewTemplate->MimeType, HKEY("; charset=utf-8"), 0);
1170         }
1171
1172         if (strstr(ChrPtr(NewTemplate->MimeType), "text") != NULL) {
1173                 StrBufAppendBufPlain(NewTemplate->MimeType, HKEY("; charset=utf-8"), 0);
1174         }
1175
1176         Put(PutThere, ChrPtr(Key), StrLength(Key), NewTemplate, FreeWCTemplate);
1177         return NewTemplate;
1178 }
1179
1180 /**
1181  * \brief Display a variable-substituted template
1182  * \param templatename template file to load
1183  */
1184 void *duplicate_template(WCTemplate *OldTemplate)
1185 {
1186         WCTemplate *NewTemplate;
1187
1188         NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
1189         memset(NewTemplate, 0, sizeof(WCTemplate));
1190         NewTemplate->Data = NULL;
1191         NewTemplate->FileName = NewStrBufDup(OldTemplate->FileName);
1192         StrBufShrinkToFit(NewTemplate->FileName, 1);
1193         NewTemplate->nTokensUsed = 0;
1194         NewTemplate->TokenSpace = 0;
1195         NewTemplate->Tokens = NULL;
1196         NewTemplate->MimeType = NewStrBufDup(OldTemplate->MimeType);
1197         return NewTemplate;
1198 }
1199
1200 /**
1201  * \brief Display a variable-substituted template
1202  * \param templatename template file to load
1203  */
1204 void *load_template(WCTemplate *NewTemplate)
1205 {
1206         int fd;
1207         struct stat statbuf;
1208         const char *pS, *pE, *pch, *Err;
1209         long Line;
1210         int pos;
1211
1212         fd = open(ChrPtr(NewTemplate->FileName), O_RDONLY);
1213         if (fd <= 0) {
1214                 syslog(1, "ERROR: could not open template '%s' - %s\n",
1215                         ChrPtr(NewTemplate->FileName), strerror(errno));
1216                 return NULL;
1217         }
1218
1219         if (fstat(fd, &statbuf) == -1) {
1220                 syslog(1, "ERROR: could not stat template '%s' - %s\n",
1221                         ChrPtr(NewTemplate->FileName), strerror(errno));
1222                 return NULL;
1223         }
1224
1225         NewTemplate->Data = NewStrBufPlain(NULL, statbuf.st_size + 1);
1226         if (StrBufReadBLOB(NewTemplate->Data, &fd, 1, statbuf.st_size, &Err) < 0) {
1227                 close(fd);
1228                 syslog(1, "ERROR: reading template '%s' - %s<br>\n",
1229                         ChrPtr(NewTemplate->FileName), strerror(errno));
1230                 //FreeWCTemplate(NewTemplate);/////tODO
1231                 return NULL;
1232         }
1233         close(fd);
1234
1235         Line = 0;
1236         StrBufShrinkToFit(NewTemplate->Data, 1);
1237         StrBufShrinkToFit(NewTemplate->MimeType, 1);
1238         pS = pch = ChrPtr(NewTemplate->Data);
1239         pE = pS + StrLength(NewTemplate->Data);
1240         while (pch < pE) {
1241                 const char *pts, *pte;
1242                 int InQuotes = 0;
1243                 int InDoubleQuotes = 0;
1244                 void *pv;
1245
1246                 /** Find one <? > */
1247                 pos = (-1);
1248                 for (; pch < pE; pch ++) {
1249                         if ((*pch=='<')&&(*(pch + 1)=='?') &&
1250                             !((pch == pS) && /* we must ommit a <?xml */
1251                               (*(pch + 2) == 'x') && 
1252                               (*(pch + 3) == 'm') && 
1253                               (*(pch + 4) == 'l')))                          
1254                                 break;
1255                         if (*pch=='\n') Line ++;
1256                 }
1257                 if (pch >= pE)
1258                         continue;
1259                 pts = pch;
1260
1261                 /** Found one? parse it. */
1262                 for (; pch <= pE - 1; pch ++) {
1263                         if (*pch == '"')
1264                                 InDoubleQuotes = ! InDoubleQuotes;
1265                         else if (*pch == '\'')
1266                                 InQuotes = ! InQuotes;
1267                         else if ((!InQuotes  && !InDoubleQuotes) &&
1268                                  ((*pch!='\\')&&(*(pch + 1)=='>'))) {
1269                                 pch ++;
1270                                 break;
1271                         }
1272                 }
1273                 if (pch + 1 > pE)
1274                         continue;
1275                 pte = pch;
1276                 pv = NewTemplateSubstitute(NewTemplate->Data, pS, pts, pte, Line, NewTemplate);
1277                 if (pv != NULL) {
1278                         PutNewToken(NewTemplate, pv);
1279                         pch ++;
1280                 }
1281         }
1282         return NewTemplate;
1283 }
1284
1285
1286 const char* PrintTemplate(void *vSubst)
1287 {
1288         WCTemplate *Tmpl = vSubst;
1289
1290         return ChrPtr(Tmpl->FileName);
1291
1292 }
1293
1294 int LoadTemplateDir(const StrBuf *DirName, HashList *big, const StrBuf *BaseKey)
1295 {
1296         int Toplevel;
1297         StrBuf *FileName;
1298         StrBuf *Key;
1299         StrBuf *SubKey;
1300         StrBuf *SubDirectory;
1301         DIR *filedir = NULL;
1302         struct dirent *filedir_entry;
1303         struct dirent *d;
1304         int d_type = 0;
1305         int d_namelen;
1306         int d_without_ext;
1307         
1308         d = (struct dirent *)malloc(offsetof(struct dirent, d_name) + PATH_MAX + 1);
1309         if (d == NULL) {
1310                 return 0;
1311         }
1312
1313         filedir = opendir (ChrPtr(DirName));
1314         if (filedir == NULL) {
1315                 free(d);
1316                 return 0;
1317         }
1318
1319         Toplevel = StrLength(BaseKey) == 0;
1320         SubDirectory = NewStrBuf();
1321         SubKey = NewStrBuf();
1322         FileName = NewStrBufPlain(NULL, PATH_MAX);
1323         Key = NewStrBuf();
1324         while ((readdir_r(filedir, d, &filedir_entry) == 0) &&
1325                (filedir_entry != NULL))
1326         {
1327                 char *MinorPtr;
1328                 char *PStart;
1329 #ifdef _DIRENT_HAVE_D_NAMELEN
1330                 d_namelen = filedir_entry->d_namelen;
1331                 d_type = filedir_entry->d_type;
1332 #else
1333
1334 #ifndef DT_UNKNOWN
1335 #define DT_UNKNOWN     0
1336 #define DT_DIR         4
1337 #define DT_REG         8
1338 #define DT_LNK         10
1339
1340 #define IFTODT(mode)   (((mode) & 0170000) >> 12)
1341 #define DTTOIF(dirtype)        ((dirtype) << 12)
1342 #endif
1343                 d_namelen = strlen(filedir_entry->d_name);
1344                 d_type = DT_UNKNOWN;
1345 #endif
1346                 d_without_ext = d_namelen;
1347
1348                 if ((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~')
1349                         continue; /* Ignore backup files... */
1350
1351                 if ((d_namelen == 1) && 
1352                     (filedir_entry->d_name[0] == '.'))
1353                         continue;
1354
1355                 if ((d_namelen == 2) && 
1356                     (filedir_entry->d_name[0] == '.') &&
1357                     (filedir_entry->d_name[1] == '.'))
1358                         continue;
1359
1360                 if (d_type == DT_UNKNOWN) {
1361                         struct stat s;
1362                         char path[PATH_MAX];
1363                         snprintf(path, PATH_MAX, "%s/%s", 
1364                                  ChrPtr(DirName), filedir_entry->d_name);
1365                         if (stat(path, &s) == 0) {
1366                                 d_type = IFTODT(s.st_mode);
1367                         }
1368                 }
1369                 switch (d_type)
1370                 {
1371                 case DT_DIR:
1372                         /* Skip directories we are not interested in... */
1373                         if (strcmp(filedir_entry->d_name, ".svn") == 0)
1374                                 continue;
1375
1376                         FlushStrBuf(SubKey);
1377                         if (!Toplevel) {
1378                                 /* If we're not toplevel, the upper dirs count as foo_bar_<local name>*/
1379                                 StrBufAppendBuf(SubKey, BaseKey, 0);
1380                                 StrBufAppendBufPlain(SubKey, HKEY("_"), 0);
1381                         }
1382                         StrBufAppendBufPlain(SubKey, filedir_entry->d_name, d_namelen, 0);
1383
1384                         FlushStrBuf(SubDirectory);
1385                         StrBufAppendBuf(SubDirectory, DirName, 0);
1386                         if (ChrPtr(SubDirectory)[StrLength(SubDirectory) - 1] != '/')
1387                                 StrBufAppendBufPlain(SubDirectory, HKEY("/"), 0);
1388                         StrBufAppendBufPlain(SubDirectory, filedir_entry->d_name, d_namelen, 0);
1389
1390                         LoadTemplateDir(SubDirectory, big, SubKey);
1391
1392                         break;
1393                 case DT_LNK: /* TODO: check whether its a file or a directory */
1394                 case DT_REG:
1395
1396
1397                         while ((d_without_ext > 0) && (filedir_entry->d_name[d_without_ext] != '.'))
1398                                 d_without_ext --;
1399                         if ((d_without_ext == 0) || (d_namelen < 3))
1400                                 continue;
1401                         if (((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~') ||
1402                             (strcmp(&filedir_entry->d_name[d_without_ext], ".orig") == 0) ||
1403                             (strcmp(&filedir_entry->d_name[d_without_ext], ".swp") == 0))
1404                                 continue; /* Ignore backup files... */
1405                         PStart = filedir_entry->d_name;
1406                         StrBufPrintf(FileName, "%s/%s", ChrPtr(DirName),  filedir_entry->d_name);
1407                         MinorPtr = strchr(filedir_entry->d_name, '.');
1408                         if (MinorPtr != NULL)
1409                                 *MinorPtr = '\0';
1410                         FlushStrBuf(Key);
1411                         if (!Toplevel) {
1412                                 /* If we're not toplevel, the upper dirs count as foo_bar_<local name>*/
1413                                 StrBufAppendBuf(Key, BaseKey, 0);
1414                                 StrBufAppendBufPlain(Key, HKEY("_"), 0);
1415                         }
1416                         StrBufAppendBufPlain(Key, filedir_entry->d_name, MinorPtr - filedir_entry->d_name, 0);
1417
1418                         if (LoadTemplates >= 1)
1419                                 syslog(1, "%s %s\n", ChrPtr(FileName), ChrPtr(Key));
1420                         prepare_template(FileName, Key, big);
1421                 default:
1422                         break;
1423                 }
1424         }
1425         free(d);
1426         closedir(filedir);
1427         FreeStrBuf(&FileName);
1428         FreeStrBuf(&Key);
1429         FreeStrBuf(&SubDirectory);
1430         FreeStrBuf(&SubKey);
1431         return 1;
1432 }
1433
1434 void InitTemplateCache(void)
1435 {
1436         int i;
1437         StrBuf *Key;
1438         StrBuf *Dir;
1439         HashList *Templates[2];
1440
1441         Dir = NewStrBuf();
1442         Key = NewStrBuf();
1443
1444         /* Primary Template set... */
1445         StrBufPrintf(Dir, "%s/t", static_dirs[0]);
1446         LoadTemplateDir(Dir,
1447                         TemplateCache, 
1448                         Key);
1449
1450         /* User local Template set */
1451         StrBufPrintf(Dir, "%s/t", static_dirs[1]);
1452         LoadTemplateDir(Dir,
1453                         LocalTemplateCache, 
1454                         Key);
1455         
1456         /* Debug Templates, just to be loaded while debugging. */
1457         
1458         StrBufPrintf(Dir, "%s/dbg", static_dirs[0]);
1459         LoadTemplateDir(Dir,
1460                         TemplateCache, 
1461                         Key);
1462         Templates[0] = TemplateCache;
1463         Templates[1] = LocalTemplateCache;
1464
1465
1466         if (LoadTemplates == 0) 
1467                 for (i=0; i < 2; i++) {
1468                         const char *Key;
1469                         long KLen;
1470                         HashPos *At;
1471                         void *vTemplate;
1472
1473                         At = GetNewHashPos(Templates[i], 0);
1474                         while (GetNextHashPos(Templates[i], 
1475                                               At, 
1476                                               &KLen,
1477                                               &Key, 
1478                                               &vTemplate) && 
1479                                (vTemplate != NULL))
1480                         {
1481                                 load_template((WCTemplate *)vTemplate);
1482                         }
1483                         DeleteHashPos(&At);
1484                 }
1485
1486
1487         FreeStrBuf(&Dir);
1488         FreeStrBuf(&Key);
1489 }
1490
1491
1492
1493 /*-----------------------------------------------------------------------------
1494  *                      Filling & processing Templates
1495  */
1496 /**
1497  * \brief executes one token
1498  * \param Target buffer to append to
1499  * \param Token da to  process.
1500  * \param Template we're iterating
1501  * \param Context Contextpoointer to pass in
1502  * \param state are we in conditional state?
1503  * \param ContextType what type of information does context giv us?
1504  */
1505 int EvaluateToken(StrBuf *Target, int state, WCTemplputParams *TP)
1506 {
1507         const char *AppendMe;
1508         long AppendMeLen;
1509         HashHandler *Handler;
1510         void *vVar;
1511         
1512 /* much output, since pName is not terminated...
1513         syslog(1,"Doing token: %s\n",Token->pName);
1514 */
1515
1516         switch (TP->Tokens->Flags) {
1517         case SV_GETTEXT:
1518                 TmplGettext(Target, TP);
1519                 break;
1520         case SV_CONDITIONAL: /** Forward conditional evaluation */
1521                 return EvaluateConditional(Target, 1, state, TP);
1522                 break;
1523         case SV_NEG_CONDITIONAL: /** Reverse conditional evaluation */
1524                 return EvaluateConditional(Target, 0, state, TP);
1525                 break;
1526         case SV_CUST_STR_CONDITIONAL: /** Conditional put custom strings from params */
1527                 if (TP->Tokens->nParameters >= 6) {
1528                         if (EvaluateConditional(Target, 0, state, TP)) {
1529                                 GetTemplateTokenString(Target, TP, 5, &AppendMe, &AppendMeLen);
1530                                 StrBufAppendBufPlain(Target, 
1531                                                      AppendMe, 
1532                                                      AppendMeLen,
1533                                                      0);
1534                         }
1535                         else{
1536                                 GetTemplateTokenString(Target, TP, 4, &AppendMe, &AppendMeLen);
1537                                 StrBufAppendBufPlain(Target, 
1538                                                      AppendMe, 
1539                                                      AppendMeLen,
1540                                                      0);
1541                         }
1542                 }
1543                 else  {
1544                         LogTemplateError(
1545                                 Target, "Conditional", ERR_NAME, TP,
1546                                 "needs at least 6 Params!"); 
1547                 }
1548                 break;
1549         case SV_SUBTEMPL:
1550                 if (TP->Tokens->nParameters == 1)
1551                         DoTemplate(TKEY(0), Target, TP);
1552                 break;
1553         case SV_PREEVALUATED:
1554                 Handler = (HashHandler*) TP->Tokens->PreEval;
1555                 if (!CheckContext(Target, &Handler->Filter, TP, "Token")) {
1556                         return -1;
1557                 }
1558                 Handler->HandlerFunc(Target, TP);
1559                 break;          
1560         default:
1561                 if (GetHash(GlobalNS, TP->Tokens->pName, TP->Tokens->NameEnd, &vVar)) {
1562                         Handler = (HashHandler*) vVar;
1563                         if (!CheckContext(Target, &Handler->Filter, TP, "Token")) {
1564                                 return -1;
1565                         }
1566                         else {
1567                                 Handler->HandlerFunc(Target, TP);
1568                         }
1569                 }
1570                 else {
1571                         LogTemplateError(
1572                                 Target, "Token UNKNOWN", ERR_NAME, TP,
1573                                 "You've specified a token that isn't known to webcit.!");
1574                 }
1575         }
1576         return 0;
1577 }
1578
1579
1580
1581 const StrBuf *ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target, WCTemplputParams *CallingTP)
1582 {
1583         WCTemplate *pTmpl = Tmpl;
1584         int done = 0;
1585         int i, state;
1586         const char *pData, *pS;
1587         long len;
1588         WCTemplputParams TP;
1589
1590         memcpy(&TP.Filter, &CallingTP->Filter, sizeof(ContextFilter));
1591
1592         TP.Context = CallingTP->Context;
1593         TP.ControlContext = CallingTP->ControlContext;
1594
1595         if (LoadTemplates != 0) {                       
1596                 if (LoadTemplates > 1)
1597                         syslog(1, "DBG: ----- loading:  [%s] ------ \n", 
1598                                 ChrPtr(Tmpl->FileName));
1599                 pTmpl = duplicate_template(Tmpl);
1600                 if(load_template(pTmpl) == NULL) {
1601                         StrBufAppendPrintf(
1602                                 Target, 
1603                                 "<pre>\nError loading Template [%s]\n See Logfile for details\n</pre>\n", 
1604                                 ChrPtr(Tmpl->FileName));
1605                         FreeWCTemplate(pTmpl);
1606                         return NULL;
1607                 }
1608
1609         }
1610
1611         pS = pData = ChrPtr(pTmpl->Data);
1612         len = StrLength(pTmpl->Data);
1613         i = 0;
1614         state = 0;
1615         while (!done) {
1616                 if (i >= pTmpl->nTokensUsed) {
1617                         StrBufAppendBufPlain(Target, 
1618                                              pData, 
1619                                              len - (pData - pS), 0);
1620                         done = 1;
1621                 }
1622                 else {
1623                         StrBufAppendBufPlain(
1624                                 Target, pData, 
1625                                 pTmpl->Tokens[i]->pTokenStart - pData, 0);
1626                         TP.Tokens = pTmpl->Tokens[i];
1627                         TP.nArgs = pTmpl->Tokens[i]->nParameters;
1628                         state = EvaluateToken(Target, state, &TP);
1629
1630                         while ((state != 0) && (i+1 < pTmpl->nTokensUsed)) {
1631                         /* condition told us to skip till its end condition */
1632                                 i++;
1633                                 TP.Tokens = pTmpl->Tokens[i];
1634                                 TP.nArgs = pTmpl->Tokens[i]->nParameters;
1635                                 if ((pTmpl->Tokens[i]->Flags == SV_CONDITIONAL) ||
1636                                     (pTmpl->Tokens[i]->Flags == SV_NEG_CONDITIONAL)) {
1637                                         if (state == EvaluateConditional(
1638                                                     Target, 
1639                                                     pTmpl->Tokens[i]->Flags, 
1640                                                     state, 
1641                                                     &TP))
1642                                                 state = 0;
1643                                 }
1644                         }
1645                         pData = pTmpl->Tokens[i++]->pTokenEnd + 1;
1646                         if (i > pTmpl->nTokensUsed)
1647                                 done = 1;
1648                 }
1649         }
1650         if (LoadTemplates != 0) {
1651                 FreeWCTemplate(pTmpl);
1652         }
1653         return Tmpl->MimeType;
1654
1655 }
1656
1657 /**
1658  * \brief Display a variable-substituted template
1659  * \param templatename template file to load
1660  * \returns the mimetype of the template its doing
1661  */
1662 const StrBuf *DoTemplate(const char *templatename, long len, StrBuf *Target, WCTemplputParams *TP) 
1663 {
1664         WCTemplputParams LocalTP;
1665         HashList *Static;
1666         HashList *StaticLocal;
1667         void *vTmpl;
1668         
1669         if (Target == NULL)
1670                 Target = WC->WBuf;
1671         if (TP == NULL) {
1672                 memset(&LocalTP, 0, sizeof(WCTemplputParams));
1673                 TP = &LocalTP;
1674         }
1675
1676         Static = TemplateCache;
1677         StaticLocal = LocalTemplateCache;
1678
1679         if (len == 0)
1680         {
1681                 syslog(1, "Can't to load a template with empty name!\n");
1682                 StrBufAppendPrintf(Target, "<pre>\nCan't to load a template with empty name!\n</pre>");
1683                 return NULL;
1684         }
1685
1686         if (!GetHash(StaticLocal, templatename, len, &vTmpl) &&
1687             !GetHash(Static, templatename, len, &vTmpl)) {
1688                 syslog(1, "didn't find Template [%s] %ld %ld\n", templatename, len , (long)strlen(templatename));
1689                 StrBufAppendPrintf(Target, "<pre>\ndidn't find Template [%s] %ld %ld\n</pre>", 
1690                                    templatename, len, 
1691                                    (long)strlen(templatename));
1692 #if 0
1693                 dbg_PrintHash(Static, PrintTemplate, NULL);
1694                 PrintHash(Static, VarPrintTransition, PrintTemplate);
1695 #endif
1696                 return NULL;
1697         }
1698         if (vTmpl == NULL) 
1699                 return NULL;
1700         return ProcessTemplate(vTmpl, Target, TP);
1701
1702 }
1703
1704
1705 void tmplput_Comment(StrBuf *Target, WCTemplputParams *TP)
1706 {
1707         if (LoadTemplates != 0)
1708         {
1709                 StrBuf *Comment;
1710                 const char *pch;
1711                 long len;
1712
1713                 GetTemplateTokenString(Target, TP, 0, &pch, &len);
1714                 Comment = NewStrBufPlain(pch, len);
1715                 StrBufAppendBufPlain(Target, HKEY("<!--"), 0);
1716                 StrBufAppendTemplate(Target, TP, Comment, 1);
1717                 StrBufAppendBufPlain(Target, HKEY("-->"), 0);
1718                 FreeStrBuf(&Comment);
1719         }
1720 }
1721
1722 /*-----------------------------------------------------------------------------
1723  *                      Iterators
1724  */
1725 typedef struct _HashIterator {
1726         HashList *StaticList;
1727         int AdditionalParams;
1728         int ContextType;
1729         int XPectContextType;
1730         int Flags;
1731         RetrieveHashlistFunc GetHash;
1732         HashDestructorFunc Destructor;
1733         SubTemplFunc DoSubTemplate;
1734 } HashIterator;
1735
1736 void RegisterITERATOR(const char *Name, long len, 
1737                       int AdditionalParams, 
1738                       HashList *StaticList, 
1739                       RetrieveHashlistFunc GetHash, 
1740                       SubTemplFunc DoSubTempl,
1741                       HashDestructorFunc Destructor,
1742                       int ContextType, 
1743                       int XPectContextType, 
1744                       int Flags)
1745 {
1746         HashIterator *It;
1747
1748         It = (HashIterator*)malloc(sizeof(HashIterator));
1749         memset(It, 0, sizeof(HashIterator));
1750         It->StaticList = StaticList;
1751         It->AdditionalParams = AdditionalParams;
1752         It->GetHash = GetHash;
1753         It->DoSubTemplate = DoSubTempl;
1754         It->Destructor = Destructor;
1755         It->ContextType = ContextType;
1756         It->XPectContextType = XPectContextType;
1757         It->Flags = Flags;
1758         Put(Iterators, Name, len, It, NULL);
1759 }
1760
1761 typedef struct _iteratestruct {
1762         int GroupChange;
1763         int oddeven;
1764         const char *Key;
1765         long KeyLen;
1766         int n;
1767         int LastN;
1768         }IterateStruct; 
1769
1770 int preeval_iterate(WCTemplateToken *Token)
1771 {
1772         WCTemplputParams TPP;
1773         WCTemplputParams *TP;
1774         void *vTmpl;
1775         void *vIt;
1776         HashIterator *It;
1777
1778         memset(&TPP, 0, sizeof(WCTemplputParams));
1779         TP = &TPP;
1780         TP->Tokens = Token;
1781         if (!GetHash(Iterators, TKEY(0), &vIt)) {
1782                 LogTemplateError(
1783                         NULL, "Iterator", ERR_PARM1, TP,
1784                         "not found");
1785                 return 0;
1786         }
1787         if (TP->Tokens->Params[1]->Type != TYPE_SUBTEMPLATE) {
1788                 LogTemplateError(NULL, "Iterator", ERR_PARM1, TP,
1789                                  "Need token with type Subtemplate as param 1, have %s", 
1790                                  TP->Tokens->Params[1]->Start);
1791         }
1792         
1793         /* well, we don't check the mobile stuff here... */
1794         if (!GetHash(LocalTemplateCache, TKEY(1), &vTmpl) &&
1795             !GetHash(TemplateCache, TKEY(1), &vTmpl)) {
1796                 LogTemplateError(NULL, "SubTemplate", ERR_PARM1, TP,
1797                                  "referenced here doesn't exist");
1798         }
1799         Token->Preeval2 = vIt;
1800         It = (HashIterator *) vIt;
1801
1802         if (TP->Tokens->nParameters < It->AdditionalParams + 2) {
1803                 LogTemplateError(                               
1804                         NULL, "Iterator", ERR_PARM1, TP,
1805                         "doesn't work with %d params", 
1806                         TP->Tokens->nParameters);
1807         }
1808
1809
1810         return 1;
1811 }
1812
1813 void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
1814 {
1815         HashIterator *It;
1816         HashList *List;
1817         HashPos  *it;
1818         SortStruct *SortBy = NULL;
1819         void *vSortBy;
1820         int DetectGroupChange = 0;
1821         int nMembersUsed;
1822         void *vContext;
1823         void *vLastContext = NULL;
1824         StrBuf *SubBuf;
1825         WCTemplputParams SubTP;
1826         IterateStruct Status;
1827
1828         long StartAt = 0;
1829         long StepWidth = 0;
1830         long StopAt = -1;
1831
1832         memset(&Status, 0, sizeof(IterateStruct));
1833         memcpy (&SubTP, &TP, sizeof(WCTemplputParams));
1834         
1835         It = (HashIterator*) TP->Tokens->Preeval2;
1836         if (It == NULL) {
1837                 LogTemplateError(
1838                         Target, "Iterator", ERR_PARM1, TP, "Unknown!");
1839                 return;
1840         }
1841
1842         if (TP->Tokens->nParameters < It->AdditionalParams + 2) {
1843                 LogTemplateError(                               
1844                         Target, "Iterator", ERR_PARM1, TP,
1845                         "doesn't work with %d params", 
1846                         TP->Tokens->nParameters - 1);
1847                 return;
1848         }
1849
1850         if ((It->XPectContextType != CTX_NONE) &&
1851             (It->XPectContextType != TP->Filter.ContextType)) {
1852                 LogTemplateError(
1853                         Target, "Iterator", ERR_PARM1, TP,
1854                         "requires context of type %s, have %s", 
1855                         ContextName(It->XPectContextType), 
1856                         ContextName(TP->Filter.ContextType));
1857                 return ;
1858                 
1859         }
1860
1861         if (It->StaticList == NULL)
1862                 List = It->GetHash(Target, TP);
1863         else
1864                 List = It->StaticList;
1865
1866         DetectGroupChange = (It->Flags & IT_FLAG_DETECT_GROUPCHANGE) != 0;
1867         if (DetectGroupChange) {
1868                 const StrBuf *BSort;
1869                 DetectGroupChange = 0;
1870                 if (havebstr("SortBy")) {
1871                         BSort = sbstr("SortBy");
1872                         if (GetHash(SortHash, SKEY(BSort), &vSortBy) &&
1873                             (vSortBy != NULL)) {
1874                                 SortBy = (SortStruct*)vSortBy;
1875                                 /* first check whether its intended for us... */
1876                                 if ((SortBy->ContextType == It->ContextType)&&
1877                                 /** Ok, its us, lets see in which direction we should sort... */
1878                                     (havebstr("SortOrder"))) {
1879                                         int SortOrder;
1880                                         SortOrder = LBSTR("SortOrder");
1881                                         if (SortOrder != 0)
1882                                                 DetectGroupChange = 1;
1883                                 }
1884                         }
1885                 }
1886         }
1887         nMembersUsed = GetCount(List);
1888         SubBuf = NewStrBuf();
1889         SubTP.Filter.ContextType = It->ContextType;
1890         SubTP.Filter.ControlContextType = CTX_ITERATE;
1891         SubTP.ControlContext = &Status;
1892         
1893         if (HAVE_PARAM(2)) {
1894                 StartAt = GetTemplateTokenNumber(Target, TP, 2, 0);
1895         }
1896         if (HAVE_PARAM(3)) {
1897                 StepWidth = GetTemplateTokenNumber(Target, TP, 3, 0);
1898         }
1899         if (HAVE_PARAM(4)) {
1900                 StopAt = GetTemplateTokenNumber(Target, TP, 4, -1);
1901         }
1902         it = GetNewHashPos(List, StepWidth);
1903         if (StopAt < 0) {
1904                 StopAt = GetCount(List);
1905         }
1906         while (GetNextHashPos(List, it, &Status.KeyLen, &Status.Key, &vContext)) {
1907                 if ((Status.n >= StartAt) && (Status.n <= StopAt)) {
1908                         if (DetectGroupChange && Status.n > 0) {
1909                                 Status.GroupChange = SortBy->GroupChange(vContext, vLastContext);
1910                         }
1911                         Status.LastN = (Status.n + 1) == nMembersUsed;
1912                         SubTP.Context = vContext;
1913                         if (It->DoSubTemplate != NULL)
1914                                 It->DoSubTemplate(SubBuf, &SubTP);
1915                         DoTemplate(TKEY(1), SubBuf, &SubTP);
1916                         
1917                         StrBufAppendBuf(Target, SubBuf, 0);
1918                         FlushStrBuf(SubBuf);
1919                         Status.oddeven = ! Status.oddeven;
1920                         vLastContext = vContext;
1921                 }
1922                 Status.n++;
1923         }
1924         FreeStrBuf(&SubBuf);
1925         DeleteHashPos(&it);
1926         if (It->Destructor != NULL)
1927                 It->Destructor(&List);
1928 }
1929
1930
1931 int conditional_ITERATE_ISGROUPCHANGE(StrBuf *Target, WCTemplputParams *TP)
1932 {
1933         IterateStruct *Ctx = CCTX;
1934         if (TP->Tokens->nParameters < 3)
1935                 return  Ctx->GroupChange;
1936
1937         return TP->Tokens->Params[2]->lvalue == Ctx->GroupChange;
1938 }
1939
1940 void tmplput_ITERATE_ODDEVEN(StrBuf *Target, WCTemplputParams *TP)
1941 {
1942         IterateStruct *Ctx = CCTX;
1943         if (Ctx->oddeven)
1944                 StrBufAppendBufPlain(Target, HKEY("odd"), 0);
1945         else
1946                 StrBufAppendBufPlain(Target, HKEY("even"), 0);
1947 }
1948
1949
1950 void tmplput_ITERATE_KEY(StrBuf *Target, WCTemplputParams *TP)
1951 {
1952         IterateStruct *Ctx = CCTX;
1953
1954         StrBufAppendBufPlain(Target, Ctx->Key, Ctx->KeyLen, 0);
1955 }
1956
1957
1958 void tmplput_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
1959 {
1960         IterateStruct *Ctx = CCTX;
1961         StrBufAppendPrintf(Target, "%d", Ctx->n);
1962 }
1963
1964 int conditional_ITERATE_FIRSTN(StrBuf *Target, WCTemplputParams *TP)
1965 {
1966         IterateStruct *Ctx = CCTX;
1967         return Ctx->n == 0;
1968 }
1969
1970 int conditional_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
1971 {
1972         IterateStruct *Ctx = CCTX;
1973         return Ctx->LastN;
1974 }
1975
1976
1977
1978 /*-----------------------------------------------------------------------------
1979  *                      Conditionals
1980  */
1981 int EvaluateConditional(StrBuf *Target, int Neg, int state, WCTemplputParams *TP)
1982 {
1983         ConditionalStruct *Cond;
1984         int rc = 0;
1985         int res;
1986
1987         if ((TP->Tokens->Params[0]->len == 1) &&
1988             (TP->Tokens->Params[0]->Start[0] == 'X'))
1989                 return (state != 0)?TP->Tokens->Params[1]->lvalue:0;
1990             
1991         Cond = (ConditionalStruct *) TP->Tokens->PreEval;
1992         if (Cond == NULL) {
1993                 LogTemplateError(
1994                         Target, "Conditional", ERR_PARM1, TP,
1995                         "unknown!");
1996                 return 1;
1997         }
1998
1999         if (!CheckContext(Target, &Cond->Filter, TP, "Conditional")) {
2000                 return 0;
2001         }
2002         res = Cond->CondF(Target, TP);
2003         if (res == Neg)
2004                 rc = TP->Tokens->Params[1]->lvalue;
2005         if (LoadTemplates > 5) 
2006                 syslog(1, "<%s> : %d %d==%d\n", 
2007                         ChrPtr(TP->Tokens->FlatToken), 
2008                         rc, res, Neg);
2009         return rc;
2010 }
2011
2012 void RegisterConditional(const char *Name, long len, 
2013                          int nParams,
2014                          WCConditionalFunc CondF, 
2015                          int ContextRequired)
2016 {
2017         ConditionalStruct *Cond;
2018
2019         Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2020         memset(Cond, 0, sizeof(ConditionalStruct));
2021         Cond->PlainName = Name;
2022         Cond->Filter.nMaxArgs = nParams;
2023         Cond->Filter.nMinArgs = nParams;
2024         Cond->CondF = CondF;
2025         Cond->Filter.ContextType = ContextRequired;
2026         Cond->Filter.ControlContextType = CTX_NONE;
2027         Put(Conditionals, Name, len, Cond, NULL);
2028 }
2029
2030 void RegisterControlConditional(const char *Name, long len, 
2031                                 int nParams,
2032                                 WCConditionalFunc CondF, 
2033                                 int ControlContextRequired)
2034 {
2035         ConditionalStruct *Cond;
2036
2037         Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2038         memset(Cond, 0, sizeof(ConditionalStruct));
2039         Cond->PlainName = Name;
2040         Cond->Filter.nMaxArgs = nParams;
2041         Cond->Filter.nMinArgs = nParams;
2042         Cond->CondF = CondF;
2043         Cond->Filter.ContextType = CTX_NONE;
2044         Cond->Filter.ControlContextType = ControlContextRequired;
2045         Put(Conditionals, Name, len, Cond, NULL);
2046 }
2047
2048 void RegisterTokenParamDefine(const char *Name, long len, 
2049                               long Value)
2050 {
2051         long *PVal;
2052
2053         PVal = (long*)malloc(sizeof(long));
2054         *PVal = Value;
2055         Put(Defines, Name, len, PVal, NULL);
2056 }
2057
2058 long GetTokenDefine(const char *Name, long len, 
2059                     long DefValue)
2060 {
2061         void *vPVal;
2062
2063         if (GetHash(Defines, Name, len, &vPVal) &&
2064              (vPVal != NULL))
2065          {
2066                  return *(long*) vPVal;
2067          }
2068          else
2069          {
2070                  return DefValue;
2071          }
2072 }
2073
2074 void tmplput_DefStr(StrBuf *Target, WCTemplputParams *TP)
2075 {
2076         const char *Str;
2077         long len;
2078         GetTemplateTokenString(Target, TP, 2, &Str, &len);
2079         
2080         StrBufAppendBufPlain(Target, Str, len, 0);
2081 }
2082
2083 void tmplput_DefVal(StrBuf *Target, WCTemplputParams *TP)
2084 {
2085         int val;
2086
2087         val = GetTemplateTokenNumber(Target, TP, 0, 0);
2088         StrBufAppendPrintf(Target, "%d", val);
2089 }
2090
2091 HashList *Defines;
2092
2093 /*-----------------------------------------------------------------------------
2094  *                      Context Strings
2095  */
2096 void tmplput_ContextString(StrBuf *Target, WCTemplputParams *TP)
2097 {
2098         StrBufAppendTemplate(Target, TP, (StrBuf*)CTX, 0);
2099 }
2100 int ConditionalContextStr(StrBuf *Target, WCTemplputParams *TP)
2101 {
2102         StrBuf *TokenText = (StrBuf*) CTX;
2103         const char *CompareToken;
2104         long len;
2105
2106         GetTemplateTokenString(Target, TP, 2, &CompareToken, &len);
2107         return strcmp(ChrPtr(TokenText), CompareToken) == 0;
2108 }
2109
2110 void tmplput_ContextStringArray(StrBuf *Target, WCTemplputParams *TP)
2111 {
2112         HashList *Arr = (HashList*) CTX;
2113         void *pV;
2114         int val;
2115
2116         val = GetTemplateTokenNumber(Target, TP, 0, 0);
2117         if (GetHash(Arr, IKEY(val), &pV) && 
2118             (pV != NULL)) {
2119                 StrBufAppendTemplate(Target, TP, (StrBuf*)pV, 1);
2120         }
2121 }
2122 int ConditionalContextStrinArray(StrBuf *Target, WCTemplputParams *TP)
2123 {
2124         HashList *Arr = (HashList*) CTX;
2125         void *pV;
2126         int val;
2127         const char *CompareToken;
2128         long len;
2129
2130         GetTemplateTokenString(Target, TP, 2, &CompareToken, &len);
2131         val = GetTemplateTokenNumber(Target, TP, 0, 0);
2132         if (GetHash(Arr, IKEY(val), &pV) && 
2133             (pV != NULL)) {
2134                 return strcmp(ChrPtr((StrBuf*)pV), CompareToken) == 0;
2135         }
2136         else
2137                 return 0;
2138 }
2139
2140 /*-----------------------------------------------------------------------------
2141  *                      Boxed-API
2142  */
2143
2144 void tmpl_do_boxed(StrBuf *Target, WCTemplputParams *TP)
2145 {
2146         WCTemplputParams SubTP;
2147
2148         StrBuf *Headline = NULL;
2149         if (TP->Tokens->nParameters == 2) {
2150                 if (TP->Tokens->Params[1]->Type == TYPE_STR) {
2151                         Headline = NewStrBuf();
2152                         DoTemplate(TKEY(1), Headline, TP);
2153                 }
2154                 else {
2155                         const char *Ch;
2156                         long len;
2157                         GetTemplateTokenString(Target, 
2158                                                TP, 
2159                                                1,
2160                                                &Ch,
2161                                                &len);
2162                         Headline = NewStrBufPlain(Ch, len);
2163                 }
2164         }
2165         /* else TODO error? logging? */
2166         memcpy (&SubTP, TP, sizeof(WCTemplputParams));
2167         SubTP.Context = Headline;
2168         SubTP.Filter.ContextType = CTX_STRBUF;
2169         DoTemplate(HKEY("beginbox"), Target, &SubTP);
2170         DoTemplate(TKEY(0), Target, TP);
2171         DoTemplate(HKEY("endbox"), Target, TP);
2172         FreeStrBuf(&Headline);
2173 }
2174
2175 /*-----------------------------------------------------------------------------
2176  *                      Tabbed-API
2177  */
2178
2179 typedef struct _tab_struct {
2180         long CurrentTab;
2181         StrBuf *TabTitle;
2182 } tab_struct;
2183
2184 int preeval_do_tabbed(WCTemplateToken *Token)
2185 {
2186         WCTemplputParams TPP;
2187         WCTemplputParams *TP;
2188         const char *Ch;
2189         long len;
2190         int i, nTabs;
2191
2192
2193         memset(&TPP, 0, sizeof(WCTemplputParams));
2194         TP = &TPP;
2195         TP->Tokens = Token;
2196         nTabs = TP->Tokens->nParameters / 2 - 1;
2197         if (TP->Tokens->nParameters % 2 != 0)
2198         {
2199                 LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2200                                  "need even number of arguments");
2201                 return 0;
2202
2203         }
2204         else for (i = 0; i < nTabs; i++) {
2205                 if (!HaveTemplateTokenString(NULL, 
2206                                              TP, 
2207                                              i * 2,
2208                                              &Ch,
2209                                              &len) || 
2210                     (TP->Tokens->Params[i * 2]->len == 0))
2211                 {
2212                         LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2213                                          "Tab-Subject %d needs to be able to produce a string, have %s", 
2214                                          i, TP->Tokens->Params[i * 2]->Start);
2215                         return 0;
2216                 }
2217                 if (!HaveTemplateTokenString(NULL, 
2218                                              TP, 
2219                                              i * 2 + 1,
2220                                              &Ch,
2221                                              &len) || 
2222                     (TP->Tokens->Params[i * 2 + 1]->len == 0))
2223                 {
2224                         LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2225                                          "Tab-Content %d needs to be able to produce a string, have %s", 
2226                                          i, TP->Tokens->Params[i * 2 + 1]->Start);
2227                         return 0;
2228                 }
2229         }
2230
2231         if (!HaveTemplateTokenString(NULL, 
2232                                      TP, 
2233                                      i * 2 + 1,
2234                                      &Ch,
2235                                      &len) || 
2236             (TP->Tokens->Params[i * 2 + 1]->len == 0))
2237         {
2238                 LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2239                                  "Tab-Content %d needs to be able to produce a string, have %s", 
2240                                  i, TP->Tokens->Params[i * 2 + 1]->Start);
2241                 return 0;
2242         }
2243         return 1;
2244 }
2245
2246
2247 void tmpl_do_tabbed(StrBuf *Target, WCTemplputParams *TP)
2248 {
2249         StrBuf **TabNames;
2250         int i, ntabs, nTabs;
2251         tab_struct TS;
2252         WCTemplputParams SubTP;
2253
2254         memset(&TS, 0, sizeof(tab_struct));
2255         memcpy (&SubTP, &TP, sizeof(WCTemplputParams));
2256
2257         nTabs = ntabs = TP->Tokens->nParameters / 2;
2258         TabNames = (StrBuf **) malloc(ntabs * sizeof(StrBuf*));
2259         memset(TabNames, 0, ntabs * sizeof(StrBuf*));
2260
2261         for (i = 0; i < ntabs; i++) {
2262                 if ((TP->Tokens->Params[i * 2]->Type == TYPE_STR) &&
2263                     (TP->Tokens->Params[i * 2]->len > 0)) {
2264                         TabNames[i] = NewStrBuf();
2265                         DoTemplate(TKEY(i * 2), TabNames[i], TP);
2266                 }
2267                 else if (TP->Tokens->Params[i * 2]->Type == TYPE_GETTEXT) {
2268                         const char *Ch;
2269                         long len;
2270                         GetTemplateTokenString(Target, 
2271                                                TP, 
2272                                                i * 2,
2273                                                &Ch,
2274                                                &len);
2275                         TabNames[i] = NewStrBufPlain(Ch, -1);
2276                 }
2277                 else { 
2278                         /** A Tab without subject? we can't count that, add it as silent */
2279                         nTabs --;
2280                 }
2281         }
2282         memcpy (&SubTP, TP, sizeof(WCTemplputParams));
2283         SubTP.Filter.ControlContextType = CTX_TAB;
2284         SubTP.ControlContext = &TS;
2285
2286         StrTabbedDialog(Target, nTabs, TabNames);
2287         for (i = 0; i < ntabs; i++) {
2288                 memset(&TS, 0, sizeof(tab_struct));
2289                 TS.CurrentTab = i;
2290                 TS.TabTitle = TabNames[i];
2291                 StrBeginTab(Target, i, nTabs, TabNames);
2292                 DoTemplate(TKEY(i * 2 + 1), Target, &SubTP);
2293                 StrEndTab(Target, i, nTabs);
2294         }
2295         for (i = 0; i < ntabs; i++) 
2296                 FreeStrBuf(&TabNames[i]);
2297 }
2298
2299 void tmplput_TAB_N(StrBuf *Target, WCTemplputParams *TP)
2300 {
2301         tab_struct *Ctx = CCTX;
2302
2303         StrBufAppendPrintf(Target, "%d", Ctx->CurrentTab);
2304 }
2305
2306 void tmplput_TAB_TITLE(StrBuf *Target, WCTemplputParams *TP)
2307 {
2308         tab_struct *Ctx = CCTX;
2309         StrBufAppendTemplate(Target, TP, Ctx->TabTitle, 0);
2310 }
2311
2312 /*-----------------------------------------------------------------------------
2313  *                      Sorting-API
2314  */
2315
2316
2317 void RegisterSortFunc(const char *name, long len, 
2318                       const char *prepend, long preplen,
2319                       CompareFunc Forward, 
2320                       CompareFunc Reverse, 
2321                       CompareFunc GroupChange, 
2322                       long ContextType)
2323 {
2324         SortStruct *NewSort;
2325
2326         NewSort = (SortStruct*) malloc(sizeof(SortStruct));
2327         memset(NewSort, 0, sizeof(SortStruct));
2328         NewSort->Name = NewStrBufPlain(name, len);
2329         if (prepend != NULL)
2330                 NewSort->PrefPrepend = NewStrBufPlain(prepend, preplen);
2331         else
2332                 NewSort->PrefPrepend = NULL;
2333         NewSort->Forward = Forward;
2334         NewSort->Reverse = Reverse;
2335         NewSort->GroupChange = GroupChange;
2336         NewSort->ContextType = ContextType;
2337         if (ContextType == CTX_NONE) {
2338                 syslog(1, "sorting requires a context. CTX_NONE won't make it.\n");
2339                 exit(1);
2340         }
2341                 
2342         Put(SortHash, name, len, NewSort, DestroySortStruct);
2343 }
2344
2345 CompareFunc RetrieveSort(WCTemplputParams *TP, 
2346                          const char *OtherPrefix, long OtherPrefixLen,
2347                          const char *Default, long ldefault, long DefaultDirection)
2348 {
2349         int isdefault = 0;
2350         const StrBuf *BSort = NULL;
2351         SortStruct *SortBy;
2352         void *vSortBy;
2353         long SortOrder = -1;
2354         
2355         if (havebstr("SortBy")) {
2356                 BSort = sbstr("SortBy");
2357                 if (OtherPrefix == NULL) {
2358                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2359                 }
2360                 else {
2361                         set_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen, NewStrBufDup(BSort), 0);
2362                 }
2363         }
2364         else { /** Try to fallback to our remembered values... */
2365                 if (OtherPrefix == NULL) {
2366                         BSort = get_room_pref("sort");
2367                 }
2368                 else {
2369                         BSort = get_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen);
2370                 }
2371                 if (BSort != NULL)
2372                         putbstr("SortBy", NewStrBufDup(BSort));
2373                 else {
2374                         StrBuf *Buf;
2375
2376                         BSort = Buf = NewStrBufPlain(Default, ldefault);
2377                         putbstr("SortBy", Buf);
2378                 }
2379         }
2380
2381         if (!GetHash(SortHash, SKEY(BSort), &vSortBy) || 
2382             (vSortBy == NULL)) {
2383                 isdefault = 1;
2384                 if (!GetHash(SortHash, Default, ldefault, &vSortBy) || 
2385                     (vSortBy == NULL)) {
2386                         LogTemplateError(
2387                                 NULL, "Sorting", ERR_PARM1, TP,
2388                                 "Illegal default sort: [%s]", Default);
2389                         wc_backtrace();
2390                 }
2391         }
2392         SortBy = (SortStruct*)vSortBy;
2393
2394         if (SortBy->ContextType != TP->Filter.ContextType)
2395                 return NULL;
2396
2397         /** Ok, its us, lets see in which direction we should sort... */
2398         if (havebstr("SortOrder")) {
2399                 SortOrder = LBSTR("SortOrder");
2400         }
2401         else { /** Try to fallback to our remembered values... */
2402                 StrBuf *Buf = NULL;
2403                 if (SortBy->PrefPrepend == NULL) {
2404                         Buf = get_room_pref("SortOrder");
2405                         SortOrder = StrTol(Buf);
2406                 }
2407                 else {
2408                         BSort = get_X_PREFS(HKEY("SortOrder"), OtherPrefix, OtherPrefixLen);
2409                 }
2410
2411                 if (Buf == NULL)
2412                         SortOrder = DefaultDirection;
2413
2414                 Buf = NewStrBufPlain(NULL, 64);
2415                 StrBufPrintf(Buf, "%ld", SortOrder);
2416                 putbstr("SortOrder", Buf);
2417         }
2418         switch (SortOrder) {
2419         default:
2420         case 0:
2421                 return NULL;
2422         case 1:
2423                 return SortBy->Forward;
2424         case 2:
2425                 return SortBy->Reverse;
2426         }
2427 }
2428
2429
2430 enum {
2431         eNO_SUCH_SORT, 
2432         eNOT_SPECIFIED,
2433         eINVALID_PARAM,
2434         eFOUND
2435 };
2436
2437 ConstStr SortIcons[] = {
2438         {HKEY("static/icons/sort_none.gif")},
2439         {HKEY("static/icons/up_pointer.gif")},
2440         {HKEY("static/icons/down_pointer.gif")},
2441 };
2442
2443 ConstStr SortNextOrder[] = {
2444         {HKEY("1")},
2445         {HKEY("2")},
2446         {HKEY("0")},
2447 };
2448
2449
2450 int GetSortMetric(WCTemplputParams *TP, SortStruct **Next, SortStruct **Param, long *SortOrder, int N)
2451 {
2452         int bSortError = eNOT_SPECIFIED;
2453         const StrBuf *BSort;
2454         void *vSort;
2455         
2456         *SortOrder = 0;
2457         *Next = NULL;
2458         if (!GetHash(SortHash, TKEY(0), &vSort) || 
2459             (vSort == NULL))
2460                 return eNO_SUCH_SORT;
2461         *Param = (SortStruct*) vSort;
2462         
2463
2464         if (havebstr("SortBy")) {
2465                 BSort = sbstr("SortBy");
2466                 bSortError = eINVALID_PARAM;
2467                 if ((*Param)->PrefPrepend == NULL) {
2468                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2469                 }
2470                 else {
2471                         set_X_PREFS(HKEY("sort"), TKEY(N), NewStrBufDup(BSort), 0);
2472                 }
2473         }
2474         else { /** Try to fallback to our remembered values... */
2475                 if ((*Param)->PrefPrepend == NULL) {
2476                         BSort = get_room_pref("sort");
2477                 }
2478                 else {
2479                         BSort = get_X_PREFS(HKEY("sort"), TKEY(N));
2480                 }
2481         }
2482
2483         if (!GetHash(SortHash, SKEY(BSort), &vSort) || 
2484             (vSort == NULL))
2485                 return bSortError;
2486
2487         *Next = (SortStruct*) vSort;
2488
2489         /** Ok, its us, lets see in which direction we should sort... */
2490         if (havebstr("SortOrder")) {
2491                 *SortOrder = LBSTR("SortOrder");
2492         }
2493         else { /** Try to fallback to our remembered values... */
2494                 if ((*Param)->PrefPrepend == NULL) {
2495                         *SortOrder = StrTol(get_room_pref("SortOrder"));
2496                 }
2497                 else {
2498                         *SortOrder = StrTol(get_X_PREFS(HKEY("SortOrder"), TKEY(N)));
2499                 }
2500         }
2501         if (*SortOrder > 2)
2502                 *SortOrder = 0;
2503
2504         return eFOUND;
2505 }
2506
2507
2508 void tmplput_SORT_ICON(StrBuf *Target, WCTemplputParams *TP)
2509 {
2510         long SortOrder;
2511         SortStruct *Next;
2512         SortStruct *Param;
2513         const ConstStr *SortIcon;
2514
2515         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2516         case eNO_SUCH_SORT:
2517                 LogTemplateError(
2518                         Target, "Sorter", ERR_PARM1, TP,
2519                         " Sorter [%s] unknown!", 
2520                         TP->Tokens->Params[0]->Start);
2521                 break;          
2522         case eINVALID_PARAM:
2523                 LogTemplateError(NULL, "Sorter", ERR_PARM1, TP,
2524                                  " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
2525                                  bstr("SortBy"));
2526         case eNOT_SPECIFIED:
2527         case eFOUND:
2528                 if (Next == Param) {
2529                         SortIcon = &SortIcons[SortOrder];
2530                 }
2531                 else { /** Not Us... */
2532                         SortIcon = &SortIcons[0];
2533                 }
2534                 StrBufAppendBufPlain(Target, SortIcon->Key, SortIcon->len, 0);
2535         }
2536 }
2537
2538 void tmplput_SORT_NEXT(StrBuf *Target, WCTemplputParams *TP)
2539 {
2540         long SortOrder;
2541         SortStruct *Next;
2542         SortStruct *Param;
2543
2544         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2545         case eNO_SUCH_SORT:
2546                 LogTemplateError(
2547                         Target, "Sorter", ERR_PARM1, TP,                                  
2548                         " Sorter [%s] unknown!", 
2549                         TP->Tokens->Params[0]->Start);
2550                 break;          
2551         case eINVALID_PARAM:
2552                 LogTemplateError(
2553                         NULL, "Sorter", ERR_PARM1, TP,
2554                         " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
2555                         bstr("SortBy"));
2556         case eNOT_SPECIFIED:
2557         case eFOUND:
2558                 StrBufAppendBuf(Target, Param->Name, 0);
2559                 
2560         }
2561 }
2562
2563 void tmplput_SORT_ORDER(StrBuf *Target, WCTemplputParams *TP)
2564 {
2565         long SortOrder;
2566         const ConstStr *SortOrderStr;
2567         SortStruct *Next;
2568         SortStruct *Param;
2569
2570         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2571         case eNO_SUCH_SORT:
2572                 LogTemplateError(
2573                         Target, "Sorter", ERR_PARM1, TP,
2574                         " Sorter [%s] unknown!",
2575                         TP->Tokens->Params[0]->Start);
2576                 break;          
2577         case eINVALID_PARAM:
2578                 LogTemplateError(
2579                         NULL, "Sorter", ERR_PARM1, TP,
2580                         " Sorter specified by BSTR 'SortBy' [%s] unknown!",
2581                         bstr("SortBy"));
2582         case eNOT_SPECIFIED:
2583         case eFOUND:
2584                 if (Next == Param) {
2585                         SortOrderStr = &SortNextOrder[SortOrder];
2586                 }
2587                 else { /** Not Us... */
2588                         SortOrderStr = &SortNextOrder[0];
2589                 }
2590                 StrBufAppendBufPlain(Target, SortOrderStr->Key, SortOrderStr->len, 0);
2591         }
2592 }
2593
2594
2595 void tmplput_long_vector(StrBuf *Target, WCTemplputParams *TP)
2596 {
2597         long *LongVector = (long*) CTX;
2598
2599         if ((TP->Tokens->Params[0]->Type == TYPE_LONG) && 
2600             (TP->Tokens->Params[0]->lvalue <= LongVector[0]))
2601         {
2602                 StrBufAppendPrintf(Target, "%ld", LongVector[TP->Tokens->Params[0]->lvalue]);
2603         }
2604         else
2605         {
2606                 if (TP->Tokens->Params[0]->Type != TYPE_LONG) {
2607                         LogTemplateError(
2608                                 Target, "Longvector", ERR_NAME, TP,
2609                                 "needs a numerical Parameter!");
2610                 }
2611                 else {
2612                         LogTemplateError(
2613                                 Target, "LongVector", ERR_PARM1, TP,
2614                                 "doesn't have %ld Parameters, its just the size of %ld!", 
2615                                 TP->Tokens->Params[0]->lvalue,
2616                                 LongVector[0]);
2617                 }
2618         }
2619 }
2620
2621 void dbg_print_longvector(long *LongVector)
2622 {
2623         StrBuf *Buf = NewStrBufPlain(HKEY("Longvector: ["));
2624         int nItems = LongVector[0];
2625         int i;
2626
2627         for (i = 0; i < nItems; i++) {
2628                 if (i + 1 < nItems)
2629                         StrBufAppendPrintf(Buf, "%d: %ld | ", i, LongVector[i]);
2630                 else
2631                         StrBufAppendPrintf(Buf, "%d: %ld]\n", i, LongVector[i]);
2632
2633         }
2634         syslog(1, "%s", ChrPtr(Buf));
2635         FreeStrBuf(&Buf);
2636 }
2637
2638 int ConditionalLongVector(StrBuf *Target, WCTemplputParams *TP)
2639 {
2640         long *LongVector = (long*) CTX;
2641
2642         if ((TP->Tokens->Params[2]->Type == TYPE_LONG) && 
2643             (TP->Tokens->Params[2]->lvalue <= LongVector[0])&&
2644             (TP->Tokens->Params[3]->Type == TYPE_LONG) && 
2645             (TP->Tokens->Params[3]->lvalue <= LongVector[0]))
2646         {
2647                 return LongVector[TP->Tokens->Params[2]->lvalue] == 
2648                         LongVector[TP->Tokens->Params[3]->lvalue];
2649         }
2650         else
2651         {
2652                 if ((TP->Tokens->Params[2]->Type == TYPE_LONG) ||
2653                     (TP->Tokens->Params[2]->Type == TYPE_LONG)) {
2654                         LogTemplateError(
2655                                 Target, "ConditionalLongvector", ERR_PARM1, TP,
2656                                 "needs two long Parameter!");
2657                 }
2658                 else {
2659                         LogTemplateError(
2660                                 Target, "Longvector", ERR_PARM1, TP,
2661                                 "doesn't have %ld / %ld Parameters, its just the size of %ld!",
2662                                 TP->Tokens->Params[2]->lvalue,
2663                                 TP->Tokens->Params[3]->lvalue,
2664                                 LongVector[0]);
2665                 }
2666         }
2667         return 0;
2668 }
2669
2670
2671 void tmplput_CURRENT_FILE(StrBuf *Target, WCTemplputParams *TP)
2672 {
2673         StrBufAppendTemplate(Target, TP, TP->Tokens->FileName, 0);
2674 }
2675
2676 void 
2677 InitModule_SUBST
2678 (void)
2679 {
2680         memset(&NoCtx, 0, sizeof(WCTemplputParams));
2681         RegisterNamespace("--", 0, 2, tmplput_Comment, NULL, CTX_NONE);
2682         RegisterNamespace("SORT:ICON", 1, 2, tmplput_SORT_ICON, NULL, CTX_NONE);
2683         RegisterNamespace("SORT:ORDER", 1, 2, tmplput_SORT_ORDER, NULL, CTX_NONE);
2684         RegisterNamespace("SORT:NEXT", 1, 2, tmplput_SORT_NEXT, NULL, CTX_NONE);
2685         RegisterNamespace("CONTEXTSTR", 0, 1, tmplput_ContextString, NULL, CTX_STRBUF);
2686         RegisterNamespace("CONTEXTSTRARR", 1, 2, tmplput_ContextStringArray, NULL, CTX_STRBUFARR);
2687         RegisterNamespace("ITERATE", 2, 100, tmpl_iterate_subtmpl, preeval_iterate, CTX_NONE);
2688         RegisterNamespace("DOBOXED", 1, 2, tmpl_do_boxed, NULL, CTX_NONE);
2689         RegisterNamespace("DOTABBED", 2, 100, tmpl_do_tabbed, preeval_do_tabbed, CTX_NONE);
2690         RegisterControlNS(HKEY("TAB:N"), 0, 0, tmplput_TAB_N, CTX_TAB);
2691         RegisterControlNS(HKEY("TAB:SUBJECT"), 0, 1, tmplput_TAB_TITLE, CTX_TAB);
2692
2693
2694         RegisterNamespace("LONGVECTOR", 1, 1, tmplput_long_vector, NULL, CTX_LONGVECTOR);
2695
2696
2697         RegisterConditional(HKEY("COND:CONTEXTSTR"), 3, ConditionalContextStr, CTX_STRBUF);
2698         RegisterConditional(HKEY("COND:CONTEXTSTRARR"), 4, ConditionalContextStrinArray, CTX_STRBUFARR);
2699         RegisterConditional(HKEY("COND:LONGVECTOR"), 4, ConditionalLongVector, CTX_LONGVECTOR);
2700
2701
2702         RegisterControlConditional(HKEY("COND:ITERATE:ISGROUPCHANGE"), 2, 
2703                                    conditional_ITERATE_ISGROUPCHANGE, 
2704                                    CTX_ITERATE);
2705         RegisterControlConditional(HKEY("COND:ITERATE:LASTN"), 2, 
2706                                    conditional_ITERATE_LASTN, 
2707                                    CTX_ITERATE);
2708         RegisterControlConditional(HKEY("COND:ITERATE:FIRSTN"), 2, 
2709                                    conditional_ITERATE_FIRSTN, 
2710                                    CTX_ITERATE);
2711
2712         RegisterControlNS(HKEY("ITERATE:ODDEVEN"), 0, 0, tmplput_ITERATE_ODDEVEN, CTX_ITERATE);
2713         RegisterControlNS(HKEY("ITERATE:KEY"), 0, 0, tmplput_ITERATE_KEY, CTX_ITERATE);
2714         RegisterControlNS(HKEY("ITERATE:N"), 0, 0, tmplput_ITERATE_LASTN, CTX_ITERATE);
2715         RegisterNamespace("CURRENTFILE", 0, 1, tmplput_CURRENT_FILE, NULL, CTX_NONE);
2716         RegisterNamespace("DEF:STR", 1, 1, tmplput_DefStr, NULL, CTX_NONE);
2717         RegisterNamespace("DEF:VAL", 1, 1, tmplput_DefVal, NULL, CTX_NONE);
2718
2719
2720
2721
2722 }
2723
2724 void
2725 ServerStartModule_SUBST
2726 (void)
2727 {
2728         LocalTemplateCache = NewHash(1, NULL);
2729         TemplateCache = NewHash(1, NULL);
2730
2731         GlobalNS = NewHash(1, NULL);
2732         Iterators = NewHash(1, NULL);
2733         Conditionals = NewHash(1, NULL);
2734         SortHash = NewHash(1, NULL);
2735         Defines = NewHash(1, NULL);
2736 }
2737
2738 void
2739 FinalizeModule_SUBST
2740 (void)
2741 {
2742
2743 }
2744
2745 void 
2746 ServerShutdownModule_SUBST
2747 (void)
2748 {
2749         DeleteHash(&TemplateCache);
2750         DeleteHash(&LocalTemplateCache);
2751
2752         DeleteHash(&GlobalNS);
2753         DeleteHash(&Iterators);
2754         DeleteHash(&Conditionals);
2755         DeleteHash(&SortHash);
2756         DeleteHash(&Defines);
2757 }
2758
2759
2760 void
2761 SessionNewModule_SUBST
2762 (wcsession *sess)
2763 {
2764
2765 }
2766
2767 void
2768 SessionAttachModule_SUBST
2769 (wcsession *sess)
2770 {
2771 }
2772
2773 void
2774 SessionDetachModule_SUBST
2775 (wcsession *sess)
2776 {
2777         FreeStrBuf(&sess->WFBuf);
2778 }
2779
2780 void 
2781 SessionDestroyModule_SUBST  
2782 (wcsession *sess)
2783 {
2784
2785 }