// This is a linux implementation of dynamic loading such that we could
// use with Imp, e.g. %dynamicroutinespec system(%string(*)%name S)

// Similar to EMAS, the first call loads the dynamic library before calling the
// routine, and subsequent calls go to it directly via the updated pointer to
// the code.  Unlike EMAS we don't have a library searching mechanism to locate
// the appropriate library, so that part will have to be system-dependent, and
// creating the dynamically loadable library to be called, if we declare an Imp
// routine as %dynamic (as opposed to %external, and not as a %spec) is also
// something that will have to be worked on, but even just calling c library
// routines dynamically like this is a good step in the right direction of
// implementing the Imp %dynamic mechanism!

#include <stdio.h>
#include <dlfcn.h>

// get the library filename from gtoal@linux:~/src/i2c$ ldd /bin/ls | grep libc
// libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7cc4000)

// The following interface code would be automatically generated by the Imp compiler
// from the %dynamicroutinespec declaration, for each such declaration that was used
// in the program.

static int (*sys_func)(const char *cmd);
static int system_loader(const char *cmd);
static int (*system)(const char *cmd) = system_loader;
static int system_loader(const char *cmd) {
  void *handle;
  char *err;

  handle = dlopen("/lib/i386-linux-gnu/libc.so.6", RTLD_LAZY);
  if (!handle) {
    fprintf(stderr, "%%dynamicroutinespec system(%%string(*)%%name S) NOT FOUND - %s\n", dlerror());
    return -1;
  }

  dlerror();
  *(void **)(&sys_func) = dlsym(handle, "system");
  err = dlerror();
  if (err) {
    fprintf(stderr, "%%dynamicroutinespec system(%%string(*)%%name S) FAILS - %s\n", err);
    dlclose(handle);
    return -1;
  }

  /* Patch implementation pointer so subsequent calls go directly: */
  system = sys_func;

  /* We keep the handle open for the rest of the program's lifetime. */
  return sys_func(cmd);
}

// The Imp program would call it like any other external routine:
int main(int argc, char **argv) {
  int rc;
  rc = system("pwd"); // The initial call invokes the dynamic loader,
  rc = system("df");  // then subsequent calls go directly to system()
  return 0;
}
