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