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