| /* |
| genidl - Generate interface defintion language listing from a |
| Portable Executable. |
| Copyright (C) 2009, 2010, 2011, 2012, 2013 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; |
| } |