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