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