after reading a template sanity check conditionals whether they're paired
[citadel.git] / webcit / subst.c
1 #include "sysdep.h"
2
3
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <dirent.h>
7 #include <errno.h>
8
9 #include <unistd.h>
10 #include <stdio.h>
11 #include <stdarg.h>
12 #include <stddef.h>
13
14 #define SHOW_ME_VAPPEND_PRINTF
15
16 #include "webcit.h"
17 #include "webserver.h"
18
19 extern char *static_dirs[PATH_MAX];  /* Disk representation */
20
21 HashList *TemplateCache;
22 HashList *LocalTemplateCache;
23
24 HashList *GlobalNS;
25 HashList *Iterators;
26 HashList *Conditionals;
27 HashList *SortHash;
28 HashList *Defines;
29
30 int DumpTemplateI18NStrings = 0;
31 int LoadTemplates = 0;
32 int dbg_backtrace_template_errors = 0;
33 WCTemplputParams NoCtx;
34 StrBuf *I18nDump = NULL;
35
36 const char EmptyStr[]="";
37
38 #define SV_GETTEXT 1
39 #define SV_CONDITIONAL 2
40 #define SV_NEG_CONDITIONAL 3
41 #define SV_CUST_STR_CONDITIONAL 4
42 #define SV_SUBTEMPL 5
43 #define SV_PREEVALUATED 6
44
45
46 /*
47  * Dynamic content for variable substitution in templates
48  */
49 typedef struct _wcsubst {
50         ContextFilter Filter;
51         int wcs_type;                           /* which type of Substitution are we */
52         char wcs_key[32];                       /* copy of our hashkey for debugging */
53         StrBuf *wcs_value;                      /* if we're a string, keep it here */
54         long lvalue;                            /* type long? keep data here */
55         WCHandlerFunc wcs_function;             /* funcion hook ???*/
56 } wcsubst;
57
58
59 typedef struct _WCTemplate {
60         StrBuf *Data;
61         StrBuf *FileName;
62         int nTokensUsed;
63         int TokenSpace;
64         StrBuf *MimeType;
65         WCTemplateToken **Tokens;
66 } WCTemplate;
67
68 typedef struct _HashHandler {
69         ContextFilter Filter;
70         WCPreevalFunc PreEvalFunc;
71         WCHandlerFunc HandlerFunc;
72 }HashHandler;
73
74 void *load_template(WCTemplate *NewTemplate);
75 int EvaluateConditional(StrBuf *Target, int Neg, int state, WCTemplputParams *TP);
76
77
78
79 typedef struct _SortStruct {
80         StrBuf *Name;
81         StrBuf *PrefPrepend;
82         CompareFunc Forward;
83         CompareFunc Reverse;
84         CompareFunc GroupChange;
85
86         long ContextType;
87 }SortStruct;
88
89 const char *CtxNames[]  = {
90         "Context NONE",
91         "Context SITECFG",
92         "Context SESSION",
93         "Context INETCFG",
94         "Context VNOTE",
95         "Context WHO",
96         "Context PREF",
97         "Context NODECONF",
98         "Context USERLIST",
99         "Context MAILSUM",
100         "Context MIME_ATACH",
101         "Context FILELIST",
102         "Context STRBUF",
103         "Context STRBUFARR",
104         "Context LONGVECTOR",
105         "Context ROOMS",
106         "Context FLOORS",
107         "Context ITERATE",
108         "Context ICAL",
109         "Context DavNamespace",
110         "Context TAB",
111         "Context VCARD",
112         "Context SIEVE List",
113         "Context SIEVE Script",
114         "Context UNKNOWN"
115 };
116
117
118
119 void DestroySortStruct(void *vSort)
120 {
121         SortStruct *Sort = (SortStruct*) vSort;
122         FreeStrBuf(&Sort->Name);
123         FreeStrBuf(&Sort->PrefPrepend);
124         free (Sort);
125 }
126
127 const char *ContextName(int ContextType)
128 {
129         if (ContextType < CTX_UNKNOWN)
130                 return CtxNames[ContextType];
131         else
132                 return CtxNames[CTX_UNKNOWN];
133 }
134
135 void LogTemplateError (StrBuf *Target, const char *Type, int ErrorPos, WCTemplputParams *TP, const char *Format, ...)
136 {
137         wcsession *WCC;
138         StrBuf *Error;
139         StrBuf *Info;
140         va_list arg_ptr;
141         const char *Err = NULL;
142
143         Info = NewStrBuf();
144         Error = NewStrBuf();
145
146         va_start(arg_ptr, Format);
147         StrBufVAppendPrintf(Error, Format, arg_ptr);
148         va_end(arg_ptr);
149
150         switch (ErrorPos) {
151         case ERR_NAME: /* the main token name... */ 
152                 Err = (TP->Tokens!= NULL)? TP->Tokens->pName:"";
153                 break;
154         default:
155                 Err = ((TP->Tokens!= NULL) && 
156                        (TP->Tokens->nParameters > ErrorPos - 1))? 
157                         TP->Tokens->Params[ErrorPos - 1]->Start : "";
158                 break;
159         }
160         if (TP->Tokens != NULL) 
161         {
162                 syslog(1, "%s [%s]  (in '%s' line %ld); %s; [%s]\n", 
163                         Type, 
164                         Err, 
165                         ChrPtr(TP->Tokens->FileName),
166                         TP->Tokens->Line, 
167                         ChrPtr(Error), 
168                         ChrPtr(TP->Tokens->FlatToken));
169         }
170         else 
171         {
172                 syslog(1, "%s: %s;\n", 
173                         Type, 
174                         ChrPtr(Error));
175         }
176 /*
177         if (Target == NULL) 
178                 return;
179 */
180         WCC = WC;
181         if (WCC == NULL) {
182                 FreeStrBuf(&Info);
183                 FreeStrBuf(&Error);
184                 return; 
185         }
186
187         if (WCC->WFBuf == NULL) WCC->WFBuf = NewStrBuf();
188         if (TP->Tokens != NULL) 
189         {
190                 /* deprecated: 
191                 StrBufAppendPrintf(                                                          
192                         Target,                                                              
193                         "<pre>\n%s [%s] (in '%s' line %ld); %s\n[%s]\n</pre>\n",
194                         Type, 
195                         Err, 
196                         ChrPtr(TP->Tokens->FileName),
197                         TP->Tokens->Line,
198                         ChrPtr(Error),
199                         ChrPtr(TP->Tokens->FlatToken));
200                 */
201                 StrBufPrintf(Info, "%s [%s]  %s; [%s]", 
202                              Type, 
203                              Err, 
204                              ChrPtr(Error), 
205                              ChrPtr(TP->Tokens->FlatToken));
206
207
208                 SerializeJson(WCC->WFBuf, WildFireException(SKEY(TP->Tokens->FileName),
209                                                         TP->Tokens->Line,
210                                                         Info,
211                                                         1), 1);
212 /*
213                 SerializeJson(Header, WildFireMessage(SKEY(TP->Tokens->FileName),
214                                                       TP->Tokens->Line,
215                                                       Error,
216                                                       eERROR), 1);
217 */
218                 
219         }
220         else
221         {
222                 /* deprecated.
223                 StrBufAppendPrintf(                                                          
224                         Target,                                                              
225                         "<pre>\n%s: %s\n</pre>\n",
226                         Type, 
227                         ChrPtr(Error));
228                 */
229                 StrBufPrintf(Info, "%s [%s]  %s; [%s]", 
230                              Type, 
231                              Err, 
232                              ChrPtr(Error), 
233                              ChrPtr(TP->Tokens->FlatToken));
234                 SerializeJson(WCC->WFBuf, WildFireException(HKEY(__FILE__), __LINE__, Info, 1), 1);
235         }
236         FreeStrBuf(&Info);
237         FreeStrBuf(&Error);
238 /*
239         if (dbg_backtrace_template_errors)
240                 wc_backtrace(); 
241 */
242 }
243
244
245
246
247 void LogError (StrBuf *Target, const char *Type, const char *Format, ...)
248 {
249         wcsession *WCC;
250         StrBuf *Error;
251         StrBuf *Info;
252         va_list arg_ptr;
253
254         Info = NewStrBuf();
255         Error = NewStrBuf();
256
257         va_start(arg_ptr, Format);
258         StrBufVAppendPrintf(Error, Format, arg_ptr);
259         va_end(arg_ptr);
260
261         syslog(1, "%s", ChrPtr(Error));
262
263         WCC = WC;
264         if (WCC->WFBuf == NULL) WCC->WFBuf = NewStrBuf();
265
266         SerializeJson(WCC->WFBuf, WildFireException(Type, strlen(Type),
267                                                     0,
268                                                     Info,
269                                                     1), 1);
270
271         FreeStrBuf(&Info);
272         FreeStrBuf(&Error);
273 /*
274         if (dbg_backtrace_template_errors)
275                 wc_backtrace(); 
276 */
277 }
278
279
280 void RegisterNS(const char *NSName, 
281                 long len, 
282                 int nMinArgs, 
283                 int nMaxArgs, 
284                 WCHandlerFunc HandlerFunc, 
285                 WCPreevalFunc PreevalFunc,
286                 int ContextRequired)
287 {
288         HashHandler *NewHandler;
289         
290         NewHandler = (HashHandler*) malloc(sizeof(HashHandler));
291         memset(NewHandler, 0, sizeof(HashHandler));
292         NewHandler->Filter.nMinArgs = nMinArgs;
293         NewHandler->Filter.nMaxArgs = nMaxArgs;
294         NewHandler->Filter.ContextType = ContextRequired;
295         NewHandler->Filter.ControlContextType = CTX_NONE;
296
297         NewHandler->PreEvalFunc = PreevalFunc;
298         NewHandler->HandlerFunc = HandlerFunc;  
299         Put(GlobalNS, NSName, len, NewHandler, NULL);
300 }
301
302 void RegisterControlNS(const char *NSName, 
303                        long len, 
304                        int nMinArgs, 
305                        int nMaxArgs, 
306                        WCHandlerFunc HandlerFunc, 
307                        int ControlContextRequired)
308 {
309         HashHandler *NewHandler;
310         
311         NewHandler = (HashHandler*) malloc(sizeof(HashHandler));
312         memset(NewHandler, 0, sizeof(HashHandler));
313         NewHandler->Filter.nMinArgs = nMinArgs;
314         NewHandler->Filter.nMaxArgs = nMaxArgs;
315         NewHandler->Filter.ContextType = CTX_NONE;
316         NewHandler->Filter.ControlContextType = ControlContextRequired;
317         NewHandler->HandlerFunc = HandlerFunc;  
318         Put(GlobalNS, NSName, len, NewHandler, NULL);
319 }
320
321
322
323 int CheckContext(StrBuf *Target, ContextFilter *Need, WCTemplputParams *TP, const char *ErrType)
324 {
325         if ((Need->ContextType != CTX_NONE) && 
326             (Need->ContextType != TP->Filter.ContextType)) {
327                 LogTemplateError(
328                         Target, ErrType, ERR_PARM1, TP,
329                         "  WARNING: requires Context: [%s], have [%s]!", 
330                         ContextName(Need->ContextType), 
331                         ContextName(TP->Filter.ContextType));
332                 return 0;
333         }
334
335         if ((Need->ControlContextType != CTX_NONE) && 
336             (Need->ControlContextType != TP->Filter.ControlContextType)) {
337                 LogTemplateError(
338                         Target, ErrType, ERR_PARM1, TP,
339                         "  WARNING: requires Control Context: [%s], have [%s]!", 
340                         ContextName(Need->ControlContextType), 
341                         ContextName(TP->Filter.ControlContextType));
342                 return 0;
343         }
344 /*                      
345         if (TP->Tokens->nParameters < Need->nMinArgs) {
346                 LogTemplateError(Target, ErrType, ERR_NAME, TP,
347                                  "needs at least %ld params, have %ld", 
348                                  Need->nMinArgs, 
349                                  TP->Tokens->nParameters);
350                 return 0;
351
352         }
353         else if (TP->Tokens->nParameters > Need->nMaxArgs) {
354                 LogTemplateError(Target, ErrType, ERR_NAME, TP,
355                                  "just needs %ld params, you gave %ld",
356                                  Need->nMaxArgs,
357                                  TP->Tokens->nParameters); 
358                 return 0;
359
360         }
361 */
362         return 1;
363 }
364
365 void FreeToken(WCTemplateToken **Token)
366 {
367         int i; 
368         FreeStrBuf(&(*Token)->FlatToken);
369         if ((*Token)->HaveParameters) 
370                 for (i = 0; i < (*Token)->nParameters; i++)
371                         free((*Token)->Params[i]);
372         free(*Token);
373         *Token = NULL;
374 }
375
376
377
378 void FreeWCTemplate(void *vFreeMe)
379 {
380         int i;
381         WCTemplate *FreeMe = (WCTemplate*)vFreeMe;
382
383         if (FreeMe->TokenSpace > 0) {
384                 for (i = 0; i < FreeMe->nTokensUsed; i ++) {
385                         FreeToken(&FreeMe->Tokens[i]);
386                 }
387                 free(FreeMe->Tokens);
388         }
389         FreeStrBuf(&FreeMe->FileName);
390         FreeStrBuf(&FreeMe->Data);
391         FreeStrBuf(&FreeMe->MimeType);
392         free(FreeMe);
393 }
394
395 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(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                                 syslog(1, "ERROR: Conditional without Endconditional: '%s'\n",
1228                                        ChrPtr(CheckMe->Tokens[i]->FlatToken));
1229                         }
1230                         break;
1231                 }
1232         }
1233 }
1234
1235 /**
1236  * \brief Display a variable-substituted template
1237  * \param templatename template file to load
1238  */
1239 void *load_template(WCTemplate *NewTemplate)
1240 {
1241         int fd;
1242         struct stat statbuf;
1243         const char *pS, *pE, *pch, *Err;
1244         long Line;
1245
1246         fd = open(ChrPtr(NewTemplate->FileName), O_RDONLY);
1247         if (fd <= 0) {
1248                 syslog(1, "ERROR: could not open template '%s' - %s\n",
1249                         ChrPtr(NewTemplate->FileName), strerror(errno));
1250                 return NULL;
1251         }
1252
1253         if (fstat(fd, &statbuf) == -1) {
1254                 syslog(1, "ERROR: could not stat template '%s' - %s\n",
1255                         ChrPtr(NewTemplate->FileName), strerror(errno));
1256                 return NULL;
1257         }
1258
1259         NewTemplate->Data = NewStrBufPlain(NULL, statbuf.st_size + 1);
1260         if (StrBufReadBLOB(NewTemplate->Data, &fd, 1, statbuf.st_size, &Err) < 0) {
1261                 close(fd);
1262                 syslog(1, "ERROR: reading template '%s' - %s<br>\n",
1263                         ChrPtr(NewTemplate->FileName), strerror(errno));
1264                 return NULL;
1265         }
1266         close(fd);
1267
1268         Line = 0;
1269         StrBufShrinkToFit(NewTemplate->Data, 1);
1270         StrBufShrinkToFit(NewTemplate->MimeType, 1);
1271         pS = pch = ChrPtr(NewTemplate->Data);
1272         pE = pS + StrLength(NewTemplate->Data);
1273         while (pch < pE) {
1274                 const char *pts, *pte;
1275                 char InQuotes = '\0';
1276                 void *pv;
1277
1278                 /** Find one <? > */
1279                 for (; pch < pE; pch ++) {
1280                         if ((*pch=='<')&&(*(pch + 1)=='?') &&
1281                             !((pch == pS) && /* we must ommit a <?xml */
1282                               (*(pch + 2) == 'x') && 
1283                               (*(pch + 3) == 'm') && 
1284                               (*(pch + 4) == 'l')))                          
1285                                 break;
1286                         if (*pch=='\n') Line ++;
1287                 }
1288                 if (pch >= pE)
1289                         continue;
1290                 pts = pch;
1291
1292                 /** Found one? parse it. */
1293                 for (; pch <= pE - 1; pch ++) {
1294                         if ((!InQuotes) &&
1295                             ((*pch == '\'') || (*pch == '"')))
1296                         {
1297                                 InQuotes = *pch;
1298                         }
1299                         else if (InQuotes && (InQuotes == *pch))
1300                         {
1301                                 InQuotes = '\0';
1302                         }
1303                         else if ((InQuotes) &&
1304                                  (*pch == '\\') &&
1305                                  (*(pch + 1) == InQuotes))
1306                         {
1307                                 pch++;
1308                         }
1309                         else if ((!InQuotes) && 
1310                                  (*pch == '>'))
1311                         {
1312                                 break;
1313                         }
1314                 }
1315                 if (pch + 1 > pE)
1316                         continue;
1317                 pte = pch;
1318                 pv = NewTemplateSubstitute(NewTemplate->Data, pS, pts, pte, Line, NewTemplate);
1319                 if (pv != NULL) {
1320                         PutNewToken(NewTemplate, pv);
1321                         pch ++;
1322                 }
1323         }
1324
1325         SanityCheckTemplate(NewTemplate);
1326         return NewTemplate;
1327 }
1328
1329
1330 const char* PrintTemplate(void *vSubst)
1331 {
1332         WCTemplate *Tmpl = vSubst;
1333
1334         return ChrPtr(Tmpl->FileName);
1335
1336 }
1337
1338 int LoadTemplateDir(const StrBuf *DirName, HashList *big, const StrBuf *BaseKey)
1339 {
1340         int Toplevel;
1341         StrBuf *FileName;
1342         StrBuf *Key;
1343         StrBuf *SubKey;
1344         StrBuf *SubDirectory;
1345         DIR *filedir = NULL;
1346         struct dirent *filedir_entry;
1347         struct dirent *d;
1348         int d_type = 0;
1349         int d_namelen;
1350         int d_without_ext;
1351         
1352         d = (struct dirent *)malloc(offsetof(struct dirent, d_name) + PATH_MAX + 1);
1353         if (d == NULL) {
1354                 return 0;
1355         }
1356
1357         filedir = opendir (ChrPtr(DirName));
1358         if (filedir == NULL) {
1359                 free(d);
1360                 return 0;
1361         }
1362
1363         Toplevel = StrLength(BaseKey) == 0;
1364         SubDirectory = NewStrBuf();
1365         SubKey = NewStrBuf();
1366         FileName = NewStrBufPlain(NULL, PATH_MAX);
1367         Key = NewStrBuf();
1368         while ((readdir_r(filedir, d, &filedir_entry) == 0) &&
1369                (filedir_entry != NULL))
1370         {
1371                 char *MinorPtr;
1372
1373 #ifdef _DIRENT_HAVE_D_NAMELEN
1374                 d_namelen = filedir_entry->d_namelen;
1375                 d_type = filedir_entry->d_type;
1376 #else
1377
1378 #ifndef DT_UNKNOWN
1379 #define DT_UNKNOWN     0
1380 #define DT_DIR         4
1381 #define DT_REG         8
1382 #define DT_LNK         10
1383
1384 #define IFTODT(mode)   (((mode) & 0170000) >> 12)
1385 #define DTTOIF(dirtype)        ((dirtype) << 12)
1386 #endif
1387                 d_namelen = strlen(filedir_entry->d_name);
1388                 d_type = DT_UNKNOWN;
1389 #endif
1390                 d_without_ext = d_namelen;
1391
1392                 if ((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~')
1393                         continue; /* Ignore backup files... */
1394
1395                 if ((d_namelen == 1) && 
1396                     (filedir_entry->d_name[0] == '.'))
1397                         continue;
1398
1399                 if ((d_namelen == 2) && 
1400                     (filedir_entry->d_name[0] == '.') &&
1401                     (filedir_entry->d_name[1] == '.'))
1402                         continue;
1403
1404                 if (d_type == DT_UNKNOWN) {
1405                         struct stat s;
1406                         char path[PATH_MAX];
1407                         snprintf(path, PATH_MAX, "%s/%s", 
1408                                  ChrPtr(DirName), filedir_entry->d_name);
1409                         if (stat(path, &s) == 0) {
1410                                 d_type = IFTODT(s.st_mode);
1411                         }
1412                 }
1413                 switch (d_type)
1414                 {
1415                 case DT_DIR:
1416                         /* Skip directories we are not interested in... */
1417                         if (strcmp(filedir_entry->d_name, ".svn") == 0)
1418                                 continue;
1419
1420                         FlushStrBuf(SubKey);
1421                         if (!Toplevel) {
1422                                 /* If we're not toplevel, the upper dirs count as foo_bar_<local name>*/
1423                                 StrBufAppendBuf(SubKey, BaseKey, 0);
1424                                 StrBufAppendBufPlain(SubKey, HKEY("_"), 0);
1425                         }
1426                         StrBufAppendBufPlain(SubKey, filedir_entry->d_name, d_namelen, 0);
1427
1428                         FlushStrBuf(SubDirectory);
1429                         StrBufAppendBuf(SubDirectory, DirName, 0);
1430                         if (ChrPtr(SubDirectory)[StrLength(SubDirectory) - 1] != '/')
1431                                 StrBufAppendBufPlain(SubDirectory, HKEY("/"), 0);
1432                         StrBufAppendBufPlain(SubDirectory, filedir_entry->d_name, d_namelen, 0);
1433
1434                         LoadTemplateDir(SubDirectory, big, SubKey);
1435
1436                         break;
1437                 case DT_LNK: /* TODO: check whether its a file or a directory */
1438                 case DT_REG:
1439
1440
1441                         while ((d_without_ext > 0) && (filedir_entry->d_name[d_without_ext] != '.'))
1442                                 d_without_ext --;
1443                         if ((d_without_ext == 0) || (d_namelen < 3))
1444                                 continue;
1445                         if (((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~') ||
1446                             (strcmp(&filedir_entry->d_name[d_without_ext], ".orig") == 0) ||
1447                             (strcmp(&filedir_entry->d_name[d_without_ext], ".swp") == 0))
1448                                 continue; /* Ignore backup files... */
1449                         StrBufPrintf(FileName, "%s/%s", ChrPtr(DirName),  filedir_entry->d_name);
1450                         MinorPtr = strchr(filedir_entry->d_name, '.');
1451                         if (MinorPtr != NULL)
1452                                 *MinorPtr = '\0';
1453                         FlushStrBuf(Key);
1454                         if (!Toplevel) {
1455                                 /* If we're not toplevel, the upper dirs count as foo_bar_<local name>*/
1456                                 StrBufAppendBuf(Key, BaseKey, 0);
1457                                 StrBufAppendBufPlain(Key, HKEY("_"), 0);
1458                         }
1459                         StrBufAppendBufPlain(Key, filedir_entry->d_name, MinorPtr - filedir_entry->d_name, 0);
1460
1461                         if (LoadTemplates >= 1)
1462                                 syslog(1, "%s %s\n", ChrPtr(FileName), ChrPtr(Key));
1463                         prepare_template(FileName, Key, big);
1464                 default:
1465                         break;
1466                 }
1467         }
1468         free(d);
1469         closedir(filedir);
1470         FreeStrBuf(&FileName);
1471         FreeStrBuf(&Key);
1472         FreeStrBuf(&SubDirectory);
1473         FreeStrBuf(&SubKey);
1474         return 1;
1475 }
1476
1477 void InitTemplateCache(void)
1478 {
1479         int i;
1480         StrBuf *Key;
1481         StrBuf *Dir;
1482         HashList *Templates[2];
1483
1484         Dir = NewStrBuf();
1485         Key = NewStrBuf();
1486
1487         /* Primary Template set... */
1488         StrBufPrintf(Dir, "%s/t", static_dirs[0]);
1489         LoadTemplateDir(Dir,
1490                         TemplateCache, 
1491                         Key);
1492
1493         /* User local Template set */
1494         StrBufPrintf(Dir, "%s/t", static_dirs[1]);
1495         LoadTemplateDir(Dir,
1496                         LocalTemplateCache, 
1497                         Key);
1498         
1499         /* Debug Templates, just to be loaded while debugging. */
1500         
1501         StrBufPrintf(Dir, "%s/dbg", static_dirs[0]);
1502         LoadTemplateDir(Dir,
1503                         TemplateCache, 
1504                         Key);
1505         Templates[0] = TemplateCache;
1506         Templates[1] = LocalTemplateCache;
1507
1508
1509         if (LoadTemplates == 0) 
1510                 for (i=0; i < 2; i++) {
1511                         const char *Key;
1512                         long KLen;
1513                         HashPos *At;
1514                         void *vTemplate;
1515
1516                         At = GetNewHashPos(Templates[i], 0);
1517                         while (GetNextHashPos(Templates[i], 
1518                                               At, 
1519                                               &KLen,
1520                                               &Key, 
1521                                               &vTemplate) && 
1522                                (vTemplate != NULL))
1523                         {
1524                                 load_template((WCTemplate *)vTemplate);
1525                         }
1526                         DeleteHashPos(&At);
1527                 }
1528
1529
1530         FreeStrBuf(&Dir);
1531         FreeStrBuf(&Key);
1532 }
1533
1534
1535
1536 /*-----------------------------------------------------------------------------
1537  *                      Filling & processing Templates
1538  */
1539 /**
1540  * \brief executes one token
1541  * \param Target buffer to append to
1542  * \param Token da to  process.
1543  * \param Template we're iterating
1544  * \param Context Contextpoointer to pass in
1545  * \param state are we in conditional state?
1546  * \param ContextType what type of information does context giv us?
1547  */
1548 int EvaluateToken(StrBuf *Target, int state, WCTemplputParams *TP)
1549 {
1550         const char *AppendMe;
1551         long AppendMeLen;
1552         HashHandler *Handler;
1553         void *vVar;
1554         
1555 /* much output, since pName is not terminated...
1556         syslog(1,"Doing token: %s\n",Token->pName);
1557 */
1558
1559         switch (TP->Tokens->Flags) {
1560         case SV_GETTEXT:
1561                 TmplGettext(Target, TP);
1562                 break;
1563         case SV_CONDITIONAL: /** Forward conditional evaluation */
1564                 return EvaluateConditional(Target, 1, state, TP);
1565                 break;
1566         case SV_NEG_CONDITIONAL: /** Reverse conditional evaluation */
1567                 return EvaluateConditional(Target, 0, state, TP);
1568                 break;
1569         case SV_CUST_STR_CONDITIONAL: /** Conditional put custom strings from params */
1570                 if (TP->Tokens->nParameters >= 6) {
1571                         if (EvaluateConditional(Target, 0, state, TP)) {
1572                                 GetTemplateTokenString(Target, TP, 5, &AppendMe, &AppendMeLen);
1573                                 StrBufAppendBufPlain(Target, 
1574                                                      AppendMe, 
1575                                                      AppendMeLen,
1576                                                      0);
1577                         }
1578                         else{
1579                                 GetTemplateTokenString(Target, TP, 4, &AppendMe, &AppendMeLen);
1580                                 StrBufAppendBufPlain(Target, 
1581                                                      AppendMe, 
1582                                                      AppendMeLen,
1583                                                      0);
1584                         }
1585                 }
1586                 else  {
1587                         LogTemplateError(
1588                                 Target, "Conditional", ERR_NAME, TP,
1589                                 "needs at least 6 Params!"); 
1590                 }
1591                 break;
1592         case SV_SUBTEMPL:
1593                 if (TP->Tokens->nParameters == 1)
1594                         DoTemplate(TKEY(0), Target, TP);
1595                 break;
1596         case SV_PREEVALUATED:
1597                 Handler = (HashHandler*) TP->Tokens->PreEval;
1598                 if (!CheckContext(Target, &Handler->Filter, TP, "Token")) {
1599                         return -1;
1600                 }
1601                 Handler->HandlerFunc(Target, TP);
1602                 break;          
1603         default:
1604                 if (GetHash(GlobalNS, TP->Tokens->pName, TP->Tokens->NameEnd, &vVar)) {
1605                         Handler = (HashHandler*) vVar;
1606                         if (!CheckContext(Target, &Handler->Filter, TP, "Token")) {
1607                                 return -1;
1608                         }
1609                         else {
1610                                 Handler->HandlerFunc(Target, TP);
1611                         }
1612                 }
1613                 else {
1614                         LogTemplateError(
1615                                 Target, "Token UNKNOWN", ERR_NAME, TP,
1616                                 "You've specified a token that isn't known to webcit.!");
1617                 }
1618         }
1619         return 0;
1620 }
1621
1622
1623
1624 const StrBuf *ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target, WCTemplputParams *CallingTP)
1625 {
1626         WCTemplate *pTmpl = Tmpl;
1627         int done = 0;
1628         int i, state;
1629         const char *pData, *pS;
1630         long len;
1631         WCTemplputParams TP;
1632
1633         memcpy(&TP.Filter, &CallingTP->Filter, sizeof(ContextFilter));
1634
1635         TP.Context = CallingTP->Context;
1636         TP.ControlContext = CallingTP->ControlContext;
1637
1638         if (LoadTemplates != 0) {                       
1639                 if (LoadTemplates > 1)
1640                         syslog(1, "DBG: ----- loading:  [%s] ------ \n", 
1641                                 ChrPtr(Tmpl->FileName));
1642                 pTmpl = duplicate_template(Tmpl);
1643                 if(load_template(pTmpl) == NULL) {
1644                         StrBufAppendPrintf(
1645                                 Target, 
1646                                 "<pre>\nError loading Template [%s]\n See Logfile for details\n</pre>\n", 
1647                                 ChrPtr(Tmpl->FileName));
1648                         FreeWCTemplate(pTmpl);
1649                         return NULL;
1650                 }
1651
1652         }
1653
1654         pS = pData = ChrPtr(pTmpl->Data);
1655         len = StrLength(pTmpl->Data);
1656         i = 0;
1657         state = 0;
1658         while (!done) {
1659                 if (i >= pTmpl->nTokensUsed) {
1660                         StrBufAppendBufPlain(Target, 
1661                                              pData, 
1662                                              len - (pData - pS), 0);
1663                         done = 1;
1664                 }
1665                 else {
1666                         StrBufAppendBufPlain(
1667                                 Target, pData, 
1668                                 pTmpl->Tokens[i]->pTokenStart - pData, 0);
1669                         TP.Tokens = pTmpl->Tokens[i];
1670                         TP.nArgs = pTmpl->Tokens[i]->nParameters;
1671                         state = EvaluateToken(Target, state, &TP);
1672
1673                         while ((state != 0) && (i+1 < pTmpl->nTokensUsed)) {
1674                         /* condition told us to skip till its end condition */
1675                                 i++;
1676                                 TP.Tokens = pTmpl->Tokens[i];
1677                                 TP.nArgs = pTmpl->Tokens[i]->nParameters;
1678                                 if ((pTmpl->Tokens[i]->Flags == SV_CONDITIONAL) ||
1679                                     (pTmpl->Tokens[i]->Flags == SV_NEG_CONDITIONAL)) {
1680                                         if (state == EvaluateConditional(
1681                                                     Target, 
1682                                                     pTmpl->Tokens[i]->Flags, 
1683                                                     state, 
1684                                                     &TP))
1685                                                 state = 0;
1686                                 }
1687                         }
1688                         pData = pTmpl->Tokens[i++]->pTokenEnd + 1;
1689                         if (i > pTmpl->nTokensUsed)
1690                                 done = 1;
1691                 }
1692         }
1693         if (LoadTemplates != 0) {
1694                 FreeWCTemplate(pTmpl);
1695         }
1696         return Tmpl->MimeType;
1697
1698 }
1699
1700 /**
1701  * \brief Display a variable-substituted template
1702  * \param templatename template file to load
1703  * \returns the mimetype of the template its doing
1704  */
1705 const StrBuf *DoTemplate(const char *templatename, long len, StrBuf *Target, WCTemplputParams *TP) 
1706 {
1707         WCTemplputParams LocalTP;
1708         HashList *Static;
1709         HashList *StaticLocal;
1710         void *vTmpl;
1711         
1712         if (Target == NULL)
1713                 Target = WC->WBuf;
1714         if (TP == NULL) {
1715                 memset(&LocalTP, 0, sizeof(WCTemplputParams));
1716                 TP = &LocalTP;
1717         }
1718
1719         Static = TemplateCache;
1720         StaticLocal = LocalTemplateCache;
1721
1722         if (len == 0)
1723         {
1724                 syslog(1, "Can't to load a template with empty name!\n");
1725                 StrBufAppendPrintf(Target, "<pre>\nCan't to load a template with empty name!\n</pre>");
1726                 return NULL;
1727         }
1728
1729         if (!GetHash(StaticLocal, templatename, len, &vTmpl) &&
1730             !GetHash(Static, templatename, len, &vTmpl)) {
1731                 syslog(1, "didn't find Template [%s] %ld %ld\n", templatename, len , (long)strlen(templatename));
1732                 StrBufAppendPrintf(Target, "<pre>\ndidn't find Template [%s] %ld %ld\n</pre>", 
1733                                    templatename, len, 
1734                                    (long)strlen(templatename));
1735 #if 0
1736                 dbg_PrintHash(Static, PrintTemplate, NULL);
1737                 PrintHash(Static, VarPrintTransition, PrintTemplate);
1738 #endif
1739                 return NULL;
1740         }
1741         if (vTmpl == NULL) 
1742                 return NULL;
1743         return ProcessTemplate(vTmpl, Target, TP);
1744
1745 }
1746
1747
1748 void tmplput_Comment(StrBuf *Target, WCTemplputParams *TP)
1749 {
1750         if (LoadTemplates != 0)
1751         {
1752                 StrBuf *Comment;
1753                 const char *pch;
1754                 long len;
1755
1756                 GetTemplateTokenString(Target, TP, 0, &pch, &len);
1757                 Comment = NewStrBufPlain(pch, len);
1758                 StrBufAppendBufPlain(Target, HKEY("<!--"), 0);
1759                 StrBufAppendTemplate(Target, TP, Comment, 1);
1760                 StrBufAppendBufPlain(Target, HKEY("-->"), 0);
1761                 FreeStrBuf(&Comment);
1762         }
1763 }
1764
1765 /*-----------------------------------------------------------------------------
1766  *                      Iterators
1767  */
1768 typedef struct _HashIterator {
1769         HashList *StaticList;
1770         int AdditionalParams;
1771         int ContextType;
1772         int XPectContextType;
1773         int Flags;
1774         RetrieveHashlistFunc GetHash;
1775         HashDestructorFunc Destructor;
1776         SubTemplFunc DoSubTemplate;
1777 } HashIterator;
1778
1779 void RegisterITERATOR(const char *Name, long len, 
1780                       int AdditionalParams, 
1781                       HashList *StaticList, 
1782                       RetrieveHashlistFunc GetHash, 
1783                       SubTemplFunc DoSubTempl,
1784                       HashDestructorFunc Destructor,
1785                       int ContextType, 
1786                       int XPectContextType, 
1787                       int Flags)
1788 {
1789         HashIterator *It;
1790
1791         It = (HashIterator*)malloc(sizeof(HashIterator));
1792         memset(It, 0, sizeof(HashIterator));
1793         It->StaticList = StaticList;
1794         It->AdditionalParams = AdditionalParams;
1795         It->GetHash = GetHash;
1796         It->DoSubTemplate = DoSubTempl;
1797         It->Destructor = Destructor;
1798         It->ContextType = ContextType;
1799         It->XPectContextType = XPectContextType;
1800         It->Flags = Flags;
1801         Put(Iterators, Name, len, It, NULL);
1802 }
1803
1804 typedef struct _iteratestruct {
1805         int GroupChange;
1806         int oddeven;
1807         const char *Key;
1808         long KeyLen;
1809         int n;
1810         int LastN;
1811         }IterateStruct; 
1812
1813 int preeval_iterate(WCTemplateToken *Token)
1814 {
1815         WCTemplputParams TPP;
1816         WCTemplputParams *TP;
1817         void *vTmpl;
1818         void *vIt;
1819         HashIterator *It;
1820
1821         memset(&TPP, 0, sizeof(WCTemplputParams));
1822         TP = &TPP;
1823         TP->Tokens = Token;
1824         if (!GetHash(Iterators, TKEY(0), &vIt)) {
1825                 LogTemplateError(
1826                         NULL, "Iterator", ERR_PARM1, TP,
1827                         "not found");
1828                 return 0;
1829         }
1830         if (TP->Tokens->Params[1]->Type != TYPE_SUBTEMPLATE) {
1831                 LogTemplateError(NULL, "Iterator", ERR_PARM1, TP,
1832                                  "Need token with type Subtemplate as param 1, have %s", 
1833                                  TP->Tokens->Params[1]->Start);
1834         }
1835         
1836         /* well, we don't check the mobile stuff here... */
1837         if (!GetHash(LocalTemplateCache, TKEY(1), &vTmpl) &&
1838             !GetHash(TemplateCache, TKEY(1), &vTmpl)) {
1839                 LogTemplateError(NULL, "SubTemplate", ERR_PARM1, TP,
1840                                  "referenced here doesn't exist");
1841         }
1842         Token->Preeval2 = vIt;
1843         It = (HashIterator *) vIt;
1844
1845         if (TP->Tokens->nParameters < It->AdditionalParams + 2) {
1846                 LogTemplateError(                               
1847                         NULL, "Iterator", ERR_PARM1, TP,
1848                         "doesn't work with %d params", 
1849                         TP->Tokens->nParameters);
1850         }
1851
1852
1853         return 1;
1854 }
1855
1856 void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
1857 {
1858         HashIterator *It;
1859         HashList *List;
1860         HashPos  *it;
1861         SortStruct *SortBy = NULL;
1862         void *vSortBy;
1863         int DetectGroupChange = 0;
1864         int nMembersUsed;
1865         void *vContext;
1866         void *vLastContext = NULL;
1867         StrBuf *SubBuf;
1868         WCTemplputParams SubTP;
1869         IterateStruct Status;
1870
1871         long StartAt = 0;
1872         long StepWidth = 0;
1873         long StopAt = -1;
1874
1875         memset(&Status, 0, sizeof(IterateStruct));
1876         memcpy (&SubTP, &TP, sizeof(WCTemplputParams));
1877         
1878         It = (HashIterator*) TP->Tokens->Preeval2;
1879         if (It == NULL) {
1880                 LogTemplateError(
1881                         Target, "Iterator", ERR_PARM1, TP, "Unknown!");
1882                 return;
1883         }
1884
1885         if (TP->Tokens->nParameters < It->AdditionalParams + 2) {
1886                 LogTemplateError(                               
1887                         Target, "Iterator", ERR_PARM1, TP,
1888                         "doesn't work with %d params", 
1889                         TP->Tokens->nParameters - 1);
1890                 return;
1891         }
1892
1893         if ((It->XPectContextType != CTX_NONE) &&
1894             (It->XPectContextType != TP->Filter.ContextType)) {
1895                 LogTemplateError(
1896                         Target, "Iterator", ERR_PARM1, TP,
1897                         "requires context of type %s, have %s", 
1898                         ContextName(It->XPectContextType), 
1899                         ContextName(TP->Filter.ContextType));
1900                 return ;
1901                 
1902         }
1903
1904         if (It->StaticList == NULL)
1905                 List = It->GetHash(Target, TP);
1906         else
1907                 List = It->StaticList;
1908
1909         DetectGroupChange = (It->Flags & IT_FLAG_DETECT_GROUPCHANGE) != 0;
1910         if (DetectGroupChange) {
1911                 const StrBuf *BSort;
1912                 DetectGroupChange = 0;
1913                 if (havebstr("SortBy")) {
1914                         BSort = sbstr("SortBy");
1915                         if (GetHash(SortHash, SKEY(BSort), &vSortBy) &&
1916                             (vSortBy != NULL)) {
1917                                 SortBy = (SortStruct*)vSortBy;
1918                                 /* first check whether its intended for us... */
1919                                 if ((SortBy->ContextType == It->ContextType)&&
1920                                 /** Ok, its us, lets see in which direction we should sort... */
1921                                     (havebstr("SortOrder"))) {
1922                                         int SortOrder;
1923                                         SortOrder = LBSTR("SortOrder");
1924                                         if (SortOrder != 0)
1925                                                 DetectGroupChange = 1;
1926                                 }
1927                         }
1928                 }
1929         }
1930         nMembersUsed = GetCount(List);
1931         SubBuf = NewStrBuf();
1932         SubTP.Filter.ContextType = It->ContextType;
1933         SubTP.Filter.ControlContextType = CTX_ITERATE;
1934         SubTP.ControlContext = &Status;
1935         
1936         if (HAVE_PARAM(2)) {
1937                 StartAt = GetTemplateTokenNumber(Target, TP, 2, 0);
1938         }
1939         if (HAVE_PARAM(3)) {
1940                 StepWidth = GetTemplateTokenNumber(Target, TP, 3, 0);
1941         }
1942         if (HAVE_PARAM(4)) {
1943                 StopAt = GetTemplateTokenNumber(Target, TP, 4, -1);
1944         }
1945         it = GetNewHashPos(List, StepWidth);
1946         if (StopAt < 0) {
1947                 StopAt = GetCount(List);
1948         }
1949         while (GetNextHashPos(List, it, &Status.KeyLen, &Status.Key, &vContext)) {
1950                 if ((Status.n >= StartAt) && (Status.n <= StopAt)) {
1951                         if (DetectGroupChange && Status.n > 0) {
1952                                 Status.GroupChange = SortBy->GroupChange(vContext, vLastContext);
1953                         }
1954                         Status.LastN = (Status.n + 1) == nMembersUsed;
1955                         SubTP.Context = vContext;
1956                         if (It->DoSubTemplate != NULL)
1957                                 It->DoSubTemplate(SubBuf, &SubTP);
1958                         DoTemplate(TKEY(1), SubBuf, &SubTP);
1959                         
1960                         StrBufAppendBuf(Target, SubBuf, 0);
1961                         FlushStrBuf(SubBuf);
1962                         Status.oddeven = ! Status.oddeven;
1963                         vLastContext = vContext;
1964                 }
1965                 Status.n++;
1966         }
1967         FreeStrBuf(&SubBuf);
1968         DeleteHashPos(&it);
1969         if (It->Destructor != NULL)
1970                 It->Destructor(&List);
1971 }
1972
1973
1974 int conditional_ITERATE_ISGROUPCHANGE(StrBuf *Target, WCTemplputParams *TP)
1975 {
1976         IterateStruct *Ctx = CCTX;
1977         if (TP->Tokens->nParameters < 3)
1978                 return  Ctx->GroupChange;
1979
1980         return TP->Tokens->Params[2]->lvalue == Ctx->GroupChange;
1981 }
1982
1983 void tmplput_ITERATE_ODDEVEN(StrBuf *Target, WCTemplputParams *TP)
1984 {
1985         IterateStruct *Ctx = CCTX;
1986         if (Ctx->oddeven)
1987                 StrBufAppendBufPlain(Target, HKEY("odd"), 0);
1988         else
1989                 StrBufAppendBufPlain(Target, HKEY("even"), 0);
1990 }
1991
1992
1993 void tmplput_ITERATE_KEY(StrBuf *Target, WCTemplputParams *TP)
1994 {
1995         IterateStruct *Ctx = CCTX;
1996
1997         StrBufAppendBufPlain(Target, Ctx->Key, Ctx->KeyLen, 0);
1998 }
1999
2000
2001 void tmplput_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
2002 {
2003         IterateStruct *Ctx = CCTX;
2004         StrBufAppendPrintf(Target, "%d", Ctx->n);
2005 }
2006
2007 int conditional_ITERATE_FIRSTN(StrBuf *Target, WCTemplputParams *TP)
2008 {
2009         IterateStruct *Ctx = CCTX;
2010         return Ctx->n == 0;
2011 }
2012
2013 int conditional_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
2014 {
2015         IterateStruct *Ctx = CCTX;
2016         return Ctx->LastN;
2017 }
2018
2019
2020
2021 /*-----------------------------------------------------------------------------
2022  *                      Conditionals
2023  */
2024 int EvaluateConditional(StrBuf *Target, int Neg, int state, WCTemplputParams *TP)
2025 {
2026         ConditionalStruct *Cond;
2027         int rc = 0;
2028         int res;
2029
2030         if ((TP->Tokens->Params[0]->len == 1) &&
2031             (TP->Tokens->Params[0]->Start[0] == 'X'))
2032                 return (state != 0)?TP->Tokens->Params[1]->lvalue:0;
2033             
2034         Cond = (ConditionalStruct *) TP->Tokens->PreEval;
2035         if (Cond == NULL) {
2036                 LogTemplateError(
2037                         Target, "Conditional", ERR_PARM1, TP,
2038                         "unknown!");
2039                 return 1;
2040         }
2041
2042         if (!CheckContext(Target, &Cond->Filter, TP, "Conditional")) {
2043                 return 0;
2044         }
2045         res = Cond->CondF(Target, TP);
2046         if (res == Neg)
2047                 rc = TP->Tokens->Params[1]->lvalue;
2048         if (LoadTemplates > 5) 
2049                 syslog(1, "<%s> : %d %d==%d\n", 
2050                         ChrPtr(TP->Tokens->FlatToken), 
2051                         rc, res, Neg);
2052         return rc;
2053 }
2054
2055 void RegisterConditional(const char *Name, long len, 
2056                          int nParams,
2057                          WCConditionalFunc CondF, 
2058                          int ContextRequired)
2059 {
2060         ConditionalStruct *Cond;
2061
2062         Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2063         memset(Cond, 0, sizeof(ConditionalStruct));
2064         Cond->PlainName = Name;
2065         Cond->Filter.nMaxArgs = nParams;
2066         Cond->Filter.nMinArgs = nParams;
2067         Cond->CondF = CondF;
2068         Cond->Filter.ContextType = ContextRequired;
2069         Cond->Filter.ControlContextType = CTX_NONE;
2070         Put(Conditionals, Name, len, Cond, NULL);
2071 }
2072
2073 void RegisterControlConditional(const char *Name, long len, 
2074                                 int nParams,
2075                                 WCConditionalFunc CondF, 
2076                                 int ControlContextRequired)
2077 {
2078         ConditionalStruct *Cond;
2079
2080         Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2081         memset(Cond, 0, sizeof(ConditionalStruct));
2082         Cond->PlainName = Name;
2083         Cond->Filter.nMaxArgs = nParams;
2084         Cond->Filter.nMinArgs = nParams;
2085         Cond->CondF = CondF;
2086         Cond->Filter.ContextType = CTX_NONE;
2087         Cond->Filter.ControlContextType = ControlContextRequired;
2088         Put(Conditionals, Name, len, Cond, NULL);
2089 }
2090
2091 void RegisterTokenParamDefine(const char *Name, long len, 
2092                               long Value)
2093 {
2094         long *PVal;
2095
2096         PVal = (long*)malloc(sizeof(long));
2097         *PVal = Value;
2098         Put(Defines, Name, len, PVal, NULL);
2099 }
2100
2101 long GetTokenDefine(const char *Name, long len, 
2102                     long DefValue)
2103 {
2104         void *vPVal;
2105
2106         if (GetHash(Defines, Name, len, &vPVal) &&
2107              (vPVal != NULL))
2108          {
2109                  return *(long*) vPVal;
2110          }
2111          else
2112          {
2113                  return DefValue;
2114          }
2115 }
2116
2117 void tmplput_DefStr(StrBuf *Target, WCTemplputParams *TP)
2118 {
2119         const char *Str;
2120         long len;
2121         GetTemplateTokenString(Target, TP, 2, &Str, &len);
2122         
2123         StrBufAppendBufPlain(Target, Str, len, 0);
2124 }
2125
2126 void tmplput_DefVal(StrBuf *Target, WCTemplputParams *TP)
2127 {
2128         int val;
2129
2130         val = GetTemplateTokenNumber(Target, TP, 0, 0);
2131         StrBufAppendPrintf(Target, "%d", val);
2132 }
2133
2134 HashList *Defines;
2135
2136 /*-----------------------------------------------------------------------------
2137  *                      Context Strings
2138  */
2139 void tmplput_ContextString(StrBuf *Target, WCTemplputParams *TP)
2140 {
2141         StrBufAppendTemplate(Target, TP, (StrBuf*)CTX, 0);
2142 }
2143 int ConditionalContextStr(StrBuf *Target, WCTemplputParams *TP)
2144 {
2145         StrBuf *TokenText = (StrBuf*) CTX;
2146         const char *CompareToken;
2147         long len;
2148
2149         GetTemplateTokenString(Target, TP, 2, &CompareToken, &len);
2150         return strcmp(ChrPtr(TokenText), CompareToken) == 0;
2151 }
2152
2153 void tmplput_ContextStringArray(StrBuf *Target, WCTemplputParams *TP)
2154 {
2155         HashList *Arr = (HashList*) CTX;
2156         void *pV;
2157         int val;
2158
2159         val = GetTemplateTokenNumber(Target, TP, 0, 0);
2160         if (GetHash(Arr, IKEY(val), &pV) && 
2161             (pV != NULL)) {
2162                 StrBufAppendTemplate(Target, TP, (StrBuf*)pV, 1);
2163         }
2164 }
2165 int ConditionalContextStrinArray(StrBuf *Target, WCTemplputParams *TP)
2166 {
2167         HashList *Arr = (HashList*) CTX;
2168         void *pV;
2169         int val;
2170         const char *CompareToken;
2171         long len;
2172
2173         GetTemplateTokenString(Target, TP, 2, &CompareToken, &len);
2174         val = GetTemplateTokenNumber(Target, TP, 0, 0);
2175         if (GetHash(Arr, IKEY(val), &pV) && 
2176             (pV != NULL)) {
2177                 return strcmp(ChrPtr((StrBuf*)pV), CompareToken) == 0;
2178         }
2179         else
2180                 return 0;
2181 }
2182
2183 /*-----------------------------------------------------------------------------
2184  *                      Boxed-API
2185  */
2186
2187 void tmpl_do_boxed(StrBuf *Target, WCTemplputParams *TP)
2188 {
2189         WCTemplputParams SubTP;
2190
2191         StrBuf *Headline = NULL;
2192         if (TP->Tokens->nParameters == 2) {
2193                 if (TP->Tokens->Params[1]->Type == TYPE_STR) {
2194                         Headline = NewStrBuf();
2195                         DoTemplate(TKEY(1), Headline, TP);
2196                 }
2197                 else {
2198                         const char *Ch;
2199                         long len;
2200                         GetTemplateTokenString(Target, 
2201                                                TP, 
2202                                                1,
2203                                                &Ch,
2204                                                &len);
2205                         Headline = NewStrBufPlain(Ch, len);
2206                 }
2207         }
2208         /* else TODO error? logging? */
2209         memcpy (&SubTP, TP, sizeof(WCTemplputParams));
2210         SubTP.Context = Headline;
2211         SubTP.Filter.ContextType = CTX_STRBUF;
2212         DoTemplate(HKEY("box_begin"), Target, &SubTP);
2213         DoTemplate(TKEY(0), Target, TP);
2214         DoTemplate(HKEY("box_end"), Target, TP);
2215         FreeStrBuf(&Headline);
2216 }
2217
2218 /*-----------------------------------------------------------------------------
2219  *                      Tabbed-API
2220  */
2221
2222 typedef struct _tab_struct {
2223         long CurrentTab;
2224         StrBuf *TabTitle;
2225 } tab_struct;
2226
2227 int preeval_do_tabbed(WCTemplateToken *Token)
2228 {
2229         WCTemplputParams TPP;
2230         WCTemplputParams *TP;
2231         const char *Ch;
2232         long len;
2233         int i, nTabs;
2234
2235
2236         memset(&TPP, 0, sizeof(WCTemplputParams));
2237         TP = &TPP;
2238         TP->Tokens = Token;
2239         nTabs = TP->Tokens->nParameters / 2 - 1;
2240         if (TP->Tokens->nParameters % 2 != 0)
2241         {
2242                 LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2243                                  "need even number of arguments");
2244                 return 0;
2245
2246         }
2247         else for (i = 0; i < nTabs; i++) {
2248                 if (!HaveTemplateTokenString(NULL, 
2249                                              TP, 
2250                                              i * 2,
2251                                              &Ch,
2252                                              &len) || 
2253                     (TP->Tokens->Params[i * 2]->len == 0))
2254                 {
2255                         LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2256                                          "Tab-Subject %d needs to be able to produce a string, have %s", 
2257                                          i, TP->Tokens->Params[i * 2]->Start);
2258                         return 0;
2259                 }
2260                 if (!HaveTemplateTokenString(NULL, 
2261                                              TP, 
2262                                              i * 2 + 1,
2263                                              &Ch,
2264                                              &len) || 
2265                     (TP->Tokens->Params[i * 2 + 1]->len == 0))
2266                 {
2267                         LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2268                                          "Tab-Content %d needs to be able to produce a string, have %s", 
2269                                          i, TP->Tokens->Params[i * 2 + 1]->Start);
2270                         return 0;
2271                 }
2272         }
2273
2274         if (!HaveTemplateTokenString(NULL, 
2275                                      TP, 
2276                                      i * 2 + 1,
2277                                      &Ch,
2278                                      &len) || 
2279             (TP->Tokens->Params[i * 2 + 1]->len == 0))
2280         {
2281                 LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2282                                  "Tab-Content %d needs to be able to produce a string, have %s", 
2283                                  i, TP->Tokens->Params[i * 2 + 1]->Start);
2284                 return 0;
2285         }
2286         return 1;
2287 }
2288
2289
2290 void tmpl_do_tabbed(StrBuf *Target, WCTemplputParams *TP)
2291 {
2292         StrBuf **TabNames;
2293         int i, ntabs, nTabs;
2294         tab_struct TS;
2295         WCTemplputParams SubTP;
2296
2297         memset(&TS, 0, sizeof(tab_struct));
2298         memcpy (&SubTP, &TP, sizeof(WCTemplputParams));
2299
2300         nTabs = ntabs = TP->Tokens->nParameters / 2;
2301         TabNames = (StrBuf **) malloc(ntabs * sizeof(StrBuf*));
2302         memset(TabNames, 0, ntabs * sizeof(StrBuf*));
2303
2304         for (i = 0; i < ntabs; i++) {
2305                 if ((TP->Tokens->Params[i * 2]->Type == TYPE_STR) &&
2306                     (TP->Tokens->Params[i * 2]->len > 0)) {
2307                         TabNames[i] = NewStrBuf();
2308                         DoTemplate(TKEY(i * 2), TabNames[i], TP);
2309                 }
2310                 else if (TP->Tokens->Params[i * 2]->Type == TYPE_GETTEXT) {
2311                         const char *Ch;
2312                         long len;
2313                         GetTemplateTokenString(Target, 
2314                                                TP, 
2315                                                i * 2,
2316                                                &Ch,
2317                                                &len);
2318                         TabNames[i] = NewStrBufPlain(Ch, -1);
2319                 }
2320                 else { 
2321                         /** A Tab without subject? we can't count that, add it as silent */
2322                         nTabs --;
2323                 }
2324         }
2325         memcpy (&SubTP, TP, sizeof(WCTemplputParams));
2326         SubTP.Filter.ControlContextType = CTX_TAB;
2327         SubTP.ControlContext = &TS;
2328
2329         StrTabbedDialog(Target, nTabs, TabNames);
2330         for (i = 0; i < ntabs; i++) {
2331                 memset(&TS, 0, sizeof(tab_struct));
2332                 TS.CurrentTab = i;
2333                 TS.TabTitle = TabNames[i];
2334                 StrBeginTab(Target, i, nTabs, TabNames);
2335                 DoTemplate(TKEY(i * 2 + 1), Target, &SubTP);
2336                 StrEndTab(Target, i, nTabs);
2337         }
2338         for (i = 0; i < ntabs; i++) 
2339                 FreeStrBuf(&TabNames[i]);
2340 }
2341
2342 void tmplput_TAB_N(StrBuf *Target, WCTemplputParams *TP)
2343 {
2344         tab_struct *Ctx = CCTX;
2345
2346         StrBufAppendPrintf(Target, "%d", Ctx->CurrentTab);
2347 }
2348
2349 void tmplput_TAB_TITLE(StrBuf *Target, WCTemplputParams *TP)
2350 {
2351         tab_struct *Ctx = CCTX;
2352         StrBufAppendTemplate(Target, TP, Ctx->TabTitle, 0);
2353 }
2354
2355 /*-----------------------------------------------------------------------------
2356  *                      Sorting-API
2357  */
2358
2359
2360 void RegisterSortFunc(const char *name, long len, 
2361                       const char *prepend, long preplen,
2362                       CompareFunc Forward, 
2363                       CompareFunc Reverse, 
2364                       CompareFunc GroupChange, 
2365                       long ContextType)
2366 {
2367         SortStruct *NewSort;
2368
2369         NewSort = (SortStruct*) malloc(sizeof(SortStruct));
2370         memset(NewSort, 0, sizeof(SortStruct));
2371         NewSort->Name = NewStrBufPlain(name, len);
2372         if (prepend != NULL)
2373                 NewSort->PrefPrepend = NewStrBufPlain(prepend, preplen);
2374         else
2375                 NewSort->PrefPrepend = NULL;
2376         NewSort->Forward = Forward;
2377         NewSort->Reverse = Reverse;
2378         NewSort->GroupChange = GroupChange;
2379         NewSort->ContextType = ContextType;
2380         if (ContextType == CTX_NONE) {
2381                 syslog(1, "sorting requires a context. CTX_NONE won't make it.\n");
2382                 exit(1);
2383         }
2384                 
2385         Put(SortHash, name, len, NewSort, DestroySortStruct);
2386 }
2387
2388 CompareFunc RetrieveSort(WCTemplputParams *TP, 
2389                          const char *OtherPrefix, long OtherPrefixLen,
2390                          const char *Default, long ldefault, long DefaultDirection)
2391 {
2392         const StrBuf *BSort = NULL;
2393         SortStruct *SortBy;
2394         void *vSortBy;
2395         long SortOrder = -1;
2396         
2397         if (havebstr("SortBy")) {
2398                 BSort = sbstr("SortBy");
2399                 if (OtherPrefix == NULL) {
2400                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2401                 }
2402                 else {
2403                         set_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen, NewStrBufDup(BSort), 0);
2404                 }
2405         }
2406         else { /** Try to fallback to our remembered values... */
2407                 if (OtherPrefix == NULL) {
2408                         BSort = get_room_pref("sort");
2409                 }
2410                 else {
2411                         BSort = get_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen);
2412                 }
2413                 if (BSort != NULL)
2414                         putbstr("SortBy", NewStrBufDup(BSort));
2415                 else {
2416                         StrBuf *Buf;
2417
2418                         BSort = Buf = NewStrBufPlain(Default, ldefault);
2419                         putbstr("SortBy", Buf);
2420                 }
2421         }
2422
2423         if (!GetHash(SortHash, SKEY(BSort), &vSortBy) || 
2424             (vSortBy == NULL)) {
2425                 if (!GetHash(SortHash, Default, ldefault, &vSortBy) || 
2426                     (vSortBy == NULL)) {
2427                         LogTemplateError(
2428                                 NULL, "Sorting", ERR_PARM1, TP,
2429                                 "Illegal default sort: [%s]", Default);
2430                         wc_backtrace();
2431                 }
2432         }
2433         SortBy = (SortStruct*)vSortBy;
2434
2435         if (SortBy->ContextType != TP->Filter.ContextType)
2436                 return NULL;
2437
2438         /** Ok, its us, lets see in which direction we should sort... */
2439         if (havebstr("SortOrder")) {
2440                 SortOrder = LBSTR("SortOrder");
2441         }
2442         else { /** Try to fallback to our remembered values... */
2443                 StrBuf *Buf = NULL;
2444                 if (SortBy->PrefPrepend == NULL) {
2445                         Buf = get_room_pref("SortOrder");
2446                         SortOrder = StrTol(Buf);
2447                 }
2448                 else {
2449                         BSort = get_X_PREFS(HKEY("SortOrder"), OtherPrefix, OtherPrefixLen);
2450                 }
2451
2452                 if (Buf == NULL)
2453                         SortOrder = DefaultDirection;
2454
2455                 Buf = NewStrBufPlain(NULL, 64);
2456                 StrBufPrintf(Buf, "%ld", SortOrder);
2457                 putbstr("SortOrder", Buf);
2458         }
2459         switch (SortOrder) {
2460         default:
2461         case 0:
2462                 return NULL;
2463         case 1:
2464                 return SortBy->Forward;
2465         case 2:
2466                 return SortBy->Reverse;
2467         }
2468 }
2469
2470
2471 enum {
2472         eNO_SUCH_SORT, 
2473         eNOT_SPECIFIED,
2474         eINVALID_PARAM,
2475         eFOUND
2476 };
2477
2478 ConstStr SortIcons[] = {
2479         {HKEY("static/webcit_icons/sort_none.gif")},
2480         {HKEY("static/webcit_icons/up_pointer.gif")},
2481         {HKEY("static/webcit_icons/down_pointer.gif")},
2482 };
2483
2484 ConstStr SortNextOrder[] = {
2485         {HKEY("1")},
2486         {HKEY("2")},
2487         {HKEY("0")},
2488 };
2489
2490
2491 int GetSortMetric(WCTemplputParams *TP, SortStruct **Next, SortStruct **Param, long *SortOrder, int N)
2492 {
2493         int bSortError = eNOT_SPECIFIED;
2494         const StrBuf *BSort;
2495         void *vSort;
2496         
2497         *SortOrder = 0;
2498         *Next = NULL;
2499         if (!GetHash(SortHash, TKEY(0), &vSort) || 
2500             (vSort == NULL))
2501                 return eNO_SUCH_SORT;
2502         *Param = (SortStruct*) vSort;
2503         
2504
2505         if (havebstr("SortBy")) {
2506                 BSort = sbstr("SortBy");
2507                 bSortError = eINVALID_PARAM;
2508                 if ((*Param)->PrefPrepend == NULL) {
2509                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2510                 }
2511                 else {
2512                         set_X_PREFS(HKEY("sort"), TKEY(N), NewStrBufDup(BSort), 0);
2513                 }
2514         }
2515         else { /** Try to fallback to our remembered values... */
2516                 if ((*Param)->PrefPrepend == NULL) {
2517                         BSort = get_room_pref("sort");
2518                 }
2519                 else {
2520                         BSort = get_X_PREFS(HKEY("sort"), TKEY(N));
2521                 }
2522         }
2523
2524         if (!GetHash(SortHash, SKEY(BSort), &vSort) || 
2525             (vSort == NULL))
2526                 return bSortError;
2527
2528         *Next = (SortStruct*) vSort;
2529
2530         /** Ok, its us, lets see in which direction we should sort... */
2531         if (havebstr("SortOrder")) {
2532                 *SortOrder = LBSTR("SortOrder");
2533         }
2534         else { /** Try to fallback to our remembered values... */
2535                 if ((*Param)->PrefPrepend == NULL) {
2536                         *SortOrder = StrTol(get_room_pref("SortOrder"));
2537                 }
2538                 else {
2539                         *SortOrder = StrTol(get_X_PREFS(HKEY("SortOrder"), TKEY(N)));
2540                 }
2541         }
2542         if (*SortOrder > 2)
2543                 *SortOrder = 0;
2544
2545         return eFOUND;
2546 }
2547
2548
2549 void tmplput_SORT_ICON(StrBuf *Target, WCTemplputParams *TP)
2550 {
2551         long SortOrder;
2552         SortStruct *Next;
2553         SortStruct *Param;
2554         const ConstStr *SortIcon;
2555
2556         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2557         case eNO_SUCH_SORT:
2558                 LogTemplateError(
2559                         Target, "Sorter", ERR_PARM1, TP,
2560                         " Sorter [%s] unknown!", 
2561                         TP->Tokens->Params[0]->Start);
2562                 break;          
2563         case eINVALID_PARAM:
2564                 LogTemplateError(NULL, "Sorter", ERR_PARM1, TP,
2565                                  " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
2566                                  bstr("SortBy"));
2567         case eNOT_SPECIFIED:
2568         case eFOUND:
2569                 if (Next == Param) {
2570                         SortIcon = &SortIcons[SortOrder];
2571                 }
2572                 else { /** Not Us... */
2573                         SortIcon = &SortIcons[0];
2574                 }
2575                 StrBufAppendBufPlain(Target, SortIcon->Key, SortIcon->len, 0);
2576         }
2577 }
2578
2579 void tmplput_SORT_NEXT(StrBuf *Target, WCTemplputParams *TP)
2580 {
2581         long SortOrder;
2582         SortStruct *Next;
2583         SortStruct *Param;
2584
2585         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2586         case eNO_SUCH_SORT:
2587                 LogTemplateError(
2588                         Target, "Sorter", ERR_PARM1, TP,                                  
2589                         " Sorter [%s] unknown!", 
2590                         TP->Tokens->Params[0]->Start);
2591                 break;          
2592         case eINVALID_PARAM:
2593                 LogTemplateError(
2594                         NULL, "Sorter", ERR_PARM1, TP,
2595                         " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
2596                         bstr("SortBy"));
2597         case eNOT_SPECIFIED:
2598         case eFOUND:
2599                 StrBufAppendBuf(Target, Param->Name, 0);
2600                 
2601         }
2602 }
2603
2604 void tmplput_SORT_ORDER(StrBuf *Target, WCTemplputParams *TP)
2605 {
2606         long SortOrder;
2607         const ConstStr *SortOrderStr;
2608         SortStruct *Next;
2609         SortStruct *Param;
2610
2611         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2612         case eNO_SUCH_SORT:
2613                 LogTemplateError(
2614                         Target, "Sorter", ERR_PARM1, TP,
2615                         " Sorter [%s] unknown!",
2616                         TP->Tokens->Params[0]->Start);
2617                 break;          
2618         case eINVALID_PARAM:
2619                 LogTemplateError(
2620                         NULL, "Sorter", ERR_PARM1, TP,
2621                         " Sorter specified by BSTR 'SortBy' [%s] unknown!",
2622                         bstr("SortBy"));
2623         case eNOT_SPECIFIED:
2624         case eFOUND:
2625                 if (Next == Param) {
2626                         SortOrderStr = &SortNextOrder[SortOrder];
2627                 }
2628                 else { /** Not Us... */
2629                         SortOrderStr = &SortNextOrder[0];
2630                 }
2631                 StrBufAppendBufPlain(Target, SortOrderStr->Key, SortOrderStr->len, 0);
2632         }
2633 }
2634
2635
2636 void tmplput_long_vector(StrBuf *Target, WCTemplputParams *TP)
2637 {
2638         long *LongVector = (long*) CTX;
2639
2640         if ((TP->Tokens->Params[0]->Type == TYPE_LONG) && 
2641             (TP->Tokens->Params[0]->lvalue <= LongVector[0]))
2642         {
2643                 StrBufAppendPrintf(Target, "%ld", LongVector[TP->Tokens->Params[0]->lvalue]);
2644         }
2645         else
2646         {
2647                 if (TP->Tokens->Params[0]->Type != TYPE_LONG) {
2648                         LogTemplateError(
2649                                 Target, "Longvector", ERR_NAME, TP,
2650                                 "needs a numerical Parameter!");
2651                 }
2652                 else {
2653                         LogTemplateError(
2654                                 Target, "LongVector", ERR_PARM1, TP,
2655                                 "doesn't have %ld Parameters, its just the size of %ld!", 
2656                                 TP->Tokens->Params[0]->lvalue,
2657                                 LongVector[0]);
2658                 }
2659         }
2660 }
2661
2662 void dbg_print_longvector(long *LongVector)
2663 {
2664         StrBuf *Buf = NewStrBufPlain(HKEY("Longvector: ["));
2665         int nItems = LongVector[0];
2666         int i;
2667
2668         for (i = 0; i < nItems; i++) {
2669                 if (i + 1 < nItems)
2670                         StrBufAppendPrintf(Buf, "%d: %ld | ", i, LongVector[i]);
2671                 else
2672                         StrBufAppendPrintf(Buf, "%d: %ld]\n", i, LongVector[i]);
2673
2674         }
2675         syslog(1, "%s", ChrPtr(Buf));
2676         FreeStrBuf(&Buf);
2677 }
2678
2679 int ConditionalLongVector(StrBuf *Target, WCTemplputParams *TP)
2680 {
2681         long *LongVector = (long*) CTX;
2682
2683         if ((TP->Tokens->Params[2]->Type == TYPE_LONG) && 
2684             (TP->Tokens->Params[2]->lvalue <= LongVector[0])&&
2685             (TP->Tokens->Params[3]->Type == TYPE_LONG) && 
2686             (TP->Tokens->Params[3]->lvalue <= LongVector[0]))
2687         {
2688                 return LongVector[TP->Tokens->Params[2]->lvalue] == 
2689                         LongVector[TP->Tokens->Params[3]->lvalue];
2690         }
2691         else
2692         {
2693                 if ((TP->Tokens->Params[2]->Type == TYPE_LONG) ||
2694                     (TP->Tokens->Params[2]->Type == TYPE_LONG)) {
2695                         LogTemplateError(
2696                                 Target, "ConditionalLongvector", ERR_PARM1, TP,
2697                                 "needs two long Parameter!");
2698                 }
2699                 else {
2700                         LogTemplateError(
2701                                 Target, "Longvector", ERR_PARM1, TP,
2702                                 "doesn't have %ld / %ld Parameters, its just the size of %ld!",
2703                                 TP->Tokens->Params[2]->lvalue,
2704                                 TP->Tokens->Params[3]->lvalue,
2705                                 LongVector[0]);
2706                 }
2707         }
2708         return 0;
2709 }
2710
2711
2712 void tmplput_CURRENT_FILE(StrBuf *Target, WCTemplputParams *TP)
2713 {
2714         StrBufAppendTemplate(Target, TP, TP->Tokens->FileName, 0);
2715 }
2716
2717 void 
2718 InitModule_SUBST
2719 (void)
2720 {
2721         memset(&NoCtx, 0, sizeof(WCTemplputParams));
2722         RegisterNamespace("--", 0, 2, tmplput_Comment, NULL, CTX_NONE);
2723         RegisterNamespace("SORT:ICON", 1, 2, tmplput_SORT_ICON, NULL, CTX_NONE);
2724         RegisterNamespace("SORT:ORDER", 1, 2, tmplput_SORT_ORDER, NULL, CTX_NONE);
2725         RegisterNamespace("SORT:NEXT", 1, 2, tmplput_SORT_NEXT, NULL, CTX_NONE);
2726         RegisterNamespace("CONTEXTSTR", 0, 1, tmplput_ContextString, NULL, CTX_STRBUF);
2727         RegisterNamespace("CONTEXTSTRARR", 1, 2, tmplput_ContextStringArray, NULL, CTX_STRBUFARR);
2728         RegisterNamespace("ITERATE", 2, 100, tmpl_iterate_subtmpl, preeval_iterate, CTX_NONE);
2729         RegisterNamespace("DOBOXED", 1, 2, tmpl_do_boxed, NULL, CTX_NONE);
2730         RegisterNamespace("DOTABBED", 2, 100, tmpl_do_tabbed, preeval_do_tabbed, CTX_NONE);
2731         RegisterControlNS(HKEY("TAB:N"), 0, 0, tmplput_TAB_N, CTX_TAB);
2732         RegisterControlNS(HKEY("TAB:SUBJECT"), 0, 1, tmplput_TAB_TITLE, CTX_TAB);
2733
2734
2735         RegisterNamespace("LONGVECTOR", 1, 1, tmplput_long_vector, NULL, CTX_LONGVECTOR);
2736
2737
2738         RegisterConditional(HKEY("COND:CONTEXTSTR"), 3, ConditionalContextStr, CTX_STRBUF);
2739         RegisterConditional(HKEY("COND:CONTEXTSTRARR"), 4, ConditionalContextStrinArray, CTX_STRBUFARR);
2740         RegisterConditional(HKEY("COND:LONGVECTOR"), 4, ConditionalLongVector, CTX_LONGVECTOR);
2741
2742
2743         RegisterControlConditional(HKEY("COND:ITERATE:ISGROUPCHANGE"), 2, 
2744                                    conditional_ITERATE_ISGROUPCHANGE, 
2745                                    CTX_ITERATE);
2746         RegisterControlConditional(HKEY("COND:ITERATE:LASTN"), 2, 
2747                                    conditional_ITERATE_LASTN, 
2748                                    CTX_ITERATE);
2749         RegisterControlConditional(HKEY("COND:ITERATE:FIRSTN"), 2, 
2750                                    conditional_ITERATE_FIRSTN, 
2751                                    CTX_ITERATE);
2752
2753         RegisterControlNS(HKEY("ITERATE:ODDEVEN"), 0, 0, tmplput_ITERATE_ODDEVEN, CTX_ITERATE);
2754         RegisterControlNS(HKEY("ITERATE:KEY"), 0, 0, tmplput_ITERATE_KEY, CTX_ITERATE);
2755         RegisterControlNS(HKEY("ITERATE:N"), 0, 0, tmplput_ITERATE_LASTN, CTX_ITERATE);
2756         RegisterNamespace("CURRENTFILE", 0, 1, tmplput_CURRENT_FILE, NULL, CTX_NONE);
2757         RegisterNamespace("DEF:STR", 1, 1, tmplput_DefStr, NULL, CTX_NONE);
2758         RegisterNamespace("DEF:VAL", 1, 1, tmplput_DefVal, NULL, CTX_NONE);
2759
2760
2761
2762
2763 }
2764
2765 void
2766 ServerStartModule_SUBST
2767 (void)
2768 {
2769         LocalTemplateCache = NewHash(1, NULL);
2770         TemplateCache = NewHash(1, NULL);
2771
2772         GlobalNS = NewHash(1, NULL);
2773         Iterators = NewHash(1, NULL);
2774         Conditionals = NewHash(1, NULL);
2775         SortHash = NewHash(1, NULL);
2776         Defines = NewHash(1, NULL);
2777 }
2778
2779 void
2780 FinalizeModule_SUBST
2781 (void)
2782 {
2783
2784 }
2785
2786 void 
2787 ServerShutdownModule_SUBST
2788 (void)
2789 {
2790         DeleteHash(&TemplateCache);
2791         DeleteHash(&LocalTemplateCache);
2792
2793         DeleteHash(&GlobalNS);
2794         DeleteHash(&Iterators);
2795         DeleteHash(&Conditionals);
2796         DeleteHash(&SortHash);
2797         DeleteHash(&Defines);
2798 }
2799
2800
2801 void
2802 SessionNewModule_SUBST
2803 (wcsession *sess)
2804 {
2805
2806 }
2807
2808 void
2809 SessionAttachModule_SUBST
2810 (wcsession *sess)
2811 {
2812 }
2813
2814 void
2815 SessionDetachModule_SUBST
2816 (wcsession *sess)
2817 {
2818         FreeStrBuf(&sess->WFBuf);
2819 }
2820
2821 void 
2822 SessionDestroyModule_SUBST  
2823 (wcsession *sess)
2824 {
2825
2826 }