blob: 943288c0b920f9de7861c4adc7b2ba80e7c25ceb [file] [log] [blame]
/*
genidl - Generate interface defintion language listing from a
Portable Executable.
Copyright (C) 2009-2016 mingw-w64 project
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "genidl_cfg.h"
#define TOK_NAME 256
#define TOK_DIGIT 257
#define TOK_STRING 258
static sCfgLib *gen_cfglib (const char *);
static sCfgLib *has_cfglib (const char *, int);
static sCfgAlias *gen_cfglib_alias (sCfgLib *, const char *);
static sCfgAlias *has_cfglib_alias (sCfgLib *, const char *);
static sCfgItem *has_cfglib_item (sCfgLib *, const char *);
static sCfgItem *gen_cfglib_item (sCfgLib *, const char *, const char *);
static int rCh (void);
static void bCh (int r);
static int pCh (void);
static int addCh (int r);
#if 0
static void delCh (void);
#endif
static void clrCh (void);
static void printError (const char *fmt, ...);
static int lex (void);
static FILE *conf_fp = NULL;
static int last_ch = -1;
static int line_no = 1;
static int seen_eof = 0;
static char *l_buffer = NULL;
static size_t l_max, l_cur;
static sCfgLib *cfg_head = NULL;
static int is_modified = 0;
static sCfgItem *
has_cfglib_item (sCfgLib *c, const char *name)
{
sCfgItem *h;
if (!c || c->item == NULL)
return NULL;
h = c->item;
do {
if (!strcmp (h->name, name))
return h;
h = h->next;
} while (h != NULL);
return NULL;
}
static sCfgItem *
gen_cfglib_item (sCfgLib *c, const char *name, const char *type)
{
sCfgItem *a, *p = NULL, *e = c->item;
int is_new = 0;
a = has_cfglib_item (c, name);
if (!a)
{
a = (sCfgItem *) malloc (sizeof (sCfgItem) + strlen (name) + 1);
memset (a, 0, sizeof (sCfgItem));
strcpy (a->name, name);
is_new = 1;
}
is_modified = 1;
if (a->type != NULL)
free (a->type);
a->type = strdup (type);
if (!is_new)
return a;
while (e != NULL)
{
p = e;
e = e->next;
}
if (!p)
c->item = a;
else
p->next = a;
return a;
}
static sCfgAlias *
gen_cfglib_alias (sCfgLib *c, const char *name)
{
sCfgAlias *p, *e;
sCfgAlias *a = has_cfglib_alias (c, name);
if (a)
return a;
a = (sCfgAlias *) malloc (sizeof (sCfgAlias) + strlen (name) + 1);
memset (a, 0, sizeof (sCfgAlias));
strcpy (a->name, name);
p = NULL; e = c->alias;
while (e != NULL)
{
p = e;
e = e->next;
}
if (!p)
c->alias = a;
else
p->next = a;
is_modified = 1;
return a;
}
static sCfgAlias *
has_cfglib_alias (sCfgLib *c, const char *name)
{
sCfgAlias *a = (c ? c->alias : NULL);
while (a != NULL)
{
if (!strcmp (a->name, name))
return a;
a = a->next;
}
return NULL;
}
static sCfgLib *
has_cfglib (const char *name, int withAlias)
{
sCfgLib *r = cfg_head;
while (r != NULL)
{
if (!strcmp (r->name, name))
return r;
if (withAlias && has_cfglib_alias (r, name) != NULL)
return r;
r = r->next;
}
return NULL;
}
static sCfgLib *
gen_cfglib (const char *name)
{
sCfgLib *r, *p, *e;
if ((r = has_cfglib (name, 0)) != NULL)
return r;
r = (sCfgLib *) malloc (sizeof (sCfgLib) + strlen (name) + 1);
memset (r, 0, sizeof (sCfgLib));
strcpy (r->name, name);
p = NULL; e = cfg_head;
while (e != NULL)
{
p = e; e = e->next;
}
if (!p) cfg_head = r;
else p->next = r;
is_modified = 1;
return r;
}
static void
printError (const char *fmt, ...)
{
va_list argp;
va_start (argp, fmt);
fprintf (stderr, "configfile at ");
if (l_buffer[0] == 0 && seen_eof)
fprintf (stderr, "end of file");
else if (l_buffer[0] == 0)
fprintf (stderr, "start of file");
else
fprintf (stderr, "line %d near ,%s'", line_no, l_buffer);
fprintf (stderr, ": ");
vfprintf (stderr, fmt, argp);
va_end (argp);
}
static int
lex (void)
{
int r;
rescan:
clrCh ();
do {
r = rCh ();
} while (r >= 0 && r <= 0x20);
if (r == -1)
return -1;
if (r == '/' && pCh () == '*')
{
rCh ();
while ((r=rCh ()) != -1)
{
if (r == '*' && pCh () == '/')
{
rCh ();
break;
}
}
goto rescan;
}
else if (r == '/' && pCh () == '/')
{
while ((r=rCh ()) != -1)
{
if (r == '\n')
break;
}
goto rescan;
}
if (r == '_' || (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z'))
{
bCh (r);
do {
addCh (rCh ());
r = pCh ();
} while (r == '_' || (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z')
|| (r >= '0' && r <= '9') || r == '$' || r == '.');
return TOK_NAME;
}
if (r >= '0' && r <= '9')
{
addCh (r);
if (r == '0')
{
switch (pCh ()) {
case 'x': case 'X': addCh (rCh ()); break;
case 'o': case 'O': addCh (rCh ()); break;
case 'b': case 'B': addCh (rCh ()); break;
default:
break;
}
}
return TOK_DIGIT;
}
if (r == '"')
{
while ((r = pCh ()) != -1 && r != '"' && r != '\n')
addCh (rCh ());
if (r != '"')
printError ("Missing '\"' at end of string.\n");
else
rCh ();
return TOK_STRING;
}
addCh (r);
switch (r)
{
case '=': case '{': case '}': case ',':
case ';':
return r;
default:
break;
}
printError ("Illegal character found.\n");
goto rescan;
}
static void
clrCh (void)
{
l_cur = 0;
l_buffer[0] = 0;
}
#if 0
static void
delCh (void)
{
if (l_cur == 0)
return;
--l_cur;
l_buffer[l_cur] = 0;
}
#endif
static int
addCh (int r)
{
if (r == -1)
return r;
if (l_cur == l_max)
{
char *h = (char *) realloc (l_buffer, l_max + 129);
if (!h)
abort ();
l_max += 128;
}
l_buffer[l_cur++] = (char) r;
l_buffer[l_cur] = 0;
return r;
}
static int
pCh (void)
{
int r = last_ch;
if (r != -1)
return r;
r = rCh ();
if (r != -1)
bCh (r);
return r;
}
static void
bCh (int r)
{
if (r == '\n')
line_no--;
last_ch = r;
}
static int
rCh (void)
{
int r;
if ((r = last_ch) == -1)
{
char ch;
if (seen_eof || feof (conf_fp))
return -1;
if (fread (&ch, 1, 1, conf_fp) != 1)
return -1;
r = ((int) ch) & 0xff;
}
else
last_ch = -1;
if (r == '\r')
r = rCh ();
if (r == '\n')
line_no++;
return r;
}
static char **
parse_export (sCfgLib *cfg, int *re, const char *tname)
{
char **ret = NULL;
int r = lex ();
if (r == '=')
r = lex ();
if (r != '{')
{
printError ("Missing '{' for alias in ,%s'\n", tname);
*re = r;
return ret;
}
while ((r = lex ()) != -1)
{
if (r == '}')
break;
if (r == ',' || r == ';')
continue;
if (r == TOK_NAME || r == TOK_STRING)
{
char *left = strdup (l_buffer);
r = lex ();
if (r == ',' || r == '=')
r = lex ();
if (r != TOK_NAME && r != TOK_STRING)
{
printError ("Expected in export second string.\n");
}
else
{
gen_cfglib_item (cfg, left, l_buffer);
}
free (left);
}
else
printError ("Ignore token in alias of ,%s'.\n", tname);
}
return ret;
}
static char **
parse_alias (sCfgLib *cfg, int *re, const char *tname)
{
char **ret = NULL;
int r = lex ();
if (r == '=')
r = lex ();
if (r != '{')
{
printError ("Missing '{' for alias in ,%s'\n", tname);
*re = r;
return ret;
}
while ((r = lex ()) != -1)
{
if (r == '}')
break;
if (r == ',' || r == ';')
continue;
if (r == TOK_NAME || r == TOK_STRING)
{
gen_cfglib_alias (cfg, l_buffer);
}
else
printError ("Ignore token in alias of ,%s'.\n", tname);
}
return ret;
}
static int
parseTableSub (const char *tname)
{
char **alias = NULL;
char **exps;
int r = lex ();
sCfgLib *cfg = gen_cfglib (tname);
while (r != '}')
{
if (r == ';')
{
r = lex ();
continue;
}
if (r != TOK_NAME)
{
printError ("Unknown content in ,%s'\n", tname);
break;
}
if (strcmp (l_buffer, "alias") == 0)
{
alias = parse_alias (cfg, &r, tname);
if (r == -1)
break;
}
else if (strcmp (l_buffer, "export") == 0)
{
exps = parse_export (cfg, &r, tname);
if (r == -1)
break;
}
else
{
printError ("Unknown command %s in ,%s'\n", l_buffer, tname);
while ((r = lex ()) != -1 && r != ';');
}
r = lex ();
}
if (r != '}')
{
}
return r;
}
static int
parseTable (void)
{
char *table_name;
int r = lex ();
if (r == ';')
return 0;
switch (r)
{
case TOK_NAME:
case TOK_STRING:
table_name = strdup (l_buffer);
break;
case -1:
return -1;
default:
printError ("Unexpected token.\n");
return 0;
}
r = lex ();
if (r == '=')
r = lex ();
if (r != '{')
{
printError ("Missing '{' after ,%s'\n", table_name);
free (table_name);
return 0;
}
r = parseTableSub (table_name);
if (r != '}')
printError ("Missing '}' at end of ,%s'\n", table_name);
free (table_name);
return 0;
}
static void
parseStmt (void)
{
int r;
while ((r = parseTable ()) != -1)
{
}
}
int
genidl_read_config (const char *fname)
{
if (!fname)
return -1;
conf_fp = fopen (fname, "rb");
if (!conf_fp)
return -1;
l_buffer = (char *) malloc (129);
if (!l_buffer)
{
fclose (conf_fp);
return -1;
}
l_max = 128;
l_cur = 0;
l_buffer[0] = 0;
line_no = 1;
seen_eof = 0;
parseStmt ();
is_modified = 0;
free (l_buffer);
fclose (conf_fp);
return 0;
}
int
genidl_save_config (const char *file)
{
FILE *fp;
int ret;
if (!genidl_ismodified_config ())
return 1;
if (!file)
return 0;
fp = fopen (file, "wb");
ret = !genidl_save_config_fp (fp);
if (fp)
fclose (fp);
is_modified = 0;
return ret;
}
int
genidl_save_config_fp (FILE *fp)
{
sCfgLib *h = cfg_head;
sCfgAlias *alias;
sCfgItem *item;
if (!fp)
return 1;
fprintf (fp, "/* Configuration of genidl tool. */\n");
if (!h)
return 0;
do {
alias = h->alias;
item = h->item;
fprintf (fp, "\"%s\" = {\n", h->name);
if (alias)
{
fprintf (fp," alias = {\n");
do {
fprintf (fp, " \"%s\"%s\n", alias->name, (alias->next != NULL ? "," : ""));
alias = alias->next;
} while (alias);
fprintf (fp, " };\n");
}
if (item)
{
fprintf (fp, " export = {\n");
do {
fprintf (fp, " \"%s\" = \"%s\";\n", item->name, item->type);
item = item->next;
} while (item);
fprintf (fp, " };\n");
}
fprintf (fp, "};\n\n");
h = h->next;
} while (h != NULL);
return 0;
}
int
genidl_ismodified_config (void)
{
return is_modified;
}
int
genidl_add_lib (const char *lib)
{
if (!lib || *lib == 0)
return 0;
if (gen_cfglib (lib))
return 1;
return 0;
}
int
genidl_add_lib_alias (const char *lib, const char *alias)
{
sCfgLib *l;
if (!lib || *lib == 0 || !alias || *alias == 0)
return 0;
l = gen_cfglib (lib);
if (!l)
return 0;
if (gen_cfglib_alias (l, alias))
return 1;
return 0;
}
int
genidl_add_lib_item (const char *lib, const char *name, const char *typ)
{
sCfgLib *l;
if (!lib || *lib == 0 || !name || *name == 0 || !typ || *typ == 0)
return 0;
l = gen_cfglib (lib);
if (!l)
return 0;
if (gen_cfglib_item (l, name, typ))
return 1;
return 0;
}
const char *
genidl_find_type (const char *lib, const char *name)
{
sCfgLib *l;
sCfgItem *h;
char *is_tlb;
if (!lib || *lib == 0)
return NULL;
is_tlb = strstr (lib, ".tlb");
if (is_tlb)
{
sCfgLib *r = cfg_head;
while (r != NULL)
{
if (!strcmp (r->name, lib))
{
sCfgAlias *a = r->alias;
while (a != NULL)
{
const char *rs = genidl_find_type (a->name, name);
if (rs != NULL)
return rs;
a = a->next;
}
}
r = r->next;
}
return NULL;
}
l = has_cfglib (lib, 1);
if (!l)
return NULL;
h = has_cfglib_item (l, name);
if (!h)
return NULL;
return h->type;
}
int
genidl_del_lib_item (const char *lib)
{
sCfgLib *l;
sCfgItem *h;
if (!lib || *lib == 0)
return 0;
l = gen_cfglib (lib);
if (!l)
return 1;
if (l->item == NULL)
return 1;
while ((h = l->item) != NULL)
{
l->item = h->next;
if (h->type)
free (h->type);
free (h);
}
return 1;
}