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