#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <xr/ThreadsMsg.h>
#define stdout XR←MSG←STDOUT

/* truncated VERSIONSTAMPPREFIX used to avoid conflicts with mimosamakedo looking for versionstamps.   -mdw 12/7/88 */

#define VERSIONSTAMPPREFIX "@(#)mob←versio"

typedef void (*InstallFunc)();

static char * XR←getModuleName();

typedef struct LoadStruct {
  struct LoadStruct *next;
  char *file←name;
  InstallFunc load←Func;
  InstallFunc run←Func;
  InstallFunc unload←Func;
  } LoadeeRecord;
typedef LoadeeRecord *Loadee;

Loadee loadees;

void XR←run←command();
char *get←nth←word();

static int silentLoading = 1;

int
XR←load←command (command)
char *command;
{
/* Currently expects the following procedures in the file: 
	"XR←install←Filename", "XR←run←Filename", and "XR←unload←Filename"
	where Filename has been stripped of any suffixes or prefixes. */
	
  char *lastchancename;
  char *file;
  char *arg, *modulename, *XR←strip();
  char func[128];
  void XR←install←command();
  Loadee new ;
  int check←load = 0;
  int nodebugger = 0;
  int argno;

  file = get←nth←word(command, 1);
  modulename = NULL;

  new = (Loadee)malloc(sizeof(LoadeeRecord));
  new->file←name = (char *)malloc(strlen(file)+1);
  strcpy(new->file←name, file);
  lastchancename = file;
  lastchancename = XR←strip(lastchancename);
  argno = 2;
arg = get←nth←word(XR←GetCurrentCommand(), argno++);
while (arg[0]) {
    if (arg[0] == '-') {
      switch(arg[1]) {
      case 'q': {
        check←load = 1;
        break;
      }
      case 'd': {
        nodebugger = 1;
        break;
      }
      default: {
        XR←FPrintF (stdout, "Unrecognized load switch: %s.  Ignored.\n", arg);
        break;
      }
      }
    } else { /* does not begin with '-' */
      if (modulename) {
        XR←FPrintF (stdout, "Unrecognized load argument: %s.  Ignored.\n", arg);
      } else {
	modulename = arg;
      }
    }
    arg = get←nth←word(XR←GetCurrentCommand(), argno++);
  }
  cpb←printf("Loading file %s", new->file←name);
  if (nodebugger) {
    cpb←printf(" (no debugging).\n");
  } else {
    cpb←printf(".\n");
  }
  if (load←file(new->file←name, NULL, check←load, nodebugger) == NULL) {
    cpb←printf("No action taken for file '%s'.\n", new->file←name);
    return 0;
  }
  if (! modulename) { 
    cpb←printf("Trying to derive module name from version stamp.\n");
    modulename = XR←getModuleName();
    };
  if (! modulename) {
    cpb←printf("Deriving module name from filename.\n");
    modulename = lastchancename;
  }
  cpb←printf("Using module name '%s'.\n", modulename);
  strcpy(func, "←XR←install←"); strcat(func, modulename);
  if (((new->load←Func = (InstallFunc)get←sym←val(func)) == NULL) 
      && ((new->load←Func = (InstallFunc)get←sym←val("←XR←install")) == NULL)) {
    cpb←printf("Could not find 'XR←install' or '%s'.\n", func);
  }
  strcpy(func, "←XR←run←"); strcat(func, modulename);
  if (((new->run←Func = (InstallFunc)get←sym←val(func)) == NULL)
      && ((new->run←Func = (InstallFunc)get←sym←val("←XR←run")) == NULL)) {
    cpb←printf("Could not find 'XR←run' or '%s'.\n", func);
  }
  strcpy(func, "←XR←unload←"); strcat(func, modulename);
    new->unload←Func = (InstallFunc)get←sym←val(func);
  new->next = loadees;
  loadees = new;

  if (!(strncmp(command, "loadonly", 8) == 0)) {
    char *fake;
    fake = (char *)calloc(strlen(new->file←name)+20, 1);
    strcat(fake, "fake ");
    strcat(fake, new->file←name);
    XR←install←command(fake);
  } else {
    cpb←printf("Loaded only--installation proc not called.\n");
  }
  return 1;
};

void
XR←install←command(command)
char *command;
{
  char *file;
  Loadee loadee = loadees;

  file = get←nth←word(command, 1);

  while (loadee != NULL) {
    if (strcmp(file, loadee->file←name) == 0) {
      if (loadee->load←Func == NULL) {
	cpb←printf(" %s has no load function.", file); 
      } else {
	loadee->load←Func();
	XR←VerboseCommit();
      };
      cpb←printf(" %s has been installed.\n", file);
      return;
    };
    loadee = loadee->next;
  }; /* while */
  cpb←printf(" %s has not been installed. ", file); 
}
  
static void
request←command(file)
char *file;
{
  char *command;
  Loadee loadee = loadees;
  while (loadee != NULL) {
    if (strcmp(file, loadee->file←name) == 0) {
      XR←FPrintF (stdout, " %s already loaded.\n", file);
      return; };
    loadee = loadee->next;
  }; /* while */
  command = (char *)calloc(strlen(file)+20, 1);
  strcat(command, "fake←command ");
  strcat(command, file);
  XR←load←command(command);
}
  
void
XR←loadAndRun←command(command)
char *command;
{
  char *cmd←rememberer;

  cmd←rememberer = (char *)malloc(strlen(command) + 1);
  strcpy(cmd←rememberer, command);
  if (XR←load←command(command))
	  XR←run←command(cmd←rememberer);
}

void
XR←run←command(command)
char *command;
{
  char *file;
  Loadee loadee = loadees;

  file = get←nth←word(command, 1);

  while (loadee != NULL) {
    if (strcmp(file, loadee->file←name) == 0) {
      if (loadee->run←Func == NULL) {
	cpb←printf(" %s has no function 'run'.\n", file); 
      } else {
	cpb←printf(" running %s.\n", file);
	loadee->run←Func();
      }
      return;
    }
    loadee = loadee->next;
  } /* while */
  cpb←printf(" %s has not been run.\n", file); 
}
	
static void
unload←command(command)
char *command;
	{
	char *file;
        Loadee loadee = loadees;

	file = get←nth←word(command, 1);

	while (loadee != NULL) {
	  if (strcmp(file, loadee->file←name) == 0) {
	    if (loadee->unload←Func == NULL) {
	      XR←FPrintF (stdout, " %s has no function 'unload'.\n", file); 
	    } else loadee->unload←Func();
	    return; };
	  loadee = loadee->next;
	  }; /* while */
	cpb←printf(" %s has not been loaded.\n", file); 
	};	

static void
dbxtoolCommand ()
{
  char num[128];
  char name[128], filename[128];
  int pid;
  XR←FPrintF (stdout, "No auto-debugger available.\n");
}; /* debugCommand */

verboseloading()
{
  silentLoading = 0;
}

silentloading()
{
  silentLoading = 1;
}

typedef (*procpointer)();
static (*install←command←ptr)() = (procpointer)XR←install←command;
static (*run←command←ptr)() = (procpointer)XR←run←command;
static (*loadAndRun←command←ptr)() = (procpointer)XR←loadAndRun←command;
static (*load←command←ptr)() = (procpointer)XR←load←command;
static (*unload←command←ptr)() = (procpointer)unload←command;
static (*dbxtoolCommand←ptr)() = (procpointer)dbxtoolCommand;

static (*verboseloading←ptr)() = (procpointer)verboseloading;
static (*silentloading←ptr)() = (procpointer)silentloading;

static
XR←run()
{
  XR←register("i", &install←command←ptr, "install a previously loaded Cedar module", 0);
  XR←register("install", &install←command←ptr, "install a previously loaded Cedar module", 0);
  XR←register("loadandrun", &loadAndRun←command←ptr, "load and then run a Cedar module (takes -d or -q switch after filename)", 0);
  XR←register("lr", &loadAndRun←command←ptr, "load and then run a Cedar module (takes -d or -q switch after filename)", 0);
  XR←register("l", &load←command←ptr, "load a Cedar module (takes -d or -q switch after filename)", 0);
  XR←register("load", &load←command←ptr, "load a Cedar module (takes -d or -q switch after filename)", 0);
  XR←register("loadonly", &load←command←ptr, "load a Cedar module (takes -d or -q switch after filename)", 0);
  XR←register("r", &run←command←ptr, "run a previously loaded Cedar module", 0);
  XR←register("run", &run←command←ptr, "run a previously loaded Cedar module", 0);
  XR←register("unload", &unload←command←ptr, "unload a previously loaded Cedar module (unimplemented).", 0);
  XR←register("dbx", &dbxtoolCommand←ptr, "start running a dbxtool pointed at the current cedarboot.", 0);
  XR←register("debug", &dbxtoolCommand←ptr, "start running a dbxtool pointed at the current cedarboot.", 0);
  XR←register("silentloading", &silentloading←ptr, "go about the business of dynamic loading quietly.", 0);
  XR←register("verboseloading", &verboseloading←ptr, "print lots of messages about what is happening during dynamic loading.", 0);
}

XR←run←cmds()
{
  XR←run();
}

cpb←printf(fmt, a, b, c, d, e, f, g, h)
{
  if (! silentLoading) {
    XR←FPrintF (stdout, fmt, a, b, c, d, e, f, g, h);
  }
}

static char *
XR←getModuleName()
{
  char *lastblank;
  char *lastdot;
  char *addr;
  char *stamp;
  if ((addr = (char *)get←sym←val("←versionStamp")) == 0) {
    cpb←printf("Mimosa version stamp not found.\n");
    return 0;
  }
  stamp = strdup(addr);
  if (strncmp(stamp, VERSIONSTAMPPREFIX, strlen(VERSIONSTAMPPREFIX)) != 0) {
    stamp[strlen(VERSIONSTAMPPREFIX)] = '\0';
    cpb←printf("Mimosa version stamp starts with '%s': not in proper form.\n", stamp);
    return 0;
  }
  lastblank = strrchr(stamp, ' ') + 1;
  lastdot = strrchr(lastblank, '.');
  if (lastdot) {
    *lastdot = '\0';
  }
  cpb←printf("Found name in version stamp. ");
  return lastblank;
}