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