/*

    DEF2DLL.C - build a dll from a DEF file

    Copyright (C) 1995-1997
	Rainer Schnitker, Heeper Str. 283, 33607 Bielefeld
	email: rainer@mathematik.uni-bielefeld.de

    All rights reserved

*/

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <process.h>

#include <sys/moddef.h>

static char *library;           /* name of library in def file */

static int n_export = 0;
static char ** export_list;

static void put_list(char *name)
{
    if (!n_export)
        export_list = (char **) malloc(sizeof(char *));

    export_list = realloc(export_list, sizeof(char *) * ++n_export);

    if (export_list == NULL) {
        static char bad[] = "Memory out for export list";
        write (2, bad, sizeof(bad));
        exit(1);
    }

    export_list[n_export - 1] = name;
}

static void def_warning(char *s)
{
    fprintf(stderr, "Warning statement not supported :");
    fputs(s, stderr);
    fflush(stderr);
}

/* callback for emx library function (parse DEF files) */

static int md_callback (struct _md *md, const _md_stmt *stmt, _md_token token,
			void *arg)
{
    switch (token) {
	case _MD_BASE:
	    def_warning ("BASE");
	    break;

       case _MD_CODE:
	   def_warning ("CODE");
	   break;

       case _MD_DATA:
	   def_warning ("DATA");
	   break;

	case _MD_DESCRIPTION:
	    break;

	case _MD_EXETYPE:
	    def_warning ("EXETYPE");
	    break;

	case _MD_EXPORTS:
            put_list (strdup (stmt->export.entryname));
	    break;

	case _MD_HEAPSIZE:
	    break;

	case _MD_IMPORTS:
	    def_warning ("IMPORTS");
	    break;

	case _MD_LIBRARY:
            library = strdup(stmt->name.name);
	    break;

	case _MD_NAME:
	    break;

	case _MD_OLD:
	    def_warning ("OLD");
	    break;

	case _MD_PROTMODE:
	    def_warning ("PROTMODE");
	    break;

	case _MD_REALMODE:
	    def_warning ("REALMODE");
	    break;

	case _MD_SEGMENTS:
	    def_warning ("SEGMENTS");
	    break;

	case _MD_STACKSIZE:
	    break;

	case _MD_STUB:
	    break;

	case _MD_VIRTUAL:
	case _MD_PHYSICAL:
	    puts("VIRTUAL DEVICE and PHYSICAL DEVICE statements not supported");
            break;

	case _MD_parseerror:
	default:
	    printf ("%s (line %ld)", _md_errmsg (stmt->error.code),
			_md_get_linenumber (md));
	    exit (1);
    }
    return (0);
}

static void read_def_file (char *def_fname)
{
    struct _md *md;

    md = _md_open (def_fname);
    if (md == NULL) {
        fprintf (stderr, "cannot open `%s'", def_fname);
        exit(1);
    }

    _md_next_token (md);
    _md_parse (md, md_callback, NULL);
    _md_close (md);
}

static int build_cfile (char *c_filename)
{
    FILE *fsource;
    int i;

    if (!(fsource = fopen (c_filename, "w+t")))
        return -1;

    fprintf(fsource, "/* Declare list */\n");
    for (i = 0; i < n_export; ++i)
        fprintf(fsource, "extern void %s(void);\n", export_list[i]);

    fprintf(fsource, "\ntypedef void (*FUNCS) (void);\n");
    fprintf(fsource, "\nFUNCS _internals_funcs[] = {\n");
    for (i = 0; i < n_export; ++i)
        fprintf(fsource, "%s,\n", export_list[i]);
    fprintf(fsource, "0 };\n");

    fclose (fsource);
    return 0;
}

static int lib2dll(char *def_filename, int argc, char **gcc_opts)
{
    char *p, *c_filename, *dll_filename, **gcc_args;
    int i, l, ret;

    read_def_file (def_filename);

    if (!library[0]) {
        printf("missing module name in DEF file\n");
        return 1;
    }

    /* kill extention */
    if ((p = strchr(library, '.')))
        *p = 0;

    l = strlen (library);

    c_filename = malloc (l + 2);
    strcpy (c_filename, library);
    strcat (c_filename, ".c");

    dll_filename = malloc (l + 4);
    strcpy (dll_filename, library);
    strcat (dll_filename, ".dll");

    strcat (library, ".def");

    if (build_cfile (c_filename) < 0) {
        printf("can't create C source file %s\n", c_filename);
        return 1;
    }

    gcc_args = (char **) malloc (argc + 10 + 1);

    gcc_args[0] = "gcc";
    gcc_args[1] = "-Zwin32";
    gcc_args[2] = "-Zdll";
    gcc_args[3] = "-s";
    gcc_args[4] = "-O";
    gcc_args[5] = "-Wall";
    gcc_args[6] = c_filename;
    gcc_args[7] = "-o";
    gcc_args[8] = dll_filename;
    gcc_args[9] = def_filename;

    for (i = 0; i < argc; ++i)
        gcc_args[10 + i] = gcc_opts[i];
    gcc_args[10 + i] = NULL;

    printf ("Running:");
    for (i = 0; i < argc + 10; ++i)
        printf(" %s", gcc_args[i]);
    putchar ('\n');

    ret = spawnvpe (P_WAIT, "gcc.exe", gcc_args, environ);

    unlink (c_filename);

    free (c_filename);
    free (dll_filename);
    free (gcc_args);

    return ret;
}

int main(int argc, char **argv, char **env)
{
    if (argc <= 1) {
        printf("usage: lib2dll <file.def> [gcc options]\n");
        puts("example: lib2dll flex.def -Zmt -Zcrtdll=crtrsxnt -lfl");
        return 1;
    }

    if (access (argv[1], 0) != 0) {
        printf("cannot find the def file %s\n", argv[1]);
        return 1;
    }

    return lib2dll (argv[1], argc - 2, argv + 2);
}
