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