Got rid of all of the .m.html templates and is_mobile cruft
[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                 PutRequestLocalMem(Buf, HFreeStrBuf);
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         Static = TemplateCache;
1685         StaticLocal = LocalTemplateCache;
1686
1687         if (len == 0)
1688         {
1689                 lprintf (1, "Can't to load a template with empty name!\n");
1690                 StrBufAppendPrintf(Target, "<pre>\nCan't to load a template with empty name!\n</pre>");
1691                 return NULL;
1692         }
1693
1694         if (!GetHash(StaticLocal, templatename, len, &vTmpl) &&
1695             !GetHash(Static, templatename, len, &vTmpl)) {
1696                 lprintf (1, "didn't find Template [%s] %ld %ld\n", templatename, len , (long)strlen(templatename));
1697                 StrBufAppendPrintf(Target, "<pre>\ndidn't find Template [%s] %ld %ld\n</pre>", 
1698                                    templatename, len, 
1699                                    (long)strlen(templatename));
1700 #if 0
1701                 dbg_PrintHash(Static, PrintTemplate, NULL);
1702                 PrintHash(Static, VarPrintTransition, PrintTemplate);
1703 #endif
1704                 return NULL;
1705         }
1706         if (vTmpl == NULL) 
1707                 return NULL;
1708         return ProcessTemplate(vTmpl, Target, TP);
1709
1710 }
1711
1712
1713 void tmplput_Comment(StrBuf *Target, WCTemplputParams *TP)
1714 {
1715         if (LoadTemplates != 0)
1716         {
1717                 StrBuf *Comment;
1718                 const char *pch;
1719                 long len;
1720
1721                 GetTemplateTokenString(Target, TP, 0, &pch, &len);
1722                 Comment = NewStrBufPlain(pch, len);
1723                 StrBufAppendBufPlain(Target, HKEY("<!--"), 0);
1724                 StrBufAppendTemplate(Target, TP, Comment, 1);
1725                 StrBufAppendBufPlain(Target, HKEY("-->"), 0);
1726                 FreeStrBuf(&Comment);
1727         }
1728 }
1729
1730 /*-----------------------------------------------------------------------------
1731  *                      Iterators
1732  */
1733 typedef struct _HashIterator {
1734         HashList *StaticList;
1735         int AdditionalParams;
1736         int ContextType;
1737         int XPectContextType;
1738         int Flags;
1739         RetrieveHashlistFunc GetHash;
1740         HashDestructorFunc Destructor;
1741         SubTemplFunc DoSubTemplate;
1742 } HashIterator;
1743
1744 void RegisterITERATOR(const char *Name, long len, 
1745                       int AdditionalParams, 
1746                       HashList *StaticList, 
1747                       RetrieveHashlistFunc GetHash, 
1748                       SubTemplFunc DoSubTempl,
1749                       HashDestructorFunc Destructor,
1750                       int ContextType, 
1751                       int XPectContextType, 
1752                       int Flags)
1753 {
1754         HashIterator *It;
1755
1756         It = (HashIterator*)malloc(sizeof(HashIterator));
1757         memset(It, 0, sizeof(HashIterator));
1758         It->StaticList = StaticList;
1759         It->AdditionalParams = AdditionalParams;
1760         It->GetHash = GetHash;
1761         It->DoSubTemplate = DoSubTempl;
1762         It->Destructor = Destructor;
1763         It->ContextType = ContextType;
1764         It->XPectContextType = XPectContextType;
1765         It->Flags = Flags;
1766         Put(Iterators, Name, len, It, NULL);
1767 }
1768
1769 typedef struct _iteratestruct {
1770         int GroupChange;
1771         int oddeven;
1772         const char *Key;
1773         long KeyLen;
1774         int n;
1775         int LastN;
1776         }IterateStruct; 
1777
1778 int preeval_iterate(WCTemplateToken *Token)
1779 {
1780         WCTemplputParams TPP;
1781         WCTemplputParams *TP;
1782         void *vTmpl;
1783         void *vIt;
1784         HashIterator *It;
1785
1786         memset(&TPP, 0, sizeof(WCTemplputParams));
1787         TP = &TPP;
1788         TP->Tokens = Token;
1789         if (!GetHash(Iterators, TKEY(0), &vIt)) {
1790                 LogTemplateError(
1791                         NULL, "Iterator", ERR_PARM1, TP,
1792                         "not found");
1793                 return 0;
1794         }
1795         if (TP->Tokens->Params[1]->Type != TYPE_SUBTEMPLATE) {
1796                 LogTemplateError(NULL, "Iterator", ERR_PARM1, TP,
1797                                  "Need token with type Subtemplate as param 1, have %s", 
1798                                  TP->Tokens->Params[1]->Start);
1799         }
1800         
1801         /* well, we don't check the mobile stuff here... */
1802         if (!GetHash(LocalTemplateCache, TKEY(1), &vTmpl) &&
1803             !GetHash(TemplateCache, TKEY(1), &vTmpl)) {
1804                 LogTemplateError(NULL, "SubTemplate", ERR_PARM1, TP,
1805                                  "referenced here doesn't exist");
1806         }
1807         Token->Preeval2 = vIt;
1808         It = (HashIterator *) vIt;
1809
1810         if (TP->Tokens->nParameters < It->AdditionalParams + 2) {
1811                 LogTemplateError(                               
1812                         NULL, "Iterator", ERR_PARM1, TP,
1813                         "doesn't work with %d params", 
1814                         TP->Tokens->nParameters);
1815         }
1816
1817
1818         return 1;
1819 }
1820
1821 void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
1822 {
1823         HashIterator *It;
1824         HashList *List;
1825         HashPos  *it;
1826         SortStruct *SortBy = NULL;
1827         void *vSortBy;
1828         int DetectGroupChange = 0;
1829         int nMembersUsed;
1830         void *vContext;
1831         void *vLastContext = NULL;
1832         StrBuf *SubBuf;
1833         WCTemplputParams SubTP;
1834         IterateStruct Status;
1835
1836         long StartAt = 0;
1837         long StepWidth = 0;
1838         long StopAt = -1;
1839
1840         memset(&Status, 0, sizeof(IterateStruct));
1841         memcpy (&SubTP, &TP, sizeof(WCTemplputParams));
1842         
1843         It = (HashIterator*) TP->Tokens->Preeval2;
1844         if (It == NULL) {
1845                 LogTemplateError(
1846                         Target, "Iterator", ERR_PARM1, TP, "Unknown!");
1847                 return;
1848         }
1849
1850         if (TP->Tokens->nParameters < It->AdditionalParams + 2) {
1851                 LogTemplateError(                               
1852                         Target, "Iterator", ERR_PARM1, TP,
1853                         "doesn't work with %d params", 
1854                         TP->Tokens->nParameters - 1);
1855                 return;
1856         }
1857
1858         if ((It->XPectContextType != CTX_NONE) &&
1859             (It->XPectContextType != TP->Filter.ContextType)) {
1860                 LogTemplateError(
1861                         Target, "Iterator", ERR_PARM1, TP,
1862                         "requires context of type %s, have %s", 
1863                         ContextName(It->XPectContextType), 
1864                         ContextName(TP->Filter.ContextType));
1865                 return ;
1866                 
1867         }
1868
1869         if (It->StaticList == NULL)
1870                 List = It->GetHash(Target, TP);
1871         else
1872                 List = It->StaticList;
1873
1874         DetectGroupChange = (It->Flags & IT_FLAG_DETECT_GROUPCHANGE) != 0;
1875         if (DetectGroupChange) {
1876                 const StrBuf *BSort;
1877                 DetectGroupChange = 0;
1878                 if (havebstr("SortBy")) {
1879                         BSort = sbstr("SortBy");
1880                         if (GetHash(SortHash, SKEY(BSort), &vSortBy) &&
1881                             (vSortBy != NULL)) {
1882                                 SortBy = (SortStruct*)vSortBy;
1883                                 /* first check whether its intended for us... */
1884                                 if ((SortBy->ContextType == It->ContextType)&&
1885                                 /** Ok, its us, lets see in which direction we should sort... */
1886                                     (havebstr("SortOrder"))) {
1887                                         int SortOrder;
1888                                         SortOrder = LBSTR("SortOrder");
1889                                         if (SortOrder != 0)
1890                                                 DetectGroupChange = 1;
1891                                 }
1892                         }
1893                 }
1894         }
1895         nMembersUsed = GetCount(List);
1896         SubBuf = NewStrBuf();
1897         SubTP.Filter.ContextType = It->ContextType;
1898         SubTP.Filter.ControlContextType = CTX_ITERATE;
1899         SubTP.ControlContext = &Status;
1900         
1901         if (HAVE_PARAM(2)) {
1902                 StartAt = GetTemplateTokenNumber(Target, TP, 2, 0);
1903         }
1904         if (HAVE_PARAM(3)) {
1905                 StepWidth = GetTemplateTokenNumber(Target, TP, 3, 0);
1906         }
1907         if (HAVE_PARAM(4)) {
1908                 StopAt = GetTemplateTokenNumber(Target, TP, 4, -1);
1909         }
1910         it = GetNewHashPos(List, StepWidth);
1911         if (StopAt < 0) {
1912                 StopAt = GetCount(List);
1913         }
1914         while (GetNextHashPos(List, it, &Status.KeyLen, &Status.Key, &vContext)) {
1915                 if ((Status.n >= StartAt) && (Status.n <= StopAt)) {
1916                         if (DetectGroupChange && Status.n > 0) {
1917                                 Status.GroupChange = SortBy->GroupChange(vContext, vLastContext);
1918                         }
1919                         Status.LastN = (Status.n + 1) == nMembersUsed;
1920                         SubTP.Context = vContext;
1921                         if (It->DoSubTemplate != NULL)
1922                                 It->DoSubTemplate(SubBuf, &SubTP);
1923                         DoTemplate(TKEY(1), SubBuf, &SubTP);
1924                         
1925                         StrBufAppendBuf(Target, SubBuf, 0);
1926                         FlushStrBuf(SubBuf);
1927                         Status.oddeven = ! Status.oddeven;
1928                         vLastContext = vContext;
1929                 }
1930                 Status.n++;
1931         }
1932         FreeStrBuf(&SubBuf);
1933         DeleteHashPos(&it);
1934         if (It->Destructor != NULL)
1935                 It->Destructor(&List);
1936 }
1937
1938
1939 int conditional_ITERATE_ISGROUPCHANGE(StrBuf *Target, WCTemplputParams *TP)
1940 {
1941         IterateStruct *Ctx = CCTX;
1942         if (TP->Tokens->nParameters < 3)
1943                 return  Ctx->GroupChange;
1944
1945         return TP->Tokens->Params[2]->lvalue == Ctx->GroupChange;
1946 }
1947
1948 void tmplput_ITERATE_ODDEVEN(StrBuf *Target, WCTemplputParams *TP)
1949 {
1950         IterateStruct *Ctx = CCTX;
1951         if (Ctx->oddeven)
1952                 StrBufAppendBufPlain(Target, HKEY("odd"), 0);
1953         else
1954                 StrBufAppendBufPlain(Target, HKEY("even"), 0);
1955 }
1956
1957
1958 void tmplput_ITERATE_KEY(StrBuf *Target, WCTemplputParams *TP)
1959 {
1960         IterateStruct *Ctx = CCTX;
1961
1962         StrBufAppendBufPlain(Target, Ctx->Key, Ctx->KeyLen, 0);
1963 }
1964
1965
1966 void tmplput_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
1967 {
1968         IterateStruct *Ctx = CCTX;
1969         StrBufAppendPrintf(Target, "%d", Ctx->n);
1970 }
1971
1972 int conditional_ITERATE_FIRSTN(StrBuf *Target, WCTemplputParams *TP)
1973 {
1974         IterateStruct *Ctx = CCTX;
1975         return Ctx->n == 0;
1976 }
1977
1978 int conditional_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
1979 {
1980         IterateStruct *Ctx = CCTX;
1981         return Ctx->LastN;
1982 }
1983
1984
1985
1986 /*-----------------------------------------------------------------------------
1987  *                      Conditionals
1988  */
1989 int EvaluateConditional(StrBuf *Target, int Neg, int state, WCTemplputParams *TP)
1990 {
1991         ConditionalStruct *Cond;
1992
1993         if ((TP->Tokens->Params[0]->len == 1) &&
1994             (TP->Tokens->Params[0]->Start[0] == 'X'))
1995                 return (state != 0)?TP->Tokens->Params[1]->lvalue:0;
1996             
1997         Cond = (ConditionalStruct *) TP->Tokens->PreEval;
1998         if (Cond == NULL) {
1999                 LogTemplateError(
2000                         Target, "Conditional", ERR_PARM1, TP,
2001                         "unknown!");
2002                 return 1;
2003         }
2004
2005         if (!CheckContext(Target, &Cond->Filter, TP, "Conditional")) {
2006                 return 0;
2007         }
2008
2009         if (Cond->CondF(Target, TP) == Neg)
2010                 return TP->Tokens->Params[1]->lvalue;
2011         return 0;
2012 }
2013
2014 void RegisterConditional(const char *Name, long len, 
2015                          int nParams,
2016                          WCConditionalFunc CondF, 
2017                          int ContextRequired)
2018 {
2019         ConditionalStruct *Cond;
2020
2021         Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2022         memset(Cond, 0, sizeof(ConditionalStruct));
2023         Cond->PlainName = Name;
2024         Cond->Filter.nMaxArgs = nParams;
2025         Cond->Filter.nMinArgs = nParams;
2026         Cond->CondF = CondF;
2027         Cond->Filter.ContextType = ContextRequired;
2028         Cond->Filter.ControlContextType = CTX_NONE;
2029         Put(Conditionals, Name, len, Cond, NULL);
2030 }
2031
2032 void RegisterControlConditional(const char *Name, long len, 
2033                                 int nParams,
2034                                 WCConditionalFunc CondF, 
2035                                 int ControlContextRequired)
2036 {
2037         ConditionalStruct *Cond;
2038
2039         Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
2040         memset(Cond, 0, sizeof(ConditionalStruct));
2041         Cond->PlainName = Name;
2042         Cond->Filter.nMaxArgs = nParams;
2043         Cond->Filter.nMinArgs = nParams;
2044         Cond->CondF = CondF;
2045         Cond->Filter.ContextType = CTX_NONE;
2046         Cond->Filter.ControlContextType = ControlContextRequired;
2047         Put(Conditionals, Name, len, Cond, NULL);
2048 }
2049
2050 void RegisterTokenParamDefine(const char *Name, long len, 
2051                               long Value)
2052 {
2053         long *PVal;
2054
2055         PVal = (long*)malloc(sizeof(long));
2056         *PVal = Value;
2057         Put(Defines, Name, len, PVal, NULL);
2058 }
2059
2060 long GetTokenDefine(const char *Name, long len, 
2061                     long DefValue)
2062 {
2063         void *vPVal;
2064
2065         if (GetHash(Defines, Name, len, &vPVal) &&
2066              (vPVal != NULL))
2067          {
2068                  return *(long*) vPVal;
2069          }
2070          else
2071          {
2072                  return DefValue;
2073          }
2074 }
2075
2076 void tmplput_DefStr(StrBuf *Target, WCTemplputParams *TP)
2077 {
2078         const char *Str;
2079         long len;
2080         GetTemplateTokenString(Target, TP, 2, &Str, &len);
2081         
2082         StrBufAppendBufPlain(Target, Str, len, 0);
2083 }
2084
2085 void tmplput_DefVal(StrBuf *Target, WCTemplputParams *TP)
2086 {
2087         int val;
2088
2089         val = GetTemplateTokenNumber(Target, TP, 0, 0);
2090         StrBufAppendPrintf(Target, "%d", val);
2091 }
2092
2093 HashList *Defines;
2094
2095 /*-----------------------------------------------------------------------------
2096  *                      Context Strings
2097  */
2098 void tmplput_ContextString(StrBuf *Target, WCTemplputParams *TP)
2099 {
2100         StrBufAppendTemplate(Target, TP, (StrBuf*)CTX, 0);
2101 }
2102 int ConditionalContextStr(StrBuf *Target, WCTemplputParams *TP)
2103 {
2104         StrBuf *TokenText = (StrBuf*) CTX;
2105         const char *CompareToken;
2106         long len;
2107
2108         GetTemplateTokenString(Target, TP, 2, &CompareToken, &len);
2109         return strcmp(ChrPtr(TokenText), CompareToken) == 0;
2110 }
2111
2112 void tmplput_ContextStringArray(StrBuf *Target, WCTemplputParams *TP)
2113 {
2114         HashList *Arr = (HashList*) CTX;
2115         void *pV;
2116         int val;
2117
2118         val = GetTemplateTokenNumber(Target, TP, 0, 0);
2119         if (GetHash(Arr, IKEY(val), &pV) && 
2120             (pV != NULL)) {
2121                 StrBufAppendTemplate(Target, TP, (StrBuf*)pV, 1);
2122         }
2123 }
2124 int ConditionalContextStrinArray(StrBuf *Target, WCTemplputParams *TP)
2125 {
2126         HashList *Arr = (HashList*) CTX;
2127         void *pV;
2128         int val;
2129         const char *CompareToken;
2130         long len;
2131
2132         GetTemplateTokenString(Target, TP, 2, &CompareToken, &len);
2133         val = GetTemplateTokenNumber(Target, TP, 0, 0);
2134         if (GetHash(Arr, IKEY(val), &pV) && 
2135             (pV != NULL)) {
2136                 return strcmp(ChrPtr((StrBuf*)pV), CompareToken) == 0;
2137         }
2138         else
2139                 return 0;
2140 }
2141
2142 /*-----------------------------------------------------------------------------
2143  *                      Boxed-API
2144  */
2145
2146 void tmpl_do_boxed(StrBuf *Target, WCTemplputParams *TP)
2147 {
2148         WCTemplputParams SubTP;
2149
2150         StrBuf *Headline = NULL;
2151         if (TP->Tokens->nParameters == 2) {
2152                 if (TP->Tokens->Params[1]->Type == TYPE_STR) {
2153                         Headline = NewStrBuf();
2154                         DoTemplate(TKEY(1), Headline, TP);
2155                 }
2156                 else {
2157                         const char *Ch;
2158                         long len;
2159                         GetTemplateTokenString(Target, 
2160                                                TP, 
2161                                                1,
2162                                                &Ch,
2163                                                &len);
2164                         Headline = NewStrBufPlain(Ch, len);
2165                 }
2166         }
2167         /* else TODO error? logging? */
2168         memcpy (&SubTP, TP, sizeof(WCTemplputParams));
2169         SubTP.Context = Headline;
2170         SubTP.Filter.ContextType = CTX_STRBUF;
2171         DoTemplate(HKEY("beginbox"), Target, &SubTP);
2172         DoTemplate(TKEY(0), Target, TP);
2173         DoTemplate(HKEY("endbox"), Target, TP);
2174         FreeStrBuf(&Headline);
2175 }
2176
2177 /*-----------------------------------------------------------------------------
2178  *                      Tabbed-API
2179  */
2180
2181 typedef struct _tab_struct {
2182         long CurrentTab;
2183         StrBuf *TabTitle;
2184 } tab_struct;
2185
2186 int preeval_do_tabbed(WCTemplateToken *Token)
2187 {
2188         WCTemplputParams TPP;
2189         WCTemplputParams *TP;
2190         const char *Ch;
2191         long len;
2192         int i, nTabs;
2193
2194
2195         memset(&TPP, 0, sizeof(WCTemplputParams));
2196         TP = &TPP;
2197         TP->Tokens = Token;
2198         nTabs = TP->Tokens->nParameters / 2 - 1;
2199         if (TP->Tokens->nParameters % 2 != 0)
2200         {
2201                 LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2202                                  "need even number of arguments");
2203                 return 0;
2204
2205         }
2206         else for (i = 0; i < nTabs; i++) {
2207                 if (!HaveTemplateTokenString(NULL, 
2208                                              TP, 
2209                                              i * 2,
2210                                              &Ch,
2211                                              &len) || 
2212                     (TP->Tokens->Params[i * 2]->len == 0))
2213                 {
2214                         LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2215                                          "Tab-Subject %d needs to be able to produce a string, have %s", 
2216                                          i, TP->Tokens->Params[i * 2]->Start);
2217                         return 0;
2218                 }
2219                 if (!HaveTemplateTokenString(NULL, 
2220                                              TP, 
2221                                              i * 2 + 1,
2222                                              &Ch,
2223                                              &len) || 
2224                     (TP->Tokens->Params[i * 2 + 1]->len == 0))
2225                 {
2226                         LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2227                                          "Tab-Content %d needs to be able to produce a string, have %s", 
2228                                          i, TP->Tokens->Params[i * 2 + 1]->Start);
2229                         return 0;
2230                 }
2231         }
2232
2233         if (!HaveTemplateTokenString(NULL, 
2234                                      TP, 
2235                                      i * 2 + 1,
2236                                      &Ch,
2237                                      &len) || 
2238             (TP->Tokens->Params[i * 2 + 1]->len == 0))
2239         {
2240                 LogTemplateError(NULL, "TabbedApi", ERR_PARM1, TP,
2241                                  "Tab-Content %d needs to be able to produce a string, have %s", 
2242                                  i, TP->Tokens->Params[i * 2 + 1]->Start);
2243                 return 0;
2244         }
2245         return 1;
2246 }
2247
2248
2249 void tmpl_do_tabbed(StrBuf *Target, WCTemplputParams *TP)
2250 {
2251         StrBuf **TabNames;
2252         int i, ntabs, nTabs;
2253         tab_struct TS;
2254         WCTemplputParams SubTP;
2255
2256         memset(&TS, 0, sizeof(tab_struct));
2257         memcpy (&SubTP, &TP, sizeof(WCTemplputParams));
2258
2259         nTabs = ntabs = TP->Tokens->nParameters / 2;
2260         TabNames = (StrBuf **) malloc(ntabs * sizeof(StrBuf*));
2261         memset(TabNames, 0, ntabs * sizeof(StrBuf*));
2262
2263         for (i = 0; i < ntabs; i++) {
2264                 if ((TP->Tokens->Params[i * 2]->Type == TYPE_STR) &&
2265                     (TP->Tokens->Params[i * 2]->len > 0)) {
2266                         TabNames[i] = NewStrBuf();
2267                         DoTemplate(TKEY(i * 2), TabNames[i], TP);
2268                 }
2269                 else if (TP->Tokens->Params[i * 2]->Type == TYPE_GETTEXT) {
2270                         const char *Ch;
2271                         long len;
2272                         GetTemplateTokenString(Target, 
2273                                                TP, 
2274                                                i * 2,
2275                                                &Ch,
2276                                                &len);
2277                         TabNames[i] = NewStrBufPlain(Ch, -1);
2278                 }
2279                 else { 
2280                         /** A Tab without subject? we can't count that, add it as silent */
2281                         nTabs --;
2282                 }
2283         }
2284         memcpy (&SubTP, TP, sizeof(WCTemplputParams));
2285         SubTP.Filter.ControlContextType = CTX_TAB;
2286         SubTP.ControlContext = &TS;
2287
2288         StrTabbedDialog(Target, nTabs, TabNames);
2289         for (i = 0; i < ntabs; i++) {
2290                 memset(&TS, 0, sizeof(tab_struct));
2291                 TS.CurrentTab = i;
2292                 TS.TabTitle = TabNames[i];
2293                 StrBeginTab(Target, i, nTabs, TabNames);
2294                 DoTemplate(TKEY(i * 2 + 1), Target, &SubTP);
2295                 StrEndTab(Target, i, nTabs);
2296         }
2297         for (i = 0; i < ntabs; i++) 
2298                 FreeStrBuf(&TabNames[i]);
2299 }
2300
2301 void tmplput_TAB_N(StrBuf *Target, WCTemplputParams *TP)
2302 {
2303         tab_struct *Ctx = CCTX;
2304
2305         StrBufAppendPrintf(Target, "%d", Ctx->CurrentTab);
2306 }
2307
2308 void tmplput_TAB_TITLE(StrBuf *Target, WCTemplputParams *TP)
2309 {
2310         tab_struct *Ctx = CCTX;
2311         StrBufAppendTemplate(Target, TP, Ctx->TabTitle, 0);
2312 }
2313
2314 /*-----------------------------------------------------------------------------
2315  *                      Sorting-API
2316  */
2317
2318
2319 void RegisterSortFunc(const char *name, long len, 
2320                       const char *prepend, long preplen,
2321                       CompareFunc Forward, 
2322                       CompareFunc Reverse, 
2323                       CompareFunc GroupChange, 
2324                       long ContextType)
2325 {
2326         SortStruct *NewSort;
2327
2328         NewSort = (SortStruct*) malloc(sizeof(SortStruct));
2329         memset(NewSort, 0, sizeof(SortStruct));
2330         NewSort->Name = NewStrBufPlain(name, len);
2331         if (prepend != NULL)
2332                 NewSort->PrefPrepend = NewStrBufPlain(prepend, preplen);
2333         else
2334                 NewSort->PrefPrepend = NULL;
2335         NewSort->Forward = Forward;
2336         NewSort->Reverse = Reverse;
2337         NewSort->GroupChange = GroupChange;
2338         NewSort->ContextType = ContextType;
2339         if (ContextType == CTX_NONE) {
2340                 lprintf(1, "sorting requires a context. CTX_NONE won't make it.\n");
2341                 exit(1);
2342         }
2343                 
2344         Put(SortHash, name, len, NewSort, DestroySortStruct);
2345 }
2346
2347 CompareFunc RetrieveSort(WCTemplputParams *TP, 
2348                          const char *OtherPrefix, long OtherPrefixLen,
2349                          const char *Default, long ldefault, long DefaultDirection)
2350 {
2351         int isdefault = 0;
2352         const StrBuf *BSort = NULL;
2353         SortStruct *SortBy;
2354         void *vSortBy;
2355         long SortOrder = -1;
2356         
2357         if (havebstr("SortBy")) {
2358                 BSort = sbstr("SortBy");
2359                 if (OtherPrefix == NULL) {
2360                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2361                 }
2362                 else {
2363                         set_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen, NewStrBufDup(BSort), 0);
2364                 }
2365         }
2366         else { /** Try to fallback to our remembered values... */
2367                 if (OtherPrefix == NULL) {
2368                         BSort = get_room_pref("sort");
2369                 }
2370                 else {
2371                         BSort = get_X_PREFS(HKEY("sort"), OtherPrefix, OtherPrefixLen);
2372                 }
2373                 if (BSort != NULL)
2374                         putbstr("SortBy", NewStrBufDup(BSort));
2375                 else {
2376                         StrBuf *Buf;
2377
2378                         BSort = Buf = NewStrBufPlain(Default, ldefault);
2379                         putbstr("SortBy", Buf);
2380                 }
2381         }
2382
2383         if (!GetHash(SortHash, SKEY(BSort), &vSortBy) || 
2384             (vSortBy == NULL)) {
2385                 isdefault = 1;
2386                 if (!GetHash(SortHash, Default, ldefault, &vSortBy) || 
2387                     (vSortBy == NULL)) {
2388                         LogTemplateError(
2389                                 NULL, "Sorting", ERR_PARM1, TP,
2390                                 "Illegal default sort: [%s]", Default);
2391                         wc_backtrace();
2392                 }
2393         }
2394         SortBy = (SortStruct*)vSortBy;
2395
2396         if (SortBy->ContextType != TP->Filter.ContextType)
2397                 return NULL;
2398
2399         /** Ok, its us, lets see in which direction we should sort... */
2400         if (havebstr("SortOrder")) {
2401                 SortOrder = LBSTR("SortOrder");
2402         }
2403         else { /** Try to fallback to our remembered values... */
2404                 StrBuf *Buf = NULL;
2405                 if (SortBy->PrefPrepend == NULL) {
2406                         Buf = get_room_pref("SortOrder");
2407                         SortOrder = StrTol(Buf);
2408                 }
2409                 else {
2410                         BSort = get_X_PREFS(HKEY("SortOrder"), OtherPrefix, OtherPrefixLen);
2411                 }
2412
2413                 if (Buf == NULL)
2414                         SortOrder = DefaultDirection;
2415
2416                 Buf = NewStrBufPlain(NULL, 64);
2417                 StrBufPrintf(Buf, "%ld", SortOrder);
2418                 putbstr("SortOrder", Buf);
2419         }
2420         switch (SortOrder) {
2421         default:
2422         case 0:
2423                 return NULL;
2424         case 1:
2425                 return SortBy->Forward;
2426         case 2:
2427                 return SortBy->Reverse;
2428         }
2429 }
2430
2431
2432 enum {
2433         eNO_SUCH_SORT, 
2434         eNOT_SPECIFIED,
2435         eINVALID_PARAM,
2436         eFOUND
2437 };
2438
2439 ConstStr SortIcons[] = {
2440         {HKEY("static/sort_none.gif")},
2441         {HKEY("static/up_pointer.gif")},
2442         {HKEY("static/down_pointer.gif")},
2443 };
2444
2445 ConstStr SortNextOrder[] = {
2446         {HKEY("1")},
2447         {HKEY("2")},
2448         {HKEY("0")},
2449 };
2450
2451
2452 int GetSortMetric(WCTemplputParams *TP, SortStruct **Next, SortStruct **Param, long *SortOrder, int N)
2453 {
2454         int bSortError = eNOT_SPECIFIED;
2455         const StrBuf *BSort;
2456         void *vSort;
2457         
2458         *SortOrder = 0;
2459         *Next = NULL;
2460         if (!GetHash(SortHash, TKEY(0), &vSort) || 
2461             (vSort == NULL))
2462                 return eNO_SUCH_SORT;
2463         *Param = (SortStruct*) vSort;
2464         
2465
2466         if (havebstr("SortBy")) {
2467                 BSort = sbstr("SortBy");
2468                 bSortError = eINVALID_PARAM;
2469                 if ((*Param)->PrefPrepend == NULL) {
2470                         set_room_pref("sort", NewStrBufDup(BSort), 0);
2471                 }
2472                 else {
2473                         set_X_PREFS(HKEY("sort"), TKEY(N), NewStrBufDup(BSort), 0);
2474                 }
2475         }
2476         else { /** Try to fallback to our remembered values... */
2477                 if ((*Param)->PrefPrepend == NULL) {
2478                         BSort = get_room_pref("sort");
2479                 }
2480                 else {
2481                         BSort = get_X_PREFS(HKEY("sort"), TKEY(N));
2482                 }
2483         }
2484
2485         if (!GetHash(SortHash, SKEY(BSort), &vSort) || 
2486             (vSort == NULL))
2487                 return bSortError;
2488
2489         *Next = (SortStruct*) vSort;
2490
2491         /** Ok, its us, lets see in which direction we should sort... */
2492         if (havebstr("SortOrder")) {
2493                 *SortOrder = LBSTR("SortOrder");
2494         }
2495         else { /** Try to fallback to our remembered values... */
2496                 if ((*Param)->PrefPrepend == NULL) {
2497                         *SortOrder = StrTol(get_room_pref("SortOrder"));
2498                 }
2499                 else {
2500                         *SortOrder = StrTol(get_X_PREFS(HKEY("SortOrder"), TKEY(N)));
2501                 }
2502         }
2503         if (*SortOrder > 2)
2504                 *SortOrder = 0;
2505
2506         return eFOUND;
2507 }
2508
2509
2510 void tmplput_SORT_ICON(StrBuf *Target, WCTemplputParams *TP)
2511 {
2512         long SortOrder;
2513         SortStruct *Next;
2514         SortStruct *Param;
2515         const ConstStr *SortIcon;
2516
2517         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2518         case eNO_SUCH_SORT:
2519                 LogTemplateError(
2520                         Target, "Sorter", ERR_PARM1, TP,
2521                         " Sorter [%s] unknown!", 
2522                         TP->Tokens->Params[0]->Start);
2523                 break;          
2524         case eINVALID_PARAM:
2525                 LogTemplateError(NULL, "Sorter", ERR_PARM1, TP,
2526                                  " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
2527                                  bstr("SortBy"));
2528         case eNOT_SPECIFIED:
2529         case eFOUND:
2530                 if (Next == Param) {
2531                         SortIcon = &SortIcons[SortOrder];
2532                 }
2533                 else { /** Not Us... */
2534                         SortIcon = &SortIcons[0];
2535                 }
2536                 StrBufAppendBufPlain(Target, SortIcon->Key, SortIcon->len, 0);
2537         }
2538 }
2539
2540 void tmplput_SORT_NEXT(StrBuf *Target, WCTemplputParams *TP)
2541 {
2542         long SortOrder;
2543         SortStruct *Next;
2544         SortStruct *Param;
2545
2546         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2547         case eNO_SUCH_SORT:
2548                 LogTemplateError(
2549                         Target, "Sorter", ERR_PARM1, TP,                                  
2550                         " Sorter [%s] unknown!", 
2551                         TP->Tokens->Params[0]->Start);
2552                 break;          
2553         case eINVALID_PARAM:
2554                 LogTemplateError(
2555                         NULL, "Sorter", ERR_PARM1, TP,
2556                         " Sorter specified by BSTR 'SortBy' [%s] unknown!", 
2557                         bstr("SortBy"));
2558         case eNOT_SPECIFIED:
2559         case eFOUND:
2560                 StrBufAppendBuf(Target, Param->Name, 0);
2561                 
2562         }
2563 }
2564
2565 void tmplput_SORT_ORDER(StrBuf *Target, WCTemplputParams *TP)
2566 {
2567         long SortOrder;
2568         const ConstStr *SortOrderStr;
2569         SortStruct *Next;
2570         SortStruct *Param;
2571
2572         switch (GetSortMetric(TP, &Next, &Param, &SortOrder, 2)){
2573         case eNO_SUCH_SORT:
2574                 LogTemplateError(
2575                         Target, "Sorter", ERR_PARM1, TP,
2576                         " Sorter [%s] unknown!",
2577                         TP->Tokens->Params[0]->Start);
2578                 break;          
2579         case eINVALID_PARAM:
2580                 LogTemplateError(
2581                         NULL, "Sorter", ERR_PARM1, TP,
2582                         " Sorter specified by BSTR 'SortBy' [%s] unknown!",
2583                         bstr("SortBy"));
2584         case eNOT_SPECIFIED:
2585         case eFOUND:
2586                 if (Next == Param) {
2587                         SortOrderStr = &SortNextOrder[SortOrder];
2588                 }
2589                 else { /** Not Us... */
2590                         SortOrderStr = &SortNextOrder[0];
2591                 }
2592                 StrBufAppendBufPlain(Target, SortOrderStr->Key, SortOrderStr->len, 0);
2593         }
2594 }
2595
2596
2597 void tmplput_long_vector(StrBuf *Target, WCTemplputParams *TP)
2598 {
2599         long *LongVector = (long*) CTX;
2600
2601         if ((TP->Tokens->Params[0]->Type == TYPE_LONG) && 
2602             (TP->Tokens->Params[0]->lvalue <= LongVector[0]))
2603         {
2604                 StrBufAppendPrintf(Target, "%ld", LongVector[TP->Tokens->Params[0]->lvalue]);
2605         }
2606         else
2607         {
2608                 if (TP->Tokens->Params[0]->Type != TYPE_LONG) {
2609                         LogTemplateError(
2610                                 Target, "Longvector", ERR_NAME, TP,
2611                                 "needs a numerical Parameter!");
2612                 }
2613                 else {
2614                         LogTemplateError(
2615                                 Target, "LongVector", ERR_PARM1, TP,
2616                                 "doesn't have %ld Parameters, its just the size of %ld!", 
2617                                 TP->Tokens->Params[0]->lvalue,
2618                                 LongVector[0]);
2619                 }
2620         }
2621 }
2622
2623 void dbg_print_longvector(long *LongVector)
2624 {
2625         StrBuf *Buf = NewStrBufPlain(HKEY("Longvector: ["));
2626         int nItems = LongVector[0];
2627         int i;
2628
2629         for (i = 0; i < nItems; i++) {
2630                 if (i + 1 < nItems)
2631                         StrBufAppendPrintf(Buf, "%d: %ld | ", i, LongVector[i]);
2632                 else
2633                         StrBufAppendPrintf(Buf, "%d: %ld]\n", i, LongVector[i]);
2634
2635         }
2636         lprintf(1, ChrPtr(Buf));
2637         FreeStrBuf(&Buf);
2638 }
2639
2640 int ConditionalLongVector(StrBuf *Target, WCTemplputParams *TP)
2641 {
2642         long *LongVector = (long*) CTX;
2643
2644         if ((TP->Tokens->Params[2]->Type == TYPE_LONG) && 
2645             (TP->Tokens->Params[2]->lvalue <= LongVector[0])&&
2646             (TP->Tokens->Params[3]->Type == TYPE_LONG) && 
2647             (TP->Tokens->Params[3]->lvalue <= LongVector[0]))
2648         {
2649                 return LongVector[TP->Tokens->Params[2]->lvalue] == 
2650                         LongVector[TP->Tokens->Params[3]->lvalue];
2651         }
2652         else
2653         {
2654                 if ((TP->Tokens->Params[2]->Type == TYPE_LONG) ||
2655                     (TP->Tokens->Params[2]->Type == TYPE_LONG)) {
2656                         LogTemplateError(
2657                                 Target, "ConditionalLongvector", ERR_PARM1, TP,
2658                                 "needs two long Parameter!");
2659                 }
2660                 else {
2661                         LogTemplateError(
2662                                 Target, "Longvector", ERR_PARM1, TP,
2663                                 "doesn't have %ld / %ld Parameters, its just the size of %ld!",
2664                                 TP->Tokens->Params[2]->lvalue,
2665                                 TP->Tokens->Params[3]->lvalue,
2666                                 LongVector[0]);
2667                 }
2668         }
2669         return 0;
2670 }
2671
2672
2673 void tmplput_CURRENT_FILE(StrBuf *Target, WCTemplputParams *TP)
2674 {
2675         StrBufAppendTemplate(Target, TP, TP->Tokens->FileName, 0);
2676 }
2677
2678 void 
2679 InitModule_SUBST
2680 (void)
2681 {
2682         memset(&NoCtx, 0, sizeof(WCTemplputParams));
2683         RegisterNamespace("--", 0, 2, tmplput_Comment, NULL, CTX_NONE);
2684         RegisterNamespace("SORT:ICON", 1, 2, tmplput_SORT_ICON, NULL, CTX_NONE);
2685         RegisterNamespace("SORT:ORDER", 1, 2, tmplput_SORT_ORDER, NULL, CTX_NONE);
2686         RegisterNamespace("SORT:NEXT", 1, 2, tmplput_SORT_NEXT, NULL, CTX_NONE);
2687         RegisterNamespace("CONTEXTSTR", 0, 1, tmplput_ContextString, NULL, CTX_STRBUF);
2688         RegisterNamespace("CONTEXTSTRARR", 1, 2, tmplput_ContextStringArray, NULL, CTX_STRBUFARR);
2689         RegisterNamespace("ITERATE", 2, 100, tmpl_iterate_subtmpl, preeval_iterate, CTX_NONE);
2690         RegisterNamespace("DOBOXED", 1, 2, tmpl_do_boxed, NULL, CTX_NONE);
2691         RegisterNamespace("DOTABBED", 2, 100, tmpl_do_tabbed, preeval_do_tabbed, CTX_NONE);
2692         RegisterControlNS(HKEY("TAB:N"), 0, 0, tmplput_TAB_N, CTX_TAB);
2693         RegisterControlNS(HKEY("TAB:SUBJECT"), 0, 1, tmplput_TAB_TITLE, CTX_TAB);
2694
2695
2696         RegisterNamespace("LONGVECTOR", 1, 1, tmplput_long_vector, NULL, CTX_LONGVECTOR);
2697
2698
2699         RegisterConditional(HKEY("COND:CONTEXTSTR"), 3, ConditionalContextStr, CTX_STRBUF);
2700         RegisterConditional(HKEY("COND:CONTEXTSTRARR"), 4, ConditionalContextStrinArray, CTX_STRBUFARR);
2701         RegisterConditional(HKEY("COND:LONGVECTOR"), 4, ConditionalLongVector, CTX_LONGVECTOR);
2702
2703
2704         RegisterControlConditional(HKEY("COND:ITERATE:ISGROUPCHANGE"), 2, 
2705                                    conditional_ITERATE_ISGROUPCHANGE, 
2706                                    CTX_ITERATE);
2707         RegisterControlConditional(HKEY("COND:ITERATE:LASTN"), 2, 
2708                                    conditional_ITERATE_LASTN, 
2709                                    CTX_ITERATE);
2710         RegisterControlConditional(HKEY("COND:ITERATE:FIRSTN"), 2, 
2711                                    conditional_ITERATE_FIRSTN, 
2712                                    CTX_ITERATE);
2713
2714         RegisterControlNS(HKEY("ITERATE:ODDEVEN"), 0, 0, tmplput_ITERATE_ODDEVEN, CTX_ITERATE);
2715         RegisterControlNS(HKEY("ITERATE:KEY"), 0, 0, tmplput_ITERATE_KEY, CTX_ITERATE);
2716         RegisterControlNS(HKEY("ITERATE:N"), 0, 0, tmplput_ITERATE_LASTN, CTX_ITERATE);
2717         RegisterNamespace("CURRENTFILE", 0, 1, tmplput_CURRENT_FILE, NULL, CTX_NONE);
2718         RegisterNamespace("DEF:STR", 1, 1, tmplput_DefStr, NULL, CTX_NONE);
2719         RegisterNamespace("DEF:VAL", 1, 1, tmplput_DefVal, NULL, CTX_NONE);
2720
2721
2722
2723
2724 }
2725
2726 void
2727 ServerStartModule_SUBST
2728 (void)
2729 {
2730         WirelessTemplateCache = NewHash(1, NULL);
2731         WirelessLocalTemplateCache = NewHash(1, NULL);
2732         LocalTemplateCache = NewHash(1, NULL);
2733         TemplateCache = NewHash(1, NULL);
2734
2735         GlobalNS = NewHash(1, NULL);
2736         Iterators = NewHash(1, NULL);
2737         Conditionals = NewHash(1, NULL);
2738         SortHash = NewHash(1, NULL);
2739         Defines = NewHash(1, NULL);
2740 }
2741
2742 void
2743 FinalizeModule_SUBST
2744 (void)
2745 {
2746
2747 }
2748
2749 void 
2750 ServerShutdownModule_SUBST
2751 (void)
2752 {
2753         DeleteHash(&WirelessTemplateCache);
2754         DeleteHash(&WirelessLocalTemplateCache);
2755         DeleteHash(&TemplateCache);
2756         DeleteHash(&LocalTemplateCache);
2757
2758         DeleteHash(&GlobalNS);
2759         DeleteHash(&Iterators);
2760         DeleteHash(&Conditionals);
2761         DeleteHash(&SortHash);
2762         DeleteHash(&Defines);
2763 }
2764
2765
2766 void
2767 SessionNewModule_SUBST
2768 (wcsession *sess)
2769 {
2770
2771 }
2772
2773 void
2774 SessionAttachModule_SUBST
2775 (wcsession *sess)
2776 {
2777 }
2778
2779 void
2780 SessionDetachModule_SUBST
2781 (wcsession *sess)
2782 {
2783         FreeStrBuf(&sess->WFBuf);
2784 }
2785
2786 void 
2787 SessionDestroyModule_SUBST  
2788 (wcsession *sess)
2789 {
2790
2791 }