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