/*
 * dllbinder.c
 *
 * Entry point and things for the DLL Binder
 *
 *  1994-1998 Straylight
 */

/*----- Licensing note ----------------------------------------------------*
 *
 * This file is part of Straylight's Dynamic Linking System (SDLS)
 *
 * SDLS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * SDLS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with SDLS.  If not, write to the Free Software Foundation,
 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "_time.h"

#include "error.h"
#include "hashtable.h"
#include "decode.h"
#include "readdef.h"
#include "binding.h"
#include "cstub.h"
#include "extentry.h"

extern char cright[];

static void cdll_readSymbols(char *p,void *handle)
{
  decode(handle,p);
}

static void cdll_writeName(char *p,void *handle)
{
  FILE *fp=handle;
  fprintf(fp,"  %s\n",p);
}

static void cdll_notsubset(char *p)
{
  error(LOSTSYM,p);
}

int main(int argc,char *argv[])
{

  enum
  {
    NOWT=0,
    DEF=1,
    STUB=2,
    BINDING=4,
    OBJECT=8,
    EXTERN=16,
    PATH=32
  };

  char *deffile=0;			/* Name of definition file	*/
  char *stubfile=0;			/* Name of stublib file		*/
  char *bindfile=0;			/* Name of binding file		*/
  char *extfile=0;			/* Name of external veneer file	*/
  char *dllfile=0;			/* Name of DLL file to use	*/
  int helped=0;				/* Have we given help?		*/
  hashtable objfiles;			/* Names of objects specified	*/
  hashtable symbols;			/* Symbols defined in deffile	*/
  hashtable objsym;			/* Symbols defined in objects	*/
  hashtable veneered;			/* Private symbols to veneer	*/
  int createdef=0;			/* Do we create the def file?	*/
  int whatnext=DEF;			/* What thing do we read next 	*/
  int override=NOWT;			/* What was the override tag?	*/
  int done=NOWT;			/* What things we've done	*/
  int i;				/* Loop counter			*/
  int process=1;			/* Do we process this time?	*/
  int apptbl=0;				/* Is this for app extension?	*/

  error_setProgName(argv[0]);		/* Set up the program name	*/

  objfiles=hash_create();
  symbols=hash_create();
  veneered=hash_create();
  objsym=hash_create();
  if (!objfiles || !symbols || !objsym)
    error(NOMEM);

  for (i=1;i<argc;i++)
  {
    if (!cistrcmp(argv[i],"-help"))
    {
      printf(

"%s: (help)\n"
"\n"
"Straylight Dynamic Link Library building system\n"
"  version %s\n"
"  %s\n"
"\n"
"Usage:\n"
"\n"
"  For a reminder of command syntax:\n"
"    %s -help\n"
"\n"
"  To create a definition file from object files:\n"
"    %s [[-def] <deffile>] -obj <objfile> [<objfile>]...\n"
"\n"
"  To create stub libraries and DLL headers:\n"
"    %s [[-def] <deffile>] [[-stub] <stublib>] [[-hdr] <header>]\n"
"      [-file <dllpath>]\n"
"\n"
"  To create application entry tables and extension stubs:\n"
"    %s -app [[-def] <deffile>] [[-stub] <stublib>] [[-hdr] <header>]\n"
"\n"
"  To create external function entry points for module tasks:\n"
"    %s [[-def] <deffile>] [-ext <extstub>]\n"
"\n"
"  All option switches may be abbreviated to single characters except\n"
"  -help.\n"
"\n",

             error_progname(),
             _time,
             cright,
             error_progname(),
             error_progname(),
             error_progname(),
             error_progname(),
             error_progname());
      helped=1;
    }
    else if (argv[i][0]=='-')		/* Is it an option tag?		*/
    {
      switch (argv[i][1])		/* Find second character	*/
      {
        case 'a':
          apptbl=1;
          break;
        case 'd':
          override=DEF;
          break;
        case 's':
          override=STUB;
          break;
        case 'h':
          override=BINDING;
          break;
        case 'e':
          override=EXTERN;
          break;
        case 'o':
          override=OBJECT;
          break;
        case 'f':
          override=PATH;
          break;
        default:
          error(BADTAG,argv[i]);
          break;
      }
      if (override & done)
      {
        error(DUPOPT,argv[i]);
        process=0;
        override=NOWT;
      }
    }
    else
    {
      switch (override==NOWT ? whatnext : override)
      {
        case NOWT:
          error(SILLYOPT,argv[i]);
          break;
        case DEF:
          deffile=argv[i];
          done|=DEF;
          break;
        case STUB:
          if (createdef)
          {
            error(SORBNOBJ);
            process=0;
          }
          else
          {
            stubfile=argv[i];
            done|=STUB;
          }
          break;
        case BINDING:
          if (createdef)
          {
            error(SORBNOBJ);
            process=0;
          }
          else
          {
            bindfile=argv[i];
            done|=BINDING;
          }
          break;
        case EXTERN:
          if (createdef)
          {
            error(SORBNOBJ);
            process=0;
          }
          else
          {
            extfile=argv[i];
            done|=EXTERN;
          }
          break;
        case OBJECT:
          if (bindfile || stubfile)
          {
            error(SORBNOBJ);
            process=0;
          }
          else if (apptbl)
          {
            error(APPNOEXT);
            process=0;
          }
          else
          {
            if (!hash_add(objfiles,argv[i]))
              error(NOMEM);
            createdef=1;
          }
          break;
        case PATH:
          dllfile=argv[i];
          break;
      }
      override=0;
    }
    if (!(done & DEF))
      whatnext=DEF;
    else if (createdef)
      whatnext=OBJECT;
    else if (!(done & STUB))
      whatnext=STUB;
    else if (!(done & BINDING))
      whatnext=BINDING;
    else
      whatnext=NOWT;
  }
  if (!deffile)
  {
    if (!helped)
      error(NODEF);
    process=0;
  }
  if (!createdef && !bindfile && !stubfile && !extfile && !helped)
    error(EASY);
  else if (!process)
  {
    if (!helped)
      error(BADCMDLINE);
  }
  else if (createdef)
  {
    FILE *fp=fopen(deffile,"w");
    if (!fp)
      error(NOOPENOUT,deffile);
    else
    {
      hash_enumerate(objfiles,cdll_readSymbols,objsym);
      fprintf(fp,";\n"
                 "; cdll definition file for <name>\n"
                 ";\n"
                 "\n"
                 "name \"<name>\"\n"
                 "  version 1.00\n"
                 "  author \"<author>\"\n"
                 "\n"
                 "; --- Exported symbols ---\n"
                 "\n"
                 "exports\n"
                 "{\n");
      hash_enumerate(objsym,cdll_writeName,fp);
      fprintf(fp,"}\n"
                 "\n"
                 "; --- Object files ---\n"
                 "\n"
                 "objects\n"
                 "{\n");
      hash_enumerate(objfiles,cdll_writeName,fp);
      fprintf(fp,"}\n");
      fclose(fp);
    }
  }
  else
  {
    readdef_info i;
    i.sym=symbols;
    i.obj=objfiles;
    i.vsym=veneered;
    i.apptbl=apptbl;
    i.dllpath=dllfile;
    readdef(deffile,&i);
    hash_enumerate(objfiles,cdll_readSymbols,objsym);
    hash_subset(objsym,symbols,cdll_notsubset);
    hash_subset(objsym,veneered,cdll_notsubset);
    if (bindfile)
      binding(bindfile,&i,objsym);
    if (stubfile)
      cstub(stubfile,&i,objsym);
    if (extfile)
      extentry(extfile,&i,objsym);
  }
  error_show();
  return (error_returnCode());
}
