/*
*********************************************************************
COPYRIGHT (c) 1991  BY
MITECH CORPORATION, CONCORD, MASSACHUSETTS.
ALL RIGHTS RESERVED.

Permission to use, copy, modify, distribute and sell this software and
its documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies, and
that the name of MITECH Corporation not be used in advertising or
publicity pertaining to distribution of the software without specific,
written prior permission.

How to call:

 dynamic_link_module("IMAGE",
                     "procedure-name-1",&ptr1,
                     "procedure-name-2",&ptr2,
                     ...);

The first argument is the name of the image (created with link/share)
to load. The IMAGE.EXE should be located in the SYS$SHARE directory,
or IMAGE should be a logical name that points some other location,
or the first argument can be "IMAGE LOCATION" where LOCATION
specifies the device and/or directory where IMAGE.EXE is located,
including the .EXE extension, e.g. DEVICE:[DIRECTORY].EXE

The rest of the arguments are pairs of a procedure name and a
location to store the pointer to procedure found in the image.

e.g. long (*ptr1)();

Suggestions for creating the sharable image to be loaded:

If the module being loaded needs to do any intialization, or if it
needs to call-back into the main program in any way, it is a good idea
to have an init routine that is always called before any other
subroutines in the module are called. For the purpose of callbacks
there are various techniques, such as passing an array of pointers to
functions, in which case you should have a file available at compile
time that defines which INDEX into the array corresponds to which
function, or you can pass a structure/record that contains pointers to
other functions and data, and make that structure definition available
at compile time. Or, you could pass an array that contained pairs of
{STRING,POINTER}, and utilize a dynamic lookup mechanism in that array
during initialization. The module being initialized could then take
the pointers it was interested in and store them into its own private
variables for fast and easy future access.

If your module XXX consists of a single file of C code XXX.C, implementing
externally called subroutines named XINIT,XOPEN,XCLOSE,XREAD,XWRITE,
then it may be most convenient to declare all toplevel variables as
static. The required linking procedure is then very simple:

    $ LINK/SHARE/MAP/FULL XXX.OBJ,SYS$INPUT:/OPT
    SYS$LIBRARY:VAXCRTL/SHARE
    UNVERSAL=XINIT
    UNVERSAL=XOPEN
    UNVERSAL=XCLOSE
    UNVERSAL=XREAD
    UNVERSAL=XWRITE

If it is not the case that all toplevel variables are declared
static, the dynamic link attempt will fail with the message:

   %SYSTEM-F-NOTINSTALL, writable shareable images must be installed

Do *NOT* respond to this by installing the image! Instead, identify
the non-static data at fault by utilizing the linker map file,

    $ search XXX.map ",  SHR,NOEXE,  RD,  WRT"

You want to arrange so that the PSECT's found end up as NOSHR.
Do this by inserting the following into the linking options:

     PSECT_ATTR=VAR1,NOSHR,LCL
     PSECT_ATTR=VAR2,NOSHR,LCL
     PSECT_ATTR=VAR3,NOSHR,LCL

for each offending toplevel variable, VAR1, VAR2, etc.

*/

#include <stdio.h>
#include <stdarg.h>
#include <descrip.h>
#include <ssdef.h>
#include <string.h>

static struct dsc$descriptor *set_dsc_cst(struct dsc$descriptor *x, char *buff)
{
  x->dsc$w_length = strlen(buff);
  x->dsc$a_pointer = buff;
  x->dsc$b_class = DSC$K_CLASS_S;
  x->dsc$b_dtype = DSC$K_DTYPE_T;
  return(x);
}

extern int lib$find_image_symbol();

int dynamic_link_module(char *module_name, int numb, ...)
{
  int loop;
  struct dsc$descriptor fnamed, symbold, imaged;
  char *fcn_name, *imagep;
  char filename[256];
  long *valuep, retval;
  va_list arglist;

  strcpy(filename, module_name);
  va_start(arglist, numb);

  if (imagep = strchr(filename,' '))
  {
    *imagep++ = 0;
    set_dsc_cst(&imaged, imagep);
  }
  set_dsc_cst(&fnamed, filename);

  for (loop=0;loop<numb;loop++)
  {
    fcn_name = va_arg(arglist, char *);
    valuep = va_arg(arglist, long *);
    retval = lib$find_image_symbol(&fnamed, set_dsc_cst(&symbold, fcn_name),
				   valuep, (imagep) ? &imaged : 0);
    if (retval != SS$_NORMAL) {
      va_end(arglist);
      printf("return value of %d\n", retval);
      return(0);
    }
  }
  va_end(arglist);
  return(1);
}
