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