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