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