/* Wide AREA INFORMATION SERVER SOFTWARE	
   No guarantees or restrictions.  See the readme file for the full standard
   disclaimer.  
  
   3.26.90	Harry Morris, morris@think.com
   4.11.90  HWM - generalized conditional includes (see c-dialect.h)
   7/19/91  speed up substrcmp by ses@ccgr.technion.ac.il (Simon E Spero)
*/

#define _C_C_util_

#include "cutil.h"
#include "panic.h"

#include <string.h>

#ifdef ANSI_LIKE
#include <stdarg.h>
#else /* ndef ANSI_LIKE */
#include <varargs.h>
#endif /* ndef ANSI_LIKE */

#ifdef M_XENIX /* perhaps this should be in ustubs.h */
#include <malloc.h>
char* calloc();
#endif /* def M_XENIX */

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

/*#define MEMORY_ACCOUNTING*/

#ifdef MEMORY_ACCOUNTING
#undef s_checkPtr
#define s_checkPtr(ptr) fs_checkPtr(ptr)
static FILE* memRecord = NULL;
static void prepMemAcct _AP((void));
static void 
prepMemAcct()
{
  if (memRecord == NULL)
    memRecord = s_fopen("MemoryAccounting","w");
}
static void flushMemAcct _AP((void));
static void 
flushMemAcct()
{
  fflush(memRecord);
}
#define tickles 0
static char* 	badPtr	= (char*)-1;
static size_t	badSize	= -1;
#endif /* def MEMORY_ACCOUNTING */

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

void
fs_checkPtr(ptr)
void* ptr;
/* If the ptr is NULL, give an error */
{ 
#ifdef MEMORY_ACCOUNTING
static char** ptrs = NULL;
long i;
static Boolean  doneSetup = false;
if (doneSetup == false)
{
#ifdef THINK_C
Debugger();
#endif /* def THINK_C */
doneSetup = true;
}
#endif /* def MEMORY_ACCOUNTING */

  if (ptr == NULL)
    panic("checkPtr found a NULL pointer");

#ifdef MEMORY_ACCOUNTING
/* look for specific ptr (useful when tracking un-freed memory) */
if (ptr == badPtr) warn("checking found bad ptr"); 
/* tickle a memory bug */
if (ptrs == NULL && tickles > 0)
  ptrs = (char**)malloc((size_t)tickles * sizeof(char*));
for (i = 0; i < tickles; i++)
{ ptrs[i] = malloc((size_t)5);
}
for (i = 0; i < tickles; i++)
{ free(ptrs[i]);
}
#endif /* def MEMORY_ACCOUNTING */

}

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

void*
fs_malloc(size)
size_t size;
/* does safety checks and optional accounting */
{ 
  register void* ptr = NULL;

#ifdef THINK_C
  ptr = (void*)NewPtr((long)size);
  s_checkPtr(ptr);
  memset(ptr,0,(size_t)size);	/* zero it */
#else  
  ptr = (void*)calloc((size_t)size,(size_t)1);
  s_checkPtr(ptr);
#endif
  
#ifdef MEMORY_ACCOUNTING
  /* look for specific size (useful when tracking un-freed memory) */
  if (size == badSize)
    warn("bad size in malloc");
  
  prepMemAcct();
  fprintf(memRecord,"malloced %lu bytes at %lu\n",size,ptr);
  flushMemAcct();
#endif

  return(ptr);
}

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

void*
fs_realloc(ptr,size)
void* ptr;
size_t size;
/* does safety checks and optional accounting 
   note - we don't know how big ptr's memory is, so we can't ensure
   that any new memory allocated is NULLed!
 */
{ 
  register void* nptr = NULL;
  
  if (ptr == NULL)		/* this is really a malloc */
    return(s_malloc(size));
    
#ifdef THINK_C
  nptr = NewPtr(size);		/* need to make a copy */ 
  s_checkPtr(nptr);
  BlockMove(ptr,nptr,size);	/* move the old contents into it */
  DisposPtr(ptr);		    /* get rid of the old ones */
#else
  nptr = (void*)realloc(ptr,size);
  s_checkPtr(ptr);
#endif  
   
#ifdef MEMORY_ACCOUNTING
  /* look for specific size (useful when tracking un-freed memory) */
  if (size == badSize) 
    warn("bad size in realloc");
  
  prepMemAcct();
  fprintf(memRecord,"realloced %lu bytes at %lu from %lu\n",size,nptr,ptr);
  flushMemAcct();
#endif

  return(nptr);
}

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

void
fs_free(ptr)
void* ptr;
/* does safety checks and optional accounting */
{
#ifdef MEMORY_ACCOUNTING
  prepMemAcct();
  /* note that the sizeof a pointer is always 4.  If only we could find out
     how much space that pointer was pointing to.  Oh well, this is a place
     holder for now
   */
  fprintf(memRecord,"freed %lu bytes at %lu\n",(size_t)sizeof(ptr),ptr);
  flushMemAcct();

  /* look for specific ptr (useful when tracking un-freed memory) */
  if (ptr == badPtr) warn("bad ptr in free");
#endif

  if (ptr != NULL)		/* some non-ansi compilers/os's cant handle freeing null */
    {				/* if we knew the size of this block of memory, we could clear it - oh well */
#ifdef THINK_C
      DisposPtr(ptr);
#else
      free(ptr);
#endif
    }
}

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

char*
s_strdup(s)
char* s;

/* return a copy of s.  This is identical to the standard library routine
   strdup(), except that it is safe.  If s == NULL or malloc fails, 
   appropriate action is taken.
 */
{
  unsigned long len;
  char* copy = NULL;
  
  if (s == NULL)		/* saftey check to postpone stupid errors */
    return(NULL);
    
  len = strlen(s);		/* length of string - terminator */
  copy = (char*)s_malloc((size_t)(sizeof(char)*(len + 1)));
  strncpy(copy,s,len + 1);
  return(copy);
}

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

char*
fs_strncat(dst,src,maxToAdd,maxTotal)
char* dst;
   char* src;
   size_t maxToAdd;
   size_t maxTotal;

/* like strncat, except the fourth argument limits the maximum total 
   length of the resulting string
 */
{
  size_t dstSize = strlen(dst);
  size_t srcSize = strlen(src);
  
  if (dstSize + srcSize < maxTotal) /* use regular old strncat */
    return(strncat(dst,src,maxToAdd));
  else
    { size_t truncateTo = maxTotal - dstSize - 1;
      char   saveChar = src[truncateTo];
      char*  result = NULL;
      src[truncateTo] = '\0';
      result = strncat(dst,src,maxToAdd);
      src[truncateTo] = saveChar;
      return(result);
    }
}

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

typedef long (longfunc) _AP((long c));

char*
strtokf(s1,isDelimiter)
char* s1;
longfunc *isDelimiter; /* really *isDelimiter() */

/* This function is exactly like strtok, except that instead of passing a 
   delimiter string, you pass a function that decides if a character is 
   a delimiter or not, returning IS_DELIMITER or NOT_DELIMITER respecively.
   Note that passing a NULL delimiter function will cause the last delimiter
   to be used.
 */
{
  static char* searchStr = NULL;
  static longfunc *delimiterFunc;
  long i;
  char* startTok = NULL;

  if (s1 != NULL)		/* passing s1 = NULL says use the last pos */
    searchStr = s1;
    
  if (isDelimiter != NULL)
    delimiterFunc = isDelimiter;
   
  if (searchStr == NULL || searchStr[0] == '\0')
    return(NULL);		/* nothing left to search */
    
  if (delimiterFunc == NULL)
    return(NULL);		/* no delimiter to search with */
    
  /* find the start of the next token */
  for (i = 0; searchStr[i] != '\0'; i++)
    { if ((*delimiterFunc)((long)searchStr[i]) == NOT_DELIMITER)
	break;
    }
  
  if (searchStr[i] == '\0') 
    return(NULL);		/* read to end of search string */
  else
    startTok = searchStr + i;	/* remember the starting point for this token*/
    
  /* find the end of the next token */
  for (; searchStr[i] != '\0'; i++)
    { if ((*delimiterFunc)((long)searchStr[i]) == IS_DELIMITER)
	break;
    }
   
  /* if the end is a delimiter (and not just the end of the search string)
     replace it with '\0', and put searchStr just beyond it, otherwise
     put searchStr at the terminator. */
  if (searchStr[i] != '\0')
    { searchStr[i] = '\0';
      searchStr = searchStr + i + 1;
    }
  else
    searchStr = searchStr + i;
   
  return(startTok);
}

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

#ifdef ANSI_LIKE /* use ansi varargs */

long
cprintf(boolean print, char *format, ...)
/* just like printf, but only prints if the first argument = 1 */
{
  va_list ap;			/* the variable arguments */
  if (print == 1)
    { long res;
      va_start(ap,format);	/* init ap */
      res = vprintf(format,ap);	/* print the contents */
      va_end(ap);		/* free ap */
      return(res);
    }
  else
    return(0);
}

#else /* use k&r varargs */

long
cprintf(va_alist)
va_dcl
/* just like printf, but only prints if the first argument = 1 */
{
  va_list ap;			/* the variable arguments */
  boolean print;
  long res;

  va_start(ap);			/* init ap */

  print = va_arg(ap,boolean);	/* get the condition */

  if (print == 1)
    { char* format = va_arg(ap,char*); /* get the format */
#ifdef BSD /* some folks can't do vfprintf */
      res = printf("%s",format); /* just print the first thing */
#else
      res = vprintf(format,ap);	/* print the contents */
#endif
    }
  else
    res = 0;

  va_end(ap);			/* free ap */

  return(res);
}

  
#endif

extern char* log_file_name;
extern FILE* logfile;

/* waislog - a new and improved logging facility.
   first two arguments are important, the rest is text.

   arg1: priority.  If priority > log_level (some global), output this
   message

   arg2: message code.  This is the kind of message (search, retrieval, etc).
*/

#ifdef ANSI_LIKE /* use ansi varargs */

void
waislog(long priority, long message, char *format, ...)
/* just like printf, but prints to the logfile, with PID and time. */
{
  va_list ap;			/* the variable arguments */
  static long line = 0;

  if(logfile == NULL && log_file_name != NULL)
    logfile = fopen(log_file_name, "a");

  if(logfile) {
    va_start(ap, format);

    fprintf(logfile, "%d: %d: %s: %d: ", getpid(), line++, printable_time(), message);

    vfprintf(logfile, format,ap);	/* print the contents */
    fprintf(logfile, "\n");
    fflush(logfile);
    va_end(ap);			/* free ap */
  }

  if(logfile != NULL && logfile != stderr) {
    fclose(logfile);
    logfile = NULL;
  }
}

#ifdef old
void
waislog(priority, message, format)
long priority;
long message;
char *format;
/* just like printf, but prints to the logfile, with PID and time. */
{
  va_list ap;			/* the variable arguments */
  static long line = 0;

  if(logfile == NULL && log_file_name != NULL)
    logfile = fopen(log_file_name, "a");

  if(logfile) {
    va_start(ap, priority);
    message = (long)va_arg(ap, long);

    fprintf(logfile, "%d: %d: %s: %d: ", getpid(), line++, printable_time(), message);

    format = (char*)va_arg(ap, char*);

    vfprintf(logfile, format,ap);	/* print the contents */
    fprintf(logfile, "\n");
    fflush(logfile);
    va_end(ap);			/* free ap */
  }

  if(logfile != NULL && logfile != stderr) {
    fclose(logfile);
    logfile = NULL;
  }
}
#endif /*old*/

#else /* use k&r varargs */


#ifdef BSD
void
waislog(priority, message, format, ap)
long priority;
long message;
char *format;
char *ap;
/* just like printf, but prints to the logfile, with PID and time. */
{
  static long line = 0;

  if(logfile == NULL && log_file_name != NULL)
    logfile = fopen(log_file_name, "a");

  if(logfile) {
    fprintf(logfile, "%d: %d: %s: %d:", getpid(), line++, printable_time(), message);

    fprintf(logfile, format, ap);

    fprintf(logfile, "\n");
    fflush(logfile);
  }

  if(logfile != NULL && logfile != stderr) {
    fclose(logfile);
    logfile = NULL;
  }
}
#endif /*old*/

#ifndef BSD
void
waislog(va_alist)
va_dcl
/* just like printf, but prints to the logfile, with PID and time. */
{
  va_list ap;			/* the variable arguments */
  char* format;
  static long line = 0;
  int priority, message;

  if(logfile == NULL && log_file_name != NULL)
    logfile = fopen(log_file_name, "a");

  
  if(logfile) {
    va_start(ap);		/* init ap */

    priority = va_arg(ap, int);
    message = va_arg(ap, int);

    fprintf(logfile, "%d: %d: %s: %d: ", getpid(), line++, printable_time(), message);

    format = va_arg(ap,char*);	/* get the format */

    vfprintf(logfile, format,ap); /* print the contents */
    fprintf(logfile, "\n");
    fflush(logfile);
    va_end(ap);			/* free ap */
  }

  if(logfile != NULL && logfile != stderr) {
    fclose(logfile);
    logfile = NULL;
  }
}
#endif /* NOT BSD */

#endif /* ANSI_LIKE */
  
/*----------------------------------------------------------------------*/

void
warn(message)
char* message;

{
#ifdef THINK_C
  Debugger();
#else
  printf("%s\n<press return to continue>\n",message);
  getchar();
#endif
}

/*----------------------------------------------------------------------*/
boolean substrcmp(string1,string2)
char *string1, *string2;
{
  /* compares the strings up until one of then ends.
   * returns true if they are the same, false if not.
   */
  register char *a, *b;
  
  a = string1;
  b = string2;
  
  while (*a && *b) 
    if(*a++ != *b++) 
      return false;
  return true;
}

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

char *printable_time()
{ 
  static char *string;
  time_t tptr;
  time(&tptr);
  string = ctime(&tptr);
  if(string){
    if(string[strlen(string)-1] == '\n')
      string[strlen(string)-1] = '\0';   
    return(string+4);
  }
  else
    return("Time Unknown");
}

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

char char_downcase(long_ch)
unsigned long long_ch;
{
  unsigned char ch = long_ch & 0xFF; /* just want one byte */
  /* when ansi is the way of the world, this can be tolower */
  return (((ch >= 'A') && (ch <= 'Z')) ? (ch + 'a' -'A') : ch);
}

char *string_downcase(word)
char *word;
{
  long i = 0;
  while(word[i] != '\0'){
    word[i] = char_downcase((unsigned long)word[i]);
    i++;
  }
  return(word);
}

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


/* parsing arguments functions */

char *next_arg(argc,argv)
int *argc;
char ***argv;

     /* Returns NULL when it is out of arguments,
        This side effects both argc and argv.  argc always contains the number
	of arguments left.
	The first returned is the command name. 
	*/
{
  if((*argc)-- > 0)
    return(*((*argv)++));
  else
    return(NULL);
}

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

char *peek_arg(argc,argv)
int *argc;
    char ***argv;

     /* Returns the next argument without popping it.
       Returns NULL when it is out of arguments.
       */
{
  if((*argc) > 0)
    return(**argv);
  else
    return(NULL);
}

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


#ifdef THINK_C
#include <EventMgr.h>
#undef MAX_FILE_NAME_LEN 
#ifdef WAIStation
#include "CRetrievalApp.h"
#endif /* def WAIStation */
#endif /* def THINK_C */

void
beFriendly()
/* this routine is called during time intensive operations
   on single processing machines (macs and DOS).  It gives
   time to other processes
 */
{ 
#ifdef never
#ifdef THINK_C
   EventRecord  	macEvent;	/* an event */

   static RgnHandle	mouseRgn = NULL; /* region for mouse moved events */
   long		        sleepTime; 	/* max time between events */
   
#ifdef WAIStation
   gApplication->FrobWaitCursor();
#endif /* def WAIStation */
   
   if (mouseRgn == NULL)
     mouseRgn = NewRgn(); /* do we need to set its value? */
     
   sleepTime = 5; /* arbitrary - a tech note recommends < 50 */
   
   WaitNextEvent(everyEvent,&macEvent,sleepTime,mouseRgn); 
#endif /* def THINK_C */
#endif
}

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