| /* | |
| genstubdll - Generate stub-library acting like an import-library | |
| using .def file syntax. | |
| Copyright (C) 2014-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 <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include "token.h" | |
| static void addCh (int); | |
| static void delCh (void); | |
| static void zeroCh (void); | |
| static int pCh (void); | |
| static int rCh (void); | |
| static void bCh (int); | |
| typedef struct sMem { | |
| struct sMem *next; | |
| size_t len; | |
| char name[1]; | |
| } sMem; | |
| static int t_max = 0, t_pos = 0; | |
| static char *t_buf = NULL; | |
| static int t_backch = -1; | |
| static FILE *t_in = NULL; | |
| static int exports_seen = 0; | |
| static sMem *t_smem = NULL; | |
| sSymbol *t_sym = NULL; | |
| static sSymbol *t_lastsym = NULL; | |
| const char * | |
| unifyCat (const char *s, const char *t) | |
| { | |
| char *h; | |
| const char *r; | |
| if (!t || *t == 0) return unifyStr (s); | |
| if (!s || *s == 0) return unifyStr (t); | |
| h = (char *) malloc (strlen (s) + strlen (t) + 1); | |
| strcpy (h, s); strcat (h, t); | |
| r = unifyStr (h); | |
| free (h); | |
| return r; | |
| } | |
| const char * | |
| unifyStr (const char *s) | |
| { | |
| sMem *p,*c,*n; | |
| size_t l; | |
| if (*s == 0) | |
| return ""; | |
| l = strlen (s); | |
| p = NULL; c = t_smem; | |
| while (c != NULL && c->len < l) | |
| c = (p = c)->next; | |
| while (c != NULL && c->len == l) | |
| { | |
| int e = memcmp (c->name, s, l); | |
| if (!e) return c->name; | |
| if (e > 0) break; | |
| c = (p = c)->next; | |
| } | |
| n = (sMem *) malloc (sizeof (sMem) + l); | |
| n->next = c; | |
| if (!p) | |
| t_smem = n; | |
| else | |
| p->next = n; | |
| n->len = l; | |
| strcpy (n->name, s); | |
| return n->name; | |
| } | |
| static void addCh (int ch) | |
| { | |
| if ((t_pos + 2) >= t_max) | |
| { | |
| char *h = malloc (t_max + 128); | |
| if (!h) | |
| { | |
| fprintf (stderr, "Fatal error: Run out of memory\n"); | |
| exit (2); | |
| } | |
| h[t_pos] = 0; | |
| if (t_pos) | |
| memcpy (h, t_buf, t_pos); | |
| if (t_buf) | |
| free (t_buf); | |
| t_buf = h; | |
| t_max += 128; | |
| } | |
| if (ch < 0) | |
| return; | |
| t_buf[t_pos++] = (char) ch; | |
| t_buf[t_pos] = 0; | |
| } | |
| static void delCh (void) | |
| { | |
| if (t_pos > 0) | |
| { | |
| t_buf[--t_pos] = 0; | |
| } | |
| else if (!t_buf) | |
| addCh (-1); | |
| } | |
| static void zeroCh (void) | |
| { | |
| if (t_pos) { t_buf[0] = 0; t_pos = 0; } | |
| else if (!t_buf) addCh (-1); | |
| } | |
| static int pCh (void) | |
| { | |
| int r = t_backch; | |
| if (r != -1) | |
| return r; | |
| r = rCh (); | |
| bCh (r); | |
| return r; | |
| } | |
| static int rCh (void) | |
| { | |
| int r = t_backch; | |
| unsigned char ch; | |
| if (r != -1) { t_backch = -1; } | |
| else { | |
| if (feof (t_in) || fread (&ch, 1, 1, t_in) != 1) | |
| return r; | |
| r = (int) ch; r &= 0xff; | |
| } | |
| if (r == '\r') | |
| return rCh (); | |
| addCh (r); | |
| return r; | |
| } | |
| static void bCh (int ch) | |
| { | |
| if (ch < 0) return; | |
| delCh (); | |
| t_backch = ch; | |
| } | |
| static void readDigit (void) | |
| { | |
| int r; | |
| r = rCh (); | |
| if (r == '0') { | |
| switch (pCh ()) { | |
| case 'x': case 'X': case 'o': case 'O': rCh (); break; | |
| default: break; | |
| } | |
| } | |
| while ((r = pCh ()) != -1 && ((r >= '0' && r <= '9') | |
| || (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z'))) | |
| rCh (); | |
| } | |
| static int lexit (void) | |
| { | |
| int r; | |
| redo: | |
| do { | |
| zeroCh (); | |
| if ((r = rCh ()) == -1) return -1; | |
| } while (r <= 0x20 && r != '\n'); | |
| /* Ignore comment. Either introduced by semicolon, or by hash sign. */ | |
| if (r == ';' || r == '#') { | |
| while (1) { | |
| if ((r = pCh ()) == -1) return 0; | |
| if (r == '\n') break; | |
| rCh (); | |
| } | |
| goto redo; | |
| } | |
| switch (r) { | |
| case '=': if (pCh () == r) { rCh(); return TK_EQUALEQUAL; } | |
| return r; | |
| case '@': case ',': case '.': case '\n': return r; | |
| case '"': | |
| zeroCh (); | |
| while ((r = pCh ()) != -1 && r != '"') | |
| { | |
| rCh (); | |
| if (r == '\\') { | |
| if (rCh () == -1) { delCh (); r = -1; break; } | |
| } | |
| } | |
| if (r == '"') | |
| { rCh (); delCh (); } | |
| return TK_STRING; | |
| case '0': case '1': case '2': case '3': case '4': | |
| case '5': case '6': case '7': case '8': case '9': | |
| bCh (r); readDigit (); return TK_DIGIT; | |
| default: | |
| if (r == '_' || r == '?' || (r >= 'a' && r <= 'z') | |
| || (r >= 'A' && r <= 'Z')); | |
| else return TK_UNKNOWN; | |
| while ((r = pCh ()) != -1) { | |
| if (r <= 0x20) break; | |
| rCh (); | |
| } | |
| if (exports_seen == 0) | |
| { | |
| if (!strcmp (t_buf, "LIBRARY")) return TK_LIBRARY; | |
| if (!strcmp (t_buf, "EXPORTS")) return TK_EXPORTS; | |
| } | |
| else | |
| { | |
| if (!strcmp (t_buf, "DATA")) return TK_DATA; | |
| } | |
| return TK_NAME; | |
| } | |
| } | |
| static int lexit2 (void) | |
| { | |
| static int last = '\n'; | |
| int r; | |
| if (last == -1) | |
| return -1; | |
| redo: | |
| r = lexit (); | |
| /* Eat double new-lines. */ | |
| if (r == '\n' && last == r) | |
| goto redo; | |
| if (r == TK_EXPORTS || r == TK_LIBRARY) | |
| { | |
| if (last != TK_NL) | |
| r = TK_NAME; | |
| } else if (r == TK_DATA && last == TK_NL) | |
| r = TK_NAME; | |
| if (r == TK_EXPORTS) | |
| exports_seen = 1; | |
| if (r == TK_UNKNOWN) | |
| { | |
| fprintf (stderr, "Unexpected character ,%s'\n", t_buf); | |
| goto redo; | |
| } | |
| last = r; | |
| return r; | |
| } | |
| const char *cur_libname = ""; | |
| static const char *cur_symbol = ""; | |
| static const char *cur_libsymbol = ""; | |
| static const char *cur_alias = ""; | |
| static int cur_data = 0; | |
| static int | |
| expect_newline (const char *stmt) | |
| { | |
| int r; | |
| int first = 1; | |
| while ((r = lexit2 ()) != -1 && r != TK_NL) | |
| { | |
| if (first == 1) | |
| fprintf (stderr, "Unexpected token in %s statment: ,%s", stmt, t_buf); | |
| else | |
| fprintf (stderr, " %s", t_buf); | |
| first = 0; | |
| } | |
| if (!first) | |
| fprintf (stderr, "'.\n"); | |
| return r; | |
| } | |
| static int parseit (void) | |
| { | |
| int r; | |
| if (!exports_seen) { | |
| if ((r = lexit2 ()) == -1) | |
| return 0; | |
| if (r == TK_EXPORTS) | |
| { | |
| r = expect_newline ("EXPORTS"); | |
| return (r != -1 ? 1 : 0); | |
| } | |
| if (r == TK_LIBRARY) | |
| { | |
| r = lexit2 (); | |
| if (r != TK_STRING && r != TK_NAME) | |
| { | |
| fprintf (stderr, "Expect name/string after LIBRARY keyword.\n"); | |
| return (r != -1 ? 1 : 0); | |
| } | |
| cur_libname = unifyStr (t_buf); | |
| fprintf (stderr, "Current library-name set to ,%s'\n", cur_libname); | |
| r = expect_newline ("LIBRARY"); | |
| return (r != -1 ? 1 : 0); | |
| } | |
| } else { | |
| if ((r = lexit2 ()) == -1) return 0; | |
| if (r != TK_NAME) | |
| { | |
| fprintf (stderr, "Unexpected token ,%s'. Would have expected a string/name.\n", t_buf); | |
| return (expect_newline ("UNKNOWN") == -1 ? 0 : 1); | |
| } | |
| cur_symbol = unifyStr (t_buf); | |
| cur_alias = ""; | |
| cur_libsymbol = ""; | |
| cur_data = 0; | |
| while ((r = lexit2 ()) != -1 && r != TK_NL) | |
| { | |
| if (r == TK_DATA) | |
| cur_data = 1; | |
| else if (r == TK_EQUAL) | |
| { | |
| r = lexit2 (); | |
| if (r != TK_NAME) | |
| { | |
| fprintf (stderr, "Expected name after = expression.\n"); | |
| if (r != -1 && r != TK_NL) | |
| r = expect_newline ("SYMBOL"); | |
| return (r == -1 ? 0 : 1); | |
| } | |
| cur_libsymbol = unifyStr (t_buf); | |
| } | |
| else if (r == TK_EQUALEQUAL) | |
| { | |
| r = lexit2 (); | |
| if (r != TK_NAME) | |
| { | |
| fprintf (stderr, "Expected name after == expression.\n"); | |
| if (r != -1 && r != TK_NL) | |
| r = expect_newline ("SYMBOL"); | |
| return (r == -1 ? 0 : 1); | |
| } | |
| cur_alias = unifyStr (t_buf); | |
| } | |
| else | |
| { | |
| fprintf (stderr, "Unknown token ,%s'.\n", t_buf); | |
| } | |
| } | |
| addSymbol (cur_symbol, cur_libsymbol, cur_alias, cur_data); | |
| } | |
| return (r != -1 ? 1 : 0); | |
| } | |
| void addSymbol (const char *sym, const char *libsym, const char *alias, int is_data) | |
| { | |
| sSymbol *n; | |
| n = (sSymbol *) malloc (sizeof (sSymbol)); | |
| n->next = NULL; | |
| n->sym = sym; | |
| n->libsym = libsym; | |
| n->alias = alias; | |
| n->is_data = is_data; | |
| n->subs = NULL; | |
| if (t_lastsym) | |
| t_lastsym->next = n; | |
| else | |
| t_sym = n; | |
| t_lastsym = n; | |
| } | |
| void sortSymbols (void) | |
| { | |
| sSymbol *p = NULL, *c = t_sym; | |
| while (c != NULL) | |
| { | |
| if (c->alias[0] != 0) | |
| { | |
| sSymbol *l = NULL, *r = t_sym; | |
| while (r != NULL && r->sym != c->alias) | |
| { | |
| l = r; r = r->next; | |
| } | |
| if (!r) | |
| { | |
| addSymbol (r->alias, unifyStr (""), unifyStr (""), 1); | |
| r = t_lastsym; | |
| } | |
| if (!p) | |
| t_sym = c->next; | |
| else | |
| p->next = c->next; | |
| c->next = r->subs; | |
| r->subs = c; | |
| c = t_sym; p = NULL; | |
| continue; | |
| } | |
| p = c; | |
| c = c->next; | |
| } | |
| } | |
| void dumpSymbols (void) | |
| { | |
| sSymbol *l, *i; | |
| l = t_sym; | |
| while (l != NULL) | |
| { | |
| fprintf (stdout, "Symbol: ,%s'", l->sym); | |
| if (l->is_data) fprintf (stdout, " DATA"); | |
| if (l->subs) { | |
| i = l->subs; | |
| fprintf (stdout, " aliases {"); | |
| while (i != NULL) | |
| { | |
| fprintf (stdout, " %s", i->sym); | |
| if (i->is_data) fprintf (stdout, ":DATA"); | |
| i = i->next; | |
| } | |
| fprintf (stdout, " }"); | |
| } | |
| fprintf (stdout, "\n"); | |
| l = l->next; | |
| } | |
| } | |
| int main() | |
| { | |
| int r; | |
| t_in = stdin; | |
| addCh (-1); | |
| while (parseit ()) | |
| { | |
| } | |
| sortSymbols (); | |
| dumpSymbols (); | |
| outputSyms (); | |
| return 0; | |
| } |