including openisis 0.9.0 into webpac tree
[webpac] / openisis / lfdt.c
diff --git a/openisis/lfdt.c b/openisis/lfdt.c
new file mode 100644 (file)
index 0000000..5dae398
--- /dev/null
@@ -0,0 +1,1040 @@
+/*
+       openisis - an open implementation of the CDS/ISIS database
+       Version 0.8.x (patchlevel see file Version)
+       Copyright (C) 2001-2003 by Erik Grziwotz, erik@openisis.org
+
+       This library is free software; you can redistribute it and/or
+       modify it under the terms of the GNU Lesser General Public
+       License as published by the Free Software Foundation; either
+       version 2.1 of the License, or (at your option) any later version.
+
+       This library is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+       Lesser General Public License for more details.
+
+       You should have received a copy of the GNU Lesser General Public
+       License along with this library; if not, write to the Free Software
+       Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+       see README for more information
+EOH */
+
+/*
+       $Id: lfdt.c,v 1.25 2003/06/15 15:57:43 mawag Exp $
+       implementation of FDT calls and static FDTs.
+*/
+
+#include <limits.h>  /* CHAR_MAX,PATH_MAX */
+#include <stdio.h>
+#include <string.h>
+
+#include "openisis.h"
+#include "loi.h"
+#include "lfdt.h"
+#include "luti.h"
+
+#define FDF_EXT       ".fdt"
+#define FDF_NAMLEN    30
+#define FDF_PATLEN    20  /* format of *.fdt */
+#define FDF_TOOLEN    6
+
+#if FDF_NAMLEN >= FD_NAMELEN
+       fix me
+#endif
+
+/* ************************************************************
+       private types
+*/
+
+/* ************************************************************
+       private data
+*/
+
+#define _FD_DFLTDB \
+       { OPENISIS_SC_DFLTDB, 0, FTX, 0, 0, DBNLEN, "defaultdb", \
+       "Name of default db", 0, 0, 0, 0 }
+
+#define _FD_DPATH \
+       { OPENISIS_DPATH, 0, FTX, 0, 0, PATH_MAX, "dbpath", \
+       "Database Path", 0, 0, 0, 0 }
+
+#define _FD_DENC \
+       { OPENISIS_DENC, 0, FTX, 0, 0, 32, "encoding", \
+       "Encoding", 0, 0, 0, 0 }
+
+static Fd _fdsys[] = {
+       /* 700 ... OpenIsis system parameters */
+       { OPENISIS_SPATH, 0, FTX, 0, 0, PATH_MAX, "syspath",
+               "Global Database Path", 0, 0, 0, 0 },
+       { OPENISIS_SLOGF, 0, FTX, 0, 0, PATH_MAX, "logfile",
+               "Logfile Name", 0, 0, 0, 0 },
+       { OPENISIS_SLOGV, 0, FTE, 0, 0, 16, "v",
+               "Verbosity of Logging", 0, 0, 0, 0 },
+       { OPENISIS_SLOGV, 0, FTV, 0, 0, 0, "off",
+               "don't log anything", 0, 0, 0, 0 },
+       { OPENISIS_SLOGV, 0, FTV, 0, 0, 1, "fatal", "", 0, 0, 0, 0 },
+       { OPENISIS_SLOGV, 0, FTV, 0, 0, 3, "syserr", "", 0, 0, 0, 0 },
+       { OPENISIS_SLOGV, 0, FTV, 0, 0, 4, "error", "", 0, 0, 0, 0 },
+       { OPENISIS_SLOGV, 0, FTV, 0, 0, 5, "warn", "", 0, 0, 0, 0 },
+       { OPENISIS_SLOGV, 0, FTV, 0, 0, 6, "info", "", 0, 0, 0, 0 },
+       { OPENISIS_SLOGV, 0, FTV, 0, 0, 7, "verbose", "", 0, 0, 0, 0 },
+       { OPENISIS_SLOGV, 0, FTV, 0, 0, 8, "trace", "", 0, 0, 0, 0 },
+       { OPENISIS_SLOGV, 0, FTV, 0, 0, 9, "debug", "", 0, 0, 0, 0 },
+       { OPENISIS_SLOGV, 0, FTV, 0, 0, 10, "all", "", 0, 0, 0, 0 },
+       _FD_DFLTDB,
+       _FD_DPATH,
+       _FD_DENC
+};
+static Fdt _fdtsys = {
+       sizeof (_fdsys) / sizeof (_fdsys[0]),
+       _fdsys,
+       0
+};
+
+static Fd _fdsch[] = {
+       { OPENISIS_SC_NAME, 0, FTX, 0, 0, SCNLEN, "name",
+               "Identification of remote scheme", 0, 0, 0, 0 },
+       { OPENISIS_SC_HOST, 0, FTX, 0, 0, 64, "host",
+               "Hostname of remote scheme", 0, 0, 0, 0 },
+       { OPENISIS_SC_PORT, 0, FTN, 0, 0, 5, "port",
+               "Port of remote scheme", 0, 0, 0, 0 },
+       _FD_DFLTDB,
+       _FD_DPATH,
+       _FD_DENC
+};
+static Fdt _fdtsch = {
+       sizeof (_fdsch) / sizeof (_fdsch[0]),
+       _fdsch,
+       0
+};
+
+static Fd _fddb[] = {
+       { OPENISIS_DNAME, 0, FTX, 0, 0, DBNLEN, "db",
+               "Identification of database", 0, 0, 0, 0 },
+       { OPENISIS_DTYPE, 0, FTE, 0, 0, 256, "format",
+               "Database Format", 0, 0, 0, 0 },
+       { OPENISIS_DTYPE, 0, FTV, 0, 0, 0, "autoformat",
+               "Database Format", 0, 0, 0, 0 },
+       { OPENISIS_DTYPE, 0, FTV, 0, 0, 1, "naligned",
+               "Database Format", 0, 0, 0, 0 },
+       { OPENISIS_DTYPE, 0, FTV, 0, 0, 2, "aligned",
+               "Database Format", 0, 0, 0, 0 },
+       { OPENISIS_DRO  , 0, FTB, 0, 0, 1, "ro",
+               "Readonly Flag", 0, 0, 0, 0 },
+       _FD_DPATH,
+       { OPENISIS_DDUMP, 0, FTB, 0, 0, 1, "internaldump",
+               "Internal Dump Flag", 0, 0, 0, 0 },
+       _FD_DENC,
+       { OPENISIS_DFDT,  0, FTX, 0, 0, 256, "fdt",
+               "Path to fdt", 0, 0, 0, 0 }
+};
+static Fdt _fdtdb = {
+       sizeof (_fddb) / sizeof (_fddb[0]),
+       _fddb,
+       0
+};
+
+static Fd _fdfd[] = {
+       { OPENISIS_FDID, 0, FTN, 0, 0, 10, "tag",
+               "Tag number of field", 0, 0, 0, 0 },
+       { OPENISIS_FDSUB, 0, FTX, 0, 0, 1, "subfield",
+               "Subfield", 0, 0, 0, 0 },
+       { OPENISIS_FDTYPE, 0, FTE, 0, 0, 2, "type",
+               "Field type", 0, 0, 0, 0 },
+       { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTX, "alphanum",
+               "Field type", 0, 0, 0, 0 },
+       { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTA, "alpha",
+               "Field type", 0, 0, 0, 0 },
+       { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTN, "numeric",
+               "Field type", 0, 0, 0, 0 },
+       { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTP, "pattern",
+               "Field type", 0, 0, 0, 0 },
+       { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTI, "iso",
+               "Field type", 0, 0, 0, 0 },
+       { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTE, "enum",
+               "Field type", 0, 0, 0, 0 },
+       { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTB, "boolean",
+               "Field type", 0, 0, 0, 0 },
+       { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTT, "table",
+               "Field type", 0, 0, 0, 0 },
+       { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTS, "structure",
+               "Field type", 0, 0, 0, 0 },
+       { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTF | FTX, "subalphanum",
+               "Subfield type", 0, 0, 0, 0 },
+       { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTF | FTA, "subalpha",
+               "Subfield type", 0, 0, 0, 0 },
+       { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTF | FTN, "subnumeric",
+               "Subfield type", 0, 0, 0, 0 },
+       { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTF | FTP, "subpattern",
+               "Subfield type", 0, 0, 0, 0 },
+       { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTF | FTI, "subiso",
+               "Subfield type", 0, 0, 0, 0 },
+       { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTF | FTE, "subenum",
+               "Subfield type", 0, 0, 0, 0 },
+       { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTF | FTB, "subbool",
+               "Subfield type", 0, 0, 0, 0 },
+       { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTV, "enum value",
+               "Enumeration value", 0, 0, 0, 0 },
+       { OPENISIS_FDREP, 0, FTB, 0, 0, 1, "repeatable",
+               "Repeatable flag", 0, 0, 0, 0 },
+       { OPENISIS_FDNUMC, 0, FTN, 0, 0, 2, "numchilds",
+               "Number of subfield childs", 0, 0, 0, 0 },
+       { OPENISIS_FDLEN, 0, FTN, 0, 0, 10, "length",
+               "Field length or enum value", 0, 0, 0, 0 },
+       { OPENISIS_FDNAME, 0, FTX, 0, 0, 30, "name",
+               "Field name", 0, 0, 0, 0 },
+       { OPENISIS_FDDESC, 0, FTX, 0, 0, 31, "description",
+               "Description", 0, 0, 0, 0 },
+       { OPENISIS_FDPAT, 0, FTX, 0, 0, 128, "pattern",
+               "Pattern", 0, 0, 0, 0 },
+       { OPENISIS_FDDFLT, 0, FTX, 0, 0, 1024, "default",
+               "Default value", 0, 0, 0, 0 },
+       { OPENISIS_FDINFO, 0, FTS, 0, 0, 1, "info",
+               "Embedded info record", 0, 0, 0, 0 },
+       { OPENISIS_FDCHLD, 0, FTX, 1, 0, 1, "children",
+               "Subfield childs", 0, 0, 0, 0 }
+};
+static Fdt _fdtfd = {
+       sizeof (_fdfd) / sizeof (_fdfd[0]),
+       _fdfd,
+       0
+};
+
+#define _FD_FDT \
+       { OPENISIS_FDT_LEN, 0, FTN, 0, 0, 3, "flen", \
+               "Length of fdt", 0, 0, 0, 0 }, \
+       { OPENISIS_FDT_FD, 0, FTS, 1, 0, 1, "fd", \
+               "Field description", 0, 0, 0, 0 }, \
+       { OPENISIS_FDT_REC, 0, FTS, 0, 0, 1, "frec", \
+               "Embedded info record", 0, 0, 0, 0 }
+       
+static Fd _fdfdt[] = {
+       _FD_FDT
+};
+static Fdt _fdtfdt = {
+       sizeof (_fdfdt) / sizeof (_fdfdt[0]),
+       _fdfdt,
+       0
+};
+
+#define _FD_COM \
+       { OPENISIS_COM_SID, 0, FTN, 0, 0, 2, "sid", \
+               "Client Session Id", 0, 0, 0, 0 }, \
+       { OPENISIS_COM_SER, 0, FTN, 0, 0, 5, "ser", \
+               "Request Serial No.", 0, 0, 0, 0 }, \
+       { OPENISIS_COM_DBN, 0, FTX, 0, 0, DBNLEN, "db", \
+               "DB Identification", 0, 0, 0, 0 }, \
+       { OPENISIS_COM_TMS, 0, FTN, 0, 0, 10, "tms", \
+               "Server Db Timestamp", 0, 0, 0, 0 }, \
+       { OPENISIS_COM_ROW, 0, FTN, 0, 0, 10, "rowid", \
+               "RowId", 0, 0, 0, 0 }, \
+       _FD_FDT, \
+       { OPENISIS_COM_CFG, 0, FTS, 0, 0, 0, "config", \
+               "Config", 0, 0, 0, 0 }, \
+       { OPENISIS_COM_REC, 0, FTS, 1, 0, 0, "rec", \
+               "Data", 0, 0, 0, 0 }
+
+static Fd _fdrqs[] = {
+       _FD_COM,
+       { OPENISIS_RQS_TYPE, 0, FTE, 0, 0, 32, "type",
+               "Request type", 0, 0, 0, 0 },
+       { OPENISIS_RQS_TYPE, 0, FTV, 0, 0, OPENISIS_RQST_OPEN, "open",
+               "open db", 0, 0, 0, 0 },
+       { OPENISIS_RQS_TYPE, 0, FTV, 0, 0, OPENISIS_RQST_CLOS, "close",
+               "close db", 0, 0, 0, 0 },
+       { OPENISIS_RQS_TYPE, 0, FTV, 0, 0, OPENISIS_RQST_MNT, "mount",
+               "mount db", 0, 0, 0, 0 },
+       { OPENISIS_RQS_TYPE, 0, FTV, 0, 0, OPENISIS_RQST_LSDB, "ls",
+               "list dbs", 0, 0, 0, 0 },
+       { OPENISIS_RQS_TYPE, 0, FTV, 0, 0, OPENISIS_RQST_MROW, "maxrow",
+               "get maxrowid", 0, 0, 0, 0 },
+       { OPENISIS_RQS_TYPE, 0, FTV, 0, 0, OPENISIS_RQST_QRY,  "query",
+               "exec query", 0, 0, 0, 0 },
+       { OPENISIS_RQS_TYPE, 0, FTV, 0, 0, OPENISIS_RQST_READ, "read",
+               "fetch row", 0, 0, 0, 0 },
+       { OPENISIS_RQS_TYPE, 0, FTV, 0, 0, OPENISIS_RQST_INS,  "insert",
+               "insert rec", 0, 0, 0, 0 },
+       { OPENISIS_RQS_TYPE, 0, FTV, 0, 0, OPENISIS_RQST_UPD,  "update",
+               "update rec", 0, 0, 0, 0 },
+       { OPENISIS_RQS_TYPE, 0, FTV, 0, 0, OPENISIS_RQST_DEL,  "delete",
+               "delete row", 0, 0, 0, 0 },
+       { OPENISIS_RQS_TYPE, 0, FTV, 0, 0, OPENISIS_RQST_EVAL,  "eval",
+               "evaluate command", 0, 0, 0, 0 },
+       { OPENISIS_RQS_FLG, 0, FTN, 0, 0, 4, "flags",
+               "Request flags", 0, 0, 0, 0 },
+       { OPENISIS_RQS_QMOD, 0, FTN, 0, 0, 5, "mode",
+               "Query Mode", 0, 0, 0, 0 },
+       { OPENISIS_RQS_SKIP, 0, FTN, 0, 0, 5, "skip",
+               "Query Skip", 0, 0, 0, 0 },
+       { OPENISIS_RQS_SIZE, 0, FTN, 0, 0, 5, "size",
+               "Result Length", 0, 0, 0, 0 },
+       { OPENISIS_RQS_KEY, 0, FTX, 0, 0, OPENISIS_QRY_KEYLEN, "key",
+               "Query Key", 0, 0, 0, 0 },
+       { OPENISIS_RQS_IDX, 0, FTS, 0, 0, 0, "idx",
+               "Index to Update", 0, 0, 0, 0 }
+};
+static Fdt _fdtrqs = {
+       sizeof (_fdrqs) / sizeof (_fdrqs[0]),
+       _fdrqs,
+       0
+};
+
+static Fd _fdrsp[] = {
+       _FD_COM,
+       { OPENISIS_RSP_DBID, 0, FTN, 0, 0, 2, "dbid",
+               "Id of local db", 0, 0, 0, 0 },
+       { OPENISIS_RSP_ERR, 0, FTN, 0, 0, 5, "error",
+               "Error Code", 0, 0, 0, 0 },
+       { OPENISIS_RSP_MSG, 0, FTX, 0, 0, OPENISIS_ERRMSGLEN, "msg",
+               "Error Message", 0, 0, 0, 0 },
+       { OPENISIS_RSP_NUMT, 0, FTN, 0, 0, 5, "total",
+               "Total No. of Records", 0, 0, 0, 0 },
+       { OPENISIS_RSP_NUMR, 0, FTN, 0, 0, 5, "size",
+               "Number of Records", 0, 0, 0, 0 },
+       { OPENISIS_RSP_CERR,  0, FTN, 0, 0, 5, "error2",
+               "Client Side Error", 0, 0, 0, 0 }
+};
+static Fdt _fdtrsp = {
+       sizeof (_fdrsp) / sizeof (_fdrsp[0]),
+       _fdrsp,
+       0
+};
+
+/* ************************************************************
+       public data
+*/
+
+const Fdt *openIsisFdtSyspar = &_fdtsys;
+const Fdt *openIsisFdtScheme = &_fdtsch;
+const Fdt *openIsisFdtDbpar  = &_fdtdb;
+const Fdt *openIsisFdtFd     = &_fdtfd;
+const Fdt *openIsisFdtFdt    = &_fdtfdt;
+const Fdt *openIsisFdtRqs    = &_fdtrqs;
+const Fdt *openIsisFdtRsp    = &_fdtrsp;
+
+
+/* ************************************************************
+       private functions
+*/
+
+static char *fFileGets (FILE *fp) {
+       static char buf[4096];
+       char *res;
+       int len;
+       while (1) {
+nxtl:
+               if (0 == (res = fgets (buf, sizeof(buf) - 1, fp))) {
+                       break;
+               }
+               buf[sizeof(buf) - 1] = 0;
+               if (!(len = strlen (res))) {
+                       continue;
+               }
+               --len;
+               while (0 <= len) {
+                       if ('\n' != res[len] && '\r' != res[len]) {
+                               break;
+                       }
+                       res[len] = 0;
+                       if (0 > --len) {
+                               goto nxtl;
+                       }
+               }
+               break;
+       }
+       return res;
+}
+
+static void strrtrim (char *str) {
+       char *p = str + strlen (str) - 1;
+       while (p >= str && (' ' == *p || '\t' == *p)) {
+               *p-- = 0;
+       }
+}
+
+static void fDesc2Name (char *d, char *n) {
+       while (1) {
+               if (!(*n = *d)) {
+                       return;
+               }
+               if ('A' <= *n && 'Z' >= *n) {
+                       *n += 'a' - 'A';
+                       ++n;
+                       ++d;
+                       continue;
+               }
+               if ('_' == *n ||
+                       ('a' <= *n && 'z' >= *n) ||
+                       ('0' <= *n && '9' >= *n)) {
+                       ++n;
+                       ++d;
+                       continue;
+               }
+               while (1) {
+                       ++d;
+                       if (!*d) {
+                               *n = 0;
+                               return;
+                       }
+                       if (('A' <= *d && 'Z' >= *d) ||
+                               ('a' <= *d && 'z' >= *d) ||
+                               ('0' <= *d && '9' >= *d)) {
+                               break;
+                       }
+               }
+               *n++ = '_';
+       }
+}
+
+static int fLine2Fd (char *line, Fd **fd, int *num) {
+       char  name[FD_NAMELEN];
+       char  pat[1 + FDF_PATLEN];
+       Fd    buff, bufs;
+       char *P;
+       char *L  =  line;
+       int   tag, len, typ, rep, idx;
+       if (FDF_NAMLEN + FDF_PATLEN >= strlen (L)) {
+               return sMsg (ERR_INVAL, "fFileFd: illegal line <%s>", line);
+       }
+       strncpy (name, L, FDF_NAMLEN) [FDF_NAMLEN] = 0;
+       strrtrim (name);
+       if (! *name) {
+               return sMsg (ERR_INVAL, "fFileFd: no descr in line <%s>", line);
+       }
+       L += FDF_NAMLEN;
+       strncpy (pat, L, FDF_PATLEN) [FDF_PATLEN] = 0;
+       strrtrim (pat);
+       L += FDF_PATLEN;
+       if (4 != sscanf (L, "%d %d %d %d", &tag, &len, &typ, &rep)) {
+               return sMsg (ERR_INVAL, "fFileFd: no type in line <%s>", line);
+       }
+       switch (typ) {
+       case 0: typ = FTX; break;
+       case 1: typ = FTA; break;
+       case 2: typ = FTN; break;
+       case 3: typ = FTP; break;
+       default: return sMsg (ERR_INVAL,
+               "fFileFd: unrecognized type %d in line <%s>", typ, line);
+       }
+       memset (&buff, 0, sizeof (Fd));
+       buff.id = tag;
+       buff.type = typ;
+       buff.rep = 0 != rep;
+       buff.len = len;
+       strcpy (buff.desc, name);
+       fDesc2Name (name, buff.name);
+       if (! *buff.name) {
+               return sMsg (ERR_INVAL, "fFileFd: illegal name in line <%s>", line);
+       }
+       if (FTP == typ) {
+               if (! *pat) {
+                       return sMsg (ERR_INVAL, "fFileFd: illegal pat in line <%s>", line);
+               }
+               buff.pat = mDup (pat, -1);
+               if (! buff.pat) {
+                       return sMsg (ERR_NOMEM, "fFileFd: cannot allocate pat");
+               }
+       }
+       idx = luti_ptrincr (fd, num, 1, sizeof(Fd), -1);
+       if (0 > idx) {
+               return sMsg (ERR_NOMEM,
+                       "fFileFd: cannot extend fd array %d", *num);
+       }
+       memcpy (*fd + idx, &buff, sizeof(Fd));
+       if (FTP != typ) {
+               for (P = pat; *P; ++P) {
+                       memcpy (&bufs, &buff, sizeof(Fd));
+                       bufs.subf = *P;
+                       bufs.type = FTX;
+                       idx = luti_ptrincr (fd, num, 1, sizeof(Fd), -1);
+                       if (0 > idx) {
+                               return sMsg (ERR_NOMEM,
+                                       "fFileFd: cannot extend fd array %d", *num);
+                       }
+                       memcpy (*fd + idx, &bufs, sizeof(Fd));
+               }
+       }
+       return 0;
+}
+
+static int fLine2Tool (char *line, Rec **rec) {
+       char  tool[1 + FDF_TOOLEN];
+       char *L   =  line;
+       int   len, typ;
+       switch (*L) {
+       case 'w':
+       case 'W':
+               typ = OPENISIS_DFMT;
+               break;
+       case 'f':
+       case 'F':
+               typ = OPENISIS_DPFT;
+               break;
+       case 's':
+       case 'S':
+               typ = OPENISIS_DFST;
+               break;
+       default:
+               return sMsg (ERR_INVAL, "fLine2Tool: illegal line <%s>", line);
+       }
+       L += 2;
+       len = strlen (L);
+       while (0 < len) {
+               strncpy (tool, L, FDF_TOOLEN) [FDF_TOOLEN] = 0;
+               strrtrim (tool);
+               RADDS (*rec, typ, tool, !0);
+               if (! *rec) {
+                       return sMsg (ERR_NOMEM, "fLine2Tool: cannot extend rec");
+               }
+               L += FDF_TOOLEN;
+               len -= FDF_TOOLEN;
+       }
+       return 0;
+}
+
+static int fResolveChilds (Fd *fd, int len) {
+       char  msg[256]  =  { 0 };
+       Fd   *buf[CHAR_MAX];
+       Fd  **C;
+       Fd   *F, *G;
+       int   numc;
+       int   err  =  0;
+       for (F = fd + len; --F >= fd;  ) {
+               if (! F->subf) {
+                       numc = 0;
+                       C = buf;
+                       for (G = fd + len; --G >= fd;  ) {
+                               if (G->subf && (! G->id || G->id == F->id)) {
+                                       if (CHAR_MAX == numc) {
+                                               err = ERR_INVAL;
+                                               sprintf (msg,
+                                                       "fResolveChilds: too many childs for %s",
+                                                       F->name);
+                                               break;
+                                       }
+                                       *C++ = G;
+                                       ++numc;
+                               }
+                       }
+                       if (numc) {
+                               C = mAlloc (numc * sizeof(Fd));
+                               if (! C) {
+                                       return sMsg (ERR_NOMEM, "fResolveChilds");
+                               }
+                               F->subs = (Fd**) memcpy (C, buf, numc * sizeof(Fd));
+                               F->slen = numc;
+                       }
+               }
+       }
+       if (err) {
+               return sMsg (err, msg);
+       }
+       return 0;
+}
+
+static void fFreeFd (Fd *fd) {
+       if (fd) {
+               if (fd->pat) {
+                       mFree (fd->pat);
+               }
+               if (fd->dflt) {
+                       mFree (fd->dflt);
+               }
+               if (fd->info) {
+                       mFree (fd->info);
+               }
+               if (fd->subs) {
+                       mFree (fd->subs);
+               }
+       }
+}
+
+static Fdt* fCleanupArr (Fdt *fdt, Fd *arr, int len) {
+       Fd *F;
+       for (F = arr + len; --F >= arr;  ) {
+               fFreeFd (F);
+       }
+       mFree (arr);
+       mFree (fdt);
+       return 0;
+}
+
+/* ************************************************************
+       package functions
+*/
+
+/* ************************************************************
+       public functions
+*/
+
+Fd* fById ( const Fdt *fdt, int id, int subf )
+{
+       Fd *f, *e;
+       if (! fdt) {
+               return 0;
+       }
+       for (e = (f = fdt->fd) + fdt->len; --e >= f;  ) {
+               if (id == e->id && subf == e->subf && ! (0xf0 & e->type)) {
+                       return e;
+               }
+       }
+       return 0;
+}
+
+Fd* fByName ( const Fdt *fdt, const char *name )
+{
+       Fd *f, *e, *g;
+       const char *p;
+       int l, cnt;
+       if (! fdt || ! name) {
+               return 0;
+       }
+       f = fdt->fd;
+       e = f + fdt->len;
+       if ( '-' == *name ) /* ignore leading dash */
+               name++;
+       if (! *name) {
+               return 0;
+       }
+       if ( '0' <= *name && *name <= '9' )
+               return fById( fdt, a2i( name, -1 ), 0 );
+       p = name;
+       while ( 'a' <= *p ? *p <= 'z' : '9' >= *p ? *p >= '0' : '_' == *p )
+               p++;
+       l = p - name;
+       if ( ! l || l > FD_NAMELEN - 1 )
+               return 0;
+       for ( cnt = 0, g = 0; f < e; f++ ) {
+               if ( *name == *f->name
+                       && !(0xf0 & f->type) /* is field */
+                       && !memcmp( name, f->name, l ) ) {
+                       if (!f->name[l]) {
+                               return f;
+                       }
+                       g = f;
+                       ++cnt;
+               }
+       }
+       return 1 == cnt ? g : 0;
+}
+
+/**
+       lookup enum:
+       if name is numeric, the value is returned, if legal
+       considered are value entries with given id or id 0
+       - if there is an exact match with same id, this is used
+       - if there is an exact match with id 0, this is used
+       -       if name is a unique prefix on given id, this is used
+       -       if name is no prefix on given id, but a unique prefix on 0, this is used
+*/
+int fEnum ( Fdt *fdt, int id, const char *name )
+{
+       Fd *f = fdt->fd, *e = f + fdt->len,
+               *x0 = 0, *pi = 0, *p0 = 0;
+       int ui = 1, u0 = 1; /* unique */
+       int l = strlen( name );
+       if ( ! l || l > FD_NAMELEN - 1 )
+               return NOENUM;
+       if ( ('0' <= *name && *name <= '9')
+               || ('-' == *name && '0' <= name[1] && name[1] <= '9')
+       ) {
+               int v = a2i( name, l );
+               for ( ; f < e; f++ ) {
+                       if ( FTV == f->type && v == f->len && (f->id == id || !f->id) )
+                               return v;
+               }
+               return NOENUM;
+       }
+       for ( ; f < e; f++ ) {
+               if ( FTV != f->type
+                       || *name != *f->name
+                       || memcmp( name, f->name, l )
+                       || (f->id && f->id != id)
+               )
+                       continue;
+               if ( !f->name[l] ) { /* exact match */
+                       if ( f->id == id )
+                               return f->len;
+                       x0 = f; /* f->id is 0 */
+                       continue;
+               }
+               /* prefix match */
+               if ( f->id == id ) {
+                       if ( pi )
+                               ui = 0;
+                       else
+                               pi = f;
+               } else
+                       if ( p0 )
+                               u0 = 0;
+                       else
+                               p0 = f;
+       }
+       return x0 ? x0->len
+               : pi ? (ui ? pi->len : NOENUM)
+               : p0 && u0 ? p0->len : NOENUM;
+}
+
+Fdt* fFree ( Fdt *fdt ) {
+       Fd *F, *E;
+       if (fdt) {
+               if (fdt->fd) {
+                       for (E = (F = fdt->fd) + fdt->len; --E >= F;  ) {
+                               fFreeFd (E);
+                       }
+                       mFree (fdt->fd);
+               }
+               if (fdt->rec) {
+                       mFree (fdt->rec);
+               }
+               mFree (fdt);
+       }
+       return 0;
+}
+
+Fdt* fFromFile (char *path) {
+       FILE   *fp;
+       char   *line;
+       Fdt    *res;
+       Fd     *fd;
+       Rec    *rec;
+       int     len, err;
+
+       /* ldb::setext */
+       len = strlen (path) - 4;
+       memcpy (path + len, FDF_EXT, 4);
+       if ( 'A'<=path[len-1] && path[len-1]<= 'Z' ) {
+               char *p = path + len;
+               for ( ;*p; p++ ) /* use uppercase extensions */
+                       if ( 'a' <= *p && *p <= 'z' )
+                               *p -= 'a'-'A';
+       }
+
+       fp = fopen (path, "r");
+       if (! fp) {
+               sMsg (LOG_INFO | ERR_BADF, "no such fdt: %s", path);
+               return 0;
+       }
+       res = mAlloc (sizeof(Fdt));
+       if (! res) {
+               fclose (fp);
+               sMsg (ERR_NOMEM, "fFromFile");
+               return 0;
+       }
+       sMsg (LOG_VERBOSE, "> reading fdt: %s", path);
+
+       fd = 0;
+       rec = 0;
+       err = len = 0;
+       while ((line = fFileGets (fp))) {
+               if ('*' == *line) {
+                       continue;
+               }
+               if (':' == line[1]) {
+                       if (-ERR_NOMEM == (err = fLine2Tool (line, &rec))) {
+                               break;
+                       }
+                       continue;
+               }
+               if (-ERR_NOMEM == (err = fLine2Fd (line, &fd, &len))) {
+                       break;
+               }
+       }
+
+       fclose (fp);
+       res->fd  = fd;
+       res->rec = rec;
+       res->len = len;
+
+       if (-ERR_NOMEM == err) {
+               return fFree (res);
+       }
+
+       sMsg (LOG_VERBOSE, "< %d entries in fdt", len);
+       return res;
+}
+
+#define ADDFDS(tag,val) \
+       RADDS (rec, tag, val, !0); \
+       if (! rec) { return 0; }
+
+Rec* fFd2Rec (const Fd *fd, Rec *rec, int embchld) {
+       char   buf[16];
+       Fd   **E, **F;
+       Rec   *child;
+       if (! fd) {
+               return rec;
+       }
+       i2a (buf, fd->id);
+       ADDFDS (OPENISIS_FDID, buf);
+       if ((*buf = fd->subf)) {
+               buf[1] = 0;
+               ADDFDS (OPENISIS_FDSUB, buf);
+       }
+       i2a (buf, fd->type);
+       ADDFDS (OPENISIS_FDTYPE, buf);
+       i2a (buf, fd->rep);
+       ADDFDS (OPENISIS_FDREP, buf);
+       i2a (buf, fd->len);
+       ADDFDS (OPENISIS_FDLEN, buf);
+       ADDFDS (OPENISIS_FDNAME, fd->name);
+       ADDFDS (OPENISIS_FDDESC, fd->desc);
+       if (fd->pat) {
+               ADDFDS (OPENISIS_FDPAT, fd->pat);
+       }
+       if (fd->dflt) {
+               ADDFDS (OPENISIS_FDDFLT, fd->dflt);
+       }
+       if (fd->info) {
+               rec = luti_wrap (rec, fd->info, OPENISIS_FDINFO);
+               if (! rec) {
+                       return 0;
+               }
+       }
+       if (embchld && fd->slen) {
+               for (E = (F = fd->subs) + fd->slen; rec && F < E; ++F) {
+                       child = fFd2Rec (*F, 0, 0);
+                       if (! child) {
+                               return rec;
+                       }
+                       rec = luti_wrap (rec, child, OPENISIS_FDCHLD);
+               }
+               if (rec) {
+                       rec = rAddI (rec, OPENISIS_FDNUMC, fd->slen, !0);
+               }
+       }
+       return rec;
+}
+
+Rec* fFdt2Rec (const Fdt *fdt, Rec *rec, int embchld) {
+       Fd   *E, *F;
+       Rec  *R;
+       int   len;
+       if (! fdt) {
+               return rec;
+       }
+       if (fdt->rec) {
+               rec = luti_wrap (rec, fdt->rec, OPENISIS_FDT_REC);
+               if (! rec) { return 0; }
+       }
+       for (E = (F = fdt->fd) + fdt->len, len = 0; F < E; ++F) {
+               if (! F->subf || ! embchld) {
+                       R = fFd2Rec (F, 0, embchld);
+                       if (! R) {
+                               return rec;
+                       }
+                       rec = luti_wrap (rec, R, OPENISIS_FDT_FD);
+                       mFree (R);
+                       if (! rec) {
+                               return 0;
+                       }
+                       ++len;
+               }
+       }
+       rec = rAddI (rec, OPENISIS_FDT_LEN, len, !0);
+       return rec;
+}
+
+Fd *fRec2Fd (Rec *rec, Fd *buf) {
+       char     name[FD_NAMELEN];
+       Field   *F, *E;
+       Fd      *fd;
+       int      got = 0;
+       if (! rec) {
+               return 0;
+       }
+       if (! (fd = buf)) {
+               fd = mAlloc (sizeof(Fd));
+               if (! fd) {
+                       return 0;
+               }
+       }
+       *name = 0;
+       for (E = (F = rec->field) + rec->len; F < E; ++F) {
+               switch (F->tag) {
+               case OPENISIS_FDID:
+                       got |= 0x01;
+                       fd->id = a2id (F->val, F->len, 0);
+                       break;
+               case OPENISIS_FDSUB:
+                       if (! (1 == F->len || (2 == F->len && ! F->val[1]))) {
+                               sMsg (ERR_INVAL,
+                                       "fRec2Fd: ignoring illegal subfield id (%s)", name);
+                       }
+                       else {
+                               fd->subf = F->val[0];
+                       }
+                       break;
+               case OPENISIS_FDTYPE:
+                       got |= 0x02;
+                       fd->type = (char) a2id (F->val, F->len, 0);
+                       break;
+               case OPENISIS_FDREP:
+                       if (! (1 == F->len || (2 == F->len && ! F->val[1]))) {
+                               sMsg (ERR_INVAL,
+                                       "fRec2Fd: ignoring illegal repeatable flag (%s)", name);
+                       }
+                       else {
+                               fd->rep = F->val[0] && '0' != F->val[0];
+                       }
+                       break;
+               case OPENISIS_FDLEN:
+                       fd->len = (short) a2id (F->val, F->len, 0);
+                       break;
+               case OPENISIS_FDNAME:
+                       got |= 0x04;
+                       if (FD_NAMELEN <= F->len) {
+                               memcpy (name, F->val, FD_NAMELEN - 1);
+                               name[FD_NAMELEN - 1] = 0;
+                               sMsg (ERR_INVAL,
+                                       "fRec2Fd: name too long (%d) - truncated to %s",
+                                       F->len, name);
+                       }
+                       else {
+                               memcpy (name, F->val, F->len);
+                               name[F->len] = 0;
+                       }
+                       if (! *fd->desc) {
+                               strcpy (fd->desc, name);
+                       }
+                       fDesc2Name (name, fd->name);
+                       if (!*(fd->name)) {
+                               sMsg (ERR_INVAL,
+                                       "fRec2Fd: illegal name (%s)", name);
+                               got &= ~0x04;
+                       }
+                       break;
+               case OPENISIS_FDDESC:
+                       if (FD_NAMELEN <= F->len) {
+                               memcpy (fd->desc, F->val, FD_NAMELEN - 1);
+                               fd->desc[FD_NAMELEN - 1] = 0;
+                               sMsg (ERR_INVAL,
+                                       "fRec2Fd: descr too long (%d) - truncated to %s (%s)",
+                                       F->len, fd->desc, name);
+                       }
+                       else {
+                               memcpy (fd->desc, F->val, F->len);
+                               fd->desc[F->len] = 0;
+                       }
+                       break;
+               case OPENISIS_FDPAT:
+                       if (fd->pat) {
+                               sMsg (ERR_INVAL,
+                                       "fRec2Fd: ignoring multiple occurences of pattern (%s)",
+                                       name);
+                       }
+                       else {
+                               fd->pat = (char*) mAlloc (1 + F->len);
+                               if (! fd->pat) {
+                                       goto err;
+                               }
+                               memcpy (fd->pat, F->val, F->len);
+                               fd->pat[F->len] = 0;
+                       }
+                       break;
+               case OPENISIS_FDDFLT:
+                       if (fd->dflt) {
+                               sMsg (ERR_INVAL,
+                                       "fRec2Fd: ignoring multiple occurences of dflt (%s)",
+                                       name);
+                       }
+                       else {
+                               fd->dflt = (char*) mAlloc (1 + F->len);
+                               if (! fd->dflt) {
+                                       goto err;
+                               }
+                               memcpy (fd->dflt, F->val, F->len);
+                               fd->dflt[F->len] = 0;
+                       }
+                       break;
+               case OPENISIS_FDINFO:
+                       if (fd->info) {
+                               sMsg (ERR_INVAL,
+                                       "fRec2Fd: ignoring multiple occurences of info (%s)",
+                                       name);
+                       }
+                       else {
+                               int pos = F - rec->field;
+                               fd->info = luti_unwrap (rec, &pos, OPENISIS_FDINFO, -1);
+                               if (! fd->info) {
+                                       goto err;
+                               }
+                               F = rec->field + pos - 1;
+                       }
+                       break;
+               default:
+                       sMsg (ERR_INVAL,
+                               "fRec2Fd: ignoring unexpected tag %d (%s)",
+                               F->tag, name);
+               }
+       }
+       if (0x07 != got) {
+               sMsg (ERR_TRASH,
+                       "fRec2Fd: incomplete field description [%x] (%s)", got, name);
+err:
+               if (fd->pat) { mFree (fd->pat); }
+               if (fd->dflt) { mFree (fd->dflt); }
+               if (fd->info) { mFree (fd->info); }
+               if (fd != buf) { mFree (fd); }
+               return 0;
+       }
+       return fd;
+}
+
+Fdt *fRec2Fdt (Rec *rec) {
+       Rec     *R, *cfg;
+       Fdt     *fdt;
+       Fd       fdbuf;
+       Fd      *F, *arr;
+       int      err, num, len, pos;
+       if (!rec) {
+               return 0;
+       }
+       num = rInt (rec, OPENISIS_FDT_LEN, 0, 0);
+       if (0 >= num) {
+               return 0;
+       }
+       arr = (Fd*) mAlloc (num * sizeof (Fd));
+       if (!arr) {
+               return 0;
+       }
+       fdt = (Fdt*) mAlloc (sizeof(Fdt));
+       if (! fdt) {
+               mFree (arr);
+               return 0;
+       }
+       for (F = arr, len = pos = 0; num; --num) {
+               R = luti_unwrap (rec, &pos, OPENISIS_FDT_FD, -1);
+               if (!R) {
+                       return fCleanupArr (fdt, arr, len);
+               }
+               memset (&fdbuf, 0, sizeof(Fd));
+               if (fRec2Fd (R, &fdbuf)) {
+                       memcpy (F, &fdbuf, sizeof(Fd));
+                       ++len;
+                       ++F;
+               }
+               mFree (R);
+       }
+       if (len != num) {
+               num = len * sizeof(Fd);
+               F = (Fd*) mAlloc (num);
+               if (! F) {
+                       return fCleanupArr (fdt, arr, len);
+               }
+               memcpy (F, arr, num);
+               mFree (arr);
+               arr = F;
+       }
+       cfg = luti_unwrap (rec, 0, OPENISIS_FDT_REC, -1);
+       fdt->len = len;
+       fdt->fd  = arr;
+       fdt->rec = cfg;
+       err = fResolveChilds (arr, len);
+       if (-ERR_NOMEM == err) {
+               return fFree (fdt);
+       }
+       return fdt;
+}
+