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