/* ARIADNE V.1.3

Copyright 

Richard Mott 2000

Wellcome Trust Centre For Human Genetics
Univeristy of Oxford
Roosevelt Drive
Oxford OX3 7AD
UK

The software package ARIADNE is distributed in the hope that it will be
useful, but in order that the University as a charitable foundation
protects its assets for the benefit of its educational and research
purposes, the University makes clear that no condition is made or to
be implied, nor is any warranty given or to be implied, as to the
accuracy of ARIADNE, or that it will be suitable for
any particular purpose or for use under any specific conditions, or that
the content or use of ARIADNE will not constitute
or result in infringement of third-party rights.  Furthermore, the
University disclaims all responsibility for the use which is made of 
ARIADNE.


*/

#include<sys/time.h>
#include<sys/types.h> 
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#include"cl.h"
#ifdef ALPHA
extern void exit(int);
#endif

extern int skip_comments( FILE *file, char *string );

/*command line option to suppress all comments*/
int comment = 1;

FILE *
OpenFile( char *filename, char *mode )
     
     /* attempts to open filename, using mode to determine function
	(read/write/append), and stops the program on failure */
{
  FILE *fp;
  char *MakeLegal();

  if ( ( fp = fopen( MakeLegal(filename), mode ) ) == NULL )
    {
      fprintf( stderr, "\n ERROR: file %s will not open for function %s\n\n", filename, mode );
      exit(1);
    }
  else
    return fp;
}

int 
GetFloat( char *format, float *variable, int argc, char **argv ) /* gets a float from command line */
     
/* format can be a string like "-a=%f", or "-a". In the former case is
looks for entries line -a=10.3 AND -a 10.3, in the latter just for -a
10.3 */

{ float t; char *s;
  char Format[256];

  sprintf(Format,"%g",*variable);
  AppendUsage( format, "float", Format, 0 );

  if ( (s = NextArg( format, argc, argv ) ) && sscanf( s, "%f", &t ) == 1 )
    {
      *variable = t;
      return 1;
    }


  s = format;
  while(*s && *s != '=')
    s++;
  if ( *s == '=' )
    sprintf(Format,"%s", format);
  else
    sprintf(Format,"%s%s", format, "=%f");

  while ( --argc > 0 )
    {
      if ( sscanf( argv[argc], Format, &t ) == 1 ) 
	{
	  *variable = t;
	  return 1;
	}
    }

  return 0;
}

char *
NextArg( char *format, int argc, char **argv )

/* scan the command lines for argument matching stub, and return the
NEXT arg 

eg if format == "-max=%d", then argv == "-max 10" returns "10" */

{
  char *s = ClStub( format );

  argc--;
  while( --argc > 0 )
    {
      if ( ! strcmp( s, argv[argc] ) )
	return argv[argc+1];
    }
  return NULL;
}

char *
ClStub( char *format ) /* extract stub from format, eg "-files=%s" -> "-files" */
{
  static char stub[256];
  char *s=stub;

  while ( *format && *format != '=' )
    *s++ = *format++;
  *s = 0;

  return stub;
}


int  
GetInt( char *format, int *variable, int argc, char **argv ) /* gets an int from the command line */
     
{
  int t;
  char *s;
  char Format[256];

  sprintf(Format,"%d",*variable);
  AppendUsage( format, "integer", Format, 0 );

  if ( (s = NextArg( format, argc, argv ) ) && sscanf( s, "%d", &t ) == 1 )
    {
      *variable = t;
      return 1;
    }

  s = format;
  while(*s && *s != '=')
    s++;
  if ( *s == '=' )
    sprintf(Format,"%s", format);
  else
    sprintf(Format,"%s%s", format, "=%d");

  while ( --argc > 0 )
    {
      if ( sscanf( argv[argc], Format, &t ) == 1 ) 
	{
	  *variable = t;
	  return 1;
	}
    }

  return 0;
} 


int 
ClCheck( char *format, int argc, char **argv ) /* checks if a string is on the command line*/
{
  AppendUsage( format, "switch", "", 0 );

  while ( --argc > 0 ) 
    {
      if ( strcmp( format, argv[argc] ) == 0 )
	return 1;
    }
	
  return 0;
}

int 
GetBool( char *format, int *bool, int argc, char **argv ) 
     
     /* checks for boolean switch, eg
	
	-gap=yes  -gap=y -gap=1 -gap=true -gap=t -gap	TRUE
	-gap=no -gap=n -gap=0 -gap=false -gap=f		FALSE
	
	format should be the string "-gap" 
	
	*/
     
{
  /* int arg;*/

  if ( *bool )
    AppendUsage( format, "switch", "true", 0 );
  else
    AppendUsage( format, "switch", "false", 0 );

  if ( GetInt( format, bool, argc, argv) )
    return 1;
  else
    {
      char reply[256];
      memset((void *)reply,0,256);
      if ( GetArg( format, reply, argc, argv ) )
	{
	  if ( ! strcasecmp( reply, "yes" ) ||
	      ! strcasecmp( reply, "y" ) ||
	      ! strcasecmp( reply, "true" ) ||
	      ! strcasecmp( reply, "t" )  )
	    {
	      *bool = 1;
	      return 1;
	    }
	  else if ( ! strcasecmp( reply, "no" ) ||
		   !  strcasecmp( reply, "n" ) ||
		   !  strcasecmp( reply, "false" ) ||
		   !  strcasecmp( reply, "f" )  )
	    {
	      *bool = 0;
	      return 1;
	    }
	}
    }

  if ( ClCheck( format, argc, argv ) )
    return 1;
  else
    return 0;
}

int 
GetBoolean( char *format, int *bool, int argc, char **argv )

/* similar to GetBool(), except that function returns 1 and sets *bool = 1 if ClCheck is true also . Therefore the switch

-switch

is equivalent to

-switch=y

-noswitch is equivalent to 

-switch=n
*/

{
  char noformat[256];
  int Argc = argc;
  /*int n;*/

  if ( *bool )
    AppendUsage( format, "switch", "true", 1 );
  else
    AppendUsage( format, "switch", "false", 1 );

  if ( *format == '-' )
    sprintf(noformat,"-no%s", &format[1] );
  else
    sprintf(noformat,"-no%s", format );
  
  while ( --Argc > 0 ) 
    {
      if ( ! strcmp( format, argv[Argc] ) )
	{
	  *bool = 1;
	  return 1;
	}
      else if ( ! strcmp( noformat,argv[Argc]) )
	{
	  *bool = 0;
	  return 1;
	}
    }

/* NB there is a slight bug here in that  a command line of the form

   program -notest -test=y

   will yield FALSE

   because it searches for -notest before -test=y 

   THerefore you should stick to one convention within a command-line

*/

  return GetBool( format, bool, argc, argv );
}


int 
GetArg( char *format, char *arg, int argc, char ** argv ) /* gets a string argument*/
{
  char *s;
  char Format[256];

  AppendUsage( format, "text", arg, 0 );

  s = format;
  while(*s && *s != '=')
    s++;
  if ( *s == '=' )
    sprintf(Format,"%s", format);
  else
    sprintf(Format,"%s%s", format, "=%s");

  if ( (s = NextArg( format, argc, argv )) )
    {
      strcpy( arg, s );
      return 1;
    }
  while ( --argc > 0 ) 
    {
      if ( sscanf( argv[argc], Format, arg ) > 0 ) return 1;
    }
  return 0;
}


FILE *
NextFile( char *filename, int argc, char **argv )
     
     /* searches the command line for arguments not beginning with "-", and tries
	to open the first one it finds for reading. The corresponding argument in the command-line list is then blanked
	out so that on the next call a different file is opened. The name of the
	file opened is returned in filename. On failure the function returns NULL */
{
  FILE *fp;
  int i;

  *filename = 0;

  while ( --argc > 0 )
    {
      if ( argv[argc][0] != 0 && argv[argc][0] != '-' )
	{
	  if ( (fp = fopen( filename, "r" )) )
	    {
	      strcpy( filename, argv[argc] );
	      for(i=0;i<=strlen(argv[argc]);i++)
		argv[argc][i] = 0;
	      return fp;
	    }
	}
    }

  return NULL;
}

char *
Extension ( char *filename, char *ext)
     
     /* changes the Extension of filename to ext.
	
	e.g.
	Extension( "file.dat", "out" );
	
	produces file.out
	
	Extension( "file", "out");
	
	produces file.out
	
	Extension( "file", ".out");
	
	produces file.out
	
	Extension( "file.out", "");
	
	produces file
	
	Note: filename MUST be long enough to cope ! 

        Returns the modified string */
{
  int n;

  if ( ! ext )
    return filename;

  if ( ext[0] == '.' )
    ext++;

  n = strlen(filename);

  while ( filename[n] != '.' && n > 0 )
    n--;

  if ( filename[n] != '.' )
    {
      n = strlen(filename);
      filename[n] = '.';
    }

  n++;

  strcpy( &filename[n], ext );

  if ( filename[n=strlen(filename)-1] == '.' )
    filename[n] = 0;

  return filename;
}

char *
BaseName (char *filename)
     
     /* strips any Directory from filename
	
	e.g.
	BaseName("/gea/users/rmott/.cshrc");
	
	produces .cshrc
	
	BaseName("/usr");
	
	produces usr
	
	*/
     
{
  int n, m;

  n = strlen(filename);

  while( filename[n] != '/' && n > 0 )
    n--;

  if ( filename[n] == '/' )
    n++;

  m = 0;
  while ( filename[m] )
    filename[m++] = filename[n++];

  return filename;
}

char *
DirName( char *pathname )

/* strips off the filename from the path, leaving the Directory */
{
  char *s = &pathname[strlen(pathname)-1];

  while( *s && s > pathname && *s != '/' )
    s--;

  if ( s == pathname && *s != '/' )
    strcpy(pathname, "./");
  else if ( s == pathname && *s == '/' )
    strcpy(pathname,"/");
  else
    *s = 0;

  return pathname;
}

char *
Directory( char *filename, char *dir )
     
     /* changes the Directory part of filename to dir.
	
	e.g.
	Directory( "/home/gea/fred/file.dat", "/usr/local/" );
	
	produces /usr/local/file.dat
	
	Directory("/usr/lib", "var");
	
	produces var/lib, i.e. a Directory slash is inserted if necessary.
	
	*/
     
{
  char name[512];
  
  BaseName( filename );
  
  if ( dir && *dir  )
    {
      if ( dir[strlen(dir)-1] != '/' )
	sprintf( name, "%s/%s", dir, filename );
      else
	sprintf( name, "%s%s", dir, filename );

      strcpy( filename, name );
    }
  return filename;
}	

char *
RootName( char *filename )
     
     /* trims off the Directory and the Extension from filename */
     
{
  return Extension(BaseName(filename),"");
}


/* use compiler switch -DVAX to use the form of MakeLegal which converts . to _ */

#ifdef VAX

char *
MakeLegal( char *filename )
     
     /* converts files like file.in.out to file_in.out */
     
{
  char *s = filename, *t;
  int n=0;

  while( *s )
    {
      if ( *s == '.' )
	{
	  n++;
	  t = s;
	}
      s++;
    }

  if ( n > 1 )
    {
      t--;
      while ( t != filename )
	{
	  if ( *t == '.' )
	    *t = '_';
	  t--;
	}
    }
  return filename;
}


#else

char *
MakeLegal( char *filename )
     
{
  return filename;
}

#endif

FILE *
ArgFile( char *format, char *mode, int argc, char **argv, char *filename )
     
     /* parses the command-line using format, attempts to get a filename and open
	it. If format does not match any argument then NULL is returned. If format
	matchs an argument but the file will not open then an error message is printed to stderr */
     
{
  FILE *fp=NULL;

  *filename = 0;
  if ( GetArg( format, filename, argc, argv ) )
    {
      if ( ( fp = fopen( filename, mode ) ) == NULL )
	fprintf( stderr, "\n ERROR: file %s will not open for function %s\n\n", filename, mode );
    }
  if ( !strcmp( mode, "w" ) )
    AppendUsage( format, "Writeable File", "", 1 );
  else if ( !strcmp( mode, "r" ) )
    AppendUsage( format, "Readable File", "", 1 );
  else
    AppendUsage( format, "File", "", 1 );

  return fp;
}


FILE *
ArgFileForce( char *format, char *mode, int argc, char **argv, char *filename )
     
     /* parses the command-line using format, attempts to get a filename and open
	it. If format does not match any argument or if format matchs an argument
	but the file will not open then the program stops*/
     
{
  FILE *fp;

  if ( GetArg( format, filename, argc, argv ) )
    {
      if ( ( fp = fopen( filename, mode ) ) == NULL )
	{
	  fprintf( stderr, "\n ERROR: file %s will not open for function %s\n\n", filename, mode );
	  exit(1);
	}
      else
	return fp;
    }
  else
    {
      fprintf( stderr, "\n ERROR: argument %s not on command line\n\n", format );
      exit(1);
    }
}


#include<sys/types.h>
#include<sys/stat.h>
#include<sys/times.h>


int 
FileTime( char *filename )
     
     /* returns the time that filename was last modified, or 0 if filename
	cannot be opened for reading or if the call to stat fails */
     
{

  FILE *fp;
  struct stat buf;

  if ( ! (fp = fopen(filename,"r")) )
    return 0;
  else
    {
      fclose(fp);
      if ( ! stat( filename, &buf ) )
	return (int)buf.st_mtime;
      else
	return 0;
    }
}

char *
FileDate( char *filename )
     
     /* similar to FileTime() execpt that a pointer to the date (in English) */

{
  FILE *fp;
  struct stat buf;
  char date[256], *t;

  strcpy( date, "?");

  if ( (fp = fopen(filename,"r")) )
    {
      fclose(fp);
      if ( ! stat( filename, &buf ) )
	{
	  sprintf( date, "%s", ctime(&buf.st_mtime) );
	  t = date;
	  while ( *t )
	    {
	      if ( *t == '\n')
		*t = 0;
	      t++;
	    }

	}
    }
  t = date;
  return t;
}

typedef struct use_list
{
  char *format;
  char *text;
  char *def;
  struct use_list *next;
}
USE_LIST;

static USE_LIST *use_begin=NULL, *use_end=NULL;
static char *help_text=NULL;

void 
AppendUsage( char *format, char *text, char *def, int overide )
     
{
  USE_LIST *use;

  if ( ! use_begin )
    use_begin = use_end = (USE_LIST *)calloc( 1, sizeof(USE_LIST) ); 
  else 
    {
      use = use_begin;
      while( use )
	{
	  if  ( ! strcmp( use->format, format ) )
	    {
	      if ( overide )
		break;
	      else
		return;
	    }
	  use = use->next;
	}

      if ( ! use )
	use_end = use_end->next = (USE_LIST *)calloc( 1, sizeof(USE_LIST) );
      else
	use_end = use;
    }

/*  printf("append %s %s %s\n", format, text, def ); */
  use_end->format = (char*)strdup( format );  
  use_end->text = (char*)strdup( text );
  use_end->def = (char*)strdup( def );
}

int 
CheckUsage( FILE *fp, int argc, char **argv )
{
  USE_LIST *u;
  int errors=0;
  char *arg, *noarg;

  while( --argc > 0 )
    {
      arg = argv[argc];
      if ( *arg == '-' )
	{
	  if ( strlen(arg) > 3  && (arg[1] == 'n') && (arg[2] == 'o') )
	    noarg = &arg[3];
	  else 
	    noarg = NULL;

	  u = use_begin;
	  while( u && strncmp( argv[argc], u->format, strlen(argv[argc]) ) && ( noarg == NULL || strncmp( noarg, &u->format[1], strlen(noarg) ) ) )
	    u = u->next;

	  if ( ! u && ! isdigit((int)arg[1]) )
	    {
	      if ( fp )
		fprintf(fp, "WARNING: unknown argument %s\n", argv[argc] );
	      errors++;
	    }
	}
    }
  
  return errors;
}

void AddHelp( char *string ) {
  if ( string ) 
    help_text = strdup(string);
}

void
GetHelp( int argc, char **argv )
{

  int want_help = ClCheck("-help", argc, argv );
  int errors = CheckUsage( stderr, argc, argv );
  if ( want_help || errors )
    PrintUsage( argc, argv, 1 );
}

void 
PrintUsage( int argc, char **argv, int stop )
     
{
  USE_LIST *u;
  char name[256];
  strcpy(name, argv[0]);
  BaseName(name);
  u = use_begin;

  if ( help_text ) 
    fprintf(stderr, "\n%s\n", help_text );
  fprintf( stderr, "\nusage: %s\n", name );
  while( u )
    {
      fprintf( stderr, "	%-15s %-20s [ %s ]\n", u->format, u->text, u->def );
      u = u->next;
    }
  fprintf(stderr, "\n\n");
  if ( stop ) 
    exit(1);
}

#define COM "-comfile="
#define MAX_COM_DEPTH 10
static int com_depth=0;

int 
AddCommandsFromFile( int argc, char **argv, int *nargc, char ***nargv )
     
     /* looks for a command file argument and parses the command file, adding at to the command line */
     
{
  FILE *fp;
  char comfile[256], line[256], *s, **targv = argv;
  int n = argc, targc=argc;

  *nargc = targc = argc;
  *nargv = targv = argv;

  if ( ++com_depth < MAX_COM_DEPTH  && (fp = ArgFile( "-comfile=%s", "r", argc, argv, comfile ) ))
    {
      /*      printf( "! %2d reading commands from file %s\n",  com_depth, comfile );*/

      while( skip_comments( fp, line ) != EOF  )
	targc++;

      rewind(fp);

      targv = (char**)calloc( targc, sizeof(char*) );

      while( n-- )
	{
	  if ( argv[n] && strncmp( COM, argv[n] , strlen(COM) ) )
	    targv[n] = argv[n];
	  else targv[n] = (char*)strdup(" ");
	}
      targc = argc;
      while( skip_comments( fp, s=line ) != EOF )
	{
	  while( isspace((int) *s) )
	    s++; /* was *s++ rtm 7.xii.99 */
	  targv[targc++] = (char*)strdup(s);
	}
      AddCommandsFromFile( targc, targv, nargc, nargv );
      com_depth--;
      return 1;
    }
  else
    {
      com_depth--;
      return 0;
    }
}

int 
LegalString( char *string, char **strings, int size, int *value )

/* checks if string is a member os strings, and sets value to the index in the array strings
returns 1 on success and 0 on failure */
{
  int i;

  if ( string )
    for(i=0;i<size;i++)
      if ( ! strcmp( string, strings[i] ) )
	{
	  *value = i;
	  return 1;
	}

  return 0;
}

	

char **
SplitOnSeparator( char *string, char separator, int *items)

/* split a string into substrings with separator 
Returns array of null-terminated strings, or null if none found
items returns the number of strings;
*/

{
  char **item=NULL;
  char *s, *t;
  int len;

  *items = 0;

  if ( string )
    {
      *items = 1;
      s = string;
      while(*s)
	{
	  (*items) += ( *s == separator );
	  s++;
	}

      item = (char**)calloc(*items,sizeof(char*));
      s = string;
      *items = 0;
      while( *s )
	{
	  t = s;
	  while( *s != separator && *s )
	    s++;
	  
	  if ( s != t )
	    {
	      len = s-t;
	      item[*items] = (char*)calloc(len+1,sizeof(char));
	      strncpy(item[*items],t,len);
	      (*items) ++;
	    }
	  if ( *s == separator )
	    s++;
	}
    }
  return item;
}

char *Now(void) /* returns a character string containing the current date and time */
{

	time_t time(), *tloc;
	char *ctime(), *t;
	/*int i;*/
	static char temp[256];

	tloc = (time_t*)calloc(1,sizeof(time_t));
	time(tloc);
	sprintf( temp, "%s", ctime(tloc) );
	free(tloc);

	t = temp;
	while ( *t )
	{
		if ( *t == '\n')
			*t = 0;
		t++;
	}

	return temp;
}

int EndsWith( char *text, char *suffix ) {

  int n = strlen(text)-1;
  int m = strlen(suffix)-1;

  if ( m > n )
    return 0;
  else {
    /*char *t, *s;*/
    while( m >= 0 ) {
      if ( text[n] != suffix[m] )
	return 0;
      n--; m--;
    }
  }
  return 1;
}


int GetSeed( int *seed, int argc, char **argv )

{

  time_t time(), *tloc;
  int s;

  tloc = NULL;
 
  s = *seed = ((int)time(tloc))% 100000; 
  GetInt( "-seed", &s, argc, argv );
  *seed = s;
  
  /*    fprintf(stderr,"! seed = %d\n", *seed);  */
  
  return *seed;
}

int GetRestrictedArg( char *format, char *arg, int argc, char ** argv, int stringc, char **strings, int *value ) {
  *value = -1;
  GetArg( format, arg, argc, argv );
  return LegalString( arg, strings, stringc, value );
}
