/* dos emulator, Matthias Lautner 
 * Extensions by Robert Sanders, 1992-93
 *
 * DANG_BEGIN_MODULE
 * 
 * Initial program executed to run DOSEMU. Gets access to libdosemu
 * and sets international character parms. Finally calls entry
 * point of DOSEMU emulate() function which is loaded above the
 * usual DOS memory area from 0 - 1meg. Emulate() is in emu.c.
 *
 * DANG_END_MODULE
 *
 * DANG_BEGIN_CHANGELOG
 *
 * $Date: 1995/04/08 22:29:37 $
 * $Source: /usr/src/dosemu0.60/RCS/dos.c,v $
 * $Revision: 2.10 $
 * $State: Exp $
 *
 *
 * DANG_END_CHANGELOG
 *
 */

#include <stdio.h>
#include <a.out.h>
#include <locale.h>
#include <errno.h>
#ifdef __NetBSD__
#include <sys/mman.h>
#include <fcntl.h>
#endif
#include "config.h"

#ifndef LIBDOSEMU
#define LIBDOSEMU	"/usr/lib/libdosemu"
#endif

/*
 * DANG_BEGIN_FUNCTION dosemu
 *
 * arguments:
 * argc - Count of argumnents.
 * argc - Actual arguments.
 *
 * description:
 *  Function created by entry point into libdosemu. Called to
 *  jump into the emulate function of DOSEMU.
 *
 * DANG_END_FUNCTION
 */
static void (*dosemu) (int argc, char **argv);

static char buf [1088 * 1024];	/* ensure that the lower 1MB+64K is unused */


int
main(int argc, char **argv)
{
  struct exec header;
  FILE *f;
  char *libpath = LIBDOSEMU; 

#if 0 /* Security hole */
  char *cp;

  cp = (unsigned char  *)getenv("LIBDOSEMU");
  if(cp) 
	libpath = cp;
#endif


  f = fopen(libpath, "r");
  if (f == NULL)
  {
    fprintf(stderr, "%s: cannot open shared library: %s\n", argv [0],
	libpath, strerror(errno));
    fprintf(stderr, "Check the LIBDOSEMU variable, default is %s\n",
		LIBDOSEMU);
    exit(1);
  }

  if (fread(&header, sizeof(header), 1, f) != 1)
  {
    fprintf (stderr, "%s: cannot read shared library: %s\n", argv [0],
	libpath, strerror(errno));
    exit(1);
  }

  if (N_BADMAG (header))
  {
    fprintf (stderr, "%s: invalid shared library format: %s\n", argv [0],
	libpath);
    exit(1);
  }

  (void) fclose(f);
  if (uselib(libpath) != 0) {
    fprintf (stderr, "%s: cannot load shared library: %s: %s\n", argv [0],
	libpath, strerror(errno));
    fprintf(stderr, "Try setting LIBDOSEMU\n");
    exit(1);
  }
  setlocale(LC_CTYPE,"");

  dosemu = (void *) header.a_entry;

  (* dosemu)(argc, argv);

}

#ifdef __NetBSD__
#include <nlist.h>

struct nlist nl[] = {
    {"curbrk" },
#define X_CURBRK 0
    {"_environ" },
#define X_ENVIRON 1
    { 0 },
};
static int		anon_fd = -1;
#ifndef MAP_ANON
#define MAP_ANON	0
#define anon_open() do {					\
	if ((anon_fd = open("/dev/zero", O_RDWR, 0)) == -1)	\
		err("open: %s", "/dev/zero");			\
} while (0)
#define anon_close() do {	\
	(void)close(anon_fd);	\
	anon_fd = -1;		\
} while (0)
#else
#define anon_open()
#define anon_close()
#endif

int
uselib(const char *path)
{
    /* map a.out file to its natural location. */
    int		fd;
    caddr_t		addr, raddr;
    struct exec	hdr;
    int		usehints = 0;
    extern char **environ;

    if ((fd = open(path, O_RDONLY, 0)) == -1) {
	return 1;
    }
    if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
	(void)close(fd);
	/*errno = x;*/
	return 1;
    }

    if (N_BADMAG(hdr)) {
	(void)close(fd);
	errno = EFTYPE;
	return 1;
    }
    if (nlist(path, nl) != 0) {
	errno = EINVAL;
	return 1;			/* XXX */
    }
    /* assume it wants to be mapped to just below its entry point.
     * It should be PIC, but just in case :)
     */
    raddr = (caddr_t)(hdr.a_entry & ~(N_PAGSIZ(hdr)-1));
    if ((addr = mmap(raddr,
		     hdr.a_text + hdr.a_data + hdr.a_bss,
		     PROT_READ|PROT_EXEC,
		     MAP_COPY|MAP_FIXED, fd, 0)) == (caddr_t)-1) {
	(void)close(fd);
	return 1;
    }
    if (addr != raddr) {
	(void) close(fd);
	errno = EFAULT;
	return 1;
    }
    if (mprotect(addr + hdr.a_text, hdr.a_data,
		 PROT_READ|PROT_WRITE|PROT_EXEC) != 0) {
	(void)close(fd);
	return 1;
    }
    anon_open();

    if (mmap(addr + hdr.a_text + hdr.a_data, hdr.a_bss,
	     PROT_READ|PROT_WRITE|PROT_EXEC,
	     MAP_ANON|MAP_COPY|MAP_FIXED,
	     anon_fd, 0) == (caddr_t)-1) {
	(void)close(fd);
	return 1;
    }
    (void)close(fd);
    anon_close();

    __asm("movl curbrk,%0" : "=r" (addr));
    *(caddr_t *) nl[0].n_value = addr;
    *(caddr_t *) nl[1].n_value = (caddr_t) environ;
#if 0
	/* Assume _DYNAMIC is the first data item */
	dp = (struct _dynamic *)(addr+hdr.a_text);

	/* Fixup __DYNAMIC structure */
	(long)dp->d_un.d_sdt += (long)addr;

	return alloc_link_map(path, sodp, smp, addr, dp);
#endif
    return 0;
}
#endif
