]> code.citadel.org Git - citadel.git/blobdiff - webcit/subst.c
* ignore .swp and .orig files so some more void files won't make it into our template...
[citadel.git] / webcit / subst.c
index b073b580ae745bb885ab37c7b0ecf71a541792d8..a4d8d2aaf06d490e9f835c5e288fb3e36e5e4c87 100644 (file)
@@ -1,26 +1,25 @@
 /*
  * $Id$
  */
-/**
- * \defgroup Subst Variable substitution type stuff
- * \ingroup CitadelConfig
- */
+#include "sysdep.h"
 
-/*@{*/
 
-#include "sysdep.h"
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <unistd.h>
 #include <dirent.h>
 #include <errno.h>
+
+#include <unistd.h>
+#include <stdio.h>
 #include <stdarg.h>
+#include <stddef.h>
+
 #define SHOW_ME_VAPPEND_PRINTF
 
 #include "webcit.h"
 #include "webserver.h"
 
-extern char *static_dirs[PATH_MAX];  /**< Disk representation */
+extern char *static_dirs[PATH_MAX];  /* Disk representation */
 
 HashList *WirelessTemplateCache;
 HashList *WirelessLocalTemplateCache;
@@ -31,6 +30,7 @@ HashList *GlobalNS;
 HashList *Iterators;
 HashList *Conditionals;
 HashList *SortHash;
+HashList *Defines;
 
 int DumpTemplateI18NStrings = 0;
 int LoadTemplates = 0;
@@ -49,15 +49,15 @@ const char EmptyStr[]="";
 
 
 /*
- * \brief Dynamic content for variable substitution in templates
+ * Dynamic content for variable substitution in templates
  */
 typedef struct _wcsubst {
        ContextFilter Filter;
-       int wcs_type;                       /* which type of Substitution are we */
-       char wcs_key[32];                   /* copy of our hashkey for debugging */
-       StrBuf *wcs_value;                  /* if we're a string, keep it here */
-       long lvalue;                        /* type long? keep data here */
-       WCHandlerFunc wcs_function; /* funcion hook ???*/
+       int wcs_type;                           /* which type of Substitution are we */
+       char wcs_key[32];                       /* copy of our hashkey for debugging */
+       StrBuf *wcs_value;                      /* if we're a string, keep it here */
+       long lvalue;                            /* type long? keep data here */
+       WCHandlerFunc wcs_function;             /* funcion hook ???*/
 } wcsubst;
 
 
@@ -72,7 +72,7 @@ typedef struct _WCTemplate {
 
 typedef struct _HashHandler {
        ContextFilter Filter;
-
+       WCPreevalFunc PreEvalFunc;
        WCHandlerFunc HandlerFunc;
 }HashHandler;
 
@@ -110,6 +110,7 @@ const char *CtxNames[]  = {
        "Context FLOORS",
        "Context ITERATE",
        "Context ICAL",
+       "Context DavNamespace",
        "Context UNKNOWN"
 };
 
@@ -153,10 +154,14 @@ void LogTemplateError (StrBuf *Target, const char *Type, int ErrorPos, WCTemplpu
                Err = (TP->Tokens!= NULL)? TP->Tokens->pName:"";
                break;
        case ERR_PARM1:
-               Err = (TP->Tokens!= NULL)? TP->Tokens->Params[0]->Start:"";
+               Err = ((TP->Tokens!= NULL) && 
+                      (TP->Tokens->nParameters > 0))? 
+                       TP->Tokens->Params[0]->Start : "";
                break;
        case ERR_PARM2:
-               Err = (TP->Tokens!= NULL)? TP->Tokens->Params[1]->Start:"";
+               Err = ((TP->Tokens!= NULL) && 
+                      (TP->Tokens->nParameters > 1))? 
+                       TP->Tokens->Params[1]->Start : "";
                break;
        }
        if (TP->Tokens != NULL) 
@@ -180,8 +185,11 @@ void LogTemplateError (StrBuf *Target, const char *Type, int ErrorPos, WCTemplpu
                return;
 */
        WCC = WC;
-       if (WCC == NULL)
-               return;
+       if (WCC == NULL) {
+               FreeStrBuf(&Info);
+               FreeStrBuf(&Error);
+               return; 
+       }
 
        Header = NewStrBuf();
        if (TP->Tokens != NULL) 
@@ -287,6 +295,7 @@ void RegisterNS(const char *NSName,
                int nMinArgs, 
                int nMaxArgs, 
                WCHandlerFunc HandlerFunc, 
+               WCPreevalFunc PreevalFunc,
                int ContextRequired)
 {
        HashHandler *NewHandler;
@@ -298,6 +307,7 @@ void RegisterNS(const char *NSName,
        NewHandler->Filter.ContextType = ContextRequired;
        NewHandler->Filter.ControlContextType = CTX_NONE;
 
+       NewHandler->PreEvalFunc = PreevalFunc;
        NewHandler->HandlerFunc = HandlerFunc;  
        Put(GlobalNS, NSName, len, NewHandler, NULL);
 }
@@ -396,12 +406,13 @@ void FreeWCTemplate(void *vFreeMe)
 }
 
 
-/**
- * \brief debugging function to print array to log
+/*
+ * debugging function to print array to log
  */
 void VarPrintTransition(void *vVar1, void *vVar2, int odd){}
-/**
- * \brief debugging function to print array to log
+
+/*
+ * debugging function to print array to log
  */
 void VarPrintEntry(const char *Key, void *vSubst, int odd)
 {
@@ -426,8 +437,8 @@ void VarPrintEntry(const char *Key, void *vSubst, int odd)
 
 
 
-/**
- * \brief Clear out the list of substitution variables local to this session
+/*
+ * Clear out the list of substitution variables local to this session
  */
 void clear_substs(wcsession *wc) {
 
@@ -436,8 +447,8 @@ void clear_substs(wcsession *wc) {
        }
 }
 
-/**
- * \brief Clear out the list of substitution variables local to this session
+/*
+ * Clear out the list of substitution variables local to this session
  */
 void clear_local_substs(void) {
        clear_substs (WC);
@@ -497,8 +508,8 @@ void FlushPayload(wcsubst *ptr, int reusestrbuf, int type)
 }
 
 
-/**
- * \brief destructor; kill one entry.
+/*
+ * destructor; kill one entry.
  */
 void deletevar(void *data)
 {
@@ -536,12 +547,12 @@ wcsubst *NewSubstVar(const char *keyname, int keylen, int type)
 }
 
 
-/**
- * \brief Add a substitution variable (local to this session) (strlen version...)
- * \param keyname the replacementstring to substitute
- * \param keytype the kind of the key
- * \param format the format string ala printf
- * \param ... the arguments to substitute in the formatstring
+/*
+ * Add a substitution variable (local to this session) (strlen version...)
+ * keyname the replacementstring to substitute
+ * keytype the kind of the key
+ * format the format string ala printf
+ * ... the arguments to substitute in the formatstring
  */
 void SVPRINTF(char *keyname, int keytype, const char *format,...)
 {
@@ -552,7 +563,7 @@ void SVPRINTF(char *keyname, int keytype, const char *format,...)
        wcsession *WCC = WC;
        
        keylen = strlen(keyname);
-       /**
+       /*
         * First look if we're doing a replacement of
         * an existing key
         */
@@ -567,18 +578,18 @@ void SVPRINTF(char *keyname, int keytype, const char *format,...)
                ptr = NewSubstVar(keyname, keylen, keytype);
        }
 
-       /** Format the string */
+       /* Format the string */
        va_start(arg_ptr, format);
        StrBufVAppendPrintf(ptr->wcs_value, format, arg_ptr);
        va_end(arg_ptr);
 }
 
-/**
- * \brief Add a substitution variable (local to this session)
- * \param keyname the replacementstring to substitute
- * \param keytype the kind of the key
- * \param format the format string ala printf
- * \param ... the arguments to substitute in the formatstring
+/*
+ * Add a substitution variable (local to this session)
+ * keyname the replacementstring to substitute
+ * keytype the kind of the key
+ * format the format string ala printf
+ * ... the arguments to substitute in the formatstring
  */
 void svprintf(char *keyname, size_t keylen, int keytype, const char *format,...)
 {
@@ -587,7 +598,7 @@ void svprintf(char *keyname, size_t keylen, int keytype, const char *format,...)
        wcsubst *ptr = NULL;
        wcsession *WCC = WC;
                
-       /**
+       /*
         * First look if we're doing a replacement of
         * an existing key
         */
@@ -608,12 +619,12 @@ void svprintf(char *keyname, size_t keylen, int keytype, const char *format,...)
        va_end(arg_ptr);
 }
 
-/**
- * \brief Add a substitution variable (local to this session)
- * \param keyname the replacementstring to substitute
- * \param keytype the kind of the key
- * \param format the format string ala printf
- * \param ... the arguments to substitute in the formatstring
+/*
+ * Add a substitution variable (local to this session)
+ * keyname the replacementstring to substitute
+ * keytype the kind of the key
+ * format the format string ala printf
+ * ... the arguments to substitute in the formatstring
  */
 void SVPut(char *keyname, size_t keylen, int keytype, char *Data)
 {
@@ -622,7 +633,7 @@ void SVPut(char *keyname, size_t keylen, int keytype, char *Data)
        wcsession *WCC = WC;
 
        
-       /**
+       /*
         * First look if we're doing a replacement of
         * an existing key
         */
@@ -764,7 +775,7 @@ void GetTemplateTokenString(StrBuf *Target,
        StrBuf *Buf;
        WCTemplputParams SubTP;
 
-       if (TP->Tokens->nParameters < N) {
+       if (N >= TP->Tokens->nParameters) {
                LogTemplateError(Target, 
                                 "TokenParameter", N, TP, 
                                 "invalid token %d. this shouldn't have come till here.\n", N);
@@ -775,6 +786,7 @@ void GetTemplateTokenString(StrBuf *Target,
 
        switch (TP->Tokens->Params[N]->Type) {
 
+       case TYPE_INTDEFINE:
        case TYPE_STR:
                *Value = TP->Tokens->Params[N]->Start;
                *len = TP->Tokens->Params[N]->len;
@@ -851,7 +863,7 @@ void GetTemplateTokenString(StrBuf *Target,
 long GetTemplateTokenNumber(StrBuf *Target, WCTemplputParams *TP, int N, long dflt)
 {
        long Ret;
-       if (TP->Tokens->nParameters < N) {
+       if (N >= TP->Tokens->nParameters) {
                LogTemplateError(Target, 
                                 "TokenParameter", N, TP, 
                                 "invalid token %d. this shouldn't have come till here.\n", N);
@@ -885,7 +897,8 @@ long GetTemplateTokenNumber(StrBuf *Target, WCTemplputParams *TP, int N, long df
                }
                if (get_PREF_LONG(TKEY(N), &Ret, dflt))
                        return Ret;
-               return 0;               
+               return 0;
+       case TYPE_INTDEFINE:
        case TYPE_LONG:
                return TP->Tokens->Params[N]->lvalue;
        case TYPE_PREFINT:
@@ -1070,6 +1083,9 @@ void StrBufAppendTemplate(StrBuf *Target,
        case 'J':
                StrECMAEscAppend(Target, Source, NULL);
          break;
+       case 'K':
+               StrHtmlEcmaEscAppend(Target, Source, NULL, 0, 0);
+         break;
        case 'U':
                StrBufUrlescAppend(Target, Source, NULL);
                break;
@@ -1103,7 +1119,12 @@ void PutNewToken(WCTemplate *Template, WCTemplateToken *NewToken)
        Template->Tokens[(Template->nTokensUsed)++] = NewToken;
 }
 
-TemplateParam *GetNextParameter(StrBuf *Buf, const char **pCh, const char *pe, WCTemplateToken *Tokens, WCTemplate *pTmpl)
+TemplateParam *GetNextParameter(StrBuf *Buf, 
+                               const char **pCh, 
+                               const char *pe, 
+                               WCTemplateToken *Tokens, 
+                               WCTemplate *pTmpl, 
+                               WCTemplputParams *TP)
 {
        const char *pch = *pCh;
        const char *pchs, *pche;
@@ -1137,6 +1158,10 @@ TemplateParam *GetNextParameter(StrBuf *Buf, const char **pCh, const char *pe, W
                        ParamBrace = 1;
                }
        }
+       else if (*pch == '#') {
+               Parm->Type = TYPE_INTDEFINE;
+               pch ++;
+       }
        else if (*pch == '_') {
                Parm->Type = TYPE_GETTEXT;
                pch ++;
@@ -1241,6 +1266,25 @@ TemplateParam *GetNextParameter(StrBuf *Buf, const char **pCh, const char *pe, W
        if (DumpTemplateI18NStrings && (Parm->Type == TYPE_GETTEXT)) {
                StrBufAppendPrintf(I18nDump, "_(\"%s\");\n", Parm->Start);
        }
+       if (Parm->Type == TYPE_INTDEFINE)
+       {
+               void *vPVal;
+
+               if (GetHash(Defines, Parm->Start, Parm->len, &vPVal) &&
+                   (vPVal != NULL))
+               {
+                       long *PVal;
+                       PVal = (long*) vPVal;
+               
+                       Parm->lvalue = *PVal;
+               }
+               else 
+               {
+                       LogTemplateError(NULL, "Define", ERR_PARM1, TP,
+                                        "%s isn't known!!",
+                                        Parm->Start);
+               }
+       }
        *pCh = pch;
        return Parm;
 }
@@ -1291,7 +1335,7 @@ WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf,
                                        "Warning, Non welformed Token; missing right parenthesis");
                        }
                        while (pch < pTmplEnd - 1) {
-                               Param = GetNextParameter(Buf, &pch, pTmplEnd - 1, NewToken, pTmpl);
+                               Param = GetNextParameter(Buf, &pch, pTmplEnd - 1, NewToken, pTmpl, &TP);
                                if (Param != NULL) {
                                        NewToken->HaveParameters = 1;
                                        if (NewToken->nParameters > MAXPARAM) {
@@ -1344,6 +1388,8 @@ WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf,
                        else {
                                NewToken->PreEval = Handler;
                                NewToken->Flags = SV_PREEVALUATED;              
+                               if (Handler->PreEvalFunc != NULL)
+                                       Handler->PreEvalFunc(NewToken);
                        }
                }
                break;
@@ -1374,7 +1420,7 @@ WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf,
        case SV_NEG_CONDITIONAL:
                if (NewToken->nParameters <2) {
                        LogTemplateError(
-                               NULL, "Conditional", ERR_NAME, &TP,
+                               NULL, "Conditional", ERR_PARM1, &TP,
                                "require at least 2 parameters, you gave %d params", 
                                NewToken->nParameters);
                        NewToken->Flags = 0;
@@ -1382,7 +1428,7 @@ WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf,
                }
                if (NewToken->Params[1]->lvalue == 0) {
                        LogTemplateError(
-                               NULL, "Conditional", ERR_NAME, &TP,
+                               NULL, "Conditional", ERR_PARM1, &TP,
                                "Conditional ID (Parameter 1) mustn't be 0!");
                        NewToken->Flags = 0;
                        break;
@@ -1396,7 +1442,7 @@ WCTemplateToken *NewTemplateSubstitute(StrBuf *Buf,
                            (NewToken->Params[0]->Start[0] == 'X'))
                                break;
                        LogTemplateError(
-                               NULL, "Conditional", ERR_NAME, &TP,
+                               NULL, "Conditional", ERR_PARM1, &TP,
                                "Not found!");
 /*
                        NewToken->Error = NewStrBuf();
@@ -1506,7 +1552,11 @@ void *load_template(StrBuf *filename, StrBuf *Key, HashList *PutThere)
                /** Find one <? > */
                pos = (-1);
                for (; pch < pE; pch ++) {
-                       if ((*pch=='<')&&(*(pch + 1)=='?'))
+                       if ((*pch=='<')&&(*(pch + 1)=='?') &&
+                           !((pch == pS) && /* we must ommit a <?xml */
+                             (*(pch + 2) == 'x') && 
+                             (*(pch + 3) == 'm') && 
+                             (*(pch + 4) == 'l')))                          
                                break;
                        if (*pch=='\n') Line ++;
                }
@@ -1547,74 +1597,187 @@ const char* PrintTemplate(void *vSubst)
 
 }
 
-int LoadTemplateDir(const char *DirName, HashList *wireless, HashList *big)
+int LoadTemplateDir(const StrBuf *DirName, HashList *wireless, HashList *big, const StrBuf *BaseKey)
 {
+       int Toplevel;
        StrBuf *FileName;
-       StrBuf *Tag;
-       StrBuf *Dir;
+       StrBuf *Key;
+       StrBuf *SubKey;
+       StrBuf *SubDirectory;
        DIR *filedir = NULL;
        struct dirent *filedir_entry;
+       struct dirent *d;
+       int d_type = 0;
        int d_namelen;
        int d_without_ext;
        int IsMobile;
        
-       Dir = NewStrBuf();
-       StrBufPrintf(Dir, "%s/t", DirName);
-       filedir = opendir (ChrPtr(Dir));
+       d = (struct dirent *)malloc(offsetof(struct dirent, d_name) + PATH_MAX + 1);
+       if (d == NULL) {
+               return 0;
+       }
+
+       filedir = opendir (ChrPtr(DirName));
        if (filedir == NULL) {
-               FreeStrBuf(&Dir);
+               free(d);
                return 0;
        }
 
-       FileName = NewStrBuf();
-       Tag = NewStrBuf();
-       while ((filedir_entry = readdir(filedir)))
+       Toplevel = StrLength(BaseKey) == 0;
+       SubDirectory = NewStrBuf();
+       SubKey = NewStrBuf();
+       FileName = NewStrBufPlain(NULL, PATH_MAX);
+       Key = NewStrBuf();
+       while ((readdir_r(filedir, d, &filedir_entry) == 0) &&
+              (filedir_entry != NULL))
        {
                char *MinorPtr;
                char *PStart;
 #ifdef _DIRENT_HAVE_D_NAMELEN
                d_namelen = filedir_entry->d_namelen;
+               d_type = filedir_entry->d_type;
 #else
+
+#ifndef DT_UNKNOWN
+#define DT_UNKNOWN     0
+#define DT_DIR         4
+#define DT_REG         8
+#define DT_LNK         10
+
+#define IFTODT(mode)   (((mode) & 0170000) >> 12)
+#define DTTOIF(dirtype)        ((dirtype) << 12)
+#endif
                d_namelen = strlen(filedir_entry->d_name);
+               d_type = DT_UNKNOWN;
 #endif
                d_without_ext = d_namelen;
-               while ((d_without_ext > 0) && (filedir_entry->d_name[d_without_ext] != '.'))
-                       d_without_ext --;
-               if ((d_without_ext == 0) || (d_namelen < 3))
-                       continue;
+
                if ((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~')
                        continue; /* Ignore backup files... */
 
-               IsMobile = (strstr(filedir_entry->d_name, ".m.html")!= NULL);
-               PStart = filedir_entry->d_name;
-               StrBufPrintf(FileName, "%s/%s", ChrPtr(Dir),  filedir_entry->d_name);
-               MinorPtr = strchr(filedir_entry->d_name, '.');
-               if (MinorPtr != NULL)
-                       *MinorPtr = '\0';
-               StrBufPlain(Tag, filedir_entry->d_name, MinorPtr - filedir_entry->d_name);
+               if ((d_namelen == 1) && 
+                   (filedir_entry->d_name[0] == '.'))
+                       continue;
 
-               if (LoadTemplates > 1)
-                       lprintf(1, "%s %d %s\n",ChrPtr(FileName), IsMobile, ChrPtr(Tag));
-               if (LoadTemplates == 0)
-                       load_template(FileName, Tag, (IsMobile)?wireless:big);
-               else
-                       prepare_template(FileName, Tag, (IsMobile)?wireless:big);
+               if ((d_namelen == 2) && 
+                   (filedir_entry->d_name[0] == '.') &&
+                   (filedir_entry->d_name[1] == '.'))
+                       continue;
+
+               if (d_type == DT_UNKNOWN) {
+                       struct stat s;
+                       char path[PATH_MAX];
+                       snprintf(path, PATH_MAX, "%s/%s", 
+                                ChrPtr(DirName), filedir_entry->d_name);
+                       if (stat(path, &s) == 0) {
+                               d_type = IFTODT(s.st_mode);
+                       }
+               }
+               switch (d_type)
+               {
+               case DT_DIR:
+                       /* Skip directories we are not interested in... */
+                       if (strcmp(filedir_entry->d_name, ".svn") == 0)
+                               continue;
+
+                       FlushStrBuf(SubKey);
+                       if (!Toplevel) {
+                               /* If we're not toplevel, the upper dirs count as foo_bar_<local name>*/
+                               StrBufAppendBuf(SubKey, BaseKey, 0);
+                               StrBufAppendBufPlain(SubKey, HKEY("_"), 0);
+                       }
+                       StrBufAppendBufPlain(SubKey, filedir_entry->d_name, d_namelen, 0);
+
+                       FlushStrBuf(SubDirectory);
+                       StrBufAppendBuf(SubDirectory, DirName, 0);
+                       if (ChrPtr(SubDirectory)[StrLength(SubDirectory) - 1] != '/')
+                               StrBufAppendBufPlain(SubDirectory, HKEY("/"), 0);
+                       StrBufAppendBufPlain(SubDirectory, filedir_entry->d_name, d_namelen, 0);
+
+                       LoadTemplateDir(SubDirectory, wireless, big, SubKey);
+
+                       break;
+               case DT_LNK: /* TODO: check whether its a file or a directory */
+               case DT_REG:
+
+
+                       while ((d_without_ext > 0) && (filedir_entry->d_name[d_without_ext] != '.'))
+                               d_without_ext --;
+                       if ((d_without_ext == 0) || (d_namelen < 3))
+                               continue;
+                       if (((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~') ||
+                           (strcmp(&filedir_entry->d_name[d_without_ext], ".orig") == 0) ||
+                           (strcmp(&filedir_entry->d_name[d_without_ext], ".swp") == 0))
+                               continue; /* Ignore backup files... */
+                       /* .m.xxx is for mobile useragents! */
+                       if (d_without_ext > 2)
+                               IsMobile = (filedir_entry->d_name[d_without_ext - 1] == 'm') &&
+                                       (filedir_entry->d_name[d_without_ext - 2] == '.');
+                       PStart = filedir_entry->d_name;
+                       StrBufPrintf(FileName, "%s/%s", ChrPtr(DirName),  filedir_entry->d_name);
+                       MinorPtr = strchr(filedir_entry->d_name, '.');
+                       if (MinorPtr != NULL)
+                               *MinorPtr = '\0';
+                       FlushStrBuf(Key);
+                       if (!Toplevel) {
+                               /* If we're not toplevel, the upper dirs count as foo_bar_<local name>*/
+                               StrBufAppendBuf(Key, BaseKey, 0);
+                               StrBufAppendBufPlain(Key, HKEY("_"), 0);
+                       }
+                       StrBufAppendBufPlain(Key, filedir_entry->d_name, MinorPtr - filedir_entry->d_name, 0);
+
+                       if (LoadTemplates >= 1)
+                               lprintf(1, "%s %d %s\n", ChrPtr(FileName), IsMobile, ChrPtr(Key));
+                       if (LoadTemplates == 0)
+                               load_template(FileName, Key, (IsMobile)?wireless:big);
+                       else
+                               prepare_template(FileName, Key, (IsMobile)?wireless:big);
+               default:
+                       break;
+               }
        }
+       free(d);
        closedir(filedir);
        FreeStrBuf(&FileName);
-       FreeStrBuf(&Tag);
-       FreeStrBuf(&Dir);
+       FreeStrBuf(&Key);
+       FreeStrBuf(&SubDirectory);
+       FreeStrBuf(&SubKey);
        return 1;
 }
 
 void InitTemplateCache(void)
 {
-       LoadTemplateDir(static_dirs[0],
+       StrBuf *Key;
+       StrBuf *Dir;
+
+       Dir = NewStrBuf();
+       Key = NewStrBuf();
+
+       /* Primary Template set... */
+       StrBufPrintf(Dir, "%s/t", static_dirs[0]);
+       LoadTemplateDir(Dir,
                        WirelessTemplateCache,
-                       TemplateCache);
-       LoadTemplateDir(static_dirs[1],
+                       TemplateCache, 
+                       Key);
+
+       /* User local Template set */
+       StrBufPrintf(Dir, "%s/t", static_dirs[1]);
+       LoadTemplateDir(Dir,
                        WirelessLocalTemplateCache,
-                       LocalTemplateCache);
+                       LocalTemplateCache, 
+                       Key);
+       
+       /* Debug Templates, just to be loaded while debugging. */
+       
+       StrBufPrintf(Dir, "%s/dbg", static_dirs[0]);
+       LoadTemplateDir(Dir,
+                       WirelessTemplateCache,
+                       TemplateCache, 
+                       Key);
+
+
+       FreeStrBuf(&Dir);
+       FreeStrBuf(&Key);
 }
 
 
@@ -1800,7 +1963,7 @@ const StrBuf *DoTemplate(const char *templatename, long len, StrBuf *Target, WCT
                TP = &LocalTP;
        }
 
-       if (WC->is_mobile) {
+       if (WC->is_mobile > 0) {
                Static = WirelessTemplateCache;
                StaticLocal = WirelessLocalTemplateCache;
        }
@@ -1882,9 +2045,27 @@ typedef struct _iteratestruct {
        int LastN;
        }IterateStruct; 
 
-void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
+int preeval_iterate(WCTemplateToken *Token)
 {
+       WCTemplputParams TPP;
+       WCTemplputParams *TP;
        void *vIt;
+
+       memset(&TPP, 0, sizeof(WCTemplputParams));
+       TP = &TPP;
+       TP->Tokens = Token;
+       if (!GetHash(Iterators, TKEY(0), &vIt)) {
+               LogTemplateError(
+                       NULL, "Iterator", ERR_PARM1, TP,
+                       "not found");
+               return 0;
+       }
+       Token->Preeval2 = vIt;
+       return 1;
+}
+
+void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
+{
        HashIterator *It;
        HashList *List;
        HashPos  *it;
@@ -1905,14 +2086,13 @@ void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
        memset(&Status, 0, sizeof(IterateStruct));
        memcpy (&SubTP, &TP, sizeof(WCTemplputParams));
        
-       if (!GetHash(Iterators, TKEY(0), &vIt)) {
+       It = (HashIterator*) TP->Tokens->Preeval2;
+       if (It == NULL) {
                LogTemplateError(
                        Target, "Iterator", ERR_PARM1, TP, "Unknown!");
                return;
        }
 
-       It = (HashIterator*) vIt;
-
        if (TP->Tokens->nParameters < It->AdditionalParams + 2) {
                LogTemplateError(                               
                        Target, "Iterator", ERR_PARM1, TP,
@@ -1978,7 +2158,7 @@ void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
        while (GetNextHashPos(List, it, &Status.KeyLen, &Status.Key, &vContext)) {
                if ((Status.n >= StartAt) && (Status.n <= StopAt)) {
                        if (DetectGroupChange && Status.n > 0) {
-                               Status.GroupChange = (SortBy->GroupChange(vContext, vLastContext))? 1:0;
+                               Status.GroupChange = SortBy->GroupChange(vContext, vLastContext);
                        }
                        Status.LastN = (Status.n + 1) == nMembersUsed;
                        SubTP.Context = vContext;
@@ -2003,7 +2183,10 @@ void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
 int conditional_ITERATE_ISGROUPCHANGE(StrBuf *Target, WCTemplputParams *TP)
 {
        IterateStruct *Ctx = CCTX;
-       return Ctx->GroupChange;
+       if (TP->Tokens->nParameters < 3)
+               return  Ctx->GroupChange;
+
+       return TP->Tokens->Params[2]->lvalue == Ctx->GroupChange;
 }
 
 void tmplput_ITERATE_ODDEVEN(StrBuf *Target, WCTemplputParams *TP)
@@ -2030,6 +2213,12 @@ void tmplput_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
        StrBufAppendPrintf(Target, "%d", Ctx->n);
 }
 
+int conditional_ITERATE_FIRSTN(StrBuf *Target, WCTemplputParams *TP)
+{
+       IterateStruct *Ctx = CCTX;
+       return Ctx->n == 0;
+}
+
 int conditional_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
 {
        IterateStruct *Ctx = CCTX;
@@ -2134,6 +2323,18 @@ void RegisterControlConditional(const char *Name, long len,
        Put(Conditionals, Name, len, Cond, NULL);
 }
 
+void RegisterTokenParamDefine(const char *Name, long len, 
+                             long Value)
+{
+       long *PVal;
+
+       PVal = (long*)malloc(sizeof(long));
+       *PVal = Value;
+       Put(Defines, Name, len, PVal, NULL);
+}
+
+HashList *Defines;
+
 /*-----------------------------------------------------------------------------
  *                      Context Strings
  */
@@ -2159,7 +2360,7 @@ void tmpl_do_boxed(StrBuf *Target, WCTemplputParams *TP)
 {
        WCTemplputParams SubTP;
 
-       StrBuf *Headline;
+       StrBuf *Headline = NULL;
        if (TP->Tokens->nParameters == 2) {
                if (TP->Tokens->Params[1]->Type == TYPE_STR) {
                        Headline = NewStrBuf();
@@ -2176,7 +2377,8 @@ void tmpl_do_boxed(StrBuf *Target, WCTemplputParams *TP)
                        Headline = NewStrBufPlain(Ch, len);
                }
        }
-       memcpy (&SubTP, TP, sizeof(WCTemplputParams));
+       /* else TODO error? logging? */
+       memcpy (&SubTP, TP, sizeof(WCTemplputParams));
        SubTP.Context = Headline;
        SubTP.Filter.ContextType = CTX_STRBUF;
        DoTemplate(HKEY("beginbox"), Target, &SubTP);
@@ -2254,6 +2456,11 @@ void RegisterSortFunc(const char *name, long len,
        NewSort->Reverse = Reverse;
        NewSort->GroupChange = GroupChange;
        NewSort->ContextType = ContextType;
+       if (ContextType == CTX_NONE) {
+               lprintf(1, "sorting requires a context. CTX_NONE won't make it.\n");
+               exit(1);
+       }
+               
        Put(SortHash, name, len, NewSort, DestroySortStruct);
 }
 
@@ -2306,6 +2513,9 @@ CompareFunc RetrieveSort(WCTemplputParams *TP,
        }
        SortBy = (SortStruct*)vSortBy;
 
+       if (SortBy->ContextType != TP->Filter.ContextType)
+               return NULL;
+
        /** Ok, its us, lets see in which direction we should sort... */
        if (havebstr("SortOrder")) {
                SortOrder = LBSTR("SortOrder");
@@ -2579,19 +2789,25 @@ int ConditionalLongVector(StrBuf *Target, WCTemplputParams *TP)
        return 0;
 }
 
+
+void tmplput_CURRENT_FILE(StrBuf *Target, WCTemplputParams *TP)
+{
+       StrBufAppendTemplate(Target, TP, TP->Tokens->FileName, 0);
+}
+
 void 
 InitModule_SUBST
 (void)
 {
        memset(&NoCtx, 0, sizeof(WCTemplputParams));
-       RegisterNamespace("SORT:ICON", 1, 2, tmplput_SORT_ICON, CTX_NONE);
-       RegisterNamespace("SORT:ORDER", 1, 2, tmplput_SORT_ORDER, CTX_NONE);
-       RegisterNamespace("SORT:NEXT", 1, 2, tmplput_SORT_NEXT, CTX_NONE);
-       RegisterNamespace("CONTEXTSTR", 0, 1, tmplput_ContextString, CTX_STRBUF);
-       RegisterNamespace("ITERATE", 2, 100, tmpl_iterate_subtmpl, CTX_NONE);
-       RegisterNamespace("DOBOXED", 1, 2, tmpl_do_boxed, CTX_NONE);
-       RegisterNamespace("DOTABBED", 2, 100, tmpl_do_tabbed, CTX_NONE);
-       RegisterNamespace("LONGVECTOR", 1, 1, tmplput_long_vector, CTX_LONGVECTOR);
+       RegisterNamespace("SORT:ICON", 1, 2, tmplput_SORT_ICON, NULL, CTX_NONE);
+       RegisterNamespace("SORT:ORDER", 1, 2, tmplput_SORT_ORDER, NULL, CTX_NONE);
+       RegisterNamespace("SORT:NEXT", 1, 2, tmplput_SORT_NEXT, NULL, CTX_NONE);
+       RegisterNamespace("CONTEXTSTR", 0, 1, tmplput_ContextString, NULL, CTX_STRBUF);
+       RegisterNamespace("ITERATE", 2, 100, tmpl_iterate_subtmpl, preeval_iterate, CTX_NONE);
+       RegisterNamespace("DOBOXED", 1, 2, tmpl_do_boxed, NULL, CTX_NONE);
+       RegisterNamespace("DOTABBED", 2, 100, tmpl_do_tabbed, NULL, CTX_NONE);
+       RegisterNamespace("LONGVECTOR", 1, 1, tmplput_long_vector, NULL, CTX_LONGVECTOR);
        RegisterConditional(HKEY("COND:SUBST"), 3, ConditionalVar, CTX_NONE);
        RegisterConditional(HKEY("COND:CONTEXTSTR"), 3, ConditionalContextStr, CTX_STRBUF);
        RegisterConditional(HKEY("COND:LONGVECTOR"), 4, ConditionalLongVector, CTX_LONGVECTOR);
@@ -2602,9 +2818,16 @@ InitModule_SUBST
        RegisterControlConditional(HKEY("COND:ITERATE:LASTN"), 2, 
                                   conditional_ITERATE_LASTN, 
                                   CTX_ITERATE);
+       RegisterControlConditional(HKEY("COND:ITERATE:FIRSTN"), 2, 
+                                  conditional_ITERATE_FIRSTN, 
+                                  CTX_ITERATE);
+
        RegisterControlNS(HKEY("ITERATE:ODDEVEN"), 0, 0, tmplput_ITERATE_ODDEVEN, CTX_ITERATE);
        RegisterControlNS(HKEY("ITERATE:KEY"), 0, 0, tmplput_ITERATE_KEY, CTX_ITERATE);
        RegisterControlNS(HKEY("ITERATE:N"), 0, 0, tmplput_ITERATE_LASTN, CTX_ITERATE);
+       RegisterNamespace("CURRENTFILE", 0, 1, tmplput_CURRENT_FILE, NULL, CTX_NONE);
+
+
 }
 
 void
@@ -2620,6 +2843,7 @@ ServerStartModule_SUBST
        Iterators = NewHash(1, NULL);
        Conditionals = NewHash(1, NULL);
        SortHash = NewHash(1, NULL);
+       Defines = NewHash(1, NULL);
 }
 
 void
@@ -2642,7 +2866,7 @@ ServerShutdownModule_SUBST
        DeleteHash(&Iterators);
        DeleteHash(&Conditionals);
        DeleteHash(&SortHash);
-
+       DeleteHash(&Defines);
 }
 
 
@@ -2673,8 +2897,3 @@ SessionDestroyModule_SUBST
 {
 
 }
-
-
-
-
-/*@}*/