blob: 59ffab01f1624b569da3366c547199ff5baa4129 [file] [log] [blame]
/*
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;
}