/*

    MAKELIB.C - create import libraries in a.out or coff format
		(as.exe/ar.exe is called)

    Copyright (C) 1995-1996
	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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include "port.h"
#include "spawn.h"
#include "makelib.h"

static char as_path[_MAX_PATH];     /* path to as.exe		    */
static char ar_path[_MAX_PATH];     /* path to ar.exe		    */
static char dll_name[_MAX_FNAME];   /* full DLL name		    */
static char dll_ext[_MAX_EXT];	    /* extention		    */
static char ar_file[_MAX_FNAME];    /* dllname.a		    */
static char as_file[_MAX_FNAME];    /* name of as file = NAME0000.S */
static char o_file[_MAX_FNAME];     /* object file = NAME0000.O     */
static char symbol[256];

static char tmp_dir[] = "MKLIBTMP";

int n_export = 0;
EXPORT_LIST *export_list;

static void x2s(int v, char *s)
{
    static char hex[] = "0123456789abcdef";
    int i;

    for (i = 0; i < 4; i++) {
	s[3 - i] = hex[v & 15];
	v >>= 4;
    }
}

static int run_as(void)
{
    static char *argv[5] = {"as", "-o", o_file, as_file, NULL};

    return spawnve(P_WAIT, as_path, argv, NULL);
}

static int run_ar(void)
{
    static char *argv[5] = {"ar", "r", ar_file, "*.o", NULL};

    return spawnve(P_WAIT, ar_path, argv, NULL);
}

static void add_function(char *name, int ord, int is_data)
{
    FILE *file;
    char *mark;

    x2s(ord, as_file+4);
    strcpy (o_file, as_file);
    o_file[9] = 'o';

    mark = strchr(name, '@');   /* stdcall names */
    if (mark) {
	int n = (int) mark - (int) name;
	strcpy (symbol, name + 1);
	symbol[n - 1] = 0;
    } else
	strcpy (symbol, name);

    file = fopen(as_file, "wt");

    if (is_data) {
	fprintf(file, ".data\n.globl _%s\n_%s:\n.long 0\n", symbol, symbol);
	fprintf(file, ".stabs \"__ImpNT_%s=%s\",38,0,%d,_%s\n",
			name, dll_name, ord, symbol);
    } else {
	fprintf(file, ".text\n.align 2,0x90\n\n.globl _%s\n_%s:\n",
			symbol, symbol);
	fprintf(file, "1:\tjmp\t*_%s\n\n", symbol);
	fprintf(file, ".stabs \"__ImpNT_%s=%s\",36,0,%d,1b+2\n",
			name, dll_name, ord);
    }

    fclose(file);

    if (run_as() < 0)
	perror("run as.exe");
    remove(as_file);
}

int makelib(char *dllname, char *output)
{
    int e;
    struct dirent *dir;
    DIR *d;
    char *olddir;

    if (_path(ar_path, "AR.EXE") < 0) {
	printf("Cannot find ar program");
	return 1;
    }
    if (_path(as_path, "AS.EXE") < 0) {
	printf("Cannot find assembler program");
	return 1;
    }

    _abspath(ar_file, output, _MAX_FNAME);
    _splitpath(dllname, NULL, NULL, dll_name, dll_ext);

    if (strlen(dll_name) > 8)
	printf("Warning for FAT: dll basename has more than 8 chars");

    /* as_file = xdll0000.o where 0000 will be ordinal value */
    strncpy(as_file, dll_name, 9);
    as_file[8] = 0;
    strlwr(as_file);
    if (strlen(as_file) <= 4)
	strcat(as_file, "0000");
    strcpy(as_file + 8, ".s");

    /* for win32s take upper name */
    strupr(dll_name);
    strcat(dll_name, dll_ext);

    printf("MakeLib: build %s\n", ar_file);

    if (access(dllname, 0) < 0) {
	printf("Cannot access dll %s\n", dllname);
	exit(1);
    }

    get_export_list(dllname);

    olddir = getcwd (NULL, S_IREAD | S_IWRITE | S_IEXEC);
    if (mkdir (tmp_dir, 0))
      puts ("warning: mkdir failed");
    if (chdir (tmp_dir))
      puts ("warning: chdir failed");

    for (e = 0; e < n_export; e++)
	add_function(export_list[e].name, export_list[e].ord, export_list[e].flags);
    run_ar();

    d = opendir(".");

    while ((dir = readdir(d)) != NULL)
	if (strcmp(dir->d_name, ".") && strcmp(dir->d_name, ".."))
	    unlink(dir->d_name);

    closedir(d);

    if (chdir (olddir))
      puts ("warning: chdir failed");
    if (rmdir (tmp_dir))
      puts ("warning: rmdir failed");
    free (olddir);

    return 0;
}

int print_dll(char *dllname)
{
    int e;

    if (access(dllname, 0) < 0) {
	printf("Cannot access dll %s\n", dllname);
	return 1;
    }
    get_export_list(dllname);

    for (e = 0; e < n_export; e++)
	printf("%c %s %d\n",
		    (export_list[e].flags & 1) ? 'D' : 'T',
		    export_list[e].name,
		    export_list[e].ord);

    return 0;
}

int main(int argc, char **argv)
{
    int opt_just_print = 0;
    char *output = NULL;
    char *input = NULL;
    int i;

    if (argc == 1) {
	printf("usage: MAKELIB dll-file [-o output-name] [--print]\n");
	return 1;
    }

    for (i = 1; i < argc; i++) {

	if (argv[i][0] == '-') {
	    if (strcmp(argv[i], "-o") == 0) {
		if (argv[i+1] == NULL) {
		    puts("option -o needs argument");
		    return 1;
		}
		else
		    output = argv[++i];
	    }
	    else if (strcmp(argv[i], "--print") == 0)
		opt_just_print = 1;
	    else {
		printf("bad argument %s\n", argv[i]);
		return 1;
	    }
	}
	else {
	    input = argv[i];
	}
    }

    if (!output) /* use 'input.a' name */
    {
	char *s = strrchr ( input, '\\');

	output = strdup ( (s) ? s + 1 : input);

	if ((s = strrchr (output, '.')) != NULL)
	    strcpy (s + 1, "a");
    }

    if (!opt_just_print)
	return makelib (input, output);
    else
	return print_dll (input);
}
