/*
* METADICT - Create metadata definitions from dictionary textfiles.
*
* Author:
* Emile van Bergen, emile@evbergen.xs4all.nl
*
* Permission to redistribute an original or modified version of this program
* in source, intermediate or object code form is hereby granted exclusively
* under the terms of the GNU General Public License, version 2. Please see the
* file COPYING for details, or refer to http://www.gnu.org/copyleft/gpl.html.
*
* History:
* 2001/03/17 - EvB - Created
* 2001/04/17 - EvB - Added support for value definitions
* 2001/09/13 - EvB - Added nodec and noenc options (don't decode, except for
* subspaces / don't encode). Does not affect interfaces.
*/
char metadict_id[] = "METADICT - Copyright (C) 2001 Emile van Bergen.";
/*
* INCLUDES & DEFINES
*/
#include <string.h> /* for memset(), strchr(), strtok() */
#include <errno.h> /* for errno */
#include <textfile.h>
#include <metadict.h>
#define DEBUGLEVEL 3 /* emit code for DEBUGx's up to 3 */
#include <debug.h>
/* Option numbers */
#define OPT_SPACE 0
#define OPT_SUBSPACE 1
#define OPT_VENDOR 2
#define OPT_VAL_TYPE 3
#define OPT_ITEM 4
#define OPT_X_LAST_ALPHA 4
#define OPT_LEN_ADJ 5
#define OPT_VAL_SIZE 6
#define OPT_X_LAST_NEG 6
#define OPT_ATR_OFS 7
#define OPT_ATR_SIZE 8
#define OPT_VND_OFS 9
#define OPT_VND_SIZE 10
#define OPT_LEN_OFS 11
#define OPT_LEN_SIZE 12
#define OPT_VAL_OFS 13
#define OPT_SINGLE 14
#define OPT_NODEC 15
#define OPT_NOENC 16
#define OPT_REJECT 17
#define OPT_ACCT 18
#define OPT_X_OPT_CNT 19
/*
* TYPES
*/
/* Structure containing parsed values for the options that can be set
with a 'set default ...' statement */
struct set_options {
/* Space options */
char atr_ofs, atr_size;
char vnd_ofs, vnd_size;
char single; /* flags */
/* Item options */
META_ORD vendor;
char len_ofs, len_size, len_adj;
char val_ofs, val_size, val_type;
META_SPC *spc, *subspace;
char nodec, noenc, reject, acct; /* flags */
/* Value options */
META_ITEM *item;
};
/*
* GLOBALS
*/
/* Option names - indexes must match constant values listed above */
static char *optnames[OPT_X_OPT_CNT] = {
"space",
"subspace",
"vendor",
"val_type",
"item", /* These can have a non-numeric option value */
"len_adj",
"val_size", /* These can have a numeric negative value */
"atr_ofs",
"atr_size",
"vnd_ofs",
"vnd_size",
"len_ofs",
"len_size",
"val_ofs",
"single",
"nodec",
"noenc",
"reject",
"acctresp", /* These all need non-negative numeric values */
};
/*
* FUNCTIONS
*/
/* Initialize options */
static void initopt(struct set_options *opts)
{
/* Set all members to zero */
memset(opts, 0, sizeof(struct set_options));
/* Init members that have non-zero default values */
opts->vendor = META_ORD_ERR;
}
/* Set an option in the options structure based on an ASCII option definition.
Warning: modifies optval. Returns -1 if error. */
static int setopt(META *m, struct set_options *opts, char *optval)
{
META_ORD ord;
META_VND *v;
char *arg;
int optnr;
/* First separate the option name and its argument. Also, try to
convert the argument to a numeric value as that is often needed.
The value will be META_ORD_ERR if the argument isn't numeric. */
arg = strchr(optval, '=');
if (!arg) { msg(F_TEXT, L_ERR, "dict_setopt: ERROR: No '=' in option "
"definition '%s'!\n", optval);return -1;}
*arg++ = 0;
ord = meta_dtoord(arg);
/* Now find the option name and complain if not found. */
for(optnr = 0;
optnr < OPT_X_OPT_CNT && strcmp(optval, optnames[optnr]);
optnr++);
if (optnr == OPT_X_OPT_CNT) {
/* Unknown option */
msg(F_TEXT, L_ERR, "dict_setopt: ERROR: Unknown option '%s'!\n",
optval);
return -1;
}
/* The first big switch */
switch(optnr) {
case OPT_SPACE:
/* Space pointer. First try by name, then by number.
Must be found. */
if ((opts->spc = meta_getspcbyname(m, arg))) return 0;
if ((opts->spc = meta_getspcbynr(m, ord))) return 0;
msg(F_TEXT, L_ERR, "dict_setopt: ERROR: Unknown space '%s'!\n",
arg);
return -1;
case OPT_SUBSPACE:
/* Subspace specification. Argument is 'x' for none, otherwise
try to find it first name, then by number; must be found. */
if (arg[0] == 'x' && arg[1] == 0) {
opts->subspace = 0;
return 0;
}
if ((opts->subspace = meta_getspcbyname(m, arg))) return 0;
if ((opts->subspace = meta_getspcbynr(m, ord))) return 0;
msg(F_TEXT, L_ERR, "dict_setopt: ERROR: Unknown subspace '%s'!"
"\n", arg);
return -1;
case OPT_VENDOR:
/* Vendor number. First try by name, then by number.
This gives the funny 'feature' that if you define a vendor
with a numeric name, then that vendor's number is used
instead of the argument nr. */
if (arg[0] == 'x' && arg[1] == 0) {
opts->vendor = META_ORD_ERR;
return 0;
}
if ((v = meta_getvndbyname(m, arg))) {
opts->vendor = v->nr;
return 0;
}
if ((v = meta_getvndbynr(m, ord))) {
opts->vendor = v->nr;
return 0;
}
msg(F_TEXT, L_ERR, "dict_setopt: ERROR: Unknown vendor '%s'!\n",
arg);
return -1;
case OPT_VAL_TYPE:
/* Type. Must be found. */
if ((opts->val_type = meta_gettypebyname(arg)) != MT_TYPE_CNT)
return 0;
msg(F_TEXT, L_ERR, "dict_setopt: ERROR: Unknown type '%s'!\n",
arg);
return -1;
case OPT_ITEM:
/* Item pointer. Search order:
1. By name, current vendor, current space
2. By number, current vendor, current space
3. By name, any vendor, current space
4. By number, any vendor, current space
5. By name, current vendor, any space
6. By number, current vendor, any space
7. By name, any vendor, any space
8. By number, any vendor, any space
Note that 'current' is current in _this_ set of options! */
if ((opts->item = meta_getitembyname(m, opts->spc,
arg, opts->vendor))) return 0;
if ((opts->item = meta_getitembynr(m, opts->spc,
ord, opts->vendor))) return 0;
if ((opts->item = meta_getitembyname(m, opts->spc,
arg, META_VND_WILD))) return 0;
if ((opts->item = meta_getitembynr(m, opts->spc,
ord, META_VND_WILD))) return 0;
if ((opts->item = meta_getitembyname(m, 0,
arg, opts->vendor))) return 0;
if ((opts->item = meta_getitembynr(m, 0,
ord, opts->vendor))) return 0;
if ((opts->item = meta_getitembyname(m, 0,
arg, META_VND_WILD))) return 0;
if ((opts->item = meta_getitembynr(m, 0,
ord, META_VND_WILD))) return 0;
msg(F_TEXT, L_ERR, "dict_setopt: ERROR: Unknown item '%s'!\n",
arg);
return -1;
}
/* Trap non-numeric arguments here as all following options need one. */
if (ord == META_ORD_ERR) {
msg(F_TEXT, L_ERR, "dict_setopt: ERROR: number expected "
"instead of '%s' for option '%s'!\n",
arg, optval);
return -1;
}
/* Next 'big' switch */
switch(optnr) {
case OPT_LEN_ADJ: opts->len_adj = ord; return 0;
case OPT_VAL_SIZE: opts->val_size = ord; return 0;
}
/* Trap negative args here as all following options need a pos. one. */
if (ord < 0) {
msg(F_TEXT, L_ERR, "dict_setopt: ERROR: positive number exp"
"ected instead of '%s' for option '%s'!\n",
arg, optval);
return -1;
}
/* The last 'big' switch */
switch(optnr) {
case OPT_VND_OFS: opts->vnd_ofs = ord; return 0;
case OPT_VND_SIZE: opts->vnd_size = ord; return 0;
case OPT_ATR_OFS: opts->atr_ofs = ord; return 0;
case OPT_ATR_SIZE: opts->atr_size = ord; return 0;
case OPT_VAL_OFS: opts->val_ofs = ord; return 0;
case OPT_LEN_OFS: opts->len_ofs = ord; return 0;
case OPT_LEN_SIZE: opts->len_size = ord; return 0;
case OPT_SINGLE: opts->single = ord != 0; return 0;
case OPT_NODEC: opts->nodec = ord != 0; return 0;
case OPT_NOENC: opts->noenc = ord != 0; return 0;
case OPT_REJECT: opts->reject = ord != 0; return 0;
case OPT_ACCT: opts->acct = ord != 0; return 0;
}
/* It's a bug if we get here */
msg(F_TEXT, L_ERR, "dict_setopt: BUG: option list is inconsistent!\n");
return -1;
}
/* Create a meta object from a dictionary */
META *meta_newfromdict(char *basepath)
{
META *m;
TEXT *t;
struct set_options g_opts, l_opts;
static char stmt[DICT_MAX_STMTLEN + 1];
int linenr, stmtlen;
char *word, *obj, *ascnr, *name;
void *p;
/* For easy cleanup */
m = 0; t = 0;
/* Create the metadata object */
m = meta_new();
if (!m) {
msg(F_MISC, L_ERR, "meta_newfromdict: ERROR: Could not create "
"metadata object: %s!\n", strerror(errno));
goto meta_newfromdict_err;
}
/* Create a textfile object with the specified base directory */
msg(F_MISC, L_NOTICE, "meta_newfromdict: Opening %s/" DICT_MAINFNAME
"\n", basepath);
t = text_new(basepath, DICT_MAX_STMTLEN + 1);
if (!t || text_include(t, DICT_MAINFNAME) == -1) {
if (basepath) msg(F_MISC, L_ERR, "meta_newfromdict: ERROR: "
"Could not open file '"
DICT_MAINFNAME
"' in directory '%s': %s!\n",
basepath, strerror(errno));
else msg(F_MISC, L_ERR, "meta_newfromdict: ERRROR: Could not "
"open file '" DICT_MAINFNAME "': %s!\n",
strerror(errno));
goto meta_newfromdict_err;
}
/* Initialise global and temporary (local) options */
initopt(&g_opts); initopt(&l_opts);
/* Main loop. Continues as long as there are statements to be
interpreted and there are no errors other than EOF. */
for(;;) {
/* Save the current line number */
linenr = text_linenr(t);
/* Get the next $-separated statement */
stmtlen = text_getparseditem(t, stmt, DICT_MAX_STMTLEN + 1,
'\\', /* Escape character */
'\'', '\'', /* Quote open/close */
'#', TEXT_META_EOL, /* Comment start/end */
'$', '$'); /* Item start/end */
/* Check error conditions */
if (stmtlen == -1) {
if (text_status(t) == RING_EOF) {
/* EOF. End the current file, continue if
there's still one open on the stack. */
if (text_endfile(t) > 0) continue;
break;
}
/* Other error. Complain and exit. */
msg(F_TEXT, L_ERR, "meta_newfromdict: ERROR: Parse "
"error %d in '%s', line %d: %s!\n",
text_fname(t), linenr, strerror(errno));
goto meta_newfromdict_err;
}
/* Okay. Get the statement's first word; skip empty ones. */
word = strtok(stmt, " \t\n\r");
if (!word) continue;
/* The big switch */
if (!strcmp(word, "include")) {
/* $include filename */
word = strtok(0, " \t\n\r");
if (!word) {
msg(F_TEXT, L_ERR, "meta_newfromdict: ERROR: Fi"
"lename expected after $incl"
"ude, in '%s', line %d!\n",
text_fname(t), linenr);
goto meta_newfromdict_err;
}
msg(F_TEXT, L_NOTICE, "meta_newfromdict: including '%s'"
" from '%s', line %d\n",
word, text_fname(t), linenr);
if (text_include(t, word) == -1) {
msg(F_TEXT, L_ERR, "meta_newfromdict: ERROR: "
"Could not include %s, in "
"'%s', line %d: %s!\n",
word,text_fname(t),linenr,strerror(errno));
goto meta_newfromdict_err;
}
/* Next statement */
continue;
}
else if (!strcmp(word, "set")) {
/* $set default opt=val opt=val opt=val ... */
word = strtok(0, " \t\n\r");
if (!word || strcmp(word, "default")) {
msg(F_TEXT, L_ERR, "meta_newfromdict: ERROR: "
"'default' expected after "
"$set, in '%s', line %d!\n",
text_fname(t), linenr);
goto meta_newfromdict_err;
}
while((word = strtok(0, " \t\n\r"))) {
msg(F_TEXT, L_DEBUG, "meta_newfromdict: setting"
" default option '%s'\n",
word);
if (setopt(m, &g_opts, word)) {
msg(F_TEXT, L_ERR, "meta_newfromdict: "
"ERROR: Invalid def"
"ault option in '%s"
"', line %d!\n",
text_fname(t), linenr);
goto meta_newfromdict_err;
}
}
/* Next statement */
continue;
}
else if (strcmp(word, "add")) {
/* An unknown keyword */
msg(F_TEXT, L_ERR, "meta_newfromdict: ERROR: Unknown "
"keyword %s, in '%s', line %d!\n",
word, text_fname(t), linenr);
goto meta_newfromdict_err;
}
/* $add something number name opt=val opt=val opt=val ... */
/* Get the type of thing to add */
obj = strtok(0, " \t\n\r");
if (!obj) {
msg(F_TEXT, L_ERR, "meta_newfromdict: ERROR: Keyword "
"expected after $add, in '%s', line "
"%d!\n", text_fname(t), linenr);
goto meta_newfromdict_err;
}
/* Get the number of the thing */
ascnr = strtok(0, " \t\n\r");
if (!ascnr) {
msg(F_TEXT, L_ERR, "meta_newfromdict: ERROR: Number exp"
"ected after $add %s, in '%s', line "
"%d!\n", obj, text_fname(t), linenr);
goto meta_newfromdict_err;
}
/* Get the name of the thing */
name = strtok(0, " \t\n\r");
if (!name) {
msg(F_TEXT, L_ERR, "meta_newfromdict: ERROR: Name "
"expected after $add %s %s, in '%s',"
" line %d!\n",
obj, ascnr, text_fname(t), linenr);
goto meta_newfromdict_err;
}
/* Create the temporary local options for the thing */
l_opts = g_opts;
while((word = strtok(0, " \t\n\r"))) {
if (setopt(m, &l_opts, word)) {
msg(F_TEXT, L_ERR, "meta_newfromdict: ERROR: "
"invalid option for %s %s "
"(%s), in '%s', line %d!\n",
obj, ascnr, name, text_fname(t), linenr);
goto meta_newfromdict_err;
}
}
/* Now add the space, vendor or item (field / attribute). */
p = 0;
if (!strcmp(obj, "space")) {
msg(F_TEXT, L_DEBUG, "meta_newfromdict: adding space "
"%d (%s) - atr_ofs=%d, atr_size="
"%d, vnd_ofs=%d, vnd_size=%d, "
"single=%d\n",
meta_dtoord(ascnr), name, l_opts.atr_ofs,
l_opts.atr_size, l_opts.vnd_ofs, l_opts.vnd_size,
l_opts.single);
p = meta_addspc(m, meta_dtoord(ascnr), name,
l_opts.atr_ofs, l_opts.atr_size,
l_opts.vnd_ofs, l_opts.vnd_size,
l_opts.single);
}
else if (!strcmp(obj, "vendor")) {
msg(F_TEXT, L_DEBUG, "meta_newfromdict: adding vendor "
"%d (%s)\n",
meta_dtoord(ascnr), name);
p = meta_addvnd(m, meta_dtoord(ascnr), name);
}
else if (!strcmp(obj, "field") || !strcmp(obj, "attribute")) {
if (!l_opts.spc) {
msg(F_TEXT, L_ERR, "meta_newfromdict: ERROR: "
"no space specified to add item %s "
"(%s) to, in '%s', line %d!\n",
ascnr, name, text_fname(t), linenr);
goto meta_newfromdict_err;
}
msg(F_TEXT, L_DEBUG, "meta_newfromdict: adding item "
"%d, vendor %d (%s) in space %d "
"(%s)\n",
meta_dtoord(ascnr), l_opts.vendor, name,
l_opts.spc->nr, l_opts.spc->name);
p = meta_additem(m, l_opts.spc,
meta_dtoord(ascnr), l_opts.vendor, name,
l_opts.len_ofs, l_opts.len_size,
l_opts.len_adj,
l_opts.val_ofs, l_opts.val_size,
l_opts.val_type,
l_opts.subspace,
l_opts.nodec, l_opts.noenc,
l_opts.reject, l_opts.acct);
}
else if (!strcmp(obj, "value")) {
if (!l_opts.item) {
msg(F_TEXT, L_ERR, "meta_newfromdict: ERROR: "
"no item specified to add value %s "
"(%s) to, in '%s', line %d!\n",
ascnr, name, text_fname(t), linenr);
goto meta_newfromdict_err;
}
msg(F_TEXT, L_DEBUG, "meta_newfromdict: adding value "
"%d (%s) for item %d, vendor %d "
"(%s) in space %d (%s)\n",
meta_dtoord(ascnr), name, l_opts.item->nr,
l_opts.item->vnd, l_opts.item->name,
l_opts.item->spc->nr, l_opts.item->spc->name);
p = meta_addval(m, l_opts.item,
meta_dtoord(ascnr), name);
}
/* Handle errors */
if (!p) {
msg(F_TEXT, L_ERR, "meta_newfromdict: ERROR: could not "
"add %s %s, %s, in '%s', line %d: "
"%s!\n",
obj, ascnr, name, text_fname(t), linenr);
goto meta_newfromdict_err;
}
}
/* Delete the text object and return the created meta object */
text_del(t);
return m;
/* Cleanup stages for error handling */
meta_newfromdict_err:
if (t) text_del(t);
if (m) meta_del(m);
/* Return nothing */
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1