/* glk.c, Author: Marc Feeley (08/01/90) */

/*

This program links .o files produced by 'gcv' into an executable program.

Sample use:

   glk -h2000 -rc module1 module2 main -lX11

*/

#include <stdio.h>
/* include <sys/types.h>
   include <sys/stat.h>
   include <sys/wait.h>
*/

/*---------------------------------------------------------------------------*/


#define MAX_NB_ARGS 100

char *argvect[MAX_NB_ARGS+1];
int argcount;
FILE *output;


/*---------------------------------------------------------------------------*/

/* string utils */


char *alloc( n )
int n;
{ char *p = (char *)malloc( n );
  if (p == NULL)
  { fprintf( stderr, "Error: out of memory\n" ); exit(1); }
  return p;
}


int string_length( str )
char *str;
{ int len = 0;
  while (*(str++) != '\0') len++;
  return len;
}


char *string_extend( ptr, str )
char *ptr, *str;
{ while (*str != '\0') *(ptr++) = *(str++);
  *ptr = '\0';
  return ptr;
}


char *string_append( str1, str2 )
char *str1, *str2;
{ char *p1;
  p1 = alloc( string_length( str1 ) + string_length( str2 ) + 1 );
  string_extend( string_extend( p1, str1 ), str2 );
  return p1;
}


/*---------------------------------------------------------------------------*/


char *filename_tail( str )
char *str;
{ char *s = str, *p = str;
  while (*p != '\0') if (*(p++) == '/') s = p;
  return s;
}


void generate_filename_key( output, str )
FILE *output;
char *str;
{ char *s = filename_tail( str );
  while (*s != '\0')
  { char c = *(s++);
    if (((c>='a') && (c<='z')) || ((c>='0') && (c<='9')) || (c=='_'))
      fprintf( output, "%c", c );
    else
      fprintf( output, "X%02x", (c & 0xff) );
  }
}


/*---------------------------------------------------------------------------*/


#define LOAD_RUNTIME  1
#define TOUCH_RUNTIME 2
#define CHECK_RUNTIME 4
#define STATS_RUNTIME 8


/* char lib_path[] = LIB_PATH; */


void link_file( filename )
char *filename;
{ if (argcount >= MAX_NB_ARGS)
  { fprintf( stderr, "Error: Too many arguments\n" ); exit(1); }
  argvect[argcount++] = string_append( filename, ".o" );
}


void init_file( output, filename, pass )
FILE *output;
char *filename;
int pass;
{ if ((pass == 1) || (pass == -1))
  { fprintf( output, "extern short link_ofile_" );
    generate_filename_key( output, filename );
    fprintf( output, "[]; extern long link_sizeof_ofile_" );
    generate_filename_key( output, filename );
    fprintf( output, ";\n" );
  }
  else
  { if (pass == 2)
      fprintf( output, ",link_ofile_" );
    else if (pass == -2)
      fprintf( output, " link_ofile_" );
    else if (pass == 3)
      fprintf( output, ",&link_sizeof_ofile_" );
    else if (pass == -3)
      fprintf( output, " &link_sizeof_ofile_" );
    generate_filename_key( output, filename );
    fprintf( output, "\n" );
  }
}


void link_runtime( output, runtime_kind, pass )
FILE *output;
int runtime_kind;
int pass;
{ init_file( output, "_kernel", -pass );
  if (runtime_kind & LOAD_RUNTIME)
  { init_file( output, "_system", pass );
    init_file( output, "_errors", pass );
    init_file( output, "_repr", pass );
    init_file( output, "_multi", pass );
    init_file( output, "_numbers", pass );
    init_file( output, "_ports", pass );
    init_file( output, "_eval", pass );
    init_file( output, "_standard", pass );
    init_file( output, "_nonstandard", pass );
  }
}


char *runtime_library( runtime_kind )
int runtime_kind;
{ char *p1, *p2;
  p1 = alloc( 4 );
  p2 = p1;
  if (runtime_kind & CHECK_RUNTIME) p2 = string_extend( p2, "c" );
  if (runtime_kind & STATS_RUNTIME) p2 = string_extend( p2, "s" );
  if (runtime_kind & TOUCH_RUNTIME) p2 = string_extend( p2, "t" );
  return p1;
}


/*---------------------------------------------------------------------------*/


void main_err( name )
char *name;
{ fprintf( stderr, "Usage: %s [-sSTACK_LENGTH_IN_KILOBYTES]\n", name );
  fprintf( stderr, "           [-hHEAP_LENGTH_IN_KILOBYTES]\n" );
  fprintf( stderr, "           [-cCONST_LENGTH_IN_KILOBYTES]\n" );
  fprintf( stderr, "           [-r[k][c][s][t]]\n" );
  fprintf( stderr, "           [file]...\n" );
  fprintf( stderr, "           [option]...\n" );
  exit(1);
}


void main( argc, argv )
int argc;
char *argv[];
{ int i, j, n;
  char *arg;
  int nb_args;
  int first_file;
  char *program_name = NULL;
  char *c_file = "link_me.c";
  char *o_file = "link_me.o";
  long stack_length_in_k = -1;
  long heap_length_in_k = -1;
  long const_length_in_k = -1;
  int runtime_kind = LOAD_RUNTIME | CHECK_RUNTIME | TOUCH_RUNTIME;

  /* parse arguments */

  for(first_file=1; first_file<argc; first_file++)
  { arg = argv[first_file];
    if (*arg == '-')
    { if (arg[1] == 's')
        stack_length_in_k = atoi( &arg[2] );
      else if (arg[1] == 'h')
        heap_length_in_k = atoi( &arg[2] );
      else if (arg[1] == 'c')
        const_length_in_k = atoi( &arg[2] );
      else if (arg[1] == 'r')
        { arg += 2;
          runtime_kind = LOAD_RUNTIME;
          while (*arg != 0)
            switch (*(arg++))
            { case 'k' : runtime_kind = runtime_kind & ~LOAD_RUNTIME; break;
              case 'c' : runtime_kind = runtime_kind | CHECK_RUNTIME; break;
              case 's' : runtime_kind = runtime_kind | STATS_RUNTIME; break;
              case 't' : runtime_kind = runtime_kind | TOUCH_RUNTIME; break;
              default  : main_err( argv[0] );
            }
        }
        else main_err( argv[0] );
    }
    else break;
  }

  output = fopen( c_file, "w" );
  if (output == NULL)
  { fprintf( stderr, "Error: can't open output file\n" ); exit(1); }

  fprintf( output, "long link_stack_length_in_k = %d;\n", stack_length_in_k );
  fprintf( output, "long link_heap_length_in_k = %d;\n\n", heap_length_in_k );
  fprintf( output, "long link_const_length_in_k = %d;\n", const_length_in_k );

  link_runtime( output, runtime_kind, 1 );
  for (i=first_file; i<argc; i++)
  { arg = argv[i];
    if (*arg == '-')
      break;
    else
      init_file( output, arg, 1 );
  }

  fprintf( output, "\nshort *link_ofiles[] =\n{\n" );

  link_runtime( output, runtime_kind, 2 );
  for (i=first_file; i<argc; i++)
  { arg = argv[i];
    if (*arg == '-')
      break;
    else
      init_file( output, arg, 2 );
  }

  fprintf( output, "};\n\nlong *link_sizeof_ofiles[] =\n{\n" );

  argcount = 0;
/*@{
  argvect[argcount++] = "cc";
  argvect[argcount++] = c_file;
  argvect[argcount++] = string_append( lib_path, "/libgloader.a" );
  argvect[argcount++] = string_append( string_append( lib_path, "/libglib_" ),
                                       string_append( runtime_library( runtime_kind ), ".a" ) );
}@*/
  link_runtime( output, runtime_kind, 3 );
  for (i=first_file; i<argc; i++)
  { arg = argv[i];
    if (*arg == '-')
      break;
    else
    { init_file( output, arg, 3 );
      link_file( arg );
      program_name = arg;
    }
  }

  fprintf( output, "};\n\nlong link_nb_ofiles = (sizeof link_ofiles) / sizeof(short *);\n" );

  fclose( output );


  argvect[argcount++] = NULL;

  exit(0);
}


/*---------------------------------------------------------------------------*/
