/***********************************
  (C) Copyright 1992-1993; dit/upm
  Distributed under the conditions stated in the
  TOPO General Public License (see file LICENSE)
  ***********************************/

/***********************************

  Santiago Pavon Gomez

  11-1-1990

  This module defines a set of functions of general interest

  emilla modification (9-6-92): erealloc function included.

  COMPILATION OPTIONS: The behaviour of this module can be modified
  by the following compilation flags:

  MEMTRACE

  ************************************/

/* LINTLIBRARY */


#include "listdh.h"
#include "limisc.h"
#include <stdlib.h>
#include  <sys/time.h>
#if defined(sun) || defined(MSDOS)
#include  <sys/resource.h>
#include  <sys/types.h>
#endif

/* KJT 22/01/23: added header */
#include <unistd.h>

/* KJT 22/01/23: added function prototypes */
void gl_cleanup();

static void Init2W();

/* InitM()
 * Inits this module.
 * This function must be invoked when the application starts.
 */
void InitM()
{
  InitCellM();
  Init2W();
}

/******************************************************************
 *
 *  Functions to manage large blocks of memory.
 *
 *******************************************************************/

/* emalloc
 * Gets memory from the system for n integers.
 * If there is an error then it halts the program.
 */
void * emalloc (n)
     int   n;
{
  /*  char*   p; */
  void * p;
#ifdef MEMTRACE
  if ( n>50 )
    (void)printf("\nmalloc %d\n",n);
#endif
  p = (void *)ALIGN( malloc((unsigned)n) );
  if (p == NULL){
    Error("Fatal error: run out of memory.");
  }
  return p;
}

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

/* erealloc
 * Reallocs memory pointed by "ptr" from the system for "size" integers.
 * If there is an error then it halts the program.
 */
void* erealloc ( ptr, size )
     void  *ptr;
     int   size;
{
  void*   p;

#ifdef MEMTRACE
  (void)printf("\nr %d\n",size);
#endif
  p = (void*)ALIGN( realloc ( (char*)ptr, (unsigned)size) );
  if ( p == NULL ) {
    Error("Fatal error: run out of memory.");
  }
  return p;
}

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

/* ecalloc
 * Get memory from the system for an array of n elements of size
 * "size" bytes.
 * If there is an error then it halts the program.
 */
void * ecalloc ( n, size )
     unsigned n,size;
{
  void * p;

#ifdef MEMTRACE
  (void)printf("\ncalloc %d\n",size);
#endif
  p = (void*) ALIGN( calloc (n,size) );
  if (p == NULL) {
    Error("Fatal error: run out of memory.");
  }
  return p;
}


/******************************************************************
 *                                                                *
 *     2 words nodes management                                   *
 *                                                                *
 *******************************************************************/

typedef struct TwoWords { void *w1, *w2;
			} *PTwoWordsTyp;

static int new_2w_count      = 0;
static int released_2w_count = 0;


/* Init2W
 * Init 2W
 */
static void Init2W()
{
#ifdef SDEBUG
  new_2w_count      = 0;
  released_2w_count = 0;
#endif
}

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

/* Stat2W
 * Number of asked  and released 2w nodes
 */
void Stat2W( new, released )
     int * new, *released;
{
  *new      = new_2w_count;
  *released = released_2w_count;
}

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

/* Emalloc2Words
 * Gets two words of memory from the system.
 * These memory is deallocated with Free2Words. In fact, this memory
 * is maintained in a list of unused 2-word-size block of memory.
 */
void * Emalloc2Words()
{
#ifdef SDEBUG
  new_2w_count++;
#endif
  return (void *)NewCellM( sizeof(struct TwoWords) );
}

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

/* Free2Words
 * Deallocated a block obtained with Emalloc2Words.
 */
void Free2Words( c )
     void * c;
{
#ifdef SDEBUG
  released_2w_count++;
#endif
  FreeCellM( c, sizeof(struct TwoWords) );
}


/******************************************************************
 *
 *  Functions used as parameters in several places.
 *
 *******************************************************************/


/* EqInt
 * Returns TRUE if x and y are equal, else FALSE.
 */
boolean EqInt( x,y )
     int x,y;
{
  return x==y;
}

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

/* VoidF
 * Void function. Returns NULL.
 */
int VoidF()
{
  return (int)NULL;
}

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

/* EchoInt
 * Echoes its argument.
 */
/* KJT 25/01/23: changed parameter and return type from "int" to "intptr_t" */
intptr_t EchoInt( i )
     intptr_t i;
{
  return i;
}

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

/* PrintInt
 * prints the integer "i".
 * "pstr" is the function used to print strings.
 */
void PrintInt( pstr, i )
     void (*pstr)();
     int    i;
{
  char str[15];

  (void)sprintf(str,"%d",i);
  pstr(str);
}


/******************************************************************
 *
 *  Messages
 *
 *******************************************************************/


/* Warning
 * Prints a warning message using "printMsgs" (listdout)
 */
void Warning(  m  )
     char * m ;
{
  LASSERT(printMsgs!=NULL)
    printMsgs("\n      WARNING : ");
  printMsgs(m);
  printMsgs("\n");
}

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


/* ToMake
 * Prints the message "The function <s> is not implemented yet" and halts.
 */
void ToMake( m )
     char * m ;
{
  (void)fprintf(stderr,"\n\nThe function %s is not implemented yet.\n\n",m);
  EXITLOLA(ERRORQUIT);
}

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

/* Halt
 * Halts the program.
 */
void Halt()
{
  EXITLOLA(ERRORQUIT);
}


/* Assert
 * Prints a message with "printError"(listdout) and halts the application
 * if the condition "ex" is false.
 * In "file" and "line" shoulb be passed the name of the file that
 * calls to Assert and the line number. Note that can be used the
 * cpp macros __FILE__ and __LINE__.
 */
void Assert( ex, file, line )
     boolean ex;
     char    *file;
     int     line;
{
  if (!(ex)) {
    (void)fprintf(stderr,"\n  Assertion failed in file:%s line:%d\n\n",
		  file,line);
    Halt();
  }
}


/******************************************************************
 *                                                                                                *
 *      Strings                                                                           *
 *                                                                                                *
 *******************************************************************/


/* CopyString
 * Allocates memory and copies a string.
 */
char* CopyString( s )
     char *s;
{
  return(strcpy((char *)emalloc(strlen(s)+1),s));
}

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

/* StringToInt
 * Returns the integer represented by the string s.
 */
int StringToInt( s )
     char *s;
{
  int i=0;

  (void)sscanf(s,"%d\0",&i);
  return i;
}

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

/* IntToString
 * Returns the integer i as a string.
 */
char *IntToString( i )
     int i;
{
  char l[32];

  (void)sprintf(l,"%d",i);
  return CopyString(l);
}

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

/* LowerString
 * Transforms the uppercase letters of the string "s" to lowercase
 * letters.
 */
void LowerString(s)
     char *s;
{
  int aux;

  for (aux=0 ; s[aux]!='\0' ; ++aux ) {
    if (isupper(s[aux]))
      s[aux] = tolower(s[aux]);
  }
}

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

/* PrintString
 * Print a string into the standard output
 */
void PrintString( str )
     char * str;
{
  /* cambiar por un bucle de puts !!!*/

  (void)printf( "%s", str );
  (void)fflush(stdout);
}

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

/* PrintError
 * Print a string into the standard error output
 */
void PrintError( str )
     char * str;
{
  /* cambiar por un bucle de puts !!!*/

  (void)fprintf(stderr, "%s", str );
  (void)fflush(stderr);
}

/******************************************************************
 *
 *  Miscellaneous
 *
 *******************************************************************/


/* Power
 * Returns the power x**n.
 * n must be greater or equal than 0
 */
int Power( x, n )
     int x, n;
{
  int i, p;

  p = 1;
  for ( i=1 ; i<=n ; ++i )
    p = p * x;
  return p;
}

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

#define BufferSize 500


/* System
 * "system" function call.
 * If "runtimeConfig" is "TextMode" then it executes a system call, else
 * a process connected with a fifo is created to execute "cmd"
 */
int System( cmd )
     char * cmd;
{
#ifndef MSDOS
  char buf[BufferSize];
  int sz;
  FILE* fp;

  FILE* popen();

  if (runtimeConfig!=TextMode) {
    fp = popen (cmd,"r");

    while ( (sz=read(fileno(fp),buf,BufferSize-1)) != 0) {
      if ( sz > 0 ) {
	buf[ sz ] = '\0';
	printTarget( buf );
      }
    }
    return pclose(fp);
  }
  else
#endif
    return system(cmd);
}

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



/******************************************************************
 *                                                                *
 *  Functions to manage small blocks of memory ( cells ) .        *
 *                                                                *
 *  Interface to the system for cell allocation requests.         *
 *  On behalf of efficiency, whenever there is a cell             *
 *  allocation request, the system is asked for a block of        *
 *   BLOCKCELLS cells.                                            *
 *  The cells can not be freed by the system call "free".         *
 *  All of them can only be released at the same time             *
 *  in order to reset the application, with  ClearM().            *
 *  The user module must take an account of granted and released  *
 *  cells ( useful to determine the origin of a cell loss ).      *
 *  Disposed cells management is provided .                       *
 *                                                                *
 *******************************************************************/


/* Number of cells actually allocated by the system.
 */

#define BLOCKCELLS 256


typedef struct block { struct block * next;
		       void         * block;
		     } BlockNodeTyp, * BlockTyp;

/*
 * the blocks of cells of size i are placed
 * at blocksTable[i].blocks
 */
static struct { BlockTyp  blocks;               /* mem blocks */
		char *    released_cells;
		char *    next_cell;            /* next block to use */
	      } blocksTable[ MAXBLOCKSIZE + 1 ];


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

/* KJT 22/01/23: added "int" type */
static int never_initialized = TRUE;
static void FreeBlockM();

/* InitCellM()
 * Initializes the cell manager
 */
void InitCellM()
{
  int      i;

  for ( i=MINBLOCKSIZE; i<=MAXBLOCKSIZE; i += WORDBYTES ){
    if ( !never_initialized )
      FreeBlockM( blocksTable[i].blocks );
    blocksTable[i].released_cells = NULL;
    blocksTable[i].blocks         = NULL;
    blocksTable[i].next_cell      = NULL;
  }
  never_initialized = FALSE;
}

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

/* StatCellM
 * return the maximum number of cells of size "size" used and
 * how many of them are disposed.
 */
void StatCellM( size, total, disposed )
     int size, * total, * disposed;
{
  BlockTyp n;
  int b;
  char ** cell;

  LASSERT(size%WORDBYTES==0);
  LASSERT(size>=MINBLOCKSIZE && size<=MAXBLOCKSIZE);

  *disposed = 0;
  *total    = 0;
  n = blocksTable[size].blocks;
  if ( n==NULL )
    return;

  for ( b=0; n!=NULL; n=n->next )
    b++;
  *total = b*BLOCKCELLS - 1 -
    ((int)blocksTable[size].next_cell -
     (int)blocksTable[size].blocks->block) / size;
  cell = (char**)ALIGN(blocksTable[size].released_cells);
  for ( ; cell!=NULL; cell = (char**)ALIGN(*cell) )
    (*disposed)++;
}

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

static void FreeBlockM( block )
     BlockTyp block;
{
  if ( block!=NULL ){
    FreeBlockM( block->next );
    free((char*)(block->block));
    free((char*)block);
  }
}


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

/* NewCellM
 * return a new cell
 */
void * NewCellM( size )
     int size;
{
  char  *  res;
  char  ** pres, ** totable;
  BlockTyp aux2;

  LASSERT(size%WORDBYTES==0);
  LASSERT(size>=MINBLOCKSIZE && size<=MAXBLOCKSIZE);

  totable = &blocksTable[size].released_cells;
  if (  *totable  != NULL ) {
    pres = (char**)ALIGN(*totable);
    *totable = *pres;
    return (void*)ALIGN(pres);
  }
  else {
    totable = &blocksTable[size].next_cell;
    res = *totable;
    if (res==NULL) {
      aux2                      = (BlockTyp)emalloc( sizeof(BlockNodeTyp) );
      aux2->block               = emalloc( BLOCKCELLS*size );
      aux2->next                = blocksTable[size].blocks;
      blocksTable[size].blocks = aux2;
      res = (char*)aux2->block+(BLOCKCELLS-1)*size;
      *totable = res-size;
    }
    else if (res != (char*)blocksTable[size].blocks->block){
      *totable -= size;
    }
    else {
      *totable = NULL;
    }
    return (void*)ALIGN(res);
  }
}

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

#ifdef DEBUGMEM
/* AlreadyDispCellM
 * If the entry "c" is in the list of deleted entries
 * then it returns TRUE, else FALSE.
 */
static boolean AlreadyDispCellM( c, l )
     void *c, *l;

{
  void **n2;

  for ( n2 = (void**)l; n2!=NULL; n2=*n2 )
    if ( c==n2 )
      return TRUE;
  return FALSE;
}
#endif

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

/* FreeCellM
 * free a cell c of size "size"
 */
void FreeCellM( c, size )
     void * c;
     int    size;
{
  void ** aux;

  LASSERT(size%WORDBYTES==0);
  LASSERT(size>=MINBLOCKSIZE && size<=MAXBLOCKSIZE);

#ifdef DEBUGMEM
  LASSERT(!AlreadyDispCellM(c,blocksTable[size].released_cells));
#endif

  aux   = (void**)c;
  *aux  = (void*)ALIGN(blocksTable[size].released_cells);
  blocksTable[size].released_cells = (char*)c;
}


/******************************************************************
 *                                                                *
 *         Statistics on Memory and Time Consumption              *
 *                                                                *
 ******************************************************************/


/* UsageMemoTime
 * Return the memory used by the program, the user and system time
 * spent by the program until now.
 */
void UsageMemoTime( user_time, sys_time, memory )
     float *user_time, *sys_time;
     long int   *memory;
{
#if defined(sun) || defined(MSDOS)
#ifdef _sys_resource_h
  /* This part is being ported to Solaris */
  struct rusage uso;
    if ( getrusage (RUSAGE_SELF, & uso)){
    printTarget("     Measure failed.\n");
    return ;
  }
  /* *memory    = (getpagesize() * uso.ru_maxrss)/1024;  SunOS only */
  *user_time = (float)uso.ru_utime.tv_sec+(float)uso.ru_utime.tv_usec/1000000;
  *sys_time  = (float)uso.ru_stime.tv_sec+(float)uso.ru_stime.tv_usec/1000000;
#else
  *user_time = 0;
  *sys_time  = 0;
#endif
#endif
  *memory    = (int) sbrk(0)/1024;
}

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

/* PrintUsageMemoTime
 * Print the memory used by the program, the user and system time
 * spent by the program until now.
 */
void PrintUsageMemoTime()
{
  float    user_time, system_time;
  long int memory;
  char     buff[256];

  UsageMemoTime( &user_time, &system_time, &memory );
  if ( memory>0 ){
    (void)sprintf(buff," %ld Kbytes. ", memory );
    printTarget(buff);
  }
  (void)sprintf(buff,"%1.2f sec.\n\n",user_time+system_time);
  printTarget(buff);
}

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



