* implement conditional switches; if true will skip to next "X" with the same value.
[citadel.git] / webcit / subst.c
1 /*
2  * $Id$
3  */
4 /**
5  * \defgroup Subst Variable substitution type stuff
6  * \ingroup CitadelConfig
7  */
8
9 /*@{*/
10
11 #include "sysdep.h"
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <unistd.h>
15 #include <dirent.h>
16 #include <errno.h>
17
18 #include "webcit.h"
19 #include "webserver.h"
20
21 extern char *static_dirs[PATH_MAX];  /**< Disk representation */
22
23 HashList *WirelessTemplateCache;
24 HashList *WirelessLocalTemplateCache;
25 HashList *TemplateCache;
26 HashList *LocalTemplateCache;
27
28 HashList *GlobalNS;
29 HashList *Iterators;
30 HashList *Contitionals;
31
32 #define SV_GETTEXT 1
33 #define SV_CONDITIONAL 2
34 #define SV_NEG_CONDITIONAL 3
35
36 typedef struct _WCTemplate {
37         StrBuf *Data;
38         int nTokensUsed;
39         int TokenSpace;
40         WCTemplateToken **Tokens;
41 } WCTemplate;
42
43 typedef struct _HashHandler {
44         int nMinArgs;
45         int nMaxArgs;
46         WCHandlerFunc HandlerFunc;
47 }HashHandler;
48
49 void RegisterNS(const char *NSName, long len, int nMinArgs, int nMaxArgs, WCHandlerFunc HandlerFunc)
50 {
51         HashHandler *NewHandler;
52         
53         NewHandler = (HashHandler*) malloc(sizeof(HashHandler));
54         NewHandler->nMinArgs = nMinArgs;
55         NewHandler->nMaxArgs = nMaxArgs;
56         NewHandler->HandlerFunc = HandlerFunc;  
57         Put(GlobalNS, NSName, len, NewHandler, NULL);
58 }
59
60
61 /**
62  * \brief debugging function to print array to log
63  */
64 void VarPrintTransition(void *vVar1, void *vVar2, int odd){}
65 /**
66  * \brief debugging function to print array to log
67  */
68 void VarPrintEntry(const char *Key, void *vSubst, int odd)
69 {
70         wcsubst *ptr;
71         lprintf(1,"Subst[%s] : ", Key);
72         ptr = (wcsubst*) vSubst;
73
74         switch(ptr->wcs_type) {
75         case WCS_STRING:
76                 lprintf(1, "  -> %s\n", ptr->wcs_value);
77                 break;
78         case WCS_SERVCMD:
79                 lprintf(1, "  -> Server [%s]\n", ptr->wcs_value);
80                 break;
81         case WCS_FUNCTION:
82                 lprintf(1, "  -> function at [%0xd]\n", ptr->wcs_function);
83                 break;
84         default:
85                 lprintf(1,"  WARNING: invalid type: [%ld]!\n", ptr->wcs_type);
86         }
87 }
88
89
90
91 /**
92  * \brief Clear out the list of substitution variables local to this session
93  */
94 void clear_substs(struct wcsession *wc) {
95
96         if (wc->vars != NULL) {
97         
98                 DeleteHash(&wc->vars);
99         }
100 }
101
102 /**
103  * \brief Clear out the list of substitution variables local to this session
104  */
105 void clear_local_substs(void) {
106         clear_substs (WC);
107 }
108
109 /**
110  * \brief destructor; kill one entry.
111  */
112 void deletevar(void *data)
113 {
114         wcsubst *ptr = (wcsubst*)data;
115 //              if ((wc->vars->wcs_type == WCS_STRING)
116 //                 || (wc->vars->wcs_type == WCS_SERVCMD)) {
117         if (ptr->wcs_type != WCS_FUNCTION)
118                 free(ptr->wcs_value);
119         free(ptr);      
120 }
121
122 /**
123  * \brief Add a substitution variable (local to this session) (strlen version...)
124  * \param keyname the replacementstring to substitute
125  * \param keytype the kind of the key
126  * \param format the format string ala printf
127  * \param ... the arguments to substitute in the formatstring
128  */
129 void SVPRINTF(char *keyname, int keytype, const char *format,...)
130 {
131         va_list arg_ptr;
132         char wbuf[SIZ];
133         void *vPtr;
134         wcsubst *ptr = NULL;
135         size_t keylen;
136         struct wcsession *WCC = WC;
137         
138         keylen = strlen(keyname);
139         /**
140          * First look if we're doing a replacement of
141          * an existing key
142          */
143         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
144         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
145                 ptr = (wcsubst*)vPtr;
146                 if (ptr->wcs_value != NULL)
147                         free(ptr->wcs_value);
148         }
149         else    /** Otherwise allocate a new one */
150         {
151                 ptr = (wcsubst *) malloc(sizeof(wcsubst));
152                 safestrncpy(ptr->wcs_key, keyname, sizeof ptr->wcs_key);
153                 Put(WCC->vars, keyname, keylen, ptr,  deletevar);
154         }
155
156         /** Format the string and save it */
157
158         va_start(arg_ptr, format);
159         vsnprintf(wbuf, sizeof wbuf, format, arg_ptr);
160         va_end(arg_ptr);
161
162         ptr->wcs_function = NULL;
163         ptr->wcs_type = keytype;
164         ptr->wcs_value = strdup(wbuf);
165 }
166
167 /**
168  * \brief Add a substitution variable (local to this session)
169  * \param keyname the replacementstring to substitute
170  * \param keytype the kind of the key
171  * \param format the format string ala printf
172  * \param ... the arguments to substitute in the formatstring
173  */
174 void svprintf(char *keyname, size_t keylen, int keytype, const char *format,...)
175 {
176         va_list arg_ptr;
177         char wbuf[SIZ];
178         void *vPtr;
179         wcsubst *ptr = NULL;
180         struct wcsession *WCC = WC;
181         size_t len;
182         
183         /**
184          * First look if we're doing a replacement of
185          * an existing key
186          */
187         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
188         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
189                 ptr = (wcsubst*)vPtr;
190                 if (ptr->wcs_value != NULL)
191                         free(ptr->wcs_value);
192         }
193         else    /** Otherwise allocate a new one */
194         {
195                 ptr = (wcsubst *) malloc(sizeof(wcsubst));
196                 safestrncpy(ptr->wcs_key, keyname, sizeof ptr->wcs_key);
197                 Put(WCC->vars, keyname, keylen, ptr,  deletevar);
198         }
199
200         /** Format the string and save it */
201
202         va_start(arg_ptr, format);
203         len = vsnprintf(wbuf, sizeof wbuf, format, arg_ptr);
204         va_end(arg_ptr);
205
206         ptr->wcs_value = (char*) malloc(len + 1);
207         memcpy(ptr->wcs_value, wbuf, len + 1);
208         ptr->wcs_function = NULL;
209         ptr->wcs_type = keytype;
210 }
211
212 /**
213  * \brief Add a substitution variable (local to this session)
214  * \param keyname the replacementstring to substitute
215  * \param keytype the kind of the key
216  * \param format the format string ala printf
217  * \param ... the arguments to substitute in the formatstring
218  */
219 void SVPut(char *keyname, size_t keylen, int keytype, char *Data)
220 {
221         void *vPtr;
222         wcsubst *ptr = NULL;
223         struct wcsession *WCC = WC;
224
225         
226         /**
227          * First look if we're doing a replacement of
228          * an existing key
229          */
230         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
231         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
232                 ptr = (wcsubst*)vPtr;
233                 if (ptr->wcs_value != NULL)
234                         free(ptr->wcs_value);
235         }
236         else    /** Otherwise allocate a new one */
237         {
238                 ptr = (wcsubst *) malloc(sizeof(wcsubst));
239                 safestrncpy(ptr->wcs_key, keyname, sizeof ptr->wcs_key);
240                 Put(WCC->vars, keyname, keylen, ptr,  deletevar);
241         }
242
243         ptr->wcs_function = NULL;
244         ptr->wcs_type = keytype;
245         ptr->wcs_value = strdup(Data);
246 }
247
248 /**
249  * \brief Add a substitution variable (local to this session)
250  * \param keyname the replacementstring to substitute
251  * \param keytype the kind of the key
252  * \param format the format string ala printf
253  * \param ... the arguments to substitute in the formatstring
254  */
255 void SVPutLong(char *keyname, size_t keylen, long Data)
256 {
257         void *vPtr;
258         wcsubst *ptr = NULL;
259         struct wcsession *WCC = WC;
260
261         
262         /**
263          * First look if we're doing a replacement of
264          * an existing key
265          */
266         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
267         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
268                 ptr = (wcsubst*)vPtr;
269                 if (ptr->wcs_value != NULL)
270                         free(ptr->wcs_value);
271         }
272         else    /** Otherwise allocate a new one */
273         {
274                 ptr = (wcsubst *) malloc(sizeof(wcsubst));
275                 safestrncpy(ptr->wcs_key, keyname, sizeof ptr->wcs_key);
276                 Put(WCC->vars, keyname, keylen, ptr,  deletevar);
277         }
278
279         ptr->wcs_function = NULL;
280         ptr->wcs_type = WCS_LONG;
281         ptr->lvalue = Data;
282 }
283
284 /**
285  * \brief Add a substitution variable (local to this session) that does a callback
286  * \param keyname the keystring to substitute
287  * \param fcn_ptr the function callback to give the substitution string
288  */
289 void SVCallback(char *keyname, size_t keylen, var_callback_fptr fcn_ptr)
290 {
291         wcsubst *ptr;
292         void *vPtr;
293         struct wcsession *WCC = WC;
294
295         /**
296          * First look if we're doing a replacement of
297          * an existing key
298          */
299         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
300         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
301                 ptr = (wcsubst*)vPtr;
302                 if (ptr->wcs_value != NULL)
303                         free(ptr->wcs_value);
304         }
305         else    /** Otherwise allocate a new one */
306         {
307                 ptr = (wcsubst *) malloc(sizeof(wcsubst));
308                 safestrncpy(ptr->wcs_key, keyname, sizeof ptr->wcs_key);
309                 Put(WCC->vars, keyname, keylen, ptr,  deletevar);
310         }
311
312         ptr->wcs_value = NULL;
313         ptr->wcs_type = WCS_FUNCTION;
314         ptr->wcs_function = fcn_ptr;
315 }
316 inline void SVCALLBACK(char *keyname, var_callback_fptr fcn_ptr)
317 {
318         SVCallback(keyname, strlen(keyname), fcn_ptr);
319 }
320
321
322
323 void SVPUTBuf(const char *keyname, int keylen, StrBuf *Buf, int ref)
324 {
325         wcsubst *ptr;
326         void *vPtr;
327         struct wcsession *WCC = WC;
328
329         /**
330          * First look if we're doing a replacement of
331          * an existing key
332          */
333         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
334         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
335                 ptr = (wcsubst*)vPtr;
336                 if (ptr->wcs_value != NULL)
337                         free(ptr->wcs_value);///TODO: respect type
338         }
339         else    /** Otherwise allocate a new one */
340         {
341                 ptr = (wcsubst *) malloc(sizeof(wcsubst));
342                 safestrncpy(ptr->wcs_key, keyname, sizeof ptr->wcs_key);
343                 Put(WCC->vars, keyname, keylen, ptr,  deletevar);
344         }
345
346         ptr->wcs_value = NULL;
347         ptr->wcs_type = (ref)?WCS_STRBUF:WCS_STRBUF_REF;
348         ptr->wcs_function = (var_callback_fptr) Buf; ////TODO
349 }
350
351 /**
352  * \brief back end for print_value_of() ... does a server command
353  * \param servcmd server command to execute on the citadel server
354  */
355 void pvo_do_cmd(StrBuf *Target, char *servcmd) {
356         char buf[SIZ];
357         int len;
358
359         serv_puts(servcmd);
360         len = serv_getln(buf, sizeof buf);
361
362         switch(buf[0]) {
363                 case '2':
364                 case '3':
365                 case '5':
366                         StrBufAppendPrintf(Target, "%s\n", &buf[4]);
367                         break;
368                 case '1':
369                         _fmout(Target, "CENTER");
370                         break;
371                 case '4':
372                         StrBufAppendPrintf(Target, "%s\n", &buf[4]);
373                         serv_puts("000");
374                         break;
375         }
376 }
377
378 /**
379  * \brief Print the value of a variable
380  * \param keyname get a key to print
381  */
382 void print_value_of(StrBuf *Target, const char *keyname, size_t keylen) {
383         struct wcsession *WCC = WC;
384         wcsubst *ptr;
385         void *fcn();
386         void *vVar;
387
388         /*if (WCC->vars != NULL) PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
389         if (keyname[0] == '=') {
390                 DoTemplate(keyname+1, keylen - 1, NULL, NULL);
391         }
392         /** Page-local variables */
393         if ((WCC->vars!= NULL) && GetHash(WCC->vars, keyname, keylen, &vVar)) {
394                 ptr = (wcsubst*) vVar;
395                 switch(ptr->wcs_type) {
396                 case WCS_STRING:
397                         StrBufAppendBufPlain(Target, (const char*)ptr->wcs_value, -1, 0);
398                         break;
399                 case WCS_SERVCMD:
400                         pvo_do_cmd(Target, ptr->wcs_value);
401                         break;
402                 case WCS_FUNCTION:
403                         (*ptr->wcs_function) ();
404                         break;
405                 case WCS_STRBUF:
406                 case WCS_STRBUF_REF:
407                         StrBufAppendBuf(Target, (StrBuf*) ptr->wcs_function, 0);
408                         break;
409                 case WCS_LONG:
410                         StrBufAppendPrintf(Target, "%l", ptr->lvalue);
411                         break;
412                 default:
413                         lprintf(1,"WARNING: invalid value in SV-Hash at %s!", keyname);
414                 }
415         }
416 }
417
418
419 void PutNewToken(WCTemplate *Template, WCTemplateToken *NewToken)
420 {
421         if (Template->nTokensUsed + 1 >= Template->TokenSpace) {
422                 if (Template->TokenSpace <= 0) {
423                         Template->Tokens = (WCTemplateToken**)malloc(
424                                 sizeof(WCTemplateToken*) * 10);
425                         Template->TokenSpace = 10;
426                 }
427                 else {
428                         WCTemplateToken **NewTokens;
429                         NewTokens= (WCTemplateToken**)malloc(
430                                 sizeof(WCTemplateToken*) * 
431                                 Template->TokenSpace * 2);
432                         memcpy(NewTokens, Template->Tokens, 
433                                sizeof(WCTemplateToken*) * Template->nTokensUsed);
434                         free(Template->Tokens);
435                         Template->TokenSpace *= 2;
436                         Template->Tokens = NewTokens;
437                 }
438         }
439         Template->Tokens[(Template->nTokensUsed)++] = NewToken;
440 }
441
442 TemplateParam *GetNextParamter(StrBuf *Buf, const char **pCh, const char *pe)
443 {
444         const char *pch = *pCh;
445         const char *pchs, *pche;
446         TemplateParam *Parm = (TemplateParam *) malloc(sizeof(TemplateParam));
447         char quote = '\0';
448         
449         /* Skip leading whitespaces */
450         while ((*pch == ' ' )||
451                (*pch == '\t')||
452                (*pch == '\r')||
453                (*pch == '\n')) pch ++;
454         if (*pch == '"')
455                 quote = '"';
456         else if (*pch == '\'')
457                 quote = '\'';
458         if (quote != '\0') {
459                 pch ++;
460                 pchs = pch;
461                 Parm->Type = TYPE_STR;
462                 while (pch <= pe &&
463                        ((*pch != quote) ||
464                         ( (pch > pchs) && (*(pch - 1) == '\\'))
465                                )) {
466                         pch ++;
467                 }
468                 pche = pch;
469                 if (*pch != quote) {
470                         lprintf(1, "Error evaluating template param [%s]\n", *pCh);
471                         pch ++;
472                         free(Parm);
473                         return NULL;
474                 }
475                 else {
476                         StrBufPeek(Buf, pch, -1, '\0');         
477                         lprintf(1, "DBG: got param [%s]\n", pchs);
478                         Parm->Start = pchs;
479                         Parm->len = pche - pchs;
480                         pch ++; /* move after trailing quote */
481                 }
482         }
483         else {
484                 Parm->Type = TYPE_LONG;
485                 pchs = pch;
486                 while ((pch <= pe) &&
487                        (isdigit(*pch) ||
488                         (*pch == '+') ||
489                         (*pch == '-')))
490                         pch ++;
491                 pch ++;
492                 if (pch - pchs > 1){
493                         StrBufPeek(Buf, pch, -1, '\0');
494                         Parm->lvalue = atol(pchs);
495                         Parm->Start = pchs;
496                         pch++;
497                 }
498                 else {
499                         Parm->lvalue = 0;
500                         lprintf(1, "Error evaluating template long param [%s]", *pCh);
501                         free(Parm);
502                         return NULL;
503                 }
504         }
505         while ((*pch == ' ' )||
506                (*pch == '\t')||
507                (*pch == '\r')||
508                (*pch == ',' )||
509                (*pch == '\n')) pch ++;
510
511         *pCh = pch;
512         return Parm;
513 }
514
515 WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf, 
516                                        const char *pStart, 
517                                        const char *pTmplStart, 
518                                        const char *pTmplEnd)
519 {
520         const char *pch;
521         TemplateParam *Param;
522         WCTemplateToken *NewToken = (WCTemplateToken*)malloc(sizeof(WCTemplateToken));
523
524         NewToken->Flags = 0;
525         NewToken->pTokenStart = pTmplStart;
526         NewToken->TokenStart = pTmplStart - pStart;
527         NewToken->TokenEnd =  (pTmplEnd - pStart) - NewToken->TokenStart;
528         NewToken->pTokenEnd = pTmplEnd;
529         NewToken->NameEnd = NewToken->TokenEnd - 2;
530         
531         StrBufPeek(Buf, pTmplStart, + 1, '\0');
532         StrBufPeek(Buf, pTmplEnd, -1, '\0');
533         pch = NewToken->pName = pTmplStart + 2;
534
535         NewToken->HaveParameters = 0;;
536         NewToken->nParameters = 0;
537
538         while (pch < pTmplEnd - 1) {
539                 if (*pch == '(') {
540                         StrBufPeek(Buf, pch, -1, '\0');
541                         NewToken->NameEnd = pch - NewToken->pName;
542                         pch ++;
543                         while (pch < pTmplEnd - 1) {
544                                 Param = GetNextParamter(Buf, &pch, pTmplEnd - 1);
545                                 if (Param != NULL) {
546                                         NewToken->HaveParameters = 1;
547                                         if (NewToken->nParameters > MAXPARAM) {
548                                                 lprintf(1, "Only %ld Tokens supported!\n", MAXPARAM);
549                                                 return NULL;
550                                         }
551                                         NewToken->Params[NewToken->nParameters++] = Param;
552                                 }
553                                 else break;
554                         }
555                         if((NewToken->NameEnd == 1) &&
556                            (NewToken->HaveParameters == 1))
557                            
558                         {
559                                 if ((NewToken->nParameters == 1) &&
560                                     (*(NewToken->pName) == '_'))
561                                         NewToken->Flags = SV_GETTEXT;
562                                 else if ((NewToken->nParameters >= 2) &&
563                                          (*(NewToken->pName) == '?'))
564                                         NewToken->Flags = SV_CONDITIONAL;
565                                 else if ((NewToken->nParameters >=2) &&
566                                          (*(NewToken->pName) == '!'))
567                                         NewToken->Flags = SV_NEG_CONDITIONAL;
568                         }
569                 }
570                 else pch ++;            
571         }
572         return NewToken;
573 }
574
575 void FreeWCTemplate(void *vFreeMe)
576 {
577         int i;
578         WCTemplate *FreeMe = (WCTemplate*)vFreeMe;
579
580         if (FreeMe->TokenSpace > 0) {
581                 for (i = 0; i < FreeMe->nTokensUsed; i ++) {
582                         free(FreeMe->Tokens[i]);
583                 }
584                 free(FreeMe->Tokens);
585         }
586         free(FreeMe);
587 }
588
589
590 int EvaluateConditional(WCTemplateToken *Token, void *Context, int Neg, int state)
591 {
592         void *vConditional;
593         ConditionalStruct *Cond;
594
595         if ((Token->Params[0]->len == 1) &&
596             (Token->Params[0]->Start[0] == 'X'))
597                 return (state != 0)?Token->Params[1]->lvalue:0;
598
599         if (!GetHash(Contitionals, 
600                  Token->Params[0]->Start,
601                  Token->Params[0]->len,
602                  &vConditional)) {
603                 lprintf(1, "Conditional %s Not found!\n", 
604                         Token->Params[0]->Start);
605         }
606             
607         Cond = (ConditionalStruct *) vConditional;
608
609         if (Cond == NULL) {
610                 lprintf(1, "Conditional %s Not found!\n", 
611                         Token->Params[0]->Start);
612                 return 0;
613         }
614         if (Token->nParameters < Cond->nParams) {
615                 lprintf(1, "Conditional [%s] needs %ld Params!\n", 
616                         Token->Params[0]->Start,
617                         Cond->nParams);
618                 return 0;
619         }
620         if (Cond->CondF(Token, Context) == Neg)
621                 return Token->Params[1]->lvalue;
622         return 0;
623 }
624
625 int EvaluateToken(StrBuf *Target, WCTemplateToken *Token, void *Context, int state)
626 {
627         void *vVar;
628 // much output, since pName is not terminated...
629 //      lprintf(1,"Doing token: %s\n",Token->pName);
630         if (Token->Flags == SV_GETTEXT) {
631                 TmplGettext(Target, Token->nParameters, Token);
632         }
633         else if (Token->Flags == SV_CONDITIONAL) {
634                 return EvaluateConditional(Token, Context, 1, state);
635         }
636         else if (Token->Flags == SV_NEG_CONDITIONAL) {
637                 return EvaluateConditional(Token, Context, 0, state);
638         }
639         else if (GetHash(GlobalNS, Token->pName, Token->NameEnd, &vVar)) {
640                 HashHandler *Handler;
641                 Handler = (HashHandler*) vVar;
642                 if ((Token->nParameters < Handler->nMinArgs) || 
643                     (Token->nParameters > Handler->nMaxArgs)) {
644                         lprintf(1, "Handler [%s] doesn't work with %ld params", 
645                                 Token->pName,
646                                 Token->nParameters);
647                 }
648                 else {
649                         Handler->HandlerFunc(Target, 
650                                              Token->nParameters,
651                                              Token,
652                                              Context); /*TODO: subset of that */
653                 
654                         
655                 }
656         }
657         else {
658                 print_value_of(Target, Token->pName, Token->NameEnd);
659         }
660         return 0;
661 }
662
663 void ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target, void *Context)
664 {
665         int done = 0;
666         int i, state;
667         const char *pData, *pS;
668         long len;
669
670         pS = pData = ChrPtr(Tmpl->Data);
671         len = StrLength(Tmpl->Data);
672         i = 0;
673         state = 0;
674         while (!done) {
675                 if (i >= Tmpl->nTokensUsed) {
676                         StrBufAppendBufPlain(Target, 
677                                              pData, 
678                                              len - (pData - pS), 0);
679                         done = 1;
680                 }
681                 else {
682                         StrBufAppendBufPlain(
683                                 Target, pData, 
684                                 Tmpl->Tokens[i]->pTokenStart - pData, 0);
685                         state = EvaluateToken(Target, Tmpl->Tokens[i], Context, state);
686                         while ((state != 0) && (i+1 < Tmpl->nTokensUsed)) {
687                         /* condition told us to skip till its end condition */
688                                 i++;
689                                 if ((Tmpl->Tokens[i]->Flags == SV_CONDITIONAL) ||
690                                     (Tmpl->Tokens[i]->Flags == SV_NEG_CONDITIONAL)) {
691                                         if (state == EvaluateConditional(Tmpl->Tokens[i], 
692                                                                          Context, 
693                                                                          Tmpl->Tokens[i]->Flags,
694                                                                          state))
695                                                 state = 0;
696                                 }
697                         }
698                         pData = Tmpl->Tokens[i++]->pTokenEnd + 1;
699                         if (i >= Tmpl->nTokensUsed)
700                                 done = 1;
701                 }
702         }
703 }
704
705
706
707 /**
708  * \brief Display a variable-substituted template
709  * \param templatename template file to load
710  */
711 void *load_template(StrBuf *filename, StrBuf *Key, HashList *PutThere)
712 {
713         int fd;
714         struct stat statbuf;
715         const char *pS, *pE, *pch, *Err;
716         int pos;
717         WCTemplate *NewTemplate;
718
719         fd = open(ChrPtr(filename), O_RDONLY);
720         if (fd <= 0) {
721                 lprintf(1, "ERROR: could not open template '%s' - %s\n",
722                         ChrPtr(filename), strerror(errno));
723                 return NULL;
724         }
725
726         if (fstat(fd, &statbuf) == -1) {
727                 lprintf(1, "ERROR: could not stat template '%s' - %s\n",
728                         ChrPtr(filename), strerror(errno));
729                 return NULL;
730         }
731
732         NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
733         NewTemplate->Data = NewStrBufPlain(NULL, statbuf.st_size);
734         NewTemplate->nTokensUsed = 0;
735         NewTemplate->TokenSpace = 0;
736         NewTemplate->Tokens = NULL;
737         if (StrBufReadBLOB(NewTemplate->Data, &fd, 1, statbuf.st_size, &Err) < 0) {
738                 close(fd);
739                 FreeWCTemplate(NewTemplate);
740                 lprintf(1, "ERROR: reading template '%s' - %s<br />\n",
741                         ChrPtr(filename), strerror(errno));
742                 return NULL;
743         }
744         close(fd);
745
746         pS = pch = ChrPtr(NewTemplate->Data);
747         pE = pS + StrLength(NewTemplate->Data);
748         while (pch < pE) {
749                 const char *pts, *pte;
750                 int InQuotes = 0;
751                 int InDoubleQuotes = 0;
752                 pos = (-1);
753                 for (; pch < pE; pch ++) {
754                         if ((*pch=='<')&&(*(pch + 1)=='?'))
755                                 break;
756                 }
757                 if (pch >= pE)
758                         continue;
759                 pts = pch;
760                 for (; pch < pE - 1; pch ++) {
761                         if (*pch == '"')
762                                 InDoubleQuotes = ! InDoubleQuotes;
763                         else if (*pch == '\'')
764                                 InQuotes = ! InQuotes;
765                         else if ((!InQuotes  && !InDoubleQuotes) &&
766                                  ((*pch!='\\')&&(*(pch + 1)=='>'))) {
767                                 pch ++;
768                                 break;
769                         }
770                 }
771                 if (pch + 1 >= pE)
772                         continue;
773                 pte = pch;
774                 PutNewToken(NewTemplate, 
775                             NewTemplateSubstitute(NewTemplate->Data, pS, pts, pte));
776                 pch ++;
777         }
778         Put(PutThere, ChrPtr(Key), StrLength(Key), NewTemplate, FreeWCTemplate);
779         return NewTemplate;
780 }
781
782 /**
783  * \brief Display a variable-substituted template
784  * \param templatename template file to load
785  */
786 void DoTemplate(const char *templatename, long len, void *Context, StrBuf *Target) 
787 {
788         HashList *Static;
789         HashList *StaticLocal;
790         void *vTmpl;
791         
792         if (Target == NULL)
793                 Target = WC->WBuf;
794         if (WC->is_mobile) {
795                 Static = WirelessTemplateCache;
796                 StaticLocal = WirelessLocalTemplateCache;
797         }
798         else {
799                 Static = TemplateCache;
800                 StaticLocal = LocalTemplateCache;
801         }
802
803         if (!GetHash(StaticLocal, templatename, len, &vTmpl) &&
804             !GetHash(Static, templatename, len, &vTmpl)) {
805                 printf ("didn't find %s\n", templatename);
806                 return;
807         }
808         if (vTmpl == NULL) 
809                 return;
810         ProcessTemplate(vTmpl, Target, Context);        
811 }
812
813 int LoadTemplateDir(const char *DirName, HashList *wireless, HashList *big)
814 {
815         StrBuf *FileName;
816         StrBuf *Tag;
817         StrBuf *Dir;
818         DIR *filedir = NULL;
819         struct dirent *filedir_entry;
820         int d_namelen;
821         int d_without_ext;
822         int IsMobile;
823         
824         Dir = NewStrBuf();
825         StrBufPrintf(Dir, "%s/t", DirName);
826         filedir = opendir (ChrPtr(Dir));
827         if (filedir == NULL) {
828                 return 0;
829         }
830
831         FileName = NewStrBuf();
832         Tag = NewStrBuf();
833         while ((filedir_entry = readdir(filedir)))
834         {
835                 char *MinorPtr;
836                 char *PStart;
837 #ifdef _DIRENT_HAVE_D_NAMELEN
838                 d_namelen = filedir_entry->d_namelen;
839 #else
840                 d_namelen = strlen(filedir_entry->d_name);
841 #endif
842                 d_without_ext = d_namelen;
843                 while ((d_without_ext > 0) && (filedir_entry->d_name[d_without_ext] != '.'))
844                         d_without_ext --;
845                 if ((d_without_ext == 0) || (d_namelen < 3))
846                         continue;
847
848                 IsMobile = (strstr(filedir_entry->d_name, ".m.html")!= NULL);
849                 PStart = filedir_entry->d_name;
850                 StrBufPrintf(FileName, "%s/%s", ChrPtr(Dir),  filedir_entry->d_name);
851                 MinorPtr = strchr(filedir_entry->d_name, '.');
852                 if (MinorPtr != NULL)
853                         *MinorPtr = '\0';
854                 StrBufPlain(Tag, filedir_entry->d_name, MinorPtr - filedir_entry->d_name);
855
856
857                 printf("%s %d %s\n",ChrPtr(FileName), IsMobile, ChrPtr(Tag));
858                 load_template(FileName, Tag, (IsMobile)?wireless:big);          
859         }
860         closedir(filedir);
861         FreeStrBuf(&FileName);
862         FreeStrBuf(&Tag);
863         FreeStrBuf(&Dir);
864         return 1;
865 }
866
867 void InitTemplateCache(void)
868 {
869         LoadTemplateDir(static_dirs[0],
870                         WirelessTemplateCache,
871                         TemplateCache);
872         LoadTemplateDir(static_dirs[1],
873                         WirelessLocalTemplateCache,
874                         LocalTemplateCache);
875 }
876
877 void tmplput_serv_ip(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
878 {
879         StrBufAppendPrintf(Target, "%d", WC->ctdl_pid);
880 }
881
882 void tmplput_serv_nodename(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
883 {
884         StrEscAppend(Target, NULL, serv_info.serv_nodename, 0, 0);
885 }
886
887 void tmplput_serv_humannode(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
888 {
889         StrEscAppend(Target, NULL, serv_info.serv_humannode, 0, 0);
890 }
891
892 void tmplput_serv_fqdn(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
893 {
894         StrEscAppend(Target, NULL, serv_info.serv_fqdn, 0, 0);
895 }
896
897 void tmmplput_serv_software(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
898 {
899         StrEscAppend(Target, NULL, serv_info.serv_software, 0, 0);
900 }
901
902 void tmplput_serv_rev_level(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
903 {
904         StrBufAppendPrintf(Target, "%d.%02d",
905                             serv_info.serv_rev_level / 100,
906                             serv_info.serv_rev_level % 100);
907 }
908
909 void tmmplput_serv_bbs_city(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
910 {
911         StrEscAppend(Target, NULL, serv_info.serv_bbs_city, 0, 0);
912 }
913
914 void tmplput_current_user(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
915 {
916         StrEscAppend(Target, NULL, WC->wc_fullname, 0, 0);
917 }
918
919 void tmplput_current_room(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
920 {
921         StrEscAppend(Target, NULL, WC->wc_roomname, 0, 0);
922 }
923
924
925 typedef struct _HashIterator {
926         HashList *StaticList;
927         RetrieveHashlistFunc GetHash;
928         HashDestructorFunc Destructor;
929         SubTemplFunc DoSubTemplate;
930 } HashIterator;
931
932 void tmpl_iterate_subtmpl(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
933 {
934         void *vIt;
935         HashIterator *It;
936         HashList *List;
937         HashPos  *it;
938         long len; 
939         const char *Key;
940         void *vContext;
941         StrBuf *SubBuf;
942         int oddeven = 0;
943         
944         if (!GetHash(Iterators, 
945                      Tokens->Params[0]->Start,
946                      Tokens->Params[0]->len,
947                      &vIt))
948                 return;
949         It = (HashIterator*) vIt;
950         if (It->StaticList == NULL)
951                 List = It->GetHash();
952         else
953                 List = It->StaticList;
954
955         SubBuf = NewStrBuf();
956         it = GetNewHashPos();
957         while (GetNextHashPos(List, it, &len, &Key, &vContext)) {
958                 svprintf(HKEY("ITERATE:ODDEVEN"), WCS_STRING, "%s", (oddeven)?"odd":"even");
959                 It->DoSubTemplate(SubBuf, vContext);
960                 DoTemplate(Tokens->Params[1]->Start,
961                            Tokens->Params[1]->len,
962                            vContext, SubBuf);
963                         
964                 StrBufAppendBuf(Target, SubBuf, 0);
965                 FlushStrBuf(SubBuf);
966         }
967         DeleteHashPos(&it);
968         It->Destructor(List);
969 }
970
971 int ConditionalVar(WCTemplateToken *Tokens, void *Context)
972 {
973         void *vsubst;
974         wcsubst *subst;
975         
976         if (!GetHash(WC->vars, 
977                      Tokens->Params[2]->Start,
978                      Tokens->Params[2]->len,
979                      &vsubst))
980                 return 0;
981         subst = (wcsubst*) vsubst;
982         switch(subst->wcs_type) {
983         case WCS_STRING:
984                 if (Tokens->nParameters < 4)
985                         return 0;
986                 return (strcmp(Tokens->Params[3]->Start, subst->wcs_value) == 0);
987         case WCS_SERVCMD:
988                 lprintf(1, "  -> Server [%s]\n", subst->wcs_value);////todo
989                 return 0;
990         case WCS_FUNCTION:
991                 return (subst->wcs_function!=NULL);
992         case WCS_STRBUF:
993         case WCS_STRBUF_REF:
994                 if (Tokens->nParameters < 4)
995                         return 0;
996                 return (strcmp(Tokens->Params[3]->Start, ChrPtr((StrBuf*) subst->wcs_function)) == 0);
997         case WCS_LONG:
998                 if (Tokens->nParameters < 4)
999                         return (subst->lvalue != 0);
1000                 return (subst->lvalue == Tokens->Params[3]->lvalue);
1001         default:
1002                 lprintf(1,"  WARNING: invalid type: [%ld]!\n", subst->wcs_type);
1003         }
1004         return 0;
1005 }
1006
1007 void RegisterITERATOR(const char *Name, long len, 
1008                       HashList *StaticList, 
1009                       RetrieveHashlistFunc GetHash, 
1010                       SubTemplFunc DoSubTempl,
1011                       HashDestructorFunc Destructor)
1012 {
1013         HashIterator *It = (HashIterator*)malloc(sizeof(HashIterator));
1014         It->StaticList = StaticList;
1015         It->GetHash = GetHash;
1016         It->DoSubTemplate = DoSubTempl;
1017         It->Destructor = Destructor;
1018         Put(Iterators, Name, len, It, NULL);
1019 }
1020
1021 void RegisterConditional(const char *Name, long len, 
1022                          int nParams,
1023                          WCConditionalFunc CondF)
1024 {
1025         ConditionalStruct *Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
1026         Cond->nParams = nParams;
1027         Cond->CondF = CondF;
1028         Put(Contitionals, Name, len, Cond, NULL);
1029 }
1030
1031 void 
1032 InitModule_SUBST
1033 (void)
1034 {
1035         RegisterNamespace("SERV_PID", 0, 0, tmplput_serv_ip);
1036         RegisterNamespace("SERV_NODENAME", 0, 0, tmplput_serv_nodename);
1037         RegisterNamespace("SERV_HUMANNODE", 0, 0, tmplput_serv_humannode);
1038         RegisterNamespace("SERV_FQDN", 0, 0, tmplput_serv_fqdn);
1039         RegisterNamespace("SERV_SOFTWARE", 0, 0, tmmplput_serv_software);
1040         RegisterNamespace("SERV_REV_LEVEL", 0, 0, tmplput_serv_rev_level);
1041         RegisterNamespace("SERV_BBS_CITY", 0, 0, tmmplput_serv_bbs_city);
1042         RegisterNamespace("CURRENT_USER", 0, 0, tmplput_current_user);
1043         RegisterNamespace("CURRENT_ROOM", 0, 0, tmplput_current_room);
1044         RegisterNamespace("ITERATE", 2, 4, tmpl_iterate_subtmpl);
1045         RegisterConditional(HKEY("COND:SUBST"), 3, ConditionalVar);
1046 }
1047
1048 /*@}*/