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