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