cd0afa979543d81917988a297c1564d5cb18a283
[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 /**
283  * \brief back end for print_value_of() ... does a server command
284  * \param servcmd server command to execute on the citadel server
285  */
286 void pvo_do_cmd(char *servcmd) {
287         char buf[SIZ];
288
289         serv_puts(servcmd);
290         serv_getln(buf, sizeof buf);
291
292         switch(buf[0]) {
293                 case '2':
294                 case '3':
295                 case '5':
296                         wprintf("%s\n", &buf[4]);
297                         break;
298                 case '1':
299                         fmout("CENTER");
300                         break;
301                 case '4':
302                         wprintf("%s\n", &buf[4]);
303                         serv_puts("000");
304                         break;
305         }
306 }
307
308 /**
309  * \brief Print the value of a variable
310  * \param keyname get a key to print
311  */
312 void print_value_of(const char *keyname, size_t keylen) {
313         struct wcsession *WCC = WC;
314         wcsubst *ptr;
315         void *fcn();
316         void *vVar;
317
318         /*if (WCC->vars != NULL) PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
319         if (keyname[0] == '=') {
320                 DoTemplate(keyname+1, keylen - 1, NULL, NULL);
321         }
322         /** Page-local variables */
323         if ((WCC->vars!= NULL) && GetHash(WCC->vars, keyname, keylen, &vVar)) {
324                 ptr = (wcsubst*) vVar;
325                 switch(ptr->wcs_type) {
326                 case WCS_STRING:
327                         wprintf("%s", (const char*)ptr->wcs_value);
328                         break;
329                 case WCS_SERVCMD:
330                         pvo_do_cmd(ptr->wcs_value);
331                         break;
332                 case WCS_FUNCTION:
333                         (*ptr->wcs_function) ();
334                         break;
335                 default:
336                         lprintf(1,"WARNING: invalid value in SV-Hash at %s!", keyname);
337                 }
338         }
339 }
340
341
342 void PutNewToken(WCTemplate *Template, WCTemplateToken *NewToken)
343 {
344         if (Template->nTokensUsed + 1 >= Template->TokenSpace) {
345                 if (Template->TokenSpace <= 0) {
346                         Template->Tokens = (WCTemplateToken**)malloc(
347                                 sizeof(WCTemplateToken*) * 10);
348                         Template->TokenSpace = 10;
349                 }
350                 else {
351                         WCTemplateToken **NewTokens;
352                         NewTokens= (WCTemplateToken**)malloc(
353                                 sizeof(WCTemplateToken*) * 
354                                 Template->TokenSpace * 2);
355                         memcpy(NewTokens, Template->Tokens, 
356                                sizeof(WCTemplateToken) * Template->nTokensUsed);
357                         free(Template->Tokens);
358                         Template->TokenSpace *= 2;
359                         Template->Tokens = NewTokens;
360                 }
361         }
362         Template->Tokens[(Template->nTokensUsed)++] = NewToken;
363 }
364
365 TemplateParam *GetNextParamter(StrBuf *Buf, const char **pCh, const char *pe)
366 {
367         const char *pch = *pCh;
368         const char *pchs, *pche;
369         TemplateParam *Parm = (TemplateParam *) malloc(sizeof(TemplateParam));
370         char quote = '\0';
371         
372         /* Skip leading whitespaces */
373         while ((*pch == ' ' )||
374                (*pch == '\t')||
375                (*pch == '\r')||
376                (*pch == '\n')) pch ++;
377         if (*pch == '"')
378                 quote = '"';
379         else if (*pch == '\'')
380                 quote = '\'';
381         if (quote != '\0') {
382                 pch ++;
383                 pchs = pch;
384                 Parm->Type = TYPE_STR;
385                 while (pch <= pe &&
386                        ((*pch != quote) ||
387                         ( (pch > pchs) && (*(pch - 1) == '\\'))
388                                )) {
389                         pch ++;
390                 }
391                 pche = pch;
392                 if (*pch != quote) {
393                         lprintf(1, "Error evaluating template param [%s]\n", *pCh);
394                         pch ++;
395                         free(Parm);
396                         return NULL;
397                 }
398                 else {
399                         StrBufPeek(Buf, pch, -1, '\0');         
400                         Parm->Start = pchs;
401                         Parm->len = pche - pchs;
402                         pch ++; /* move after trailing quote */
403                 }
404         }
405         else {
406                 Parm->Type = TYPE_LONG;
407                 pchs = pch;
408                 while ((pch <= pe) &&
409                        (isdigit(*pch) ||
410                         (*pch == '+') ||
411                         (*pch == '-')))
412                         pch ++;
413                 pch ++;
414                 if (pch - pchs > 1){
415                         StrBufPeek(Buf, pch, -1, '\0');
416                         Parm->lvalue = atol(pchs);
417                         Parm->Start = pchs;
418                         pch++;
419                 }
420                 else {
421                         Parm->lvalue = 0;
422                         lprintf(1, "Error evalating template long param [%s]", *pCh);
423                         free(Parm);
424                         return NULL;
425                 }
426         }
427         while ((*pch == ' ' )||
428                (*pch == '\t')||
429                (*pch == '\r')||
430                (*pch == '\n')) pch ++;
431
432         *pCh = pch;
433         return Parm;
434 }
435
436 WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf, 
437                                        const char *pStart, 
438                                        const char *pTmplStart, 
439                                        const char *pTmplEnd)
440 {
441         const char *pch;
442         TemplateParam *Param;
443         WCTemplateToken *NewToken = (WCTemplateToken*)malloc(sizeof(WCTemplateToken));
444
445         NewToken->IsGettext = 0;
446         NewToken->pTokenStart = pTmplStart;
447         NewToken->TokenStart = pTmplStart - pStart;
448         NewToken->TokenEnd =  (pTmplEnd - pStart) - NewToken->TokenStart;
449         NewToken->pTokenEnd = pTmplEnd;
450         NewToken->NameEnd = NewToken->TokenEnd - 2;
451         
452         StrBufPeek(Buf, pTmplStart, + 1, '\0');
453         StrBufPeek(Buf, pTmplEnd, -1, '\0');
454         pch = NewToken->pName = pTmplStart + 2;
455
456         NewToken->HaveParameters = 0;;
457         NewToken->nParameters = 0;
458
459         while (pch <= pTmplEnd - 1) {
460                 if (*pch == '(') {
461                         StrBufPeek(Buf, pch, -1, '\0');
462                         NewToken->NameEnd = pch - NewToken->pName;
463                         pch ++;
464                         while (pch <= pTmplEnd - 1) {
465                                 Param = GetNextParamter(Buf, &pch, pTmplEnd - 1);
466                                 if (Param != NULL) {
467                                         NewToken->HaveParameters = 1;
468                                         if (NewToken->nParameters > MAXPARAM) {
469                                                 lprintf(1, "Only %ld Tokens supported!\n", MAXPARAM);
470                                                 return NULL;
471                                         }
472                                         NewToken->Params[NewToken->nParameters++] = Param;
473                                 }
474                                 else break;
475                         }
476                         if((NewToken->NameEnd == 1) &&
477                            (NewToken->HaveParameters == 1) && 
478                            (NewToken->nParameters == 1) &&
479                            (*(NewToken->pName) == '_'))
480                                 NewToken->IsGettext = 1;
481                 }
482                 else pch ++;            
483         }
484         return NewToken;
485 }
486
487 void FreeWCTemplate(void *vFreeMe)
488 {
489         int i;
490         WCTemplate *FreeMe = (WCTemplate*)vFreeMe;
491
492         if (FreeMe->TokenSpace > 0) {
493                 for (i = 0; i < FreeMe->nTokensUsed; i ++) {
494                         free(FreeMe->Tokens[i]);
495                 }
496                 free(FreeMe->Tokens);
497         }
498         free(FreeMe);
499 }
500
501 void EvaluateToken(StrBuf *Target, WCTemplateToken *Token, void *Context)
502 {
503         void *vVar;
504 // much output, since pName is not terminated...
505 //      lprintf(1,"Doing token: %s\n",Token->pName);
506         if (Token->IsGettext)
507                 TmplGettext(Target, Token->nParameters, Token);
508         else if (GetHash(GlobalNS, Token->pName, Token->NameEnd, &vVar)) {
509                 HashHandler *Handler;
510                 Handler = (HashHandler*) vVar;
511                 if ((Token->nParameters < Handler->nMinArgs) || 
512                     (Token->nParameters < Handler->nMaxArgs)) {
513                         lprintf(1, "Handler [%s] doesn't work with %ld params", 
514                                 Token->pName,
515                                 Token->nParameters);
516                 }
517                 else {
518                         Handler->HandlerFunc(Target, 
519                                              Token->nParameters,
520                                              Token,
521                                              Context); /*TODO: subset of that */
522                 
523                         
524                 }
525         }
526         else {
527                 print_value_of(Token->pName, Token->NameEnd);
528         }
529 }
530
531 void ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target, void *Context)
532 {
533         int done = 0;
534         int i;
535         const char *pData, *pS;
536         long len;
537
538         pS = pData = ChrPtr(Tmpl->Data);
539         len = StrLength(Tmpl->Data);
540         i = 0;
541         while (!done) {
542                 if (i >= Tmpl->nTokensUsed) {
543                         StrBufAppendBufPlain(Target, pData, len, 0);
544                         done = 1;
545                 }
546                 else {
547                         StrBufAppendBufPlain(
548                                 Target, pData, 
549                                 Tmpl->Tokens[i]->pTokenStart - pData, 0);
550                         EvaluateToken(Target, Tmpl->Tokens[i], Context);
551                         pData = Tmpl->Tokens[i++]->pTokenEnd + 1;
552                 }
553         }
554 }
555
556
557
558 /**
559  * \brief Display a variable-substituted template
560  * \param templatename template file to load
561  */
562 void *load_template(StrBuf *filename, StrBuf *Key, HashList *PutThere)
563 {
564         int fd;
565         struct stat statbuf;
566         const char *pS, *pE, *pch, *Err;
567         int pos;
568         WCTemplate *NewTemplate;
569
570         fd = open(ChrPtr(filename), O_RDONLY);
571         if (fd <= 0) {
572                 lprintf(1, "ERROR: could not open template '%s' - %s\n",
573                         ChrPtr(filename), strerror(errno));
574                 return NULL;
575         }
576
577         if (fstat(fd, &statbuf) == -1) {
578                 lprintf(1, "ERROR: could not stat template '%s' - %s\n",
579                         ChrPtr(filename), strerror(errno));
580                 return NULL;
581         }
582
583         NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
584         NewTemplate->Data = NewStrBufPlain(NULL, statbuf.st_size);
585         NewTemplate->nTokensUsed = 0;
586         NewTemplate->TokenSpace = 0;
587         NewTemplate->Tokens = NULL;
588         if (StrBufReadBLOB(NewTemplate->Data, &fd, 1, statbuf.st_size, &Err) < 0) {
589                 close(fd);
590                 FreeWCTemplate(NewTemplate);
591                 lprintf(1, "ERROR: reading template '%s' - %s<br />\n",
592                         ChrPtr(filename), strerror(errno));
593                 return NULL;
594         }
595         close(fd);
596
597         pS = pch = ChrPtr(NewTemplate->Data);
598         pE = pS + StrLength(NewTemplate->Data);
599         while (pch < pE) {
600                 const char *pts, *pte;
601                 int InQuotes = 0;
602                 int InDoubleQuotes = 0;
603                 pos = (-1);
604                 for (; pch < pE; pch ++) {
605                         if ((*pch=='<')&&(*(pch + 1)=='?'))
606                                 break;
607                 }
608                 if (pch >= pE)
609                         continue;
610                 pts = pch;
611                 for (; pch < pE - 1; pch ++) {
612                         if (*pch == '"')
613                                 InDoubleQuotes = ! InDoubleQuotes;
614                         else if (*pch == '\'')
615                                 InQuotes = ! InQuotes;
616                         else if ((!InQuotes  && !InDoubleQuotes) &&
617                                  ((*pch!='\\')&&(*(pch + 1)=='>'))) {
618                                 pch ++;
619                                 break;
620                         }
621                 }
622                 if (pch + 1 >= pE)
623                         continue;
624                 pte = pch;
625                 PutNewToken(NewTemplate, 
626                             NewTemplateSubstitute(NewTemplate->Data, pS, pts, pte));
627                 pch ++;
628         }
629         Put(PutThere, ChrPtr(Key), StrLength(Key), NewTemplate, FreeWCTemplate);
630         return NewTemplate;
631 }
632
633 /**
634  * \brief Display a variable-substituted template
635  * \param templatename template file to load
636  */
637 void DoTemplate(const char *templatename, long len, void *Context, StrBuf *Target) 
638 {
639         HashList *Static;
640         HashList *StaticLocal;
641         void *vTmpl;
642
643         if (WC->is_mobile) {
644                 Static = WirelessTemplateCache;
645                 StaticLocal = WirelessLocalTemplateCache;
646         }
647         else {
648                 Static = TemplateCache;
649                 StaticLocal = LocalTemplateCache;
650         }
651
652         if (!GetHash(StaticLocal, templatename, len, &vTmpl) &&
653             !GetHash(Static, templatename, len, &vTmpl)) {
654                 printf ("didn't find %s\n", templatename);
655                 return;
656         }
657         if (vTmpl == NULL) 
658                 return;
659         ProcessTemplate(vTmpl, WC->WBuf, Context);      
660 }
661
662 int LoadTemplateDir(const char *DirName, HashList *wireless, HashList *big)
663 {
664         StrBuf *FileName;
665         StrBuf *Tag;
666         StrBuf *Dir;
667         DIR *filedir = NULL;
668         struct dirent *filedir_entry;
669         int d_namelen;
670         int d_without_ext;
671         int IsMobile;
672         
673         Dir = NewStrBuf();
674         StrBufPrintf(Dir, "%s/t", DirName);
675         filedir = opendir (ChrPtr(Dir));
676         if (filedir == NULL) {
677                 return 0;
678         }
679
680         FileName = NewStrBuf();
681         Tag = NewStrBuf();
682         while ((filedir_entry = readdir(filedir)))
683         {
684                 char *MinorPtr;
685                 char *PStart;
686 #ifdef _DIRENT_HAVE_D_NAMELEN
687                 d_namelen = filedir_entry->d_namelen;
688 #else
689                 d_namelen = strlen(filedir_entry->d_name);
690 #endif
691                 d_without_ext = d_namelen;
692                 while ((d_without_ext > 0) && (filedir_entry->d_name[d_without_ext] != '.'))
693                         d_without_ext --;
694                 if ((d_without_ext == 0) || (d_namelen < 3))
695                         continue;
696
697                 IsMobile = (strstr(filedir_entry->d_name, ".m.html")!= NULL);
698                 PStart = filedir_entry->d_name;
699                 StrBufPrintf(FileName, "%s/%s", ChrPtr(Dir),  filedir_entry->d_name);
700                 MinorPtr = strchr(filedir_entry->d_name, '.');
701                 if (MinorPtr != NULL)
702                         *MinorPtr = '\0';
703                 StrBufPlain(Tag, filedir_entry->d_name, MinorPtr - filedir_entry->d_name);
704
705
706                 printf("%s %d %s\n",ChrPtr(FileName), IsMobile, ChrPtr(Tag));
707                 load_template(FileName, Tag, (IsMobile)?wireless:big);          
708         }
709         closedir(filedir);
710         FreeStrBuf(&FileName);
711         FreeStrBuf(&Tag);
712         FreeStrBuf(&Dir);
713         return 1;
714 }
715
716 void InitTemplateCache(void)
717 {
718         LoadTemplateDir(static_dirs[0],
719                         WirelessTemplateCache,
720                         TemplateCache);
721         LoadTemplateDir(static_dirs[1],
722                         WirelessLocalTemplateCache,
723                         LocalTemplateCache);
724 }
725
726 void tmplput_serv_ip(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
727 {
728         StrBufAppendPrintf(Target, "%d", WC->ctdl_pid);
729 }
730
731 void tmplput_serv_nodename(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
732 {
733         escputs(serv_info.serv_nodename); ////TODO: respcect Target
734 }
735
736 void tmplput_serv_humannode(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
737 {
738         escputs(serv_info.serv_humannode);////TODO: respcect Target
739 }
740
741 void tmplput_serv_fqdn(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
742 {
743         escputs(serv_info.serv_fqdn);////TODO: respcect Target
744 }
745
746 void tmmplput_serv_software(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
747 {
748         escputs(serv_info.serv_software);////TODO: respcect Target
749 }
750
751 void tmplput_serv_rev_level(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
752 {
753         StrBufAppendPrintf(Target, "%d.%02d",
754                             serv_info.serv_rev_level / 100,
755                             serv_info.serv_rev_level % 100);
756 }
757
758 void tmmplput_serv_bbs_city(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
759 {
760         escputs(serv_info.serv_bbs_city);////TODO: respcect Target
761 }
762
763 void tmplput_current_user(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
764 {
765         escputs(WC->wc_fullname);////TODO: respcect Target
766 }
767
768 void tmplput_current_room(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
769 {
770         escputs(WC->wc_roomname);////TODO: respcect Target
771 }
772
773
774 typedef struct _HashIterator {
775         HashList *StaticList;
776         RetrieveHashlistFunc GetHash;
777         HashDestructorFunc Destructor;
778         SubTemplFunc DoSubTemplate;
779 } HashIterator;
780
781 void tmpl_iterate_subtmpl(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context)
782 {
783         void *vIt;
784         HashIterator *It;
785         HashList *List;
786         HashPos  *it;
787         long len; 
788         const char *Key;
789         void *vContext;
790         StrBuf *SubBuf;
791         
792         if (!GetHash(Iterators, 
793                      Tokens->Params[0]->Start,
794                      Tokens->Params[0]->len,
795                      &vIt))
796                 return;
797         It = (HashIterator*) vIt;
798         if (It->StaticList == NULL)
799                 List = It->GetHash();
800         else
801                 List = It->StaticList;
802
803         SubBuf = NewStrBuf();
804         it = GetNewHashPos();
805         while (GetNextHashPos(List, it, &len, &Key, &vContext)) {
806                 It->DoSubTemplate(SubBuf, vContext);
807                 DoTemplate(Tokens->Params[0]->Start,
808                            Tokens->Params[0]->len,
809                            vContext, SubBuf);
810                         
811                 StrBufAppendBuf(Target, SubBuf, 0);
812                 FlushStrBuf(SubBuf);
813         }
814         DeleteHashPos(&it);
815         It->Destructor(List);
816 }
817
818
819 void RegisterITERATOR(const char *Name, long len, 
820                       HashList *StaticList, 
821                       RetrieveHashlistFunc GetHash, 
822                       SubTemplFunc DoSubTempl,
823                       HashDestructorFunc Destructor)
824 {
825         HashIterator *It = (HashIterator*)malloc(sizeof(HashIterator));
826         It->StaticList = StaticList;
827         It->GetHash = GetHash;
828         It->DoSubTemplate = DoSubTempl;
829         It->Destructor = Destructor;
830         Put(Iterators, Name, len, It, NULL);
831 }
832
833 void 
834 InitModule_SUBST
835 (void)
836 {
837         RegisterNamespace("SERV_PID", 0, 0, tmplput_serv_ip);
838         RegisterNamespace("SERV_NODENAME", 0, 0, tmplput_serv_nodename);
839         RegisterNamespace("SERV_HUMANNODE", 0, 0, tmplput_serv_humannode);
840         RegisterNamespace("SERV_FQDN", 0, 0, tmplput_serv_fqdn);
841         RegisterNamespace("SERV_SOFTWARE", 0, 0, tmmplput_serv_software);
842         RegisterNamespace("SERV_REV_LEVEL", 0, 0, tmplput_serv_rev_level);
843         RegisterNamespace("SERV_BBS_CITY", 0, 0, tmmplput_serv_bbs_city);
844         RegisterNamespace("CURRENT_USER", 0, 0, tmplput_current_user);
845         RegisterNamespace("CURRENT_ROOM", 0, 0, tmplput_current_room);
846         RegisterNamespace("ITERATE", 2, 4, tmpl_iterate_subtmpl);
847 }
848
849 /*@}*/