improvement: path limit in URL works without full result display
[webpac] / openisis / lfdt.c
1 /*
2         openisis - an open implementation of the CDS/ISIS database
3         Version 0.8.x (patchlevel see file Version)
4         Copyright (C) 2001-2003 by Erik Grziwotz, erik@openisis.org
5
6         This library is free software; you can redistribute it and/or
7         modify it under the terms of the GNU Lesser General Public
8         License as published by the Free Software Foundation; either
9         version 2.1 of the License, or (at your option) any later version.
10
11         This library is distributed in the hope that it will be useful,
12         but WITHOUT ANY WARRANTY; without even the implied warranty of
13         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14         Lesser General Public License for more details.
15
16         You should have received a copy of the GNU Lesser General Public
17         License along with this library; if not, write to the Free Software
18         Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20         see README for more information
21 EOH */
22
23 /*
24         $Id: lfdt.c,v 1.25 2003/06/15 15:57:43 mawag Exp $
25         implementation of FDT calls and static FDTs.
26 */
27
28 #include <limits.h>  /* CHAR_MAX,PATH_MAX */
29 #include <stdio.h>
30 #include <string.h>
31
32 #include "openisis.h"
33 #include "loi.h"
34 #include "lfdt.h"
35 #include "luti.h"
36
37 #define FDF_EXT       ".fdt"
38 #define FDF_NAMLEN    30
39 #define FDF_PATLEN    20  /* format of *.fdt */
40 #define FDF_TOOLEN    6
41
42 #if FDF_NAMLEN >= FD_NAMELEN
43         fix me
44 #endif
45
46 /* ************************************************************
47         private types
48 */
49
50 /* ************************************************************
51         private data
52 */
53
54 #define _FD_DFLTDB \
55         { OPENISIS_SC_DFLTDB, 0, FTX, 0, 0, DBNLEN, "defaultdb", \
56         "Name of default db", 0, 0, 0, 0 }
57
58 #define _FD_DPATH \
59         { OPENISIS_DPATH, 0, FTX, 0, 0, PATH_MAX, "dbpath", \
60         "Database Path", 0, 0, 0, 0 }
61
62 #define _FD_DENC \
63         { OPENISIS_DENC, 0, FTX, 0, 0, 32, "encoding", \
64         "Encoding", 0, 0, 0, 0 }
65
66 static Fd _fdsys[] = {
67         /* 700 ... OpenIsis system parameters */
68         { OPENISIS_SPATH, 0, FTX, 0, 0, PATH_MAX, "syspath",
69                 "Global Database Path", 0, 0, 0, 0 },
70         { OPENISIS_SLOGF, 0, FTX, 0, 0, PATH_MAX, "logfile",
71                 "Logfile Name", 0, 0, 0, 0 },
72         { OPENISIS_SLOGV, 0, FTE, 0, 0, 16, "v",
73                 "Verbosity of Logging", 0, 0, 0, 0 },
74         { OPENISIS_SLOGV, 0, FTV, 0, 0, 0, "off",
75                 "don't log anything", 0, 0, 0, 0 },
76         { OPENISIS_SLOGV, 0, FTV, 0, 0, 1, "fatal", "", 0, 0, 0, 0 },
77         { OPENISIS_SLOGV, 0, FTV, 0, 0, 3, "syserr", "", 0, 0, 0, 0 },
78         { OPENISIS_SLOGV, 0, FTV, 0, 0, 4, "error", "", 0, 0, 0, 0 },
79         { OPENISIS_SLOGV, 0, FTV, 0, 0, 5, "warn", "", 0, 0, 0, 0 },
80         { OPENISIS_SLOGV, 0, FTV, 0, 0, 6, "info", "", 0, 0, 0, 0 },
81         { OPENISIS_SLOGV, 0, FTV, 0, 0, 7, "verbose", "", 0, 0, 0, 0 },
82         { OPENISIS_SLOGV, 0, FTV, 0, 0, 8, "trace", "", 0, 0, 0, 0 },
83         { OPENISIS_SLOGV, 0, FTV, 0, 0, 9, "debug", "", 0, 0, 0, 0 },
84         { OPENISIS_SLOGV, 0, FTV, 0, 0, 10, "all", "", 0, 0, 0, 0 },
85         _FD_DFLTDB,
86         _FD_DPATH,
87         _FD_DENC
88 };
89 static Fdt _fdtsys = {
90         sizeof (_fdsys) / sizeof (_fdsys[0]),
91         _fdsys,
92         0
93 };
94
95 static Fd _fdsch[] = {
96         { OPENISIS_SC_NAME, 0, FTX, 0, 0, SCNLEN, "name",
97                 "Identification of remote scheme", 0, 0, 0, 0 },
98         { OPENISIS_SC_HOST, 0, FTX, 0, 0, 64, "host",
99                 "Hostname of remote scheme", 0, 0, 0, 0 },
100         { OPENISIS_SC_PORT, 0, FTN, 0, 0, 5, "port",
101                 "Port of remote scheme", 0, 0, 0, 0 },
102         _FD_DFLTDB,
103         _FD_DPATH,
104         _FD_DENC
105 };
106 static Fdt _fdtsch = {
107         sizeof (_fdsch) / sizeof (_fdsch[0]),
108         _fdsch,
109         0
110 };
111
112 static Fd _fddb[] = {
113         { OPENISIS_DNAME, 0, FTX, 0, 0, DBNLEN, "db",
114                 "Identification of database", 0, 0, 0, 0 },
115         { OPENISIS_DTYPE, 0, FTE, 0, 0, 256, "format",
116                 "Database Format", 0, 0, 0, 0 },
117         { OPENISIS_DTYPE, 0, FTV, 0, 0, 0, "autoformat",
118                 "Database Format", 0, 0, 0, 0 },
119         { OPENISIS_DTYPE, 0, FTV, 0, 0, 1, "naligned",
120                 "Database Format", 0, 0, 0, 0 },
121         { OPENISIS_DTYPE, 0, FTV, 0, 0, 2, "aligned",
122                 "Database Format", 0, 0, 0, 0 },
123         { OPENISIS_DRO  , 0, FTB, 0, 0, 1, "ro",
124                 "Readonly Flag", 0, 0, 0, 0 },
125         _FD_DPATH,
126         { OPENISIS_DDUMP, 0, FTB, 0, 0, 1, "internaldump",
127                 "Internal Dump Flag", 0, 0, 0, 0 },
128         _FD_DENC,
129         { OPENISIS_DFDT,  0, FTX, 0, 0, 256, "fdt",
130                 "Path to fdt", 0, 0, 0, 0 }
131 };
132 static Fdt _fdtdb = {
133         sizeof (_fddb) / sizeof (_fddb[0]),
134         _fddb,
135         0
136 };
137
138 static Fd _fdfd[] = {
139         { OPENISIS_FDID, 0, FTN, 0, 0, 10, "tag",
140                 "Tag number of field", 0, 0, 0, 0 },
141         { OPENISIS_FDSUB, 0, FTX, 0, 0, 1, "subfield",
142                 "Subfield", 0, 0, 0, 0 },
143         { OPENISIS_FDTYPE, 0, FTE, 0, 0, 2, "type",
144                 "Field type", 0, 0, 0, 0 },
145         { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTX, "alphanum",
146                 "Field type", 0, 0, 0, 0 },
147         { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTA, "alpha",
148                 "Field type", 0, 0, 0, 0 },
149         { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTN, "numeric",
150                 "Field type", 0, 0, 0, 0 },
151         { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTP, "pattern",
152                 "Field type", 0, 0, 0, 0 },
153         { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTI, "iso",
154                 "Field type", 0, 0, 0, 0 },
155         { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTE, "enum",
156                 "Field type", 0, 0, 0, 0 },
157         { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTB, "boolean",
158                 "Field type", 0, 0, 0, 0 },
159         { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTT, "table",
160                 "Field type", 0, 0, 0, 0 },
161         { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTS, "structure",
162                 "Field type", 0, 0, 0, 0 },
163         { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTF | FTX, "subalphanum",
164                 "Subfield type", 0, 0, 0, 0 },
165         { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTF | FTA, "subalpha",
166                 "Subfield type", 0, 0, 0, 0 },
167         { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTF | FTN, "subnumeric",
168                 "Subfield type", 0, 0, 0, 0 },
169         { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTF | FTP, "subpattern",
170                 "Subfield type", 0, 0, 0, 0 },
171         { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTF | FTI, "subiso",
172                 "Subfield type", 0, 0, 0, 0 },
173         { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTF | FTE, "subenum",
174                 "Subfield type", 0, 0, 0, 0 },
175         { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTF | FTB, "subbool",
176                 "Subfield type", 0, 0, 0, 0 },
177         { OPENISIS_FDTYPE, 0, FTV, 0, 0, FTV, "enum value",
178                 "Enumeration value", 0, 0, 0, 0 },
179         { OPENISIS_FDREP, 0, FTB, 0, 0, 1, "repeatable",
180                 "Repeatable flag", 0, 0, 0, 0 },
181         { OPENISIS_FDNUMC, 0, FTN, 0, 0, 2, "numchilds",
182                 "Number of subfield childs", 0, 0, 0, 0 },
183         { OPENISIS_FDLEN, 0, FTN, 0, 0, 10, "length",
184                 "Field length or enum value", 0, 0, 0, 0 },
185         { OPENISIS_FDNAME, 0, FTX, 0, 0, 30, "name",
186                 "Field name", 0, 0, 0, 0 },
187         { OPENISIS_FDDESC, 0, FTX, 0, 0, 31, "description",
188                 "Description", 0, 0, 0, 0 },
189         { OPENISIS_FDPAT, 0, FTX, 0, 0, 128, "pattern",
190                 "Pattern", 0, 0, 0, 0 },
191         { OPENISIS_FDDFLT, 0, FTX, 0, 0, 1024, "default",
192                 "Default value", 0, 0, 0, 0 },
193         { OPENISIS_FDINFO, 0, FTS, 0, 0, 1, "info",
194                 "Embedded info record", 0, 0, 0, 0 },
195         { OPENISIS_FDCHLD, 0, FTX, 1, 0, 1, "children",
196                 "Subfield childs", 0, 0, 0, 0 }
197 };
198 static Fdt _fdtfd = {
199         sizeof (_fdfd) / sizeof (_fdfd[0]),
200         _fdfd,
201         0
202 };
203
204 #define _FD_FDT \
205         { OPENISIS_FDT_LEN, 0, FTN, 0, 0, 3, "flen", \
206                 "Length of fdt", 0, 0, 0, 0 }, \
207         { OPENISIS_FDT_FD, 0, FTS, 1, 0, 1, "fd", \
208                 "Field description", 0, 0, 0, 0 }, \
209         { OPENISIS_FDT_REC, 0, FTS, 0, 0, 1, "frec", \
210                 "Embedded info record", 0, 0, 0, 0 }
211         
212 static Fd _fdfdt[] = {
213         _FD_FDT
214 };
215 static Fdt _fdtfdt = {
216         sizeof (_fdfdt) / sizeof (_fdfdt[0]),
217         _fdfdt,
218         0
219 };
220
221 #define _FD_COM \
222         { OPENISIS_COM_SID, 0, FTN, 0, 0, 2, "sid", \
223                 "Client Session Id", 0, 0, 0, 0 }, \
224         { OPENISIS_COM_SER, 0, FTN, 0, 0, 5, "ser", \
225                 "Request Serial No.", 0, 0, 0, 0 }, \
226         { OPENISIS_COM_DBN, 0, FTX, 0, 0, DBNLEN, "db", \
227                 "DB Identification", 0, 0, 0, 0 }, \
228         { OPENISIS_COM_TMS, 0, FTN, 0, 0, 10, "tms", \
229                 "Server Db Timestamp", 0, 0, 0, 0 }, \
230         { OPENISIS_COM_ROW, 0, FTN, 0, 0, 10, "rowid", \
231                 "RowId", 0, 0, 0, 0 }, \
232         _FD_FDT, \
233         { OPENISIS_COM_CFG, 0, FTS, 0, 0, 0, "config", \
234                 "Config", 0, 0, 0, 0 }, \
235         { OPENISIS_COM_REC, 0, FTS, 1, 0, 0, "rec", \
236                 "Data", 0, 0, 0, 0 }
237
238 static Fd _fdrqs[] = {
239         _FD_COM,
240         { OPENISIS_RQS_TYPE, 0, FTE, 0, 0, 32, "type",
241                 "Request type", 0, 0, 0, 0 },
242         { OPENISIS_RQS_TYPE, 0, FTV, 0, 0, OPENISIS_RQST_OPEN, "open",
243                 "open db", 0, 0, 0, 0 },
244         { OPENISIS_RQS_TYPE, 0, FTV, 0, 0, OPENISIS_RQST_CLOS, "close",
245                 "close db", 0, 0, 0, 0 },
246         { OPENISIS_RQS_TYPE, 0, FTV, 0, 0, OPENISIS_RQST_MNT, "mount",
247                 "mount db", 0, 0, 0, 0 },
248         { OPENISIS_RQS_TYPE, 0, FTV, 0, 0, OPENISIS_RQST_LSDB, "ls",
249                 "list dbs", 0, 0, 0, 0 },
250         { OPENISIS_RQS_TYPE, 0, FTV, 0, 0, OPENISIS_RQST_MROW, "maxrow",
251                 "get maxrowid", 0, 0, 0, 0 },
252         { OPENISIS_RQS_TYPE, 0, FTV, 0, 0, OPENISIS_RQST_QRY,  "query",
253                 "exec query", 0, 0, 0, 0 },
254         { OPENISIS_RQS_TYPE, 0, FTV, 0, 0, OPENISIS_RQST_READ, "read",
255                 "fetch row", 0, 0, 0, 0 },
256         { OPENISIS_RQS_TYPE, 0, FTV, 0, 0, OPENISIS_RQST_INS,  "insert",
257                 "insert rec", 0, 0, 0, 0 },
258         { OPENISIS_RQS_TYPE, 0, FTV, 0, 0, OPENISIS_RQST_UPD,  "update",
259                 "update rec", 0, 0, 0, 0 },
260         { OPENISIS_RQS_TYPE, 0, FTV, 0, 0, OPENISIS_RQST_DEL,  "delete",
261                 "delete row", 0, 0, 0, 0 },
262         { OPENISIS_RQS_TYPE, 0, FTV, 0, 0, OPENISIS_RQST_EVAL,  "eval",
263                 "evaluate command", 0, 0, 0, 0 },
264         { OPENISIS_RQS_FLG, 0, FTN, 0, 0, 4, "flags",
265                 "Request flags", 0, 0, 0, 0 },
266         { OPENISIS_RQS_QMOD, 0, FTN, 0, 0, 5, "mode",
267                 "Query Mode", 0, 0, 0, 0 },
268         { OPENISIS_RQS_SKIP, 0, FTN, 0, 0, 5, "skip",
269                 "Query Skip", 0, 0, 0, 0 },
270         { OPENISIS_RQS_SIZE, 0, FTN, 0, 0, 5, "size",
271                 "Result Length", 0, 0, 0, 0 },
272         { OPENISIS_RQS_KEY, 0, FTX, 0, 0, OPENISIS_QRY_KEYLEN, "key",
273                 "Query Key", 0, 0, 0, 0 },
274         { OPENISIS_RQS_IDX, 0, FTS, 0, 0, 0, "idx",
275                 "Index to Update", 0, 0, 0, 0 }
276 };
277 static Fdt _fdtrqs = {
278         sizeof (_fdrqs) / sizeof (_fdrqs[0]),
279         _fdrqs,
280         0
281 };
282
283 static Fd _fdrsp[] = {
284         _FD_COM,
285         { OPENISIS_RSP_DBID, 0, FTN, 0, 0, 2, "dbid",
286                 "Id of local db", 0, 0, 0, 0 },
287         { OPENISIS_RSP_ERR, 0, FTN, 0, 0, 5, "error",
288                 "Error Code", 0, 0, 0, 0 },
289         { OPENISIS_RSP_MSG, 0, FTX, 0, 0, OPENISIS_ERRMSGLEN, "msg",
290                 "Error Message", 0, 0, 0, 0 },
291         { OPENISIS_RSP_NUMT, 0, FTN, 0, 0, 5, "total",
292                 "Total No. of Records", 0, 0, 0, 0 },
293         { OPENISIS_RSP_NUMR, 0, FTN, 0, 0, 5, "size",
294                 "Number of Records", 0, 0, 0, 0 },
295         { OPENISIS_RSP_CERR,  0, FTN, 0, 0, 5, "error2",
296                 "Client Side Error", 0, 0, 0, 0 }
297 };
298 static Fdt _fdtrsp = {
299         sizeof (_fdrsp) / sizeof (_fdrsp[0]),
300         _fdrsp,
301         0
302 };
303
304 /* ************************************************************
305         public data
306 */
307
308 const Fdt *openIsisFdtSyspar = &_fdtsys;
309 const Fdt *openIsisFdtScheme = &_fdtsch;
310 const Fdt *openIsisFdtDbpar  = &_fdtdb;
311 const Fdt *openIsisFdtFd     = &_fdtfd;
312 const Fdt *openIsisFdtFdt    = &_fdtfdt;
313 const Fdt *openIsisFdtRqs    = &_fdtrqs;
314 const Fdt *openIsisFdtRsp    = &_fdtrsp;
315
316
317 /* ************************************************************
318         private functions
319 */
320
321 static char *fFileGets (FILE *fp) {
322         static char buf[4096];
323         char *res;
324         int len;
325         while (1) {
326 nxtl:
327                 if (0 == (res = fgets (buf, sizeof(buf) - 1, fp))) {
328                         break;
329                 }
330                 buf[sizeof(buf) - 1] = 0;
331                 if (!(len = strlen (res))) {
332                         continue;
333                 }
334                 --len;
335                 while (0 <= len) {
336                         if ('\n' != res[len] && '\r' != res[len]) {
337                                 break;
338                         }
339                         res[len] = 0;
340                         if (0 > --len) {
341                                 goto nxtl;
342                         }
343                 }
344                 break;
345         }
346         return res;
347 }
348
349 static void strrtrim (char *str) {
350         char *p = str + strlen (str) - 1;
351         while (p >= str && (' ' == *p || '\t' == *p)) {
352                 *p-- = 0;
353         }
354 }
355
356 static void fDesc2Name (char *d, char *n) {
357         while (1) {
358                 if (!(*n = *d)) {
359                         return;
360                 }
361                 if ('A' <= *n && 'Z' >= *n) {
362                         *n += 'a' - 'A';
363                         ++n;
364                         ++d;
365                         continue;
366                 }
367                 if ('_' == *n ||
368                         ('a' <= *n && 'z' >= *n) ||
369                         ('0' <= *n && '9' >= *n)) {
370                         ++n;
371                         ++d;
372                         continue;
373                 }
374                 while (1) {
375                         ++d;
376                         if (!*d) {
377                                 *n = 0;
378                                 return;
379                         }
380                         if (('A' <= *d && 'Z' >= *d) ||
381                                 ('a' <= *d && 'z' >= *d) ||
382                                 ('0' <= *d && '9' >= *d)) {
383                                 break;
384                         }
385                 }
386                 *n++ = '_';
387         }
388 }
389
390 static int fLine2Fd (char *line, Fd **fd, int *num) {
391         char  name[FD_NAMELEN];
392         char  pat[1 + FDF_PATLEN];
393         Fd    buff, bufs;
394         char *P;
395         char *L  =  line;
396         int   tag, len, typ, rep, idx;
397         if (FDF_NAMLEN + FDF_PATLEN >= strlen (L)) {
398                 return sMsg (ERR_INVAL, "fFileFd: illegal line <%s>", line);
399         }
400         strncpy (name, L, FDF_NAMLEN) [FDF_NAMLEN] = 0;
401         strrtrim (name);
402         if (! *name) {
403                 return sMsg (ERR_INVAL, "fFileFd: no descr in line <%s>", line);
404         }
405         L += FDF_NAMLEN;
406         strncpy (pat, L, FDF_PATLEN) [FDF_PATLEN] = 0;
407         strrtrim (pat);
408         L += FDF_PATLEN;
409         if (4 != sscanf (L, "%d %d %d %d", &tag, &len, &typ, &rep)) {
410                 return sMsg (ERR_INVAL, "fFileFd: no type in line <%s>", line);
411         }
412         switch (typ) {
413         case 0: typ = FTX; break;
414         case 1: typ = FTA; break;
415         case 2: typ = FTN; break;
416         case 3: typ = FTP; break;
417         default: return sMsg (ERR_INVAL,
418                 "fFileFd: unrecognized type %d in line <%s>", typ, line);
419         }
420         memset (&buff, 0, sizeof (Fd));
421         buff.id = tag;
422         buff.type = typ;
423         buff.rep = 0 != rep;
424         buff.len = len;
425         strcpy (buff.desc, name);
426         fDesc2Name (name, buff.name);
427         if (! *buff.name) {
428                 return sMsg (ERR_INVAL, "fFileFd: illegal name in line <%s>", line);
429         }
430         if (FTP == typ) {
431                 if (! *pat) {
432                         return sMsg (ERR_INVAL, "fFileFd: illegal pat in line <%s>", line);
433                 }
434                 buff.pat = mDup (pat, -1);
435                 if (! buff.pat) {
436                         return sMsg (ERR_NOMEM, "fFileFd: cannot allocate pat");
437                 }
438         }
439         idx = luti_ptrincr (fd, num, 1, sizeof(Fd), -1);
440         if (0 > idx) {
441                 return sMsg (ERR_NOMEM,
442                         "fFileFd: cannot extend fd array %d", *num);
443         }
444         memcpy (*fd + idx, &buff, sizeof(Fd));
445         if (FTP != typ) {
446                 for (P = pat; *P; ++P) {
447                         memcpy (&bufs, &buff, sizeof(Fd));
448                         bufs.subf = *P;
449                         bufs.type = FTX;
450                         idx = luti_ptrincr (fd, num, 1, sizeof(Fd), -1);
451                         if (0 > idx) {
452                                 return sMsg (ERR_NOMEM,
453                                         "fFileFd: cannot extend fd array %d", *num);
454                         }
455                         memcpy (*fd + idx, &bufs, sizeof(Fd));
456                 }
457         }
458         return 0;
459 }
460
461 static int fLine2Tool (char *line, Rec **rec) {
462         char  tool[1 + FDF_TOOLEN];
463         char *L   =  line;
464         int   len, typ;
465         switch (*L) {
466         case 'w':
467         case 'W':
468                 typ = OPENISIS_DFMT;
469                 break;
470         case 'f':
471         case 'F':
472                 typ = OPENISIS_DPFT;
473                 break;
474         case 's':
475         case 'S':
476                 typ = OPENISIS_DFST;
477                 break;
478         default:
479                 return sMsg (ERR_INVAL, "fLine2Tool: illegal line <%s>", line);
480         }
481         L += 2;
482         len = strlen (L);
483         while (0 < len) {
484                 strncpy (tool, L, FDF_TOOLEN) [FDF_TOOLEN] = 0;
485                 strrtrim (tool);
486                 RADDS (*rec, typ, tool, !0);
487                 if (! *rec) {
488                         return sMsg (ERR_NOMEM, "fLine2Tool: cannot extend rec");
489                 }
490                 L += FDF_TOOLEN;
491                 len -= FDF_TOOLEN;
492         }
493         return 0;
494 }
495
496 static int fResolveChilds (Fd *fd, int len) {
497         char  msg[256]  =  { 0 };
498         Fd   *buf[CHAR_MAX];
499         Fd  **C;
500         Fd   *F, *G;
501         int   numc;
502         int   err  =  0;
503         for (F = fd + len; --F >= fd;  ) {
504                 if (! F->subf) {
505                         numc = 0;
506                         C = buf;
507                         for (G = fd + len; --G >= fd;  ) {
508                                 if (G->subf && (! G->id || G->id == F->id)) {
509                                         if (CHAR_MAX == numc) {
510                                                 err = ERR_INVAL;
511                                                 sprintf (msg,
512                                                         "fResolveChilds: too many childs for %s",
513                                                         F->name);
514                                                 break;
515                                         }
516                                         *C++ = G;
517                                         ++numc;
518                                 }
519                         }
520                         if (numc) {
521                                 C = mAlloc (numc * sizeof(Fd));
522                                 if (! C) {
523                                         return sMsg (ERR_NOMEM, "fResolveChilds");
524                                 }
525                                 F->subs = (Fd**) memcpy (C, buf, numc * sizeof(Fd));
526                                 F->slen = numc;
527                         }
528                 }
529         }
530         if (err) {
531                 return sMsg (err, msg);
532         }
533         return 0;
534 }
535
536 static void fFreeFd (Fd *fd) {
537         if (fd) {
538                 if (fd->pat) {
539                         mFree (fd->pat);
540                 }
541                 if (fd->dflt) {
542                         mFree (fd->dflt);
543                 }
544                 if (fd->info) {
545                         mFree (fd->info);
546                 }
547                 if (fd->subs) {
548                         mFree (fd->subs);
549                 }
550         }
551 }
552
553 static Fdt* fCleanupArr (Fdt *fdt, Fd *arr, int len) {
554         Fd *F;
555         for (F = arr + len; --F >= arr;  ) {
556                 fFreeFd (F);
557         }
558         mFree (arr);
559         mFree (fdt);
560         return 0;
561 }
562
563 /* ************************************************************
564         package functions
565 */
566
567 /* ************************************************************
568         public functions
569 */
570
571 Fd* fById ( const Fdt *fdt, int id, int subf )
572 {
573         Fd *f, *e;
574         if (! fdt) {
575                 return 0;
576         }
577         for (e = (f = fdt->fd) + fdt->len; --e >= f;  ) {
578                 if (id == e->id && subf == e->subf && ! (0xf0 & e->type)) {
579                         return e;
580                 }
581         }
582         return 0;
583 }
584
585 Fd* fByName ( const Fdt *fdt, const char *name )
586 {
587         Fd *f, *e, *g;
588         const char *p;
589         int l, cnt;
590         if (! fdt || ! name) {
591                 return 0;
592         }
593         f = fdt->fd;
594         e = f + fdt->len;
595         if ( '-' == *name ) /* ignore leading dash */
596                 name++;
597         if (! *name) {
598                 return 0;
599         }
600         if ( '0' <= *name && *name <= '9' )
601                 return fById( fdt, a2i( name, -1 ), 0 );
602         p = name;
603         while ( 'a' <= *p ? *p <= 'z' : '9' >= *p ? *p >= '0' : '_' == *p )
604                 p++;
605         l = p - name;
606         if ( ! l || l > FD_NAMELEN - 1 )
607                 return 0;
608         for ( cnt = 0, g = 0; f < e; f++ ) {
609                 if ( *name == *f->name
610                         && !(0xf0 & f->type) /* is field */
611                         && !memcmp( name, f->name, l ) ) {
612                         if (!f->name[l]) {
613                                 return f;
614                         }
615                         g = f;
616                         ++cnt;
617                 }
618         }
619         return 1 == cnt ? g : 0;
620 }
621
622 /**
623         lookup enum:
624         if name is numeric, the value is returned, if legal
625         considered are value entries with given id or id 0
626         - if there is an exact match with same id, this is used
627         - if there is an exact match with id 0, this is used
628         -       if name is a unique prefix on given id, this is used
629         -       if name is no prefix on given id, but a unique prefix on 0, this is used
630 */
631 int fEnum ( Fdt *fdt, int id, const char *name )
632 {
633         Fd *f = fdt->fd, *e = f + fdt->len,
634                 *x0 = 0, *pi = 0, *p0 = 0;
635         int ui = 1, u0 = 1; /* unique */
636         int l = strlen( name );
637         if ( ! l || l > FD_NAMELEN - 1 )
638                 return NOENUM;
639         if ( ('0' <= *name && *name <= '9')
640                 || ('-' == *name && '0' <= name[1] && name[1] <= '9')
641         ) {
642                 int v = a2i( name, l );
643                 for ( ; f < e; f++ ) {
644                         if ( FTV == f->type && v == f->len && (f->id == id || !f->id) )
645                                 return v;
646                 }
647                 return NOENUM;
648         }
649         for ( ; f < e; f++ ) {
650                 if ( FTV != f->type
651                         || *name != *f->name
652                         || memcmp( name, f->name, l )
653                         || (f->id && f->id != id)
654                 )
655                         continue;
656                 if ( !f->name[l] ) { /* exact match */
657                         if ( f->id == id )
658                                 return f->len;
659                         x0 = f; /* f->id is 0 */
660                         continue;
661                 }
662                 /* prefix match */
663                 if ( f->id == id ) {
664                         if ( pi )
665                                 ui = 0;
666                         else
667                                 pi = f;
668                 } else
669                         if ( p0 )
670                                 u0 = 0;
671                         else
672                                 p0 = f;
673         }
674         return x0 ? x0->len
675                 : pi ? (ui ? pi->len : NOENUM)
676                 : p0 && u0 ? p0->len : NOENUM;
677 }
678
679 Fdt* fFree ( Fdt *fdt ) {
680         Fd *F, *E;
681         if (fdt) {
682                 if (fdt->fd) {
683                         for (E = (F = fdt->fd) + fdt->len; --E >= F;  ) {
684                                 fFreeFd (E);
685                         }
686                         mFree (fdt->fd);
687                 }
688                 if (fdt->rec) {
689                         mFree (fdt->rec);
690                 }
691                 mFree (fdt);
692         }
693         return 0;
694 }
695
696 Fdt* fFromFile (char *path) {
697         FILE   *fp;
698         char   *line;
699         Fdt    *res;
700         Fd     *fd;
701         Rec    *rec;
702         int     len, err;
703
704         /* ldb::setext */
705         len = strlen (path) - 4;
706         memcpy (path + len, FDF_EXT, 4);
707         if ( 'A'<=path[len-1] && path[len-1]<= 'Z' ) {
708                 char *p = path + len;
709                 for ( ;*p; p++ ) /* use uppercase extensions */
710                         if ( 'a' <= *p && *p <= 'z' )
711                                 *p -= 'a'-'A';
712         }
713
714         fp = fopen (path, "r");
715         if (! fp) {
716                 sMsg (LOG_INFO | ERR_BADF, "no such fdt: %s", path);
717                 return 0;
718         }
719         res = mAlloc (sizeof(Fdt));
720         if (! res) {
721                 fclose (fp);
722                 sMsg (ERR_NOMEM, "fFromFile");
723                 return 0;
724         }
725         sMsg (LOG_VERBOSE, "> reading fdt: %s", path);
726
727         fd = 0;
728         rec = 0;
729         err = len = 0;
730         while ((line = fFileGets (fp))) {
731                 if ('*' == *line) {
732                         continue;
733                 }
734                 if (':' == line[1]) {
735                         if (-ERR_NOMEM == (err = fLine2Tool (line, &rec))) {
736                                 break;
737                         }
738                         continue;
739                 }
740                 if (-ERR_NOMEM == (err = fLine2Fd (line, &fd, &len))) {
741                         break;
742                 }
743         }
744
745         fclose (fp);
746         res->fd  = fd;
747         res->rec = rec;
748         res->len = len;
749
750         if (-ERR_NOMEM == err) {
751                 return fFree (res);
752         }
753
754         sMsg (LOG_VERBOSE, "< %d entries in fdt", len);
755         return res;
756 }
757
758 #define ADDFDS(tag,val) \
759         RADDS (rec, tag, val, !0); \
760         if (! rec) { return 0; }
761
762 Rec* fFd2Rec (const Fd *fd, Rec *rec, int embchld) {
763         char   buf[16];
764         Fd   **E, **F;
765         Rec   *child;
766         if (! fd) {
767                 return rec;
768         }
769         i2a (buf, fd->id);
770         ADDFDS (OPENISIS_FDID, buf);
771         if ((*buf = fd->subf)) {
772                 buf[1] = 0;
773                 ADDFDS (OPENISIS_FDSUB, buf);
774         }
775         i2a (buf, fd->type);
776         ADDFDS (OPENISIS_FDTYPE, buf);
777         i2a (buf, fd->rep);
778         ADDFDS (OPENISIS_FDREP, buf);
779         i2a (buf, fd->len);
780         ADDFDS (OPENISIS_FDLEN, buf);
781         ADDFDS (OPENISIS_FDNAME, fd->name);
782         ADDFDS (OPENISIS_FDDESC, fd->desc);
783         if (fd->pat) {
784                 ADDFDS (OPENISIS_FDPAT, fd->pat);
785         }
786         if (fd->dflt) {
787                 ADDFDS (OPENISIS_FDDFLT, fd->dflt);
788         }
789         if (fd->info) {
790                 rec = luti_wrap (rec, fd->info, OPENISIS_FDINFO);
791                 if (! rec) {
792                         return 0;
793                 }
794         }
795         if (embchld && fd->slen) {
796                 for (E = (F = fd->subs) + fd->slen; rec && F < E; ++F) {
797                         child = fFd2Rec (*F, 0, 0);
798                         if (! child) {
799                                 return rec;
800                         }
801                         rec = luti_wrap (rec, child, OPENISIS_FDCHLD);
802                 }
803                 if (rec) {
804                         rec = rAddI (rec, OPENISIS_FDNUMC, fd->slen, !0);
805                 }
806         }
807         return rec;
808 }
809
810 Rec* fFdt2Rec (const Fdt *fdt, Rec *rec, int embchld) {
811         Fd   *E, *F;
812         Rec  *R;
813         int   len;
814         if (! fdt) {
815                 return rec;
816         }
817         if (fdt->rec) {
818                 rec = luti_wrap (rec, fdt->rec, OPENISIS_FDT_REC);
819                 if (! rec) { return 0; }
820         }
821         for (E = (F = fdt->fd) + fdt->len, len = 0; F < E; ++F) {
822                 if (! F->subf || ! embchld) {
823                         R = fFd2Rec (F, 0, embchld);
824                         if (! R) {
825                                 return rec;
826                         }
827                         rec = luti_wrap (rec, R, OPENISIS_FDT_FD);
828                         mFree (R);
829                         if (! rec) {
830                                 return 0;
831                         }
832                         ++len;
833                 }
834         }
835         rec = rAddI (rec, OPENISIS_FDT_LEN, len, !0);
836         return rec;
837 }
838
839 Fd *fRec2Fd (Rec *rec, Fd *buf) {
840         char     name[FD_NAMELEN];
841         Field   *F, *E;
842         Fd      *fd;
843         int      got = 0;
844         if (! rec) {
845                 return 0;
846         }
847         if (! (fd = buf)) {
848                 fd = mAlloc (sizeof(Fd));
849                 if (! fd) {
850                         return 0;
851                 }
852         }
853         *name = 0;
854         for (E = (F = rec->field) + rec->len; F < E; ++F) {
855                 switch (F->tag) {
856                 case OPENISIS_FDID:
857                         got |= 0x01;
858                         fd->id = a2id (F->val, F->len, 0);
859                         break;
860                 case OPENISIS_FDSUB:
861                         if (! (1 == F->len || (2 == F->len && ! F->val[1]))) {
862                                 sMsg (ERR_INVAL,
863                                         "fRec2Fd: ignoring illegal subfield id (%s)", name);
864                         }
865                         else {
866                                 fd->subf = F->val[0];
867                         }
868                         break;
869                 case OPENISIS_FDTYPE:
870                         got |= 0x02;
871                         fd->type = (char) a2id (F->val, F->len, 0);
872                         break;
873                 case OPENISIS_FDREP:
874                         if (! (1 == F->len || (2 == F->len && ! F->val[1]))) {
875                                 sMsg (ERR_INVAL,
876                                         "fRec2Fd: ignoring illegal repeatable flag (%s)", name);
877                         }
878                         else {
879                                 fd->rep = F->val[0] && '0' != F->val[0];
880                         }
881                         break;
882                 case OPENISIS_FDLEN:
883                         fd->len = (short) a2id (F->val, F->len, 0);
884                         break;
885                 case OPENISIS_FDNAME:
886                         got |= 0x04;
887                         if (FD_NAMELEN <= F->len) {
888                                 memcpy (name, F->val, FD_NAMELEN - 1);
889                                 name[FD_NAMELEN - 1] = 0;
890                                 sMsg (ERR_INVAL,
891                                         "fRec2Fd: name too long (%d) - truncated to %s",
892                                         F->len, name);
893                         }
894                         else {
895                                 memcpy (name, F->val, F->len);
896                                 name[F->len] = 0;
897                         }
898                         if (! *fd->desc) {
899                                 strcpy (fd->desc, name);
900                         }
901                         fDesc2Name (name, fd->name);
902                         if (!*(fd->name)) {
903                                 sMsg (ERR_INVAL,
904                                         "fRec2Fd: illegal name (%s)", name);
905                                 got &= ~0x04;
906                         }
907                         break;
908                 case OPENISIS_FDDESC:
909                         if (FD_NAMELEN <= F->len) {
910                                 memcpy (fd->desc, F->val, FD_NAMELEN - 1);
911                                 fd->desc[FD_NAMELEN - 1] = 0;
912                                 sMsg (ERR_INVAL,
913                                         "fRec2Fd: descr too long (%d) - truncated to %s (%s)",
914                                         F->len, fd->desc, name);
915                         }
916                         else {
917                                 memcpy (fd->desc, F->val, F->len);
918                                 fd->desc[F->len] = 0;
919                         }
920                         break;
921                 case OPENISIS_FDPAT:
922                         if (fd->pat) {
923                                 sMsg (ERR_INVAL,
924                                         "fRec2Fd: ignoring multiple occurences of pattern (%s)",
925                                         name);
926                         }
927                         else {
928                                 fd->pat = (char*) mAlloc (1 + F->len);
929                                 if (! fd->pat) {
930                                         goto err;
931                                 }
932                                 memcpy (fd->pat, F->val, F->len);
933                                 fd->pat[F->len] = 0;
934                         }
935                         break;
936                 case OPENISIS_FDDFLT:
937                         if (fd->dflt) {
938                                 sMsg (ERR_INVAL,
939                                         "fRec2Fd: ignoring multiple occurences of dflt (%s)",
940                                         name);
941                         }
942                         else {
943                                 fd->dflt = (char*) mAlloc (1 + F->len);
944                                 if (! fd->dflt) {
945                                         goto err;
946                                 }
947                                 memcpy (fd->dflt, F->val, F->len);
948                                 fd->dflt[F->len] = 0;
949                         }
950                         break;
951                 case OPENISIS_FDINFO:
952                         if (fd->info) {
953                                 sMsg (ERR_INVAL,
954                                         "fRec2Fd: ignoring multiple occurences of info (%s)",
955                                         name);
956                         }
957                         else {
958                                 int pos = F - rec->field;
959                                 fd->info = luti_unwrap (rec, &pos, OPENISIS_FDINFO, -1);
960                                 if (! fd->info) {
961                                         goto err;
962                                 }
963                                 F = rec->field + pos - 1;
964                         }
965                         break;
966                 default:
967                         sMsg (ERR_INVAL,
968                                 "fRec2Fd: ignoring unexpected tag %d (%s)",
969                                 F->tag, name);
970                 }
971         }
972         if (0x07 != got) {
973                 sMsg (ERR_TRASH,
974                         "fRec2Fd: incomplete field description [%x] (%s)", got, name);
975 err:
976                 if (fd->pat) { mFree (fd->pat); }
977                 if (fd->dflt) { mFree (fd->dflt); }
978                 if (fd->info) { mFree (fd->info); }
979                 if (fd != buf) { mFree (fd); }
980                 return 0;
981         }
982         return fd;
983 }
984
985 Fdt *fRec2Fdt (Rec *rec) {
986         Rec     *R, *cfg;
987         Fdt     *fdt;
988         Fd       fdbuf;
989         Fd      *F, *arr;
990         int      err, num, len, pos;
991         if (!rec) {
992                 return 0;
993         }
994         num = rInt (rec, OPENISIS_FDT_LEN, 0, 0);
995         if (0 >= num) {
996                 return 0;
997         }
998         arr = (Fd*) mAlloc (num * sizeof (Fd));
999         if (!arr) {
1000                 return 0;
1001         }
1002         fdt = (Fdt*) mAlloc (sizeof(Fdt));
1003         if (! fdt) {
1004                 mFree (arr);
1005                 return 0;
1006         }
1007         for (F = arr, len = pos = 0; num; --num) {
1008                 R = luti_unwrap (rec, &pos, OPENISIS_FDT_FD, -1);
1009                 if (!R) {
1010                         return fCleanupArr (fdt, arr, len);
1011                 }
1012                 memset (&fdbuf, 0, sizeof(Fd));
1013                 if (fRec2Fd (R, &fdbuf)) {
1014                         memcpy (F, &fdbuf, sizeof(Fd));
1015                         ++len;
1016                         ++F;
1017                 }
1018                 mFree (R);
1019         }
1020         if (len != num) {
1021                 num = len * sizeof(Fd);
1022                 F = (Fd*) mAlloc (num);
1023                 if (! F) {
1024                         return fCleanupArr (fdt, arr, len);
1025                 }
1026                 memcpy (F, arr, num);
1027                 mFree (arr);
1028                 arr = F;
1029         }
1030         cfg = luti_unwrap (rec, 0, OPENISIS_FDT_REC, -1);
1031         fdt->len = len;
1032         fdt->fd  = arr;
1033         fdt->rec = cfg;
1034         err = fResolveChilds (arr, len);
1035         if (-ERR_NOMEM == err) {
1036                 return fFree (fdt);
1037         }
1038         return fdt;
1039 }
1040