/****** HBBSCommon.library/--Description-- ***************************
*
*   NAME
*      HBBSCommon.library - Shared library for HBBS and it's Nodes,
*      doors and utilities to use.
*
*********************************************************************/

/****** HBBSCommon.library/--Private Functions-- *********************
*
*   NAME
*      Private Functions
*                 
*   NOTES
*      Do not use the following library functions:
*
*      SafePutToPort
*      SendMessage
*      HBBS_SetBBS
*      HBBS_ResetNodeData
*      HBBS_RunDOSCMD
*      HBBS_ResetNodeData
*      LoadPrivateData
*      UpdatePrivateData
*      HBBS_SaveCallsData
*      HBBS_LoadCallsData
*      HBBS_LoadKey :-)
*
*********************************************************************/

/*

  todo
  ====

  remove HBBS_Reserved_1() at next full re-build

*/

#define HBBSCOMMON
#define MAIN

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include <math.h>
#include <time.h>

#include <exec/exec.h>
#include <exec/types.h>

#include <dos/dos.h>
#include <dos/dostags.h>
#include <dos/filehandler.h>
#include <dos/dosextens.h>

#include <libraries/reqtools.h>

#include <utility/date.h>
//#include <resources/battclock.h>


#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/alib_protos.h>
#include <clib/reqtools_protos.h>
//#include <clib/battclock_protos.h>
#include <clib/utility_protos.h>

#include <pragmas/exec_pragmas.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/reqtools_pragmas.h>
//#include <pragmas/battclock_pragmas.h>
#include <pragmas/utility_pragmas.h>


#include "/common/types.h"
#include "/common/errors.h"
#include "/common/defines.h"
#include "/common/structures.h"
#include "/common/strings.h"
#include "/common/files.h"
#include "/common/release.h"

#include "/Node/!SecInfo.h"

// some forward defines

void __asm __saveds strNcpy(register __a0 UBYTE *dest,register __a1 UBYTE *source,register __d0 int chars);
void __asm __saveds HBBS_rterror(register __a0 char *str);
BOOL __asm __saveds AssignOK(register __a0 char *checkme );
struct List __asm __saveds *HBBS_CreateList( void );
char  __asm __saveds *HBBS_GetTime(register __a0 char *timestr);
char __asm __saveds *HBBS_GetDate(register __a0 char *datestr);
char __asm __saveds *HBBS_GetDateString(register __a0 char *datestr,register __d0 ULONG AmigaTime);
//void __asm __saveds HBBS_DateStrFromTM(register __a0 UBYTE *datestr, register __a1 struct tm *timestruct);
V_BOOL __asm __saveds HBBS_AddCfgItemQuick(register __a0 struct CfgFileData *cfgfile,register __a1 char *ItemName, register __a2 char *Params);
void   __asm __saveds HBBS_FlushConfig(register __a0 struct CfgFileData *cfgfile);
BOOL __asm __saveds PathOK(register __a0 char *str);
char __asm __saveds *HBBS_GetWeekStartDate(register __a0 char *datestr, register __d0 ULONG AmigaTime);

// needed libs..

struct DosLibrary *DOSBase=NULL;
struct ExecBase *SysBase=NULL;
struct ReqToolsBase *ReqToolsBase=NULL;
struct Library *UtilityBase = NULL;
//struct Library *BattClockBase; // dont need to close this so skip initialisation

char *versionstr="$VER: HBBSCommon.library "RELEASE_STR;

// global variables.. (keep to a minimum..)

#ifndef __SASC

struct LibBase {
  struct Library base;
};

#pragma libbase LibBase
#endif

struct BBSGlobalData *BBSGlobal=NULL;

UBYTE *OPT_PRIV_LastUserNum="LastUserNum";
UBYTE *OPT_PRIV_CallsEver="CallsEver";

char *str_userdataconsistancy="Have the file checked for consistancy as soon as possible";
char monthstr[13][4]={"JAN","FEB","MAR","APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC"};
char daystr[8][4]={"Mon","Tue","Wed","Thu","Fri","Sat","Sun"};
char last_time[27]; // format: "DDD MMM dd hh:mm:ss YYYY\0"
char *typestr[]={"TIME",
                 "SMALLNUM",
                 "BIGNUM",
                 "STRING",
                 "BOOL",
                 "STRINGLIST",
                 "FLAGS",       // see hbbs/types.h and hbbs/defines.h
                 "PATH",
                 "PATHLIST",
                 "WORD",
                 "DATE",
                 "HUGENUM"};


// actual functions..

/****** HBBSCommon.library/HBBS_GetAmigaTime *********************************
*
*   NAME
*      HBBS_GetAmigaTime - Get time in packed format
*
*   SYNOPSIS
*      AmigaTime = HBBS_GetAmigaTime( void )
*
*      ULONG HBBS_GetAmigaTime( void )
*
*   FUNCTION
*      This function performs same task as ReadBattClock() except that it
*      returns the time in the same packed format (seconds since 1/1/1978)
*      but does not require a battery backed clock.
*
*   INPUTS
*      None
*
*   RESULT
*      see ReadBattClock() in battclock.resource
*
*********************************************************************/

ULONG __asm __saveds HBBS_GetAmigaTime( void )
{
  struct DateStamp ds;

  DateStamp(&ds);

  return ((ULONG)(ds.ds_Days * 86400 + ds.ds_Minute * 60 + ds.ds_Tick/TICKS_PER_SECOND ));

/* Using Timer.device
  struct timeval tvNow;

  GetSysTime(&tvNow);
  return(tvNow.tv_Secs);
*/

/* Using battclock
  return(ReadBattClock());
*/
}

char __asm __saveds *HBBS_GetTimeDateString( register __d0 const ULONG AmigaTime )
{
  struct ClockData MyClock;

  Amiga2Date(AmigaTime,&MyClock);

  sprintf(last_time,"%s %s %02ld %02ld:%02ld:%02ld %4ld",
          daystr[MyClock.wday-1],monthstr[MyClock.month-1],MyClock.mday,
          MyClock.hour,MyClock.min,MyClock.sec,MyClock.year);

  return(last_time);
}

/****** HBBSCommon.library/strNIcmp *********************************
*
*   NAME
*      strNIcmp - length limited case in-sensitive string compare
*
*   SYNOPSIS
*      strNIcmp( s1, s2, n)
*                A0  A1  D0
*      int strNIcmp( char *s1, char *s2, int const n)
*
*   FUNCTION
*      This function compares the first n bytes of a string to
*      another string.
*
*   INPUTS
*      s1 - null terminated string
*      s2 - null terminated string
*      n - amount of characters to compare.
*
*   RESULT
*      0 if the first n bytes match, see stricmp() for more return
*      codes
*
*********************************************************************/

int __asm __saveds strNIcmp(register __a0 char *s1, register __a1 char *s2,register __d0 int const n)
{
  char *cs1,*cs2;
  int retval=-1;

  if (cs1=AllocVec(n+1,MEMF_PUBLIC))
  {
    if (cs2=AllocVec(n+1,MEMF_PUBLIC))
    {
      strNcpy(cs1,s1,n);
      strNcpy(cs2,s2,n);
      retval=stricmp(cs1,cs2);
      FreeVec(cs2);
    }
    FreeVec(cs1);
  }
  return(retval);
}

/****** HBBSCommon.library/FreeStr *********************************
*
*   NAME
*      FreeStr - Frees a string.
*
*   SYNOPSIS
*      FreeStr( str )
*               A0
*
*      void FreeStr( char *str )
*
*   FUNCTION
*      This function free's a string, using FreeVec().
*      It *is* checked for a non-NULL value first.
*
*   INPUTS
*      str - block of memory to be freed (wether containing a string
*          or not.)
*
*   RESULT
*      None
*
*********************************************************************/

void __asm __saveds FreeStr(register __a0 char *str)
{
  if (str) FreeVec(str);
}

/****** HBBSCommon.library/DupStr ************************************
*
*   NAME
*      DupStr - Duplicates a string
*
*   SYNOPSIS
*      DupStr( str )
*               A0
*
*      char *DupStr( char *str )
*
*   FUNCTION
*      This function allocates a new block of memory, large enough
*      to hold a copy of the string and a null-terminator and then
*      copies the string to this new block of memory.
*
*   INPUTS
*      str - null terminated string to be duplicated.
*
*   RESULT
*      pointer to a newly allocated block of memory holding a copy of
*      the string.
*
*   SEE ALSO
*      FreeStr()
*
*********************************************************************/

char __asm __saveds *DupStr(register __a0 char *str)
{
  char *newstr=NULL;
  if (str)
  {
    if (newstr=(char *)AllocVec(strlen(str)+1,MEMF_PUBLIC))
    {
      newstr[0]=0;
      strcpy(newstr,str);
    }
  }
  return(newstr);
}

/****** HBBSCommon.library/FreeAndSet *********************************
*
*   NAME
*      FreeAndSet - Free and then set a variable
*
*   SYNOPSIS
*      FunctionName( varname, newstr )
*                    A0       A1
*
*      char *FreeAndSet(char **varname, char *newstr)
*
*   FUNCTION
*      FreeStr()'s the block of memory pointed to by varname and then
*      DupStr()'s newstr and stores result in varname.
*
*   INPUTS
*      varname - address of a pointer to a string
*          *must* be initialised to NULL or to an AllocVec()'d pointer
*      newstr - pointer to a string to be copied, or NULL
*
*      e.g.
*
*      char *mystr = NULL;
*
*      if (mystr = DupStr("bananas"))
*      {
*         ...
*         FreeAndSet(&mystr,"apples");
*         ...
*         FreeVec(mystr);
*      }
*
*   RESULT
*      returns address of new string.
*
*   SEE ALSO
*      HBBS_LoadConfig(), HBBS_GetSetting()
*
*********************************************************************/

char __asm __saveds *FreeAndSet(register __a0 char **varname,register __a1 char *newstr)
{
  // this routines frees memory allocated to strings using strdup or
  // and duplicates the string in newstr and sets varname to the newly allocated str

  if (*varname!=NULL)
  {
    FreeStr(*varname);
    *varname=NULL;
  }

  if (newstr)
  {
    *varname=DupStr(newstr);
  }
  return(*varname); // return varname if you want to test result..
}

/****** HBBSCommon.library/CheckBoolean ******************************
*
*   NAME
*      CheckBoolean - Check a string for a boolean value name
*
*   SYNOPSIS
*      CheckBoolean( str )
*                    A0
*      void CheckBoolean( char *str )
*
*   FUNCTION
*      This function checks str against boolean words: YES, ON, TRUE
*      and returns the boolean equivalent
*
*   INPUTS
*      str - null terminated string
*
*   RESULT
*      FALSE, or TRUE if the string equals "YES", "ON" or "TRUE"
*
*   NOTES
*      This is case in-sensitive.
*
*********************************************************************/

V_BOOL __asm __saveds CheckBoolean(register __a0 char *str)
{
  if (stricmp(str,"TRUE")==0 || stricmp(str,"ON")==0 || stricmp(str,"YES")==0)
    return(TRUE);
  else
    return(FALSE);
}

/****** HBBSCommon.library/NewStrNode ********************************
*
*   NAME
*      NewStrNode - Allocate a new string node.
*
*   SYNOPSIS
*      NewStrNode( str, list )
*                  A0   A1
*
*      V_ERROR NewStrNode( char *str, struct List *list )
*
*   FUNCTION
*      This function allocates a new node with ln_Name set to a copy
*      of str, and appends it to the list specified in list
*
*   INPUTS
*      str - pointer to a string, or NULL
*      list - list to add the newly created node to
*
*   RESULT
*      TYPE_MEMORY if there was a memory error while duplicating the
*          string or allocating a new node
*      ERR_NO_ERR if all was ok
*
*   NOTES
*      None
*
*   SEE ALSO
*      HBBS_CreateNode(), HBBS_CreateList(), HBBS_FreeNode()
*      HBBS_FreeListNodes(), FreeStrList()
*
*********************************************************************/

V_ERROR __asm __saveds NewStrNode(register __a0 char *str,register __a1 struct List *list)
{
  struct Node *NewNode;
  V_ERROR retval=TYPE_MEMORY;

  if (NewNode=AllocVec(sizeof(struct Node),MEMF_PUBLIC|MEMF_CLEAR))
  {
    if ((str==NULL) || (NewNode->ln_Name=DupStr(str)))
    {
      AddTail(list,NewNode);
      retval=ERR_NO_ERROR;
    }
    else
    {
      FreeVec(NewNode);
    }
  }
  return(retval);
}

/****** HBBSCommon.library/UpperCase *********************************
*
*   NAME
*      UpperCase - converts a string to upper case
*
*   SYNOPSIS
*      UpperCase( str )
*                 A0
*
*      void UpperCase( char *str )
*
*   FUNCTION
*      This function converts a string to upper case
*
*   INPUTS
*      str - a null terminated string
*
*   RESULT
*      None
*
*   SEE ALSO
*      upcase()
*
*********************************************************************/

void __asm __saveds UpperCase(register __a0 char *str)
{
  // converts str into uppercase

  short loop;

  for (loop=0;str[loop]=toupper(str[loop]);loop++);
}

/****** HBBSCommon.library/upcase *********************************
*
*   NAME
*      upcase - duplicate and converts a string to upper case
*
*   SYNOPSIS
*      upcase( str )
*                 A0
*
*      char *upcase( char *str )
*
*   FUNCTION
*      This function duplicates a string and then converts it to
*      string to upper case
*
*   INPUTS
*      str - a null terminated string
*
*   RESULT
*      pointer to a newly allocated string, free with FreeStr() or
*      dos.library/FreeVec().
*      or NULL if there was an error
*
*   SEE ALSO
*      UpperCase()
*
*********************************************************************/

char __asm __saveds *upcase(register __a0 char *str)
{
  // converts str into uppercase and returns a pointer to a new
  // string.
  // returns NULL if it fails.

  // dont forget to FreeStr() the output!

  char *outstr;

  if (outstr=DupStr(str))
  {
    UpperCase(outstr);
  }
  return(outstr);
}

/****** HBBSCommon.library/position **********************************
*
*   NAME
*      position - find a substring in a string
*
*   SYNOPSIS
*      position( substr, str )
*                A0      A1
*
*      short position( char *substr, char *str )
*
*   FUNCTION
*      This function checks for the occurence of substr in str and
*      returns the character offset from the start of the str where
*      substr was found.
*
*      Case sensitive
*
*   INPUTS
*      substr - pointer to a null-terminated sub string
*      str - pointer to a null terminated string possibly containing
*          substr
*
*   RESULT
*      -1 if substr was not found in str
*      or the character index substr starts at in str starting from 0
*
*   SEE ALSO
*      iposition()
*
*********************************************************************/

short __asm __saveds position(register __a0 char *substr,register __a1 char *str)
{
  // returns an character offset of a substring in a string
  // this version is CASE SENSITIVE
  // returns -1 if substring not found in the string

  char *whstr;

  if (substr[0] == 0)
    return(-1);
  else
    return((short)((whstr=strstr(str,substr)) ? (short)((LONG)whstr-(LONG)str) : -1));
}

/****** HBBSCommon.library/iposition *********************************
*
*   NAME
*      iposition - find a substring in a string (case insensitive)
*
*   SYNOPSIS
*      iposition( substr, str )
*                A0      A1
*
*      short iposition( char *substr, char *str )
*
*   FUNCTION
*      This function checks for the occurence of substr in str and
*      returns the character offset from the start of the str where
*      substr was found.
*
*      Case in-sensitive
*
*   INPUTS
*      substr - pointer to a null-terminated sub string
*      str - pointer to a null terminated string possibly containing
*          substr
*
*   RESULT
*      -2 if memory error
*      -1 if substr was not found in str
*      or the character index substr starts at in str starting from 0
*
*   SEE ALSO
*      position()
*
*********************************************************************/

short __asm __saveds iposition(register __a0 char *substr,register __a1 char *str)
{
  // returns a character offset of a substring in a string
  // this version is CASE INSENSITIVE
  // returns -1 if substring not found in the string
  //      or -2 if memory allocation error

  short where=-2;
  char *isubstr,*istr;

  if (isubstr=upcase(substr))
  {
    if (istr=upcase(str))
    {
      where=position(isubstr,istr);
      FreeStr(istr);
    }
    FreeStr(isubstr);
  }
  return(where);
}

/****** HBBSCommon.library/strfcpy ***********************************
*
*   NAME
*      strfcpy - copy string to string using an offset
*
*   SYNOPSIS
*      strfcpy( dest, source, from )
*               A0    A1      D0
*
*      void strfcpy( char *dest, char *source, int from )
*
*   FUNCTION
*      This function copies source to dest starting at offset
*      specified in from
*
*   INPUTS
*      dest - pointer to an allocated area of memory to store the
*          destination string
*      source - null terminated string
*      from - offset, starting from 0.
*
*   RESULT
*      None
*
*   NOTES
*      Do not use, instead use strcpy(dest,source+from)
*      as it does the same thing and is quicker.
*
*   SEE ALSO
*      strcpy()
*
*********************************************************************/

void __asm __saveds strfcpy(register __a0 char *dest,register __a1 char *source,register __d0 int from)
{
  /*
     copy all chars from "source" to "dest" starting at offset "from"
     (kinda the reverse from strncpy() )
   */

  strcpy(dest,source+from);
}

/****** HBBSCommon.library/stripcr ***********************************
*
*   NAME
*      stripcr - remove trailing \r and \n's from a string
*
*   SYNOPSIS
*      stripcr( str )
*               A0    A1      D0
*
*      void stripcr( char *str )
*
*   FUNCTION
*      This function removes all \r or \n charaters from the end of a
*      string until the string is empty or it finds a character other
*      than a \r or a \n. The string may contain \r's or \n's in the
*      middle still...
*
*   INPUTS
*      str - null-terminated string to remove \r's and \n's from
*
*   RESULT
*      None
*
*   SEE ALSO
*      StripComments(), StripSpaces(), RemoveSpaces()
*
*********************************************************************/

void __asm __saveds stripcr(register __a0 char *s)
{
  /* New Optimized Version 04-JAN-1996 */

  LONG Len;
  if (s)
  {
    Len=strlen(s)-1; // set to point to last char in string 's'

    while (Len>=0 && (s[Len]=='\r' || s[Len]=='\n'))
    {
      s[Len--]=0; // string is now 1 char smaller, and overwrite the \r or \n with a null terminator
    }
  }
}

/****** HBBSCommon.library/StripComments *****************************
*
*   NAME
*      StripComments - Remove's comments from strings.
*
*   SYNOPSIS
*      StripComments( str )
*                     A0
*
*      UBYTE *StripComments( char *str )
*
*   FUNCTION
*      This function Removes the first ';' and everything after it from
*      the string str.
*
*      Used to remove comments from config files.
*
*      Works by changing the first ; to a \0 (moves the null terminator)
*
*   INPUTS
*      str - pointer to a null-terminated string
*
*   RESULT
*      a pointer to the start of the comments in string s
*      or NULL if there were no comments in the string.
*
*   SEE ALSO
*     StripSpaces(), stripcr(), RemoveSpaces()
*
*********************************************************************/

UBYTE __asm __saveds  *StripComments(register __a0 char *s)
{
  /* New Optimized Version 04-JAN-1996 */

  // returns a pointer to the start of the comments too..

  UBYTE *strptr;

  if (strptr=strchr(s,';'))
  {
    *strptr=0;
    strptr++; //we've just killed the ';' so point to next char..
  }
  return(strptr);
}

/****** HBBSCommon.library/StripSpaces *******************************
*
*   NAME
*      StripSpaces - remove trailing spaces from a string
*
*   SYNOPSIS
*      StripSpaces( str )
*                   A0
*
*      void StripSpaces( char *str )
*
*   FUNCTION
*      This function removes all trailing spaces from str
*
*      This is handy for removing end of line blanks from config files
*      and strings input by users
*
*   INPUTS
*      str - pointer to a null-terminated string
*
*   RESULT
*      None
*
*   SEE ALSO
*      StripComments(), stripcr(), RemoveSpaces()
*
*********************************************************************/

void __asm __saveds StripSpaces(register __a0 char *s)
{
  /* New Optimized Version 04-JAN-1996 */
  /* this version only parses the string once, much nicer.. */

  LONG len=strlen(s)-1;

  // while the last char is a ' ', set it to a 0
  while (len>=0 && s[len]==' ') s[len--]=0;  // kooler Kompakt koding! :-)
}

/* old version, too slow, keeps parsing strings..
void __asm __saveds StripSpaces(register __a0 char *s)
{
  // while the last char is a ' ', set it to a 0
  while (s[strlen(s)-1]==' ') s[strlen(s)-1]=0;  // Kompakt coding! :-)
}
*/

/****** HBBSCommon.library/replace ***********************************
*
*   NAME
*      replace - Replace a substring with a different sub-string
*
*   SYNOPSIS
*      replace( dest, source, from, to )
*               A0    A1       A2    A3
*
*      void replace( char *dest, char *source, char *from, char *to )
*
*   FUNCTION
*      This function replace all occurences of "from" in "source"
*      with "to" and store result in "dest"
*
*      make sure dest is large enough to hold all the replacements
*      that are made!
*
*      e.g.  replace(dest,source,"Whops","Whoops!");
*
*      compare and dest can be the same variable...
*
*      e.g.
*
*      char mystr[100]="this is a banana";
*      replace(mystr,mystr,"banana","test");
*
*   INPUTS
*      dest - pointer a some memory to hold a string
*      source - null-terminated string
*      from - null-terminated string
*      to - null-terminated string
*
*   RESULT
*      None
*
*   NOTES
*      this is really handy!
*
*   TODO
*      Add a max-size for dest.
*      return amount of replacements that were made
*
*********************************************************************/

void __asm __saveds replace(register __a0 char *dest,register __a1 char *compare,register __a2 char *from,register __a3 char *to)
{

  /*
     replace all occurences of "from" in "compare" with "to" and store result in "dest"
     (note: this is dead usefull!!!)

     e.g.  replace(dest,source,"Whops","Whoops!");

     compare and dest can be the same variable...

   */
  int where;
  char *mystring;

  if (mystring=DupStr(compare))
  {
    strcpy(dest,"");
    do
    {
      where=position(from,mystring);
      if (where!=-1)
      {
        strncat(dest,mystring,where);                    /* copy first part of string */
        strcat(dest,to);                                 /* add repeaced chars */
        strfcpy(mystring,mystring,where+strlen(from));   /* copy rest of string  so we find first position of from */
      }
    } while (where!=-1);                                 /* keep doing until "from" is not found */
    strcat(dest,mystring);                               /* add the rest of the string */
    FreeStr(mystring);
  }
}

/****** HBBSCommon.library/GetParams *********************************
*
*   NAME
*      GetParams - Process a string to get paramaters
*
*   SYNOPSIS
*      GetParams( dest, source)
*                 A1    A1
*
*      BOOL GetParams( char *dest, char *source)
*
*   FUNCTION
*      This function copies everything after the "=" in "source" in
*      "dest"
*
*      i.e pass it "Item=Paramaters..." as the source and dest would
*      contain "paramaters..."
*
*   INPUTS
*      dest - must point to an allocated section of memory to store
*          the paramaters in, must be strlen(source)+1 or smaller
*      source - pointer to a string
*
*   RESULT
*      TRUE if anything was copied to "dest"
*
*   SEE ALSO
*      GetItem()
*
*********************************************************************/

BOOL __asm __saveds GetParams(register __a0 char *dest,register __a1 char *source)
{
  UBYTE *strptr;

  dest[0]=0;

  if (strptr=strchr(source,'='))
  {
    strcpy(dest,strptr+1);

    return(TRUE);
  }
  return(FALSE);
}

/****** HBBSCommon.library/GetItem ***********************************
*
*   NAME
*      GetItem - Process a string to get the item
*
*   SYNOPSIS
*      GetItem( dest, source)
*                 A1    A1
*
*      BOOL GetItem( char *dest, char *source)
*
*   FUNCTION
*      This function copies everything before the "=" in "source" in
*      "dest"
*
*      i.e pass it "Item=Paramaters..." as the source and dest would
*      contain "Item"
*
*   INPUTS
*      dest - must point to an allocated section of memory to store
*          the paramaters in, must be strlen(source)+1 or smaller
*      source - pointer to a string
*
*   RESULT
*      TRUE if anything was copied to "dest"
*
*   SEE ALSO
*      GetParams()
*
*********************************************************************/

BOOL __asm __saveds GetItem(register __a0 char *dest,register __a1 char *source)
{
  int loop=0;

  // increment start counter until we encounter a character that's not a space or tab

  while (source[loop]==' ' || source[loop]==8) loop++;

  while (source[loop] && source[loop]!='=') // not end of string && '=' ?
  {
    dest[loop]=source[loop];
    loop++;
  }
  dest[loop]='\0';
  if (dest[0]!=0) return(TRUE);

  return(FALSE);
}

/****** HBBSCommon.library/GetNumber *********************************
*
*   NAME
*      GetNumber - Convert last few digits of a string to a number
*
*   SYNOPSIS
*      GetNumber( num, item )
*                 A0   A1
*
*      void GetNumber( int *num, char *item )
*
*   FUNCTION
*      This function copies the last n digits from item into an
*      internal buffer, up to a maximum of 5 digits and converts
*      the buffer to an integer and stores it in the address pointed
*      to by "*num"
*      This function is mainly used while loading config items.
*
*      int mynum;
*
*      GetNumber(&mynum,"thisisanumber123456");
*
*      mynum would then contain 23456
*
*   INPUTS
*      num - pointer to an address to store an integer
*      item - pointer to a string
*
*   RESULT
*      None
*
*   SEE ALSO
*      GetItem(), GetParams()
*
*********************************************************************/

void __asm __saveds GetNumber(register __a0 int *num,register __a1 char *item)
{
  int where;
  char numstr[6];
  for (where=strlen(item);isdigit(item[where-1]);where--);

  strNcpy(numstr+where,item,5); // 5 bytes max
  *num=atoi(numstr);
}

/*
    Description

      sends "message" to a named port "portname".
      Checks for existance of the portname, sends the message

    Inputs

      Message - pointer to a struct Message with correctly filled fields.
      portname - string containing name of port.

    Returns

      NULL if port does not exist, or the address of the port otherwise.
*/


ULONG __asm __saveds  SafePutToPort(register __a0 struct Message *message, register __a1 STRPTR portname)
{
  struct MsgPort *port;
  Forbid();
  if (port=FindPort(portname))
  {
    PutMsg(port,message);
  }
  Permit();
  return((ULONG)port);
}

/*
    Description

      Same as SafePutToPort() except that if the message you are passing has a mn_ReplyPort that is not NULL
      it will wait for the reply to the message.

    Returns                                  

      NULL  if port does not exist.
      1 if message was sent but no reply port was specified
      or the address of the Message that was sent back. (i.e. the return from GetMsg(Msg->mn_ReplyPort)

*/

struct Message __asm __saveds *SendMessage(register __a0 struct Message *Msg,register __a1 char *MPortName)
{
  struct MsgPort *port;
  struct MsgPort *replyport;
  
  if (port=FindPort(MPortName))
  { 

    // save a pointer, so we don't cause enforcer hits
    // by trying to access free'd memory if the a reply is not wanted.

    replyport = Msg->mn_ReplyPort; 
    
    PutMsg(port,Msg);

    if (replyport)
    {
      if (1L << Msg->mn_ReplyPort->mp_SigBit==Wait(1L << Msg->mn_ReplyPort->mp_SigBit))
      {
        return(GetMsg(Msg->mn_ReplyPort));
      }
    }
    else return((struct Message *)1L);
  }
  return(NULL);
}

V_BIGNUM __asm __saveds HBBS_Reserved_1( register __a0 struct List *list) // *C* remove at next full re-build
{
  return(0);
}

/****** HBBSCommon.library/FreeStrList *******************************
*
*   NAME
*      FreeStrList - Free a linked list and all ln_Name fields.
*
*   SYNOPSIS
*      FreeStrList( list )
*                   A0
*
*      void FreeStrList( struct List *list )
*
*   FUNCTION
*      This function frees all the ln_Name records of each struct Node
*      in the linked list.  It then frees all the nodes, and finally
*      the list itself.
*
*      It uses FreeVec() to free the ln_Name files, nodes and list,
*      so you can free all kinds of lists, if an ln_Name record of
*      a node is NULL then it will not be freed
*
*   INPUTS
*      list - a list that has been allocated with AllocVec() and it's
*          nodes created with AllocVec(), such as those created by
*          HBBS_CreateList() and HBBS_CreateNode(), and NewStrNode()
*
*   RESULT
*      None
*
*   NOTES
*      to be on the safe side set the list value that you pass this
*      function to NULL after you have called it.
*
*      e.g.
*
*      struct List *mylist;
*
*      ...
*
*      FreeStrList(mylist);
*      mylist=NULL;
*
*
*   SEE ALSO
*      None
*
*********************************************************************/

void __asm __saveds FreeStrList(register __a0 V_STRINGLIST strlist)
{
  // free all strings in a list and remove all nodes from the list.
  // then free the nodes and the list itself..

  struct Node *node;
  if (strlist)
  {
    while (strlist->lh_Head->ln_Succ)
    {
      node=strlist->lh_Head;
      FreeStr(node->ln_Name);
      Remove(node);
      FreeVec(node);
    }
    FreeVec(strlist);
  }
}

/****** HBBSCommon.library/HBBS_GimmeBBS *****************************
*
*   NAME
*      HBBS_GimmeBBS - Requests a pointer to the main BBS structures
*
*   SYNOPSIS
*      HBBS_GimmeBBS( void )
*
*      struct BBSGlobalData *HBBS_GimmeBBS( void )
*
*   FUNCTION
*      This function requests a pointer to the main BBS structures
*
*      Is is used to obtain access to the BBS data structures.
*
*   INPUTS
*      None
*
*   RESULT
*      pointer to a BBSGlobalData structure
*
*   NOTES
*      See structures.h for readonly areas of this structure.
*
*********************************************************************/

// called by doors and the node programs to get the system data structure pointer
struct BBSGlobalData __asm __saveds *HBBS_GimmeBBS( void )
{
  return(BBSGlobal);
}

// Only the Control program can call this routine, noone else should
// **EVER** call this or your system WILL crash!
void __asm __saveds  HBBS_SetBBS(register __a0 struct BBSGlobalData *B)
{
  BBSGlobal=B;
}

/****** HBBSCommon.library/GetNode ***********************************
*
*   NAME
*      GetNode - Returns a pointer to a struct Node in a list.
*
*   SYNOPSIS
*      GetNode( list, nodenum)
*               A0    D0
*
*      struct Node *GetNode( struct List *list,UWORD nodenum)
*
*   FUNCTION
*      This function returns a pointer to the nodenum'th node in the
*      list.  The list must have nodenum or more nodes in it or a
*      crash will result.
*
*   INPUTS
*      list - lest to find node in.
*      nodenum - number of node in list, 0 is the first node in the
*          list
*
*   RESULT
*      pointer to a struct *Node
*
*********************************************************************/

struct Node __asm __saveds  *GetNode(register __a0 struct List *lh,register __d0 UWORD n)
{ // starts from 0, 0 being the first node in the list..
  // thanks to the author of YAK for this and the next function..
  struct Node *ln;

  for (ln = lh->lh_Head; n--; ln = ln->ln_Succ);
  return ln;
}

/****** HBBSCommon.library/GetNodeNum ********************************
*
*   NAME
*      GetNodeNum - Returns a pointer to a struct Node in a list.
*
*   SYNOPSIS
*      GetNodeNum( list, node)
*                  A0    A1
*
*      UWORD GetNodeNum( struct List *list, struct Node *node)
*
*   FUNCTION
*      This function checks the list for the node specified and
*      returns its position in the list, 0 being the first node in
*      the list.
*      the node must be in the list, or a crash will result.
*
*   INPUTS
*      list - lest to find node in.
*      node - a pointer of a node to find in the list
*
*   RESULT
*      the index of the node in the list, starting at 0
*
*********************************************************************/

UWORD __asm __saveds GetNodeNum(register __a0 struct List *lh,register __a1 struct Node *node)
{
  struct Node *ln;
  UWORD i;

  for (i = 0, ln = lh->lh_Head; ln != node; ln = ln->ln_Succ, i++);
  return i;
}

/****** HBBSCommon.library/HBBS_NodeDataPtr **************************
*
*   NAME
*      HBBS_NodeDataPtr - Request a NodeData structure
*
*   SYNOPSIS
*      HBBS_NodeDataPtr(nodenum)
*                       D0
*
*      struct NodeData *HBBS_NodeDataPtr(short nodenum)
*
*   FUNCTION
*      This function requests the NodeData structure for a given node
*
*   INPUTS
*      nodenum - node number, starting at 0.
*
*   RESULT
*      pointer to a NodeData structure.  Note. the node may be
*      off-line. check NodeData->Status before using any other parts
*      of the structure.  If the node is offline then DO NOT attempt
*      to use any other records of the structure
*
*      (see defines.h/stat_#?)
*
*   NOTES
*      None
*
*   SEE ALSO
*      includes/hbbs/structures.h
*
*********************************************************************/

struct NodeData __asm __saveds  *HBBS_NodeDataPtr(register __d0 short nodenum)
{
  struct NodeData *nd;

  for (nd = (struct NodeData *)BBSGlobal->NodeList->lh_Head ; nd->node.ln_Succ ; nd =(struct NodeData *) nd->node.ln_Succ)
  {
    if (nd->NodeNum==nodenum) return(nd);
  }
  return(NULL);
}

/****** HBBSCommon.library/HBBS_LogError *****************************
*
*   NAME
*      HBBS_LogError - Log an error message
*
*   SYNOPSIS
*      HBBS_LogError(filename, errnum, string, errtype)
*                    A0        D0      A1      D1
*
*      V_ERROR HBBS_LogError(char *filename, V_ERROR errnum,
*          char *errstring, V_ERROR errtype)
*
*   FUNCTION
*      This function write an error message to a logfile.
*
*   INPUTS
*      filename - string containing name of log file.
*      errnum - one of ERR_#? (see defines.h)
*      errstring - the error message to write to a file
*      errtype - one of TYPE_#? (see defines.h)
*
*      e.g. HBBS_LogError( N_ND->NodeSettings.ModemLogFile,
*          ERR_GERERAL, "Error initalising modem", TYPE_INFO);
*   RESULT
*      returns errnum
*
*********************************************************************/


V_ERROR __asm __saveds HBBS_LogError(register __a0 V_STRING filename,register __d0 V_ERROR errnum,register __a1 V_STRING string,register __d1 V_ERROR errtype)
{
  // this function returns the parameter errnum supplied and unmodified.
  BPTR FH;
  V_STRING errstr;
  char *strptr;
  char DateStr[LEN_DATESTR+1];
  char TimeStr[LEN_TIMESTR+1];

  if (filename)
  {
    if (FH=Open(filename,MODE_READWRITE))
    {
      Seek(FH,0,OFFSET_END);
      if (errstr=AllocVec(BIG_STR,MEMF_PUBLIC))
      {

        // add time and date..

        sprintf(errstr,"%s %s %s: ",HBBS_GetDate(DateStr),HBBS_GetTime(TimeStr),TYPE_STR[errtype]);

        // remove '\n's from string.

        while (strptr=strchr(errstr,'\n'))
        {
          errstr[strptr-errstr]=' ';
        }

        Write(FH,errstr,strlen(errstr)); // don't care if it fails.. (could be diskfull :-)

        switch(errnum)
        {
          case ERR_ERROR_OPENING:
            sprintf(errstr,"Error opening the file -> %s",string);
            break;
          case ERR_ERROR_READING:
            sprintf(errstr,"Error reading the file -> %s",string);
            break;
          case ERR_ERROR_WRITING:
            sprintf(errstr,"Error writing the file -> %s",string);
            break;
          case ERR_NODESETTINGS:
            strcpy(errstr,"NodeLocal File Error");
            break;
          case ERR_GENERAL:
          case ERR_INFO:
            strcpy(errstr,string);
            break;
          case ERR_DOORTIMEOUT:
            sprintf(errstr,"Timout Occured When Loading Door -> %s",string);
            break;
          default:
            sprintf(errstr,"Unknown Error!");
            break;
        }

        // remove '\n's from string.

        while (strptr=strchr(errstr,'\n'))
        {
          errstr[strptr-errstr]=' ';
        }

        Write(FH,errstr,strlen(errstr));

        Write(FH,"\n",1);

        FreeVec(errstr);
      }
      Close(FH);
    }
  }
  return(errnum);
}


struct CfgFileData *LoadConfig(UBYTE *filename,ULONG Flags,UBYTE *patternstr)
{
  BPTR FH;

  V_BOOL Done,keepthis;
  LONG found=0;
  V_STRING buffer;
  V_STRING ItemName;
  V_STRING Params;
  struct CfgItemData *node;
  struct CfgFileData *cfgfile=NULL;
  UBYTE infostr[BIG_STR+1];
  char match[BIG_STR + BIG_STR + 3]; // for pattern matching

  if (BBSGlobal->LogConfig)
  {
    strcpy(infostr,"Searching for: ");
    strcat(infostr,filename);
    HBBS_LogError(BBSGlobal->ConfigLogFile,ERR_INFO,infostr,TYPE_INFO);
    if(patternstr)
    {
      sprintf(infostr,"NOTE: Only items matching %s will be scanned!",patternstr);
    }
    HBBS_LogError(BBSGlobal->ConfigLogFile,ERR_INFO,infostr,TYPE_INFO);
  }

  if (FH=Open(filename,MODE_OLDFILE))
  {
    if (BBSGlobal->LogConfig) HBBS_LogError(BBSGlobal->ConfigLogFile,ERR_INFO,"File Opened OK",TYPE_INFO);
    if (cfgfile=AllocVec(sizeof(struct CfgFileData),MEMF_PUBLIC|MEMF_CLEAR))
    {
      if (cfgfile->ItemsList=HBBS_CreateList())
      {
        buffer=AllocVec(BIG_STR,MEMF_PUBLIC);
        ItemName=AllocVec(BIG_STR,MEMF_PUBLIC);
        Params=AllocVec(BIG_STR,MEMF_PUBLIC);
        if (buffer && ItemName && Params)
        {
          Done=FALSE;
          while (!Done)
          {
            if (!(FGets(FH,buffer,BIG_STR)))
            {
              Done=TRUE; // EOF
            }
            else
            {
              stripcr(buffer);
              keepthis=TRUE;

              if (patternstr) // are we matching patterns ?
              {
                keepthis=FALSE;
                ParsePatternNoCase(patternstr,match,BIG_STR+BIG_STR+2);

                // if the match fails skip this line of text..

                if (MatchPatternNoCase(match,buffer))
                {
                  keepthis=TRUE;
                }
              }

              if (keepthis)
              {
                ItemName[0]=0; // null terminate both strings..
                Params[0]=0;

                if (!(Flags & LCFG_NOSTRIPCOMMENTS)) StripComments(buffer);
                if (!(Flags & LCFG_NOSTRIPSPACES))   StripSpaces(buffer);

                if (GetItem(ItemName,buffer))
                {
                  if (GetParams(Params,buffer))
                  {
                    if (node=AllocVec(sizeof(struct CfgItemData),MEMF_PUBLIC))
                    {
                      if (node->node.ln_Name=DupStr(ItemName))
                      {
                        if (node->Params=DupStr(Params))
                        {
                          AddTail(cfgfile->ItemsList,(struct Node*)node);
                          found++;
                          if (BBSGlobal->LogConfig)
                          {
                            sprintf(infostr,"Config Item: %s=%s",ItemName,Params);
                            HBBS_LogError(BBSGlobal->ConfigLogFile,ERR_INFO,infostr,TYPE_INFO);
                          }
                        }
                        else
                        {
                          FreeVec(node->node.ln_Name);
                        }
                      }
                    }
                  }
                }
              }
            }
          }
          if (Params)     FreeVec(Params);
          if (ItemName)   FreeVec(ItemName);
          if (buffer)     FreeVec(buffer);
        }
      }
      if (found)
      {
        cfgfile->filename=DupStr(filename);
      }
      else
      {
        if (BBSGlobal->LogConfig) HBBS_LogError(BBSGlobal->ConfigLogFile,ERR_INFO,"No Items Found in file",TYPE_INFO);
        HBBS_FlushConfig(cfgfile);
        cfgfile=NULL; // just to make sure..
      }
    }
    Close(FH);
    if (BBSGlobal->LogConfig) HBBS_LogError(BBSGlobal->ConfigLogFile,ERR_INFO,"File Closed OK",TYPE_INFO);
  }
  else
  {
    if (BBSGlobal->LogConfig) HBBS_LogError(BBSGlobal->ConfigLogFile,ERR_INFO,"File not found",TYPE_INFO);
  }
  return(cfgfile);
}

/****** HBBSCommon.library/HBBS_LoadConfig ***************************
*
*   NAME
*      HBBS_LoadConfig - Load a config file
*
*   SYNOPSIS
*      HBBS_LoadConfig( filename, Flags)
*                       A0        D0
*
*      struct CfgFileData *HBBS_LoadConfig(UBYTE *filename,
*          ULONG Flags)
*
*   FUNCTION
*      This function loads the items in a config file to memory.
*
*   INPUTS
*      filename - name of config file to load, e.g. progdir:mydoor.cfg
*      flags - one of LCFG_#? (see defines.h) normally LCFG_NONE
*
*   RESULT
*      NULL if config file could not be loaded.
*      otherwise a pointer to a struct ConfigFileData
*
*   NOTES
*      Must be paired with a HBBS_FlushConfig() if the loading of the
*      config suceeded.
*
*      See the section on config files in the main HBBS docs for
*      config file format.
*
*      In short, each item on seperate line, ;'s denote comments.
*      items take the form of Item=Paramater.
*      items must start at beginning of each new line, blank lines
*      allowed.
*
*   SEE ALSO
*      HBBS_FlushConfig(), HBBS_GetSetting(), HBBS_LoadConfigQuick()
*      HBBS_SaveConfig(), HBBS_CreateConfig(), HBBS_RemoveCfgItem(),
*      HBBS_AddCfgItem(), HBBS_AddCfgList()
*
*********************************************************************/

struct CfgFileData __asm __saveds *HBBS_LoadConfig(register __a0 UBYTE *filename,register __d0 ULONG Flags)
{
  return(LoadConfig(filename,Flags,NULL));
}

/****** HBBSCommon.library/HBBS_LoadConfigQuick **********************
*
*   NAME
*      HBBS_LoadConfigQuick - Load a config file, with item pattern
*          matching.
*
*   SYNOPSIS
*      HBBS_LoadConfigQuick( filename, Flags, PatternString)
*                            A0        D0     A1
*
*      struct CfgFileData *HBBS_LoadConfigQuick(UBYTE *filename,
*          ULONG Flags, UBYTE *PatternString)
*
*   FUNCTION
*      This function loads the items in a config file to memory,
*      it is the same as HBBS_LoadConfig() and should be treated the
*      same way, except that it only loads items that match the
*      amigados pattern specified.
*
*   INPUTS
*      filename - name of config file to load, e.g. progdir:mydoor.cfg
*      flags - one of LCFG_#? (see defines.h) normally LCFG_NONE
*
*   RESULT
*      NULL if config file could not be loaded.
*      otherwise a pointer to a struct ConfigFileData
*      PatternStr - normal amigados pattern. e.g. "FR_#?"
*
*   NOTES
*      This is usefull if you only want one ore two items from a
*      config file.  For instance, when HBBS is searching command
*      config files for door information it uses "<doorname>#?" as a
*      pattern.  e.g.  CheckFiles#?
*
*      This is great for speeding HBBS and your doors up.
*
*   SEE ALSO
*      HBBS_LoadConfig(), HBBS_FlushConfig(), HBBS_GetSetting()
*      HBBS_SaveConfig(), HBBS_CreateConfig(), HBBS_RemoveCfgItem(),
*      HBBS_AddCfgItem(), HBBS_AddCfgList()
*
*********************************************************************/

struct CfgFileData __asm __saveds *HBBS_LoadConfigQuick(register __a0 UBYTE *filename,register __d0 ULONG Flags,register __a1 UBYTE *PatternStr)
{
  return(LoadConfig(filename,Flags,PatternStr));
}

/****** HBBSCommon.library/HBBS_FlushConfig **************************
*
*   NAME
*      HBBS_FlushConfig - Flushes a config and it's items from memory.
*
*   SYNOPSIS
*      HBBS_FlushConfig(cfgfile)
*                       A0
*
*      void HBBS_FlushConfig(struct CfgFileData *cfgfile)
*
*   FUNCTION
*      This function flushes a config and it's items from memory.
*
*   INPUTS
*      cfgfile - pointer to a config file created or loaded using
*      HBBS_CreateConfig() or HBBS_LoadConfig()
*
*   RESULT
*      None
*
*   SEE ALSO
*      HBBS_CreateConfig(), HBBS_LoadConfig()
*
*********************************************************************/

void  __asm __saveds  HBBS_FlushConfig(register __a0 struct CfgFileData *cfgfile)
{
  struct CfgItemData *node;
  if (cfgfile)
  {
    if (cfgfile->ItemsList)
    {
      while ((node = (struct CfgItemData *)cfgfile->ItemsList->lh_Head) && (node->node.ln_Succ))
      {
        FreeStr(node->node.ln_Name);
        FreeStr(node->Params);
        Remove((struct Node *)node);
        FreeVec(node);
      }
      FreeVec(cfgfile->ItemsList);
    }
    FreeStr(cfgfile->filename);
    FreeVec(cfgfile);
  }
}

/****** HBBSCommon.library/HBBS_SaveConfig **************************
*
*   NAME
*      HBBS_SaveConfig - Saves a config file
*
*   SYNOPSIS
*      HBBS_SaveConfig(cfgfile)
*                       A0
*
*      BOOL HBBS_SaveConfig(struct CfgFileData *cfgfile)
*
*   FUNCTION
*      This function saves a config file to disk.
*
*      The filename used is the same as the one passed to
*      HBBS_LoadConfig() or HBBS_CreateConfig().
*
*   INPUTS
*      cfgfile - pointer to a config file created or loaded using
*      HBBS_CreateConfig() or HBBS_LoadConfig()
*
*   RESULT
*      TRUE if save suceeded.
*
*   SEE ALSO
*      HBBS_CreateConfig(), HBBS_LoadConfig()
*
*********************************************************************/

V_BOOL __asm __saveds HBBS_SaveConfig(register __a0 struct CfgFileData *cfgfile)
{

  // returns TRUE on successful completion.

  BPTR FH;
  struct CfgItemData *node;
  char outstr[BIG_STR];

  if (BBSGlobal->LogConfig)
  {
    strcpy(outstr,"Attempting to save config file: ");
    strcat(outstr,cfgfile->filename);
    HBBS_LogError(BBSGlobal->ConfigLogFile,ERR_INFO,outstr,TYPE_INFO);
  }

  if (AssignOK(cfgfile->filename))
  {
    if (FH=Open(cfgfile->filename,MODE_NEWFILE))
    {
      for (node = (struct CfgItemData*)cfgfile->ItemsList->lh_Head ; node->node.ln_Succ ; node =(struct CfgItemData *)node->node.ln_Succ)
      {
        sprintf(outstr,"%s=%s\n",node->node.ln_Name,node->Params);
        Write(FH,outstr,strlen(outstr));

        if (BBSGlobal->LogConfig)
        {
          sprintf(outstr,"Config item: %s=%s",node->node.ln_Name,node->Params);
          HBBS_LogError(BBSGlobal->ConfigLogFile,ERR_INFO,outstr,TYPE_INFO);
        }

      }
      Close(FH);
      return(TRUE);
    }
  }

  if (BBSGlobal->LogConfig)
  {
    strcpy(outstr,"Could not save config file: ");
    strcat(outstr,cfgfile->filename);
    HBBS_LogError(BBSGlobal->ConfigLogFile,ERR_INFO,outstr,TYPE_INFO);
  }

  return(FALSE);
}

struct CfgItemData __asm __saveds *HBBS_GetCfgNode(register __a0 struct CfgFileData *cfgfile,register __a1 V_STRING optionstr)
{

  struct CfgItemData *Item;

  for (Item = (struct CfgItemData *)cfgfile->ItemsList->lh_Head ; Item->node.ln_Succ ; Item =(struct CfgItemData *) Item->node.ln_Succ)
  {
    if (stricmp(optionstr,Item->node.ln_Name)==0)
    {
      return(Item);
    }
  }
  return(NULL);
}

/****** HBBSCommon.library/HBBS_GetSetting ***************************
*
*   NAME
*      HBBS_GetSetting - Get a setting from a config file
*
*   SYNOPSIS
*      HBBS_GetSetting(cfgfile, result, optiontype, optionstr, multi)
*                      A0       A1      D0          A2         D1
*
*      V_SMALLNUM HBBS_GetSetting(struct CfgFileData *cfgfile,
*          void **result, V_FLAGS optiontype, V_STRING optionstr,
*          V_BOOL multi)
*
*   FUNCTION
*      This function gets a config item from a config and stores the
*      paramater of the item in "result"
*
*   INPUTS
*      cfgfile - pointer to a config file created or loaded using
*          HBBS_CreateConfig() or HBBS_LoadConfig()
*      result - pointer to store results in, depends on type of
*          variable being requested.
*      optiontype - one of TYPE_#? (see includes/HBBS/types.h)
*      optionstr - the option name as it would appear in the config
*          file.  (see notes below)
*      multi - either OPT_MULTI or OPT_SINGLE.  (see notes below)
*
*
*      Example:
*
*      Say you had this config file:
*
*      A_Number=5
*      A_Path=HBBS:Apples
*      A_Boolean=TRUE
*      Some_Paths_1=HBBS:Fruit/Fresh
*      Some_Paths_2=HBBS:Fruit/Mouldy/
*      Some_Paths_3=HBBS:Fruit/Crisp
*      Some_Paths_4=HBBS:Fruit/Stale/
*
*      You would read it like this:
*
*      V_BIGNUM A_Number = 0;
*      char *A_Path = NULL;
*      struct List *Some_Paths = NULL;
*      BOOL A_Boolean = TRUE;
*      struct CfgFileData *cfg;
*
*      A_Path=StrDup("HBBS:Bananas/");
*
*      if (cfg=HBBS_LoadConfig("progdir:myconfig.cfg",LCFG_NONE))
*      {
*        HBBS_GetSetting(Cfg,(void *)&A_Number,VTYPE_BIGNUM,"A_Number",
*            OPT_SINGLE);
*        HBBS_GetSetting(Cfg,(void *)&A_Path,VTYPE_PATH,
*            "A_Path",OPT_SINGLE);
*        HBBS_GetSetting(Cfg,(void *)&Some_Paths,VTYPE_PATHLIST,
*            "Some_Paths",OPT_MULTI);
*        HBBS_GetSetting(Cfg,(void *)&A_Boolean,VTYPE_BOOL,"A_Boolean",
*            OPT_SINGLE);
*
*        HBBS_FlushConfig(cfg);
*      }
*
*      FreeStrList(Some_Paths)
*      FreeStr(A_Path);
*
*      Note that the paths will all have trailing '/'s added to them
*      so that you'd get:
*
*      A_Path=HBBS:Apples;/
*      Some_Paths_1=HBBS:Fruit/Fresh/
*      Some_Paths_2=HBBS:Fruit/Mouldy/
*      Some_Paths_3=HBBS:Fruit/Crisp/
*      Some_Paths_4=HBBS:Fruit/Stale/
*
*      Note how a default is applied for some items, and how the
*      memory is free'd by FreeStr() and FreeStrList()
*
*      Strings and stringlists are treaded the same as paths,
*      except that no trailing '/' will be added to the string.
*
*   RESULT
*      amount of items read from config file, 0 or 1 if multi is
*      OPT_SINGLE, or 0 or >0 if multi is OPT_MULTI
*
*   NOTES
*      if OPT_MULTI is used then the optionstr should not contain the
*      "_XX". i.e. if you want to get Item_1=blah and Item_2=more blah
*      then just set optiontype to "Item".
*
*      Currently the only two variable types that support OPT_MULTI
*      are TYPE_STRINGLIST or TYPE_PATHLIST.  Which are defined as
*      struct List *.  The ln_Name field of each node will contain
*      the strings or paths.
*
*      See HBBS:Developer/Examples/ConfigFiles/Configs.c for an
*      compilable example.
*
*   SEE ALSO
*      HBBS_LoadConfig(), HBBS_FlushConfig(), FreeStr(), FreeStrList()
*
*********************************************************************/

V_SMALLNUM __asm __saveds  HBBS_GetSetting(register __a0 struct CfgFileData *cfgfile,register __a1 void **result,register __d0 V_FLAGS optiontype,register __a2 V_STRING optionstr,register __d1 V_BOOL multi)
{
  // returns the amount of items found (1 for non multi, >0 for multi, 0 for none)

  V_SMALLNUM found=0;
  V_BOOL Done=FALSE;
  V_STRING CompareStr;
  struct Node *node;
  struct CfgItemData *Item;
  char outstr[BIG_STR];

  // allocate a big string for comparing the strings..
  if (CompareStr=AllocVec(BIG_STR,MEMF_PUBLIC))
  {
    // parse list..
    for (Item = (struct CfgItemData *)cfgfile->ItemsList->lh_Head ; Item->node.ln_Succ && !Done ; Item =(struct CfgItemData *) Item->node.ln_Succ)
    {
      if (multi==OPT_MULTI)
      {
        sprintf(CompareStr,"%s_%d",optionstr,found+1);
      }

      if ((multi==OPT_SINGLE) && (stricmp(optionstr,Item->node.ln_Name)==0) ||
         (multi==OPT_MULTI) && (stricmp(CompareStr,Item->node.ln_Name)==0))
      {
        switch(optiontype)
        {
          case VTYPE_STRING:
          case VTYPE_DATE:
            if (*result!=NULL) FreeStr(*result);
            *result=(V_STRING *)DupStr(Item->Params);
            found++;
            break;
          case VTYPE_PATH:
            if (*result!=NULL) FreeStr(*result);
            if (*result=(V_STRING)AllocVec(strlen(Item->Params)+1,MEMF_CLEAR|MEMF_PUBLIC))
            {
              strcpy((V_STRING)*result,Item->Params);
              if ((Item->Params[strlen(Item->Params)-1]!='/') && (Item->Params[strlen(Item->Params)-1]!=':'))
              {
                strcat((V_STRING)*result,"/");
              }
              found++;

            }
            break;
          case VTYPE_BOOL:
            *result=(V_BOOL *)CheckBoolean(Item->Params);
            found++;
            break;
          case VTYPE_HUGENUM:

            // *C* currently this option is causing a linker error! argh...

            *result=(V_HUGENUM *)atof(Item->Params);
            found++;
            break;
          case VTYPE_BIGNUM:
          case VTYPE_TIME:
            *result=(V_BIGNUM *)atoi(Item->Params);
            found++;
            break;
          case VTYPE_WORD:
            *result=(WORD *)atoi(Item->Params);
            found++;
            break;
          case VTYPE_SMALLNUM:
            *result=(V_SMALLNUM *)atoi(Item->Params);
            found++;
            break;
          case VTYPE_STRINGLIST:
          case VTYPE_PATHLIST:
            // free existing stringlist if present

            if ((found==0) && (*result))
            {
              FreeStrList((V_STRINGLIST)*result);
            }
            if (!(*result))
            {
              if(*result=AllocVec(sizeof (struct List),MEMF_PUBLIC))
              {
                NewList((struct List*)*result);
              }
            }
            if (*result) // allocated new list ok ?
            {
              if (node=AllocVec(sizeof(struct Node),MEMF_PUBLIC))
              {
                if (optiontype==VTYPE_PATHLIST)
                {
                  if (node->ln_Name=AllocVec(strlen(Item->Params)+2,MEMF_CLEAR|MEMF_PUBLIC)) // +1 for terminator +1 for added /
                  {
                    strcpy(node->ln_Name,Item->Params);
                    if ((node->ln_Name[strlen(node->ln_Name)-1]!=':') && (node->ln_Name[strlen(node->ln_Name)-1]!='/'))
                    {
                      strcat(node->ln_Name,"/");
                    }
                  }
                }
                else
                {
                  node->ln_Name=DupStr(Item->Params);
                }

                if (!(node->ln_Name))
                {
                  FreeVec(node);
                }
                else
                {
                  AddTail((struct List *)*result,node);

                  found++;

                  // go to start of list again or we might miss out something..
                  Item = (struct CfgItemData *)cfgfile->ItemsList->lh_Head;
                }

              }
            }
        }
      }
      if (multi==OPT_SINGLE && found) Done=TRUE; // speedup fix
    }
    FreeVec(CompareStr);
  }

  if (BBSGlobal->LogConfig)
  {
    sprintf(outstr,"Requesting item \"%s\" (%s), file: \"%s\". %d matches",optionstr,typestr[optiontype-1],cfgfile->filename,found); //-1 because array index starts at 0
    HBBS_LogError(BBSGlobal->ConfigLogFile,ERR_INFO,outstr,TYPE_INFO);
  }

  return(found);
}

/****** HBBSCommon.library/HBBS_CreateConfig *************************
*
*   NAME
*      HBBS_CreateConfig - Create a config file (in memory)
*
*   SYNOPSIS
*      HBBS_CreateConfig( filename )
*                         A0
*
*      struct CfgFileData *HBBS_CreateConfig( UBYTE *filename )
*
*   FUNCTION
*      This function creates a config file in memory, that you can add
*      config items to, before saving it to disk using the function
*      HBBS_SaveConfig().
*
*   INPUTS
*      filename - filename of config file (only used when it's saved)
*
*   RESULT
*      NULL or a pointer to a struct CfgFileData that you should pass
*      to other config file related functions.
*
*   NOTES
*      Calling this does not write the config file to disk. it just
*      allocates a new config file structure in memory.  Save the
*      config file using HBBS_SaveConfig(), add items using
*      HBBS_AddCfgItem(), free it using HBBS_FlushConfig().
*
*   SEE ALSO
*      HBBS_AddCfgItem(), HBBS_AddCfgList(), HBBS_SaveConfig(),
*      HBBS_FlushConfig()
*
*********************************************************************/

struct CfgFileData __asm __saveds *HBBS_CreateConfig(register __a0 UBYTE *filename)
{
  struct CfgFileData *newcfg;

  if (newcfg=(struct CfgFileData *)AllocVec(sizeof(struct CfgFileData),MEMF_PUBLIC|MEMF_CLEAR))
  {
    if (newcfg->ItemsList=HBBS_CreateList())
    {
      if (newcfg->filename=DupStr(filename))
      {
        return(newcfg);
      }
      FreeVec(newcfg->ItemsList);
    }
    FreeVec(newcfg);
  }
  return(NULL);
}

/****** HBBSCommon.library/HBBS_RemoveCfgItem ************************
*
*   NAME
*      HBBS_RemoveCfgItem - Remove an item from a config file
*
*   SYNOPSIS
*      HBBS_RemoveCfgItem( cfgfile, itemname)
*                          A0       A1
*
*      HBBS_RemoveCfgItem( struct CfgFileData *cfgfile,
*         UBYTE *itemname )
*
*   FUNCTION
*      This function remove items from a config file in memory
*
*   INPUTS
*      cfgfile - pointer to a config file created or loaded using
*          HBBS_CreateConfig() or HBBS_LoadConfig()
*      itemname - An item to remove, wildcards are allowed here.
*
*
*   RESULT
*      TRUE if one or more items were removed.
*
*   NOTES
*      This does not affect the config file on the disk
*
*   SEE ALSO
*      HBBS_SaveConfig(), HBBS_LoadConfig(), HBBS_CreateConfig()
*      HBBS_AddCfgItem(), HBBS_AddCfgItemQuick()
*
*********************************************************************/

BOOL __asm __saveds HBBS_RemoveCfgItem(register __a0 struct CfgFileData *cfgfile,register __a1 UBYTE *itemname)
{
  struct CfgItemData *node;
  BOOL Removed=FALSE;
  char *match;

  if (match=AllocVec((strlen(itemname)*2)+2,MEMF_PUBLIC))
  {
    if (cfgfile && cfgfile->ItemsList)
    {
      for (node = (struct CfgItemData*)cfgfile->ItemsList->lh_Head ;(node->node.ln_Succ) ; node =(struct CfgItemData *)node->node.ln_Succ)
      {
        ParsePatternNoCase(itemname,match,(strlen(itemname)*2)+2);
        if (MatchPatternNoCase(match,node->node.ln_Name))
        {
          Remove((struct Node*)node);
          FreeStr(node->Params);
          FreeStr(node->node.ln_Name);
          FreeVec(node);
          // go to start of list again..
          node=node = (struct CfgItemData*)cfgfile->ItemsList->lh_Head;
          Removed=TRUE;
        }
      }
    }
    FreeVec(match);
  }
  return(Removed);
}

/****** HBBSCommon.library/HBBS_AddCfgItem ***************************
*
*   NAME
*      HBBS_AddCfgItem - Add an item to a config file, overwriting old
*          items with the same name.
*
*   SYNOPSIS
*      HBBS_AddCfgItem( cfgfile, itemname, params)
*                       A0       A1        A2
*
*      BOOL HBBS_AddCfgItem(struct CfgFileData *cfgfile,
*          UBYTE *itemname, UBYTE *params)
*
*   FUNCTION
*      This function adds items to a configfile (structure in memory)
*
*   INPUTS
*      cfgfile - pointer to a config file created or loaded using
*          HBBS_CreateConfig() or HBBS_LoadConfig()
*      itemname - An item to add
*      params - parameters for the item. in the format of a null
*          terminated string.
*
*   RESULT
*      TRUE if the item was added.
*
*   NOTES
*      Do not use this function if you are creating a config from
*      scratch (i.e. your cfgfile was created using
*      HBBS_CreateConfig() ), use HBBS_AddCfgItemQuick() which does
*      not check and remove items with the same name first.
*      This function is meant for modifying config files loaded with
*      HBBS_LoadConfig().  HBBS_AddCfgItemQuick() is less CPU
*      intensive and should be used where ever possible.
*
*   SEE ALSO
*      HBBS_AddCfgItemQuick(), HBBS_AddCfgList()
*
*********************************************************************/

V_BOOL __asm __saveds HBBS_AddCfgItem(register __a0 struct CfgFileData *cfgfile,register __a1 UBYTE *itemname,register __a2 UBYTE *params)
{
  if (cfgfile && cfgfile->ItemsList && itemname && params)
  {
    // check to see if itemname is already there..

    HBBS_RemoveCfgItem(cfgfile,itemname);

    // then add the item

    return(HBBS_AddCfgItemQuick(cfgfile,itemname,params));
  }
  return(FALSE);
}

void __asm __saveds HBBS_ResetNodeData(register __a0 struct NodeData *node)
{
  // this function is called a) when a node is initialised
  // and b) when a node shuts down. (everything is freed first tho..

  node->LoginType=LOGIN_NONE;
  node->OnlineStatus=OS_OFFLINE; // node program sets this to ONLINE or OFFLINE
                              // this shot be used in all door programs to determine
                              // carrier status (even for local logins..)
  node->User->Valid=FALSE;
  node->User->ConfAcs.Name=NULL;
  node->NodeFlags=0L;
  node->Status=STAT_CLOSED;
  node->NodePort=NULL;
  node->SettingsOpen=FALSE;
  node->InformationOpen=FALSE;
  node->ConOK=FALSE;
  node->SerOK=FALSE;
  node->SerOPEN=FALSE;
  node->SerWaiting=FALSE;
  node->ConWaiting=FALSE;
  node->ReplyPort=NULL;
  node->RequestShutdown=FALSE;
  node->ActiveDoor=NULL;
  node->DoorsRunning=0;
  node->DoorReturn[0]=0;
  node->CurrentLine[0]=0;
  node->TransferringFile=FALSE;
  node->TaggedFiles=0;
  node->AllowLogins=TRUE;
  node->DoorLogOverride=FALSE;
  node->RunningAwait=FALSE;
  strcpy(node->Action,"Initialising!"); // max 29 chars!
  node->ConnectBaud[0]=0; // max MAX_CPSBAUD_LEN chars..
  node->LastCPS[0]=0; // max MAX_CPSBAUD_LEN chars..

  node->ReservedNode=FALSE;
  node->ReservedHandle[0]=0;

  node->Current_Last_Callers=0;
  node->Current_Last_Uploads=0;
  node->Current_Last_Downloads=0;
  node->Current_Last_PWFails=0;
  node->Current_Last_Carrier=0;
  node->Current_Last_Pagers=0;

  // *C* make sysop definable..
  node->Max_Last_Callers=10;
  node->Max_Last_Uploads=10;
  node->Max_Last_Downloads=10;
  node->Max_Last_PWFails=10;
  node->Max_Last_Carrier=10;
  node->Max_Last_Pagers=10;
}

/****** HBBSCommon.library/HBBS_rterror ******************************
*
*   NAME
*      HBBS_rterror - Display an error, using a requestor
*
*   SYNOPSIS
*      HBBS_rterror( messagestr )
*                    A0
*
*      void HBBS_rterror( char *messagestr )
*
*   FUNCTION
*      This function display an error message using reqtools.
*
*
*   INPUTS
*      messagestr - null-terminated string containing a message
*
*   RESULT
*      None
*
*   NOTES
*      This will halt operation of a program until the sysop
*      clicks the "OK" button on the requester.  So it should be
*      used with care.
*
*********************************************************************/

void __asm __saveds  HBBS_rterror(register __a0 char *str)
{
  rtEZRequestTags(str,str_OK,NULL,NULL,RTGS_Flags, GSREQF_CENTERTEXT,TAG_END);
}

/****** HBBSCommon.library/HBBS_DosCommand ***************************
*
*   NAME
*      HBBS_DosCommand - Run an amigados program or script.
*
*   SYNOPSIS
*      HBBS_DosCommand( command, Flags, outputfilename )
*                       A0       D0     A1
*
*      LONG HBBS_DosCommand( char *command, ULONG Flags,
*          char *outputfilename)
*
*   FUNCTION
*      This function runs a program (script or executable, path is
*      the same as the system path)
*
*   INPUTS
*      command - name of a command to run with paramaters (e.g.
*          "DIR HBBS:")
*      FLAGS - Specifiy one of the following flags:
*
*          RDC_NONE - None specified
*
*          RDC_ASYNC - use this flag if you want to have your
*              program run in the background.
*
*          RDC_DEFAULTOUTPUT - HBBS will not attempt to open a new
*              console window for the output of your program.
*
*      outputfilename - filename of a file (will be overwritten) to
*          send your command's output to, the file will be opened
*          and closed by HBBS, do NOT use RDC_ASYNC or
*          RDC_DEFAULTOUTPUT if you specify this.
*          Set this to NULL if you don't want a file opened for you.
*
*          remember to remove any output files that are created for you
*          if you do not want them.
*
*   RESULT
*      Returns the same as SystemTagList()
*
*   NOTES
*      stacksize if 16384 by default (or HBBS's configured default
*      stacksize for external programs)
*
*      if your command string contains @S@ it is replaced with the
*      public screen name that the control gui is open on.
*
*   SEE ALSO
*      None
*
*********************************************************************/

LONG __asm __saveds HBBS_DosCommand(register __a0 char *command,register __d0 ULONG Flags,register __a1 char *outputfilename)
{
  LONG retval;
  char newcmd[BIG_STR],consolename[BIG_STR];
  char *str_Workbench="Workbench";

  ULONG sysargs[] =
  {
    SYS_Input,NULL,
    SYS_Output,NULL,
    SYS_Asynch,FALSE,
    SYS_UserShell,TRUE,
    NP_Priority,0L,     // *C* make configurable
    NP_StackSize,16384, // *C* make configurable
    TAG_DONE
  };
  BPTR outputfile=NULL;

  sysargs[5]=(Flags & RDC_ASYNC);

  //*C* document @S@ replacement

  if (BBSGlobal)  // fixes enforcer hits if user tries to load Control more than once in quick succession
  {
    replace(newcmd,command,"@S@",BBSGlobal->ScreenInfo->PubScreenName);
    replace(consolename,str_CONSOLE,"@S@",BBSGlobal->ScreenInfo->PubScreenName);
  }
  else
  {
    replace(newcmd,command,"@S@",str_Workbench);
    replace(consolename,str_CONSOLE,"@S@",str_Workbench);
  }

  if (!(Flags & RDC_DEFAULTOUTPUT))
  {
    if (outputfilename)
    {
      if (outputfile = Open(outputfilename,MODE_NEWFILE))
      {
        sysargs[3]=(ULONG)outputfile;  // set SYS_Output
      }
    }
    else
    {
      if (outputfile = Open(consolename,MODE_OLDFILE))
      {
        sysargs[1]=(ULONG)outputfile;  // set SYS_Input
      }
    }
  }

  retval=SystemTagList(newcmd,(struct TagItem *)sysargs);

  if ( (!(Flags & RDC_ASYNC)) && (outputfile) ) Close(outputfile); // close window if not async

  return(retval);

  // *C* perhaps add a routine to track and close async'ed windows after 5 mins ??
}

LONG __asm __saveds HBBS_RunDOSCMD(register __a0 char *command,register __d0 BOOL ASYNC)
{
  return(HBBS_DosCommand(command,ASYNC ? RDC_ASYNC : RDC_NONE,NULL));
}

/****** HBBSCommon.library/HBBS_DoErrorMessage ***********************
*
*   NAME
*      HBBS_DoErrorMessage - Display an error message via an external
*          program.
*
*   SYNOPSIS
*      HBBS_DoErrorMessage( errnum, NodeNum, errstr )
*                           D0      D1       A1
*
*      void HBBS_DoErrorMessage( ULONG errnum, ULONG NodeNum,
*          char *errstr )
*
*   FUNCTION
*      This function spawns the external error message program.
*      i.e. display an error message and continue without waiting
*      for the sysop to click ok.
*
*      The message must be defined in:
*      HBBS:Doors/System/ErrorMessage/ErrorMessage.txt
*      and a #define value must be created in errors.h.
*
*      Thus this function is not really meant for gereral use.
*      But if you need to display one of the pre-defined errors
*      then you can.
*
*   INPUTS
*      errnum - error number, see errors.h
*      NodeNum - current node number
*      errstr - if the message string defined in the ErrorMessage.txt
*         file contains @E@ it will be replaced with this string.
*
*   RESULT
*      None
*
*********************************************************************/

void __asm __saveds HBBS_DoErrorMessage(register __d0 ULONG num,register __d1 ULONG node,register __a0 char *errstr)
{
  char *str;
  int len=20; // 20 extra chars should be ok for the numbers

  if (errstr) len+=strlen(errstr)+1;

  if (str=AllocVec(len+strlen(PRG_ERRORMESSAGE),MEMF_PUBLIC))
  {
    sprintf(str,"%s %d %d%s",PRG_ERRORMESSAGE,num,node,errstr ? " " : ""); // adds and extra space to seperate parameters if needed
    if (errstr) strcat(str,errstr);
    HBBS_RunDOSCMD(str,TRUE);
    FreeVec(str);
  }
}

void __asm __saveds HBBS_Reserved_2(register __a0  char *str) // *C* remove this function at next re-build
{
  UpperCase(str); // *C* only for now!
}

/****** HBBSCommon.library/AssignOK **********************************
*
*   NAME
*      AssignOK - Checks the assign/volume/device in the path is valid
*
*   SYNOPSIS
*      AssignOK( path )
*                A0
*
*      BOOL AssignOK(char *path )
*
*   FUNCTION
*      This function will check the assign, volume or device in the
*      path specified.
*
*   INPUTS
*      path - null terminated string with a ':' in it somewhere.
*
*   RESULT
*      TRUE if the assign is valid.
*
*   NOTES
*      When using this function, try to make sure that you use volume
*      names rather than device names, as DF0: may be valid, but there
*      may not be a disk present, also, if you load any paths from
*      config files then make sure your documentation reflects this.
*
*      This function should be called before you open a file or lock
*      a directory, this is because BBS systems have to be designed
*      for un-attended operation, the sysop doesn't want to have to
*      come home to find a "please insert volume xxx:" requester!
*      So use it often!
*
*      This function will also cope with "progdir:" which is not
*      normally in the dos device list listed
*
*   SEE ALSO
*      PathOK()
*
*********************************************************************/

BOOL __asm __saveds AssignOK(register __a0 char *checkme )
{
  struct DosList      *DosList;
  struct DeviceNode   *DevNode;
  UBYTE           *Pointer;
  char string[32]="",cmpstring[32]="";
  int loop;
  BOOL found=FALSE;

  // progdir: always exists, so ignore and return true.

  if (strNIcmp(checkme,"Progdir:",8)==0)
  {
    found=TRUE;
  }
  else
  {
    // set loop to the first : or / or null terminator in the string
    for (loop = 0 ; checkme[loop]!=0 && checkme[loop]!=':' && checkme[loop]!='/' ; loop++);

    // copy the first part to the cmpstring and convert to uppercase
    strNcpy(cmpstring,checkme,loop);
    UpperCase(cmpstring);

    // check each node of the DosList to see if it matches the cmdstring
    DosList = LockDosList(LDF_ALL|LDF_READ);
    while((DosList = NextDosEntry(DosList,LDF_ALL|LDF_READ)) && !found)
    {
      DevNode = (struct DeviceNode *)DosList;

      // assign or volume ?
      if (DevNode->dn_Type==DLT_DIRECTORY || DevNode->dn_Type==DLT_DEVICE || DevNode->dn_Type==DLT_VOLUME)
      {
        Pointer = (UBYTE *)BADDR(DevNode -> dn_Name);
        if (Pointer[0])
        {
          for(loop = 0 ; loop < Pointer[0] ; loop++)
                            string[loop] = Pointer[loop + 1];

          string[Pointer[0]]   = 0; /* terminate string */
          UpperCase(string);

          if (strcmp(cmpstring,string)==0) found=TRUE;
        }
      }
    }
    UnLockDosList(LDF_ALL|LDF_READ);
  }
  return(found);
}

/****** HBBSCommon.library/HBBS_InitCommon ***************************
*
*   NAME
*      HBBS_InitCommon - Library Initaliser
*
*   SYNOPSIS
*      HBBS_InitCommon( void )
*
*      BOOL HBBS_InitCommon( void )
*
*   FUNCTION
*      This function initialises the library, and it should be
*      called immediately after opening the HBBSCommon.library
*      with dos.library/OpenLibrary()
*
*      This function can only be caled when HBBS's control
*      program is running. (no nodes need be started however)
*
*   INPUTS
*      None
*
*   RESULT
*      TRUE if successful.
*
*   NOTES
*      Do not call any other library functions unless this function
*      returned TRUE.
*
*      Must be paired with HBBS_CleanUpCommon()
*
*   SEE ALSO
*      HBBS_CleanUpCommon()
*
*********************************************************************/

BOOL __asm __saveds HBBS_InitCommon( void )
{
  SysBase = *(struct ExecBase **) 4L;
  if (DOSBase = (struct DosLibrary *) OpenLibrary ("dos.library", 0))
  {
    if (ReqToolsBase = (struct ReqToolsBase *) OpenLibrary (REQTOOLSNAME, REQTOOLSVERSION))
    {
      if (UtilityBase = (struct Library *)OpenLibrary("utility.library",33))
      {
//        if (BattClockBase= OpenResource(BATTCLOCKNAME))
//        {
          return(TRUE);
//        }
      }
    }
  }
  return(FALSE);
}

/****** HBBSCommon.library/HBBS_CleanUpCommon ************************
*
*   NAME
*      HBBS_CleanUpCommon - Library De-initaliser
*
*   SYNOPSIS
*      HBBS_CleanUpCommon( void )
*
*      void HBBS_CleanUpCommon( void )
*
*   FUNCTION
*      This function does all the cleanup work for the library.
*      It should be called just before you use the
*      dos.library/CloseLibrary() function.
*
*   INPUTS
*      None
*
*   RESULT
*      None
*
*   NOTES
*      Do not call any other library functions unless this function
*      returned TRUE.
*
*      Must be paired with HBBS_InitCommon() and must only be
*      called if HBBS_InitCommon() succeded
*
*   SEE ALSO
*      HBBS_InitCommon()
*
*********************************************************************/

void __asm __saveds HBBS_CleanUpCommon( void )
{
  if (UtilityBase) CloseLibrary(UtilityBase);
  if (ReqToolsBase) CloseLibrary ((struct Library *)ReqToolsBase);
  if (DOSBase) CloseLibrary ((struct Library *) DOSBase);
}

/****** HBBSCommon.library/SubmitTimer *******************************
*
*   NAME
*      SubmitTimer - Submit a timer request
*
*   SYNOPSIS
*      SubmitTimer( TSD, Seconds, MicroSeconds )
*                   A0   D0       D1
*
*      struct TimerData *SubmitTimer(struct TimerSetupData *TSD,
*          ULONG Seconds, ULONG MicroSeconds )
*
*   FUNCTION
*      This function submits a timer, you can wait() for completion
*      using the message port TimerSetupData->TimerPort or use
*      CheckTimer().
*
*      You can submit as many timers at the same time as you like
*
*   INPUTS
*      TSD - a pointer to a struct TimerSetupData allocated with
*          InitTimer()
*      seconds - seconds to wait
*      microseconds - microseconds to wait
*
*   RESULT
*      A struct TimerData which should be freed using AbortTimer()
*      or NULL if there was a problem.
*
*   SEE ALSO
*      InitTimer(), CleanupTimer(), CheckTimer(), AbortTimer()
*
*********************************************************************/

struct TimerData __asm __saveds *SubmitTimer(register __a0 struct TimerSetupData *TSD, register __d0 ULONG Seconds,register __d1 ULONG MicroSeconds )
{
  struct TimerData *TD=NULL;

  if (TSD && (Seconds || MicroSeconds))
  {
    if (TD=(struct TimerData*)AllocVec(sizeof(struct TimerData),MEMF_PUBLIC))
    {
      // copy fields accross from our blank timer IO request
      // that was initialised by InitTimer();

      //      Source             Dest   Size
      CopyMem(TSD->BlankTimerIO,&TD->TR,sizeof(struct timerequest));

      TD->TR.tr_time.tv_secs   = Seconds;
      TD->TR.tr_time.tv_micro   = MicroSeconds;
      AddHead(TSD->TimerList,(struct Node*)TD);

      SendIO((struct IORequest *)&TD->TR);
    }
  }
  return(TD);
}

/****** HBBSCommon.library/AbortTimer ********************************
*
*   NAME
*      AbortTimer - Abort a timer
*
*   SYNOPSIS
*      AbortTimer( TSD, CTD )
*                  A0   A1
*
*      void AbortTimer( struct TimerSetupData *TSD,
*          struct TimerData *CTD )
*
*   FUNCTION
*      This function aborts a timer and frees the memory allocated by
*      SubmitTimer()
*
*   INPUTS
*      TSD - a pointer to a struct TimerSetupData allocated with
*          InitTimer()
*      CTD - a pointer to a struct TimerData allocated by
*          SubmitTimer()
*
*   RESULT
*      None
*
*   NOTES
*      Do not use the CTD input again, preferably, set it to NULL
*      yourself, after calling this routine.
*
*   SEE ALSO
*      SubmitTimer(), CheckTimer(), InitTimer()
*
*********************************************************************/

void __asm __saveds AbortTimer(register __a0 struct TimerSetupData *TSD, register __a1 struct TimerData *CTD)
{
  struct TimerData *TD;

  if (CTD)
  {
    for (TD = (struct TimerData *)TSD->TimerList->lh_Head ; TD->node.ln_Succ ; TD =(struct TimerData *) TD->node.ln_Succ)
    {
      if (TD==CTD)
      {
        AbortIO((struct IORequest *)&CTD->TR);
        WaitIO((struct IORequest *)&CTD->TR);
        Remove((struct Node*)CTD);
        FreeVec(CTD);
        // TD is now null so we dont wat to reference it again so return from the func..
        return;
      }
    }
  }
}

/****** HBBSCommon.library/CheckTimer ********************************
*
*   NAME
*      CheckTimer - Check a timer
*
*   SYNOPSIS
*      CheckTimer( TSD, CTD )
*                  A0   A1
*
*      BOOL CheckTimer( struct TimerSetupData *TSD,
*          struct TimerData *CTD )
*
*   FUNCTION
*      This function checks a timer that was submitted by the
*      SubmitTimer() function.  If the time has passed then this
*      a call to this function will free the memory allocated by
*      SubmitTimer().  See the notes below.
*
*   INPUTS
*      TSD - a pointer to a struct TimerSetupData allocated with
*          InitTimer()
*      CTD - a pointer to a struct TimerData allocated by
*          SubmitTimer()
*
*   RESULT
*      TRUE if the about of time specified by SubmitTimer() has
*      passed.
*
*   NOTES
*      If this function returned TRUE, then do not use the CTD input
*      again, preferably, set it to NULL yourself.
*
*   SEE ALSO
*      SubmitTimer(), AbortTimer(), InitTimer()
*
*********************************************************************/

BOOL __asm __saveds CheckTimer(register __a0 struct TimerSetupData *TSD, register __a1 struct TimerData *CTD)
{
  struct TimerData *TD;

  if (TSD && CTD)
  {
    for (TD = (struct TimerData *)TSD->TimerList->lh_Head ; TD->node.ln_Succ ; TD =(struct TimerData *) TD->node.ln_Succ)
    {
      if (TD==CTD)
      {
        if (CheckIO((struct IORequest *)&CTD->TR))
        {
          WaitIO((struct IORequest *)&CTD->TR);
          Remove((struct Node*)CTD);
          FreeVec(CTD);
          return(TRUE);
        }
      }
    }
  }
  return(FALSE);
}

/****** HBBSCommon.library/CleanupTimer ******************************
*
*   NAME
*      CleanupTimer - Cleanup a timer.
*
*   SYNOPSIS
*      CleanupTimer( TSD )
*                    A0
*
*      void CleanupTimer( struct TimerSetupData *TSD )
*
*   FUNCTION
*      This function cleans up a timer created with InitTimer()
*
*   INPUTS
*      TSD - a pointer to a struct TimerSetupData allocated with
*          InitTimer()
*
*   RESULT
*      None
*
*   NOTES
*      preferably set the variable passed as TSD to NULL after
*      calling this function.
*
*   SEE ALSO
*      InitTimer(), SubmitTimer(), CheckTimer(), AbortTimer()
*
*********************************************************************/

void __asm __saveds CleanupTimer( register __a0 struct TimerSetupData *TSD )
{
  struct TimerData *TD;

  if (TSD)
  {
    if (TSD->TimerPort)
    {
      if (TSD->BlankTimerIO)
      {
        if (TSD->TimerOpen)
        {
          if (TSD->TimerList)
          {
            // remove all outstanding io requests..
            while ((struct TimerData *)TSD->TimerList->lh_Head->ln_Succ)
            {
              TD=(struct TimerData *)TSD->TimerList->lh_Head;
              AbortIO((struct IORequest *)&TD->TR);
              WaitIO((struct IORequest *)&TD->TR);
              Remove((struct Node*)TD);
              FreeVec(TD);
            }
            FreeVec(TSD->TimerList);
          }
          CloseDevice((struct IORequest *) TSD->BlankTimerIO);
          TSD->TimerOpen=FALSE;
        }
        DeleteExtIO((struct IORequest *) TSD->BlankTimerIO);
      }
      DeleteMsgPort(TSD->TimerPort);
    }
    FreeVec(TSD);
  }
}

/****** HBBSCommon.library/InitTimer ******************************
*
*   NAME
*      InitTimer - Initalise a timer.
*
*   SYNOPSIS
*      InitTimer( void )
*
*      struct TimerSetupData *InitTimer( void )
*
*   FUNCTION
*      This function creates a timer.
*
*      Pass the returned variable to other timer related functions.
*
*      Must be paired with a call to CleanupTimer(), but only if
*      a non-NULL value is returned.
*
*      You can call this routine as many times as you like in your
*      program, but be careful you don't run out of signal bits!
*
*      You can use the message port TimerSetupData->TimerPort if
*      you need to Wait() for timer completion.
*
*   INPUTS
*      TSD - a pointer to a struct TimerSetupData allocated with
*          InitTimer()
*
*   RESULT
*      A pointer to a struct TimerSetupData structure, or NULL
*      if there was a problem.
*
*   SEE ALSO
*      CleanupTimer(), SubmitTimer(), CheckTimer(), AbortTimer()
*      includes/hbbs/structures.h
*
*********************************************************************/

struct TimerSetupData __asm __saveds *InitTimer( void )
{
  struct TimerSetupData *TSD;

  if (TSD=AllocVec(sizeof(struct TimerSetupData),MEMF_PUBLIC|MEMF_CLEAR))
  {
    TSD->TimerOpen=FALSE;
    if (TSD->TimerPort = CreateMsgPort())
    {
      if (TSD->BlankTimerIO = (struct timerequest *) CreateExtIO(TSD->TimerPort,sizeof(struct timerequest)) )
      {
        if (OpenDevice( TIMERNAME, UNIT_VBLANK,(struct IORequest *) TSD->BlankTimerIO, 0L)==0) // success ?
        {
          TSD->TimerOpen=TRUE;
          TSD->BlankTimerIO->tr_node.io_Command = TR_ADDREQUEST;
          if (TSD->TimerList=AllocVec(sizeof(struct List),MEMF_PUBLIC))
          {
            NewList((struct List *)TSD->TimerList);
            return(TSD);
          }
        }
      }
    }
    CleanupTimer(TSD);
  }
  return(NULL);
}

/****** HBBSCommon.library/PathOK ************************************
*
*   NAME
*      PathOK - Checks a path  (or file) is ok.
*
*   SYNOPSIS
*      PathOK( path )
*              A0
*
*      BOOL PathOK( char *path )
*
*   FUNCTION
*      This function checks to see if the path (or file) is ok by
*      first checking the assign/volume/device is present, and then
*      by obtaining a shared lock on the directory/path.
*
*   INPUTS
*      path - null terminated string containing an absolute path
*         e.g. "hbbs:system/data/test.cfg" rather than
*         "system/data/test.cfg"
*
*   RESULT
*      TRUE if the path (or file) exists.
*
*   NOTES
*      You might not be able to actualy open the file/directory
*      due to other reasons, even if this returns true (e.g. the
*      object may be in use)
*
*   SEE ALSO
*      AssignOK()
*
*********************************************************************/

BOOL __asm __saveds PathOK( register __a0 char *str )
{
  BPTR FL;

  if (AssignOK(str))
  {
    if (FL=Lock(str,ACCESS_READ))
    {
      UnLock(FL);
      return(TRUE);
    }
  }
  return(FALSE);
}

/****** HBBSCommon.library/HBBS_UnLockUserData ***********************
*
*   NAME
*      HBBS_UnLockUserData - Unlock the data file
*
*   SYNOPSIS
*      HBBS_UnLockUserData( void )
*
*      void HBBS_UnLockUserData( void )
*
*   FUNCTION
*      Call this function when you've finished accessing the userdata
*      file.  And after you have Close()'d or UnLock()'d the file.
*
*      If you don't call this function, other doors and the bbs will
*      not be able to access the user data file.
*
*   INPUTS
*      None
*
*   RESULT
*      None
*
*   SEE ALSO
*      HBBS_LockUserData()
*
*********************************************************************/

void __asm __saveds HBBS_UnLockUserData( void )
{
  BBSGlobal->UserDataLocked=FALSE;
}

/****** HBBSCommon.library/HBBS_LockUserData *************************
*
*   NAME
*      HBBS_LockUserData - Lock the user data file
*
*   SYNOPSIS
*      HBBS_LockUserData( LockMode )
*                         D0
*
*      BPTR HBBS_LockUserData( ULONG LockMode )
*
*   FUNCTION
*      When a door needs to access the user data file, they should
*      call this function, which returns a lock to the user data file.
*      They can then manipulate the file (use OpenFromLock().)
*
*      Must be paired with HBBS_UnLockUserData().
*
*      This function will wait until a lock is established, so use it
*      with care.
*
*      If you want to see if the file is already locked by another
*      program/door, then check BBSGlobal->UserDataLocked (boolean)
*      first.
*
*      The lock obtained from this function must be treated just as
*      if you did Lock(filename).  So read the autodocs on
*      dos.library/Lock()!
*
*      When accessing the user data file, make sure that you keep the
*      file open/locked for the shortest amount of time possible
*      because other doors/utils may want access to it at the same
*      time.
*
*      If possible, copy the items from the datafile you need, then
*      close the file and call HBBS_UnLockUserData().  Then, if you
*      have to write back to the file, call the function again.
*
*   INPUTS
*      LockMode - see dos.library/Lock() for valid lock modes
*          (normally either ACCESS_READ or ACCESS_WRITE)
*
*   RESULT
*      A Lock() on the user data file.
*
*   SEE ALSO
*      HBBS_UnLockUserData()
*
*********************************************************************/

BPTR __asm __saveds HBBS_LockUserData( register __d0 ULONG LockMode )
{
  BPTR FL;

  // obtain a lock on the user.data file, other parts of the bbs might be using it
  // to write to, so we might not be able to get a lock on it, so we have to
  // keep trying..

  // *C* we could do with a delay before we check again here really.

  while (!(FL=Lock(BBSGlobal->UserDataFileName,LockMode)));

  BBSGlobal->UserDataLocked=TRUE;

  return(FL);
}

/****** HBBSCommon.library/HBBS_LoadUser *****************************
*
*   NAME
*      HBBS_LoadUser - Load a user from the user data file into memory
*
*   SYNOPSIS
*      HBBS_LoadUser( fileoffset, ID, handle, realname, User, Flags )
*                     D0          D1  A0      A1        A2    D2
*
*      BOOL HBBS_LoadUser( V_BIGNUM fileoffset, V_BIGNUM ID,
*          char *handle, char *realname, struct UserData *User,
*          ULONG Flags);
*
*   FUNCTION
*      This function loads a users information from the user data
*      file into memory.
*
*   INPUTS
*      FileOffset - Load a specific section of the user data file
*          file offset starts at 1 (i.e. first user in the file
*          is at file offset 1, the second is at 2 and so on), or 0
*
*      ID - UserID of user to load, or 0
*
*      handle - handle of user to load, or NULL
*
*      realname - realname of user to load, or NULL
*
*      user - previously allocated memory to store the user's
*          information in.
*      Flags - currently supports the following options (see defines.h)
*          (logical OR the flags together to specify more than one at
*          a time, specify LOADUSER_NONE if you're not using any
*          other flags)
*
*          LOADUSER_NONE - Check all users.
*          LOADUSER_SKIPOVERWRITABLE - Don't return user accounts
*              with a ->Status of USER_OVERWRITEABLE.
*
*          LOADUSER_SKIPID - If you are searching for a user by their
*              handle, and want to skip a particular ID then specify
*              this flag, and the ID and handle paramaters.
*              The function HBBS_HandleInUse() uses this flag.
*
*      Example:
*
*      struct UserData myuser;
*
*      if (HBBS_LoadUser(0,0,"Hydra",NULL,&myuser,
*          LOADUSER_SKIPOVERWRITABLE))
*      {
*        .
*        . do stuff with data in myuser
*        .
*      }
*
*   RESULT
*      None
*
*   NOTES
*      Specify only one of FileOffset, ID, handle or realname.  set
*      unused inputs to NULL or 0.
*
*      The LOADUSER_SKIPOVERWRITABLE flag does not apply to FileOffset
*      searching mode, Only ever specify LOADUSER_NONE when searching
*      using the FileOffset mode.
*
*   SEE ALSO
*      HBBS_SaveUserData(), HBBS_HandleInUse(), HBBS_LockUserData()
*      HBBS_ValidUserHandle()
*
*********************************************************************/

V_BOOL __asm __saveds  HBBS_LoadUser(register __d0 V_BIGNUM fileoffset,
                                     register __d1 V_BIGNUM ID,
                                     register __a0 char *handle,
                                     register __a1 char *realname,
                                     register __a2 struct UserData *User,
                                     register __d2 ULONG Flags)
{
  // Specify only one of fileoffset,handle or realname (ID and fileoffset start from 1, not 0)
  // "User" should already be allocated.

  BPTR FH;
  BPTR FL;
  V_BOOL Found = FALSE;
  V_BOOL Done = FALSE;
  char outstr[1024];
  ULONG BytesRead;

  // do we have a place to store the data ?

  if(User)
  {

    // open the user data file

    FL=HBBS_LockUserData(ACCESS_READ);

    if (FH=OpenFromLock(FL))
    {

      // use a fast method for fileoffset loading of a user.

      if (fileoffset>0)
      {
        if (Seek(FH,(fileoffset-1)*sizeof(struct UserData),OFFSET_BEGINNING)!=-1)
        {
          if ((BytesRead = Read(FH,User,sizeof(struct UserData)))==sizeof(struct UserData))
          {
            Found=TRUE;
          }
          else
          {
            if (BytesRead) // check if we're not at the EOF, if not then log an error!
            {
              sprintf(outstr,"Short read (%ld < %ld) when loading a user from the user-data file",BytesRead,sizeof(struct UserData));
              HBBS_LogError(BBSGlobal->ErrorLogFile,ERR_INFO,outstr,TYPE_WARNING);
            }
          }
        }
        else
        {
          HBBS_LogError(BBSGlobal->ErrorLogFile,ERR_INFO,"Seek() error when loading a user from the user-data file",TYPE_WARNING);
        }
      }
      else
      {
        // search the file for the required user, examine 1 user at a time

        if (handle!=NULL || realname !=NULL || ID > 0)
        {
          do
          {
            // read a section of the file.

            BytesRead=Read(FH,User,sizeof(struct UserData));

            // error, EOF or incorrect amount of data.

            if ( (BytesRead <= 0) || (BytesRead != sizeof(struct UserData)) )
            {
              Done=TRUE;
            }
            else
            {

              /*
                Small Truth Table: (SO = SkipOverwritable paramater, S = User's status)

                SO = TRUE, S = 'O'
                !SO = FALSE, 'O' != 'O' = FALSE
                FALSE or FALSE = FALSE
                -
                SO = TRUE, S = 'V'
                !SO = FALSE, 'V' != 'O' = TRUE
                FALSE or TRUE = TRUE
                -
                SO = FALSE, S = 'O'
                !SO = TRUE, 'O' != 'O' = FALSE
                TRUE or FALSE = TRUE
              */

              // compare this user ?

              if (!(Flags & LOADUSER_SKIPOVERWRITABLE) || User->Status!=USER_OVERWRITABLE)
              {
                if (handle != NULL)
                {
                  // check against the handle we're looking for...

                  if (stricmp(handle,User->Handle)==0)
                  {
                    // the handle is the same, if LOADUSER_SKIPID is set then
                    // we have to check the ID specified to the ID we just loaded
                    // and if they're the same then continue searching.

                    if (! ( (Flags & LOADUSER_SKIPID) && ( ID == User -> UserID ) ))
                    {
                      Found=TRUE;
                    }
                  }
                }
                else
                {
                  if (realname != NULL)
                  {
                    // check against the handle we're looking for...

                    if (stricmp(realname,User->RealName)==0)
                    {
                      Found=TRUE;
                    }
                  }
                  else
                  {
                    if (ID > 0)
                    {
                      // check against the handle we're looking for...

                      if ( ID == User -> UserID )
                      {
                        Found=TRUE;
                      }
                    }
                  }
                }
              }
            }
          } while (!Done && !Found);

        }
      }
      Close(FH);
    }
    else
    {
      UnLock(FL);
    }
		HBBS_UnLockUserData();
  }
  return(Found);
}

/****** HBBSCommon.library/HBBS_ValidUserHandle **********************
*
*   NAME
*      HBBS_ValidUserHandle - Checks if a handle is in use.
*
*   SYNOPSIS
*      HBBS_ValidUserHandle( userhandle, FillUser )
*                            A0          A1
*
*      BOOL HBBS_ValidUserHandle( char *userhandle,
*          struct UserData *FillUser )
*
*   FUNCTION
*      This function checks to see if a handle is already in use.
*      It will also return handles of users that have been deleted.
*      If a user has a status of 'O' (overwritable) it will NOT be
*      returned.
*
*      You can also use this to re-instate deleted users etc..
*
*   INPUTS
*      userhandle - name of users handle to find.
*      FillUser - NULL, or a pointer to store the information about
*          the user.
*
*      Example:
*
*      struct UserData myuser;
*
*      if (HBBS_ValidUserHandle("Hydra",&myuser))
*      {
*        .
*        . do stuff with data in myuser
*        .
*      }
*
*   RESULT
*      TRUE if user was found.
*
*   NOTES
*      None
*
*   SEE ALSO
*      HBBS_LoadUser(), HBBS_HandleInUse()
*
*********************************************************************/

V_BOOL __asm __saveds HBBS_ValidUserHandle(register __a0 char *userhandle, register __a1 struct UserData *FillUser)
{

  // Small routine to see if a user name does exist, if the user does
  // exist and FillUser points an allocated data structure the size of
  // sizeof(struct UserData) then the users info that was found will be copied
  // to the location specified.

  struct UserData *User;
  V_BOOL retval=FALSE;

  if (User=AllocVec(sizeof(struct UserData),MEMF_PUBLIC))
  {

    // search the file for the account, skipping any accounts that are
    // marked overwritable.

    if (HBBS_LoadUser(0,0,userhandle,NULL,User,LOADUSER_SKIPOVERWRITABLE))
    {
      // Do we have to copy the data to the provided location ?

      if (FillUser)
      {
        CopyMem(User,FillUser,sizeof (struct UserData));
      }
      retval=TRUE;
    }
    FreeVec(User);
  }

  return(retval);
}

V_BOOL __asm __saveds HBBS_HandleInUse(register __a0 char *userhandle, register __d0 CurrentID, register __a1 struct UserData *FillUser)
{

  // Small routine to see if a particular handle is in use.
  // similar to ValidUserHandle except for the fact that it does not
  // check the user who's ID is specified using CurrentID.
  // This is very usefull when a user wants to change their handle.

  // If the handle exists and FillUser points an allocated data structure the size of
  // sizeof(struct UserData) then the users info that was found will be copied
  // to the location specified.

  struct UserData *User;
  V_BOOL retval=FALSE;

  if (User=AllocVec(sizeof(struct UserData),MEMF_PUBLIC))
  {

    // search the file for the account, skipping any accounts that are
    // marked overwritable.

    if (HBBS_LoadUser(0,CurrentID,userhandle,NULL,User,LOADUSER_SKIPOVERWRITABLE | LOADUSER_SKIPID))
    {
      // Do we have to copy the data to the provided location ?

      if (FillUser)
      {
        CopyMem(User,FillUser,sizeof (struct UserData));
      }
      retval=TRUE;
    }
    FreeVec(User);
  }

  return(retval);
}

/****** HBBSCommon.library/HBBS_FindTotalUsers ***********************
*
*   NAME
*      HBBS_FindTotalUsers - Find amount of users in the data file
*
*   SYNOPSIS
*      HBBS_FindTotalUsers( void )
*
*      V_BIGNUM HBBS_FindTotalUsers( void )
*
*   FUNCTION
*      This function finds the amount of users in the user data file
*      This includes active, deleted and over-writable users too.
*
*      To find out how many *active* users there are then you must
*      examine the UserData->Status of each user in the file.
*
*      This function will not return until it can access the user
*      data file, which may be locked by another task.
*
*      A call to this function also sets BBSGlobalData->TotalUsers
*      though that value may become outdated.  If you don't
*      require an *exact* amount then use BBSGlobalData->TotalUsers
*      otherwise use this routine.
*
*      When a user is added via HBBS_AddUser(),
*      BBSGlobalData->TotalUser is updated too.
*
*   INPUTS
*      None
*
*   RESULT
*      amount of users.
*
*********************************************************************/


V_BIGNUM __asm __saveds HBBS_FindTotalUsers( void )
{

  // this only returns the about of users in the user data file, not the amount
  // of active users, the latter can only be equal or less than the former
  // though!  So for stat's doors, it's about right...
  // to find the amount of active users, you have to check each user in the file.

  BPTR FL;
  struct FileInfoBlock FB;

  BBSGlobal->TotalUsers=-1;

  // keep trying!

  FL = HBBS_LockUserData(ACCESS_READ);

  if (Examine(FL,&FB));
  {
    BBSGlobal->TotalUsers=(V_BIGNUM) (FB.fib_Size / sizeof(struct UserData));
  }
  UnLock(FL);

  HBBS_UnLockUserData();

  return(BBSGlobal->TotalUsers);
}

BOOL __asm __saveds LoadPrivateData( void )
{
  struct CfgFileData *PrivateCFG;
  BOOL retval=FALSE;

  if (PrivateCFG=HBBS_LoadConfig(FILE_PRIVATEDATA,LCFG_NONE))
  {
    HBBS_GetSetting(PrivateCFG,(void *)&BBSGlobal->CallsEver,VTYPE_BIGNUM,OPT_PRIV_CallsEver,OPT_SINGLE);
    if (HBBS_GetSetting(PrivateCFG,(void *)&BBSGlobal->LastUserNum,VTYPE_BIGNUM,OPT_PRIV_LastUserNum,OPT_SINGLE))
    {
      retval=TRUE;
    }
    HBBS_FlushConfig(PrivateCFG);
  }
  else
  {
    HBBS_LogError(BBSGlobal->ErrorLogFile,ERR_INFO,"Failed to load private data",TYPE_WARNING);
  }

  return(retval);
}

void __asm __saveds UpdatePrivateData ( void )
{
  struct CfgFileData *PrivateCFG;
  char str[BIG_STR];

  if (PrivateCFG=HBBS_CreateConfig(FILE_PRIVATEDATA))
  {
    sprintf(str,"%ld",BBSGlobal->CallsEver);
    HBBS_AddCfgItemQuick(PrivateCFG,OPT_PRIV_CallsEver,str);

    sprintf(str,"%ld",BBSGlobal->LastUserNum);
    HBBS_AddCfgItemQuick(PrivateCFG,OPT_PRIV_LastUserNum,str);

    if (HBBS_SaveConfig(PrivateCFG))
    {
      HBBS_LogError(BBSGlobal->ErrorLogFile,ERR_INFO,"Updated private data",TYPE_INFO);
    }
    else
    {
      HBBS_LogError(BBSGlobal->ErrorLogFile,ERR_INFO,"Failed to update private data",TYPE_WARNING);
    }
    HBBS_FlushConfig(PrivateCFG);
  }
}

/****** HBBSCommon.library/HBBS_AddUser ******************************
*
*   NAME
*      HBBS_AddUser - Add a user to the user-data file.
*
*   SYNOPSIS
*      HBBS_AddUser( user )
*                    A0
*
*      BOOL HBBS_AddUser( struct UserData *user )
*
*   FUNCTION
*      This function adds a user to the user-data file.
*
*   INPUTS
*      user - a correctly completed struct UserData structure. See
*          HBBS:Source/Doors_System/NewUser/Main.C for details.
*
*   RESULT
*      TRUE if user added OK.
*
*   NOTES
*      This function is not for the faint hearted, as filling in a
*      struct UserData from scratch is not an easy process.
*
*      Calls to this function are logged in the error log files, so
*      take note if you were planning to write a back door into
*      your door/program!!
*
*   SEE ALSO
*      HBBS_HandleNameOK(), HBBS_InitUserData(),
*      HBBS_ApplyAccountDefaults()
*
*********************************************************************/

BOOL __asm __saveds HBBS_AddUser(register __a0 struct UserData *user)
{
  BPTR FH;
  BPTR FL;

  BOOL Found,Saved=FALSE,ReadError;
  char dirname[BIG_STR],filenote[80];
  struct UserData *OldUser;
  LONG SeekError;
  BOOL Appending;
  char *outstr;
  ULONG BytesRead;

  if (OldUser = AllocVec(sizeof(struct UserData),MEMF_PUBLIC))
  {

    FL=HBBS_LockUserData(ACCESS_WRITE);

    if (FH=OpenFromLock(FL))
    {

      // search the user data for an old user that we can overwrite.

      Found=FALSE;
      ReadError=FALSE;

      while (!Found && !ReadError && (BytesRead = Read(FH,OldUser,sizeof(struct UserData))))
      {
        ReadError = (BytesRead != sizeof(struct UserData));

        if (!ReadError)
        {
          if (OldUser->Status==USER_OVERWRITABLE)
          {
            Found=TRUE;
          }
        }
      }

      if (ReadError)
      {
        HBBS_LogError(BBSGlobal->ErrorLogFile,ERR_INFO,"Error reading user data file",TYPE_WARNING);
      }
      else
      {

        if (Found)
        {
          // go back to the start of the record and overwrite the old user.

          SeekError=Seek(FH,0-sizeof(struct UserData),OFFSET_CURRENT);
          Appending=FALSE;
        }
        else
        {
          // seek to end of file and append new user

          SeekError=Seek(FH,0,OFFSET_END);
          Appending=TRUE;
        }

        if (SeekError != -1)
        {
          // we update this here, so that we don't re-use an ID that may
          // have been partially saved to the user data file
          BBSGlobal->LastUserNum++;
          user->UserID=BBSGlobal->LastUserNum;

          // now save the user's data.

          if (Write(FH,user,sizeof(struct UserData))==sizeof(struct UserData))
          {
            Close(FH);
            FH=NULL;
            if (Appending)
            {
              HBBS_LogError(BBSGlobal->ErrorLogFile,ERR_INFO,"Appended new user to user data file",TYPE_INFO);
            }
            else
            {
              HBBS_LogError(BBSGlobal->ErrorLogFile,ERR_INFO,"Overwrote old user in user data file",TYPE_INFO);
            }
            UpdatePrivateData();

            Saved=TRUE;
          }
          else
          {
            if (outstr = AllocVec(strlen(BBSGlobal->UserDataFileName) + 80,MEMF_PUBLIC))
            {
              sprintf(outstr,"Error while %s to %s",Appending ? "appending" : "writing",BBSGlobal->UserDataFileName);
              HBBS_LogError(BBSGlobal->ErrorLogFile,ERR_INFO,outstr,TYPE_WARNING);
              FreeVec(outstr);
            }


            if (Appending)
            {
              HBBS_LogError(BBSGlobal->ErrorLogFile,ERR_INFO,"Attempting to fix it.",TYPE_WARNING);

              if (SetFileSize(FH,OFFSET_BEGINNING,BBSGlobal->TotalUsers * sizeof(struct UserData))==-1)
              {
                HBBS_LogError(BBSGlobal->ErrorLogFile,ERR_INFO,"File truncated ok, check disk space!",TYPE_WARNING);
              }
              else
              {
                HBBS_LogError(BBSGlobal->ErrorLogFile,ERR_INFO,"Could not fix it",TYPE_WARNING);
                HBBS_LogError(BBSGlobal->ErrorLogFile,ERR_INFO,str_userdataconsistancy,TYPE_WARNING);
              }
            }
          }
        }
        else
        {
          HBBS_LogError(BBSGlobal->ErrorLogFile,ERR_INFO,"Error while seeking in userdata file, could not save user data",TYPE_WARNING);
        }
      }
      if (FH) Close(FH);
    }
    else UnLock(FL);

    HBBS_UnLockUserData();

    if (Saved)
    {
      sprintf(filenote,"Handle: %s, Group: %s",user->Handle, user->Group);

      sprintf(dirname,"HBBS:System/Data/Users/%d",user->UserID);
      if (FL=CreateDir(dirname))
      {
        UnLock(FL);
        SetComment(dirname,filenote);
      }

      sprintf(dirname,"HBBS:Mail/Users/%d",user->UserID);
      if (FL=CreateDir(dirname))
      {
        UnLock(FL);
        SetComment(dirname,filenote);
      }
    }

    HBBS_FindTotalUsers();
    FreeVec(OldUser);
  }
  return(Saved);
}

/****** HBBSCommon.library/HBBS_FindNode *****************************
*
*   NAME
*      HBBS_FindNode - Find a node with a specific ln_Name field
*
*   SYNOPSIS
*      HBBS_FindNode( list, str )
*                     A0    A1
*
*      struct Node *HBBS_FindNode( struct List *list, char *str )
*
*   FUNCTION
*      This function returns the node in the "list" who's ln_Name
*      field matches the string specified.
*
*      This function accepts wild cards in the str field, and
*      is case-insensitive.
*
*   INPUTS
*      list - a list of nodes.
*      str - a null-terminated string. (may contain wildcards)
*
*   RESULT
*      a pointer to a struct Node of the node that had it's ln_Name
*      field set to "str" or NULL otherwise.
*
*   SEE ALSO
*      HBBSFindNodeNum(), GetNodeNum()
*
*********************************************************************/

struct Node __asm __saveds *HBBS_FindNode(register __a0 struct List *list,register __a1 char *str)
{
  struct Node *node,*retval=NULL;
  V_BOOL found=FALSE;
  char *matchstr;

  if (matchstr = AllocVec(BIG_STR+1,MEMF_PUBLIC))
  {
    if (list && str)
    {
      for (node=list->lh_Head;!found && node->ln_Succ;node=node->ln_Succ)
      {
        if (node->ln_Name)
        {
          ParsePatternNoCase(node->ln_Name,matchstr,BIG_STR);
          if (MatchPatternNoCase(matchstr,str))
          {
            found=TRUE;
            retval=node;
          }
        }
      }
    }
    FreeVec(matchstr);
  }
  return(retval);
}

/****** HBBSCommon.library/HBBS_FindNodeNum *****************************
*
*   NAME
*      HBBS_FindNodeNum - Find a node with a specific ln_Name field
*
*   SYNOPSIS
*      HBBS_FindNodeNum( list, str )
*                        A0    A1
*
*      V_BIGNUM HBBS_FindNodeNum( struct List *list, char *str )
*
*   FUNCTION
*      This function returns the number of a node in the "list"
*      who's ln_Name field matches the string specified.
*
*      This function accepts wild cards in the str field, and
*      is case-insensitive.
*
*   INPUTS
*      list - a list of nodes.
*      str - a null-terminated string. (may contain wildcards)
*
*   RESULT
*      the number of the first node in the list whose ln_Name field
*      is set to "str" or -1.  The first node in the list is 0, not 1
*
*   NOTES
*      None
*
*   SEE ALSO
*      HBBS_FindNode(), GetNodeNum()
*
*********************************************************************/

V_BIGNUM __asm __saveds HBBS_FindNodeNum(register __a0 struct List *list, register __a1 char *str)
{
  struct Node *node;
  V_BIGNUM retval=-1;
  if (node=HBBS_FindNode(list,str))
  {
    retval=GetNodeNum(list,node);
  }
  return(retval);
}

/****** HBBSCommon.library/HBBS_InitUserData *************************
*
*   NAME
*      HBBS_InitUserData - Initalise a struct UserData structure.
*
*   SYNOPSIS
*      HBBS_InitUserData( User, AccessLevel, ConfNum )
*                         A0    D0           D1
*
*      void HBBS_InitUserData( struct UserData *User,
*          V_BIGNUM AccessLevel, V_BIGNUM ConfNum )
*
*   FUNCTION
*      This function initialises a struct UserData structure.
*
*      Use this function, and then fill out some other fields in the
*      struct UserData, before calling HBBS_AddUser() to add the user
*      to the data file.
*
*   INPUTS
*      User - pointer to some allocated memory the size of
*          sizeof(struct UerData)
*      AccessLevel - the access level to set the user to, this must
*          be a valid access level that has been defined in
*          HBBS:Access/Levels/Level_List
*      ConfNum - the default conference for the user.
*
*   RESULT
*      None
*
*   SEE ALSO
*      HBBS_AccessName(), HBBS_ApplyAccountDefaults()
*      HBBS_AddUser()
*
*********************************************************************/

void __asm __saveds HBBS_InitUserData(register __a0 struct UserData *User,register __d0 V_BIGNUM AccessLevel,register __d1 V_BIGNUM ConfNum)
{
  struct Node *node;
  char tmpstr[BIG_STR];
  int num;

  User->Status=USER_NEW;
  User->Access=AccessLevel;
  User->Handle[0]=0;
  User->RealName[0]=0;
  User->Group[0]=0;
  User->GeoLocation[0]=0;
  User->Country[0]=0;
  User->PhoneNumber[0]=0;
  User->Password[0]=0;
  User->ComputerType[0]=0;
  User->SentBy[0]=0;

  User->ConfAcsDataFile[0]=0;

  sprintf(tmpstr,"%d",AccessLevel);
  if ((num=HBBS_FindNodeNum(BBSGlobal->AcsLevelList,tmpstr))!=-1)
  {
    node=GetNode(BBSGlobal->AcsLevelConfAcsDef,num);
    strcpy(User->ConfAcsDataFile,node->ln_Name);
  }
  User->LeechAccDataFile[0]=0;
  User->UploadBytes=0;
  User->UploadFiles=0;
  User->DownloadBytes=0;
  User->DownloadFiles=0;
  User->ActualUploadBytes=0;
  User->ActualUploadFiles=0;
  User->ActualDownloadBytes=0;
  User->ActualDownloadFiles=0;
  User->LastUploadDate=0; // *C* set to todays's date ?
  User->LastCalledDate=0;
  User->BestCPSUp=0;
  User->BestCPSDown=0;
  User->CallsMade=0;
  User->PagesMade=0;
  User->MessagesWritten=0;
  User->FilesRatio=3;
  User->BytesRatio=3;
  User->LastConf=ConfNum;
  User->PreferredConf=0;
  User->LinesPerScreen=24;
  User->Protocol=0; // ASK
  User->Editor=0; // ASK
  User->TimeAccessFile[0]=0;
  User->ExtraTimeLimit=0;
  User->ExtraBytesLimit=0;
  User->ExtraCallsLimit=0;
  User->ExtraChatLimit=0;
  User->TimeUsed=0;
  User->BytesUsed=0;
  User->CallsUsed=0;
  User->ChatUsed=0;
  User->BytesAllowed=0;
  User->TimeAllowed=0;
  User->CallsAllowed=0;
  User->ChatAllowed=0;
  User->UserType=USERTYPE_NORMAL;
  User->Language=0; // defaults to an INVALID language (index starts at 1)
}

/****** HBBSCommon.library/strNcpy ***********************************
*
*   NAME
*      strNcpy - copy a string, length limited.
*
*   SYNOPSIS
*      strNcpy( dest, source, length )
*
*      void strNcpy( char *dest, char *source, int length )
*
*   FUNCTION
*      This function copies length bytes from the source string to
*      the dest string.  And then null-terminates the dest string.
*
*      This is similar to strncpy() except that strncpy() does NOT
*      *always* null terminate the dest string.
*
*   INPUTS
*      dest - null terminated string.
*      source - null terminated string.
*      length - amount of characters to copy.
*
*   RESULT
*      None
*
*********************************************************************/

void __asm __saveds strNcpy(register __a0 UBYTE *dest,register __a1 UBYTE *source,register __d0 int chars)
{
  int count=0;

  while ( (count<chars) &&
          (dest[count]=source[count++]) );
  dest[count]=0;
}

/****** HBBSCommon.library/HBBS_ListName *****************************
*
*   NAME
*      HBBS_ListName - get a pointer to an ln_Name field of a node in
*          a list.
*
*   SYNOPSIS
*      HBBS_ListName( list, ItemNum )
*                     A0    D0
*
*      char *HBBS_ListName( struct List *ListName, V_SMALLNUM ItemNum)
*
*   FUNCTION
*      This function gets a pointer to the ln_Name field of a node
*      in a list.  There *must* be ItemNum nodes in the list.
*
*   INPUTS
*      list - a linked list of nodes.
*      ItemNum - the number of the node in the list.  Starting from 0
*          (i.e. 0 is the first node in the list, 1 is the second...)
*
*   RESULT
*      A pointer to the ln_Name field of the node, or NULL if the
*      ln_Name field of the node is also NULL.
*
*********************************************************************/

char __asm __saveds *HBBS_ListName(register __a0 V_STRINGLIST ListName,register __d0 V_SMALLNUM ItemNum)
{
  //starts from offset 0, 0 being the first node in the list..

  struct Node *node;

  if(node=GetNode(ListName,ItemNum))
  {
    return(node->ln_Name);
  }
  return(NULL);
}

/****** HBBSCommon.library/FreeFileTags ******************************
*
*   NAME
*      FreeFileTags - Free's all the files the user has tagged for
*         a specified node.
*
*   SYNOPSIS
*      FreeFileTags( nodedata )
*                    A0
*
*      void FreeFileTags( struct NodeData *nodedata )
*
*   FUNCTION
*      This function free's all tagged files that the user might have.
*
*   INPUTS
*      nodedata - a pointer to a struct NodeData
*
*   RESULT
*      None
*
*   NOTES
*      The node must have a OnlineStatus of OS_ONLINE (i.e. a user
*      must be online)
*
*********************************************************************/

void __asm __saveds FreeFileTags( register __a0 struct NodeData *N_ND )
{
  struct TaggedFile *tfnode;

  if (N_ND)
  {
    while ((N_ND->TaggedFileList) && (N_ND->TaggedFileList->lh_Head->ln_Succ))
    {
      tfnode=(struct TaggedFile *)N_ND->TaggedFileList->lh_Head;
      FreeStr(tfnode->node.ln_Name);
      Remove((struct Node*)tfnode);
      FreeVec(tfnode);
    }
    N_ND->TaggedFiles=0;
  }
}


/****** HBBSCommon.library/HBBS_LoadFile *****************************
*
*   NAME
*      HBBS_LoadFile - Load a file into a linked list.
*
*   SYNOPSIS
*      HBBS_LoadFile( filename )
*                     A0
*
*      struct List *HBBS_LoadFile( char *filename )
*
*   FUNCTION
*      This function loads a text file into a linked list, with the
*      ln_Name of each node in the list being a line of the file.
*
*   INPUTS
*      filename - the name of the file to load, must be an absolute
*          filename.  e.g. progdir:myfile.txt or hbbs:stuff/stuff.txt
*
*   RESULT
*      NULL if file could not be opened or if we ran out of memory
*      otherwise a pointer to a linked list.
*
*   NOTES
*      The nodes and the list are allocated using AllocVec so you can
*      use FreeStrList() to free all the nodes and the list itself.
*
*   SEE ALSO
*      FreeStrList(), HBBS_SaveFile()
*
*********************************************************************/

#define bufflen 1024

struct List __asm __saveds *HBBS_LoadFile(register __a0 char *filename)
{
  /* New Optimized Version: 4-JAN-1996 */

  BPTR FH;
  UBYTE buffer[bufflen];
  struct List *FileList=NULL;
  BOOL error=FALSE;

  if (PathOK(filename))
  {
    if (FH=Open(filename,MODE_OLDFILE))
    {
      if (FileList=HBBS_CreateList())
      {
        while (FGets(FH,buffer,bufflen) && !error)
        {
          stripcr(buffer);
          if (NewStrNode(buffer,FileList))
          {
            error=TRUE;
          }
        }
      }
      Close(FH);
    }
  }
  if (error)
  {
    FreeStrList(FileList);
    FileList=NULL;
  }
  return(FileList);
}

/****** HBBSCommon.library/HBBS_SaveFile *****************************
*
*   NAME
*      HBBS_SaveFile - Save a file from a linked list.
*
*   SYNOPSIS
*      HBBS_SaveFile( filename, list )
*                     A0        A1
*
*      BOOL HBBS_SaveFile( char *filename, struct List *list )
*
*   FUNCTION
*      This function saves a text file from a linked list, with the
*      ln_Name of each node in the list being a line of the file.
*
*   INPUTS
*      filename - the name of the file to save the linked list as
*      list - the linked list of nodes
*
*   RESULT
*      TRUE if sucessful.
*
*   NOTES
*      not all nodes in the "list" need have a ln_Name defined,
*      nodes with an ln_Name value of NULL will be omited from the
*      resulting file.
*
*      make sure you've got enough disk space as no Write() error
*      checking is done.
*
*   SEE ALSO
*      HBBS_LoadFile()
*
*********************************************************************/

BOOL __asm __saveds HBBS_SaveFile( register __a0 char *file_name, register __a1 struct List *list)
{
  BPTR FH;
  struct Node *node;

  if (FH=Open(file_name,MODE_NEWFILE))
  {
    if (list)
    {
      for (node = list->lh_Head ; node->ln_Succ ; node =node->ln_Succ)
      {
        if (node->ln_Name)
        {
          Write(FH,node->ln_Name,strlen(node->ln_Name));
          Write(FH,"\n",1); //add cr+lf
        }
      }
    }
    Close(FH);
  }
  return((BOOL)(FH ? TRUE : FALSE));
}

/****** HBBSCommon.library/HBBS_CreateList ***************************
*
*   NAME
*      HBBS_CreateList - Create and initalise a linked list.
*
*   SYNOPSIS
*      HBBS_CreateList( void )
*
*      struct List *HBBS_CreateList( void )
*
*   FUNCTION
*      This function creates a list structure with no nodes in it,
*      and initalises the list using NewList()
*
*   INPUTS
*      None
*
*   RESULT
*      A pointer to an empty list.
*
*   NOTES
*      Free the list using FreeVec(), or FreeStrList() if appropriate.
*
*   SEE ALSO
*      HBBS_CreateNode(), HBBS_FreeNode(), FreeStrList(), NewStrNode()
*
*********************************************************************/

struct List __asm __saveds *HBBS_CreateList( void )
{
  struct List *NList;
  if (NList=AllocVec(sizeof(struct List),MEMF_PUBLIC))
  {
    NewList(NList);
  }
  return(NList);
}

/****** HBBSCommon.library/strftcpy **********************************
*
*   NAME
*      strftcpy - copy a substring from a string.
*
*   SYNOPSIS
*      strftcpy( dest, source, frompos, topos )
*                A0    A1      D0       D1
*
*      void strftcpy( char *dest, char *source, int frompos, int topos )
*
*   FUNCTION
*      This function copies a source string to a destination string
*      starting for charater "from" and ending at character "topos"
*
*      The destination string will be null-terminated.
*
*      Example:
*
*      char substr[5];
*
*      strftcpy(substr,"this is a kewl test",10,13);
*
*      substr would now contain "kewl\0"
*
*   INPUTS
*      dest - pointer to buffer to hold destination string.
*      source - pointer to a null terminated string
*      from - index to start copying chars from source to dest,
*          starting at 0.
*      topos - index to stop copying chars from soure to dest,
*          starting at 0.
*
*   RESULT
*      None
*
*   NOTES
*      Make sure the frompos and topos are not out of range.
*
*********************************************************************/

void __asm __saveds strftcpy(register __a0 char *dest,register __a1 char *source,register __d0 int from,register __d1 int topos)
{

/* New Improved version below!

  int loop=from,pos=0;
  if (from<=strlen(source)-1)
  {
    while (source[loop] && loop<=topos)
    {
      dest[pos]=source[loop];
      loop++;
      pos++;
    }
  }
  dest[pos]=0;
*/

  strNcpy(dest,source+from,topos+1-from);
}

/****** HBBSCommon.library/RemoveSpaces *******************************
*
*   NAME
*      RemoveSpaces - remove pre and post spaces from a string
*
*   SYNOPSIS
*      RemoveSpaces( str )
*                   A0
*
*      void RemoveSpaces( char *str )
*
*   FUNCTION
*      This function removes all spaces from the beginning and end
*      of the string "str"
*
*      This function is handy for any program that has to ask the
*      user for a line of text, as sometimes users accidentally
*      press space before they type in the line etc..
*
*      Example:
*
*      char *mystr="   spaces   ";
*
*      RemoveSpaces(mystr);
*
*      mystr would now contain the null terminated string "spaces"
*      
*   INPUTS
*      str - pointer to a null-terminated string
*
*   RESULT
*      None
*   
*   NOTES
*      This function does NOT distrub memory either side of the first
*      or last space.
*
*      So this is safe:
*
*      char *test="  test,test  ";
*
*      RemoveSpaces(strchr(test,',')+1);
*
*   SEE ALSO
*      StripComments(), stripcr(), RemoveSpaces()
*
*********************************************************************/

void __asm __saveds RemoveSpaces(register __a0 char *string)
{
  // removes spaces from the start end end of strings

  ULONG start,end,len;

  if (string)
  {
    len=strlen(string);
    for (start=0;string[start]==' ';start++);
    for (end=len-1;string[end]==' ';end--);
    strftcpy(string,string,start,end);
  }
}

/****** HBBSCommon.library/HBBS_SaveUserData *************************
*
*   NAME
*      HBBS_SaveUserData - Save a modified user data structure back
*          to the user data file.
*
*   SYNOPSIS
*      HBBS_SaveUserData( User )
*                         A0
*
*      void HBBS_SaveUserData( struct UserData *User )
*
*   FUNCTION
*      This function saves specified user information back to the
*      user data file.
*
*      Generally, most doors will only ever pass this function
*      the paramater NodeData->User->NormalData.
*
*      The only time you'd ever want to pass anything different is
*      if you are writing an account editor.
*
*      Example normal use:
*
*      struct NodeData *N_ND;
*
*      if (N_ND=HBBS_NodeDataPtr(N_NodeNum))
*      {
*         ... edit the current on-line user's data here ...
*
*         if (HBBS_SaveUserData( N_ND-> User-> NormalData ))
*         {
*            ... saved ok ...
*         }
*      }
*
*      Example account editor use:
*
*      struct UserData user;
*
*      if ( HBBS_LoadUser( 1, 0, NULL, NULL, &user, LOADUSER_NONE ))
*      {
*         ... modify data in "user" here ...
*         if (HBBS_SaveUserData( &user ))
*         {
*            ... saved ok ...
*         }
*      }
*
*   INPUTS
*      User - pointer to a correctly filled out struct UserData
*
*   RESULT
*      TRUE if sucessful, this function will (for all intents and
*      purposes) always succeed.
*      The only thing that would make it fail would be a lack of
*      memory or a corrupt disk.
*
*   NOTES
*      The user specified must already be present in the user data
*      file.
*      Location of the existing user is done via UserData->UserID. so
*      you *must* *not* ever change this value.
*
*   SEE ALSO
*      HBBS_LoadUser(), HBBS_AddUser()
*
*********************************************************************/

BOOL __asm __saveds HBBS_SaveUserData(register __a0 struct UserData *User)
{
  BPTR FH,FL;
  BOOL Found=FALSE,Error=FALSE,Result = FALSE;
  struct UserData *OldUser;
  char *outstr;

  if (outstr = AllocVec(200,MEMF_PUBLIC)) // used for errormessages
  {
    if (OldUser = AllocVec(sizeof(struct UserData),MEMF_PUBLIC))
    {
      FL=HBBS_LockUserData(ACCESS_WRITE);

      if (FH=OpenFromLock(FL))
      {
        do
        {
          // keep going until the end of the file or we get a read error..
          Error=((Read(FH,OldUser,sizeof(struct UserData))==sizeof(struct UserData)) ? FALSE : TRUE );

          if (!Error)
          {
            if (OldUser->UserID==User->UserID)
            {
              Found=TRUE;
            }
          }
        }
        while (!Found && !Error);

        if (Found)
        {

          // seek to start of record.
          if (Seek(FH,0-sizeof(struct UserData),OFFSET_CURRENT) == -1)
          {
            sprintf(outstr,"Could not seek in userdata file, changes for \"%s\" (ID=%ld) have not been saved.",User->Handle,User->UserID);
            HBBS_LogError(BBSGlobal->ErrorLogFile,ERR_INFO,outstr,TYPE_WARNING);
          }
          else
          {
            if (Write(FH,User,sizeof(struct UserData)) != sizeof(struct UserData))
            {
              sprintf(outstr,"Could update userdata file, changes for \"%s\" (ID=%ld) have not been saved correctly.",User->Handle,User->UserID);
              HBBS_LogError(BBSGlobal->ErrorLogFile,ERR_INFO,outstr,TYPE_WARNING);
              HBBS_LogError(BBSGlobal->ErrorLogFile,ERR_INFO,str_userdataconsistancy,TYPE_WARNING);
            }
            else
            {
              Result = TRUE;
            }
          }
        }
        Close(FH);
      }
      else
      {
        sprintf(outstr,"Could open userdata file, changes for \"%s\" (ID=%ld) have not been saved.",User->Handle,User->UserID);
        HBBS_LogError(BBSGlobal->ErrorLogFile,ERR_INFO,outstr,TYPE_WARNING);
        HBBS_LogError(BBSGlobal->ErrorLogFile,ERR_INFO,str_userdataconsistancy,TYPE_WARNING);
        UnLock(FL);
      }

      HBBS_UnLockUserData();

      FreeVec(OldUser);
    }
    FreeVec(outstr);
  }
  return( Result );
}

/****** HBBSCommon.library/HBBS_GetDate ******************************
*
*   NAME
*      HBBS_GetDate - Get the current date in string format.
*
*   SYNOPSIS
*      datestr = HBBS_GetDate( datestr )
*                              A0
*
*      char *HBBS_GetDate(char *datestr)
*
*   FUNCTION
*      This function stores the current date in a string which must
*      be LEN_DATESTR+1 characters or longer
*
*      Example:
*
*      char mydate[LEN_DATESTR+1];
*
*      puts(HBBS_GetDate(mydate));
*
*   INPUTS
*      datestr - place to store the datestring
*
*   RESULT
*      a pointer to datestr.
*
*   NOTES
*      datestr will be in the format DD-MMM-YYYY and will be null terminated.
*
*   SEE ALSO
*      includes/hbbs/defines.h
*
*********************************************************************/

char __asm __saveds *HBBS_GetDate(register __a0 char *datestr)
{
  HBBS_GetDateString(datestr,HBBS_GetAmigaTime());
  return(datestr);
}

/****** HBBSCommon.library/HBBS_AddCfgItemQuick **********************
*
*   NAME
*      HBBS_AddCfgItemQuick - Add an item to a config file
*
*   SYNOPSIS
*      HBBS_AddCfgItemQuick( cfgfile, itemname, params)
*                            A0       A1        A2
*
*      BOOL HBBS_AddCfgItemQuick(struct CfgFileData *cfgfile,
*          UBYTE *itemname, UBYTE *params)
*
*   FUNCTION
*      This function adds items to a configfile (structure in memory)
*
*   INPUTS
*      cfgfile - pointer to a config file created or loaded using
*          HBBS_CreateConfig() or HBBS_LoadConfig()
*      itemname - An item to add
*      params - parameters for the item. in the format of a null
*          terminated string.
*
*   RESULT
*      TRUE if the item was added.
*
*   NOTES
*      This function does NOT remove items with the same name from
*      the config file, use HBBS_AddCfgItem() instead.
*
*   SEE ALSO
*      HBBS_AddCfgItem()
*
*********************************************************************/

V_BOOL __asm __saveds HBBS_AddCfgItemQuick(register __a0 struct CfgFileData *cfgfile,register __a1 char *ItemName, register __a2 char *Params)
{
  struct CfgItemData *node;

  if (cfgfile && ItemName && Params)
  {
    if (node=AllocVec(sizeof(struct CfgItemData),MEMF_PUBLIC))
    {
      if (node->node.ln_Name=DupStr(ItemName))
      {
        if (node->Params=DupStr(Params))
        {
          AddTail(cfgfile->ItemsList,(struct Node*)node);
          return(TRUE);
        }
        FreeVec(node->node.ln_Name);
      }
      FreeVec(node);
    }
  }
  return(FALSE);
}

void __asm __saveds HBBS_SaveCallsData( void )
{
  struct CfgFileData *CfgFile;
  struct NodeData *nd;
  UBYTE tmpstr[200],optionstr[200];


  if (CfgFile=HBBS_CreateConfig("HBBS:System/Data/Calls.CFG"))
  {
    HBBS_AddCfgItemQuick(CfgFile,"Today",HBBS_GetDate(tmpstr));
    HBBS_AddCfgItemQuick(CfgFile,"WeekStart",HBBS_GetWeekStartDate(tmpstr,HBBS_GetAmigaTime()));

    sprintf(tmpstr,"%d",BBSGlobal->CallsToday);
    HBBS_AddCfgItemQuick(CfgFile,"CallsToday",tmpstr);

    sprintf(tmpstr,"%d",BBSGlobal->CallsThisWeek);
    HBBS_AddCfgItemQuick(CfgFile,"CallsThisWeek",tmpstr);

    for (nd=(struct NodeData *)BBSGlobal->NodeList->lh_Head;nd->node.ln_Succ;nd=(struct NodeData *)nd->node.ln_Succ)
    {
      sprintf(optionstr,"Node_%d_Calls",nd->NodeNum);
      sprintf(tmpstr,"%d",nd->CallsToday);
      HBBS_AddCfgItemQuick(CfgFile,optionstr,tmpstr);
    }
    HBBS_SaveConfig(CfgFile);
    HBBS_FlushConfig(CfgFile);
  }

}

void __asm __saveds HBBS_LoadCallsData( void ) // Private
{
  struct CfgFileData *CfgFile;
  BOOL Loaded=FALSE;
  V_STRING TodayStr=NULL;
  UBYTE tmpstr[200];
  struct NodeData *nd;

  HBBS_GetDate(tmpstr);

  if (CfgFile=HBBS_LoadConfig("HBBS:System/Data/Calls.CFG",LCFG_NONE))
  {
    if (HBBS_GetSetting(CfgFile,(void *)&TodayStr,VTYPE_STRING,"Today",OPT_SINGLE))
    {
      if (stricmp(TodayStr,tmpstr)==0) // same day ???
      {
        HBBS_GetSetting(CfgFile,(void *)&BBSGlobal->CallsToday,VTYPE_BIGNUM,"CallsToday",OPT_SINGLE);

        for (nd=(struct NodeData *)BBSGlobal->NodeList->lh_Head;nd->node.ln_Succ;nd=(struct NodeData *)nd->node.ln_Succ)
        {
          sprintf(tmpstr,"Node_%d_Calls",nd->NodeNum);
          if (!HBBS_GetSetting(CfgFile,(void *)&nd->CallsToday,VTYPE_BIGNUM,tmpstr,OPT_SINGLE))
          {
            nd->CallsToday=0;
          }
        }
        Loaded=TRUE;
      }
    }
    
    if (TodayStr) 
    {
      FreeStr(TodayStr);
      TodayStr = NULL;
    }
                                    
                                  // *C* vvvvvvvv Should really change this name
    if (HBBS_GetSetting(CfgFile,(void *)&TodayStr,VTYPE_STRING,"WeekStart",OPT_SINGLE))
    {
      if (stricmp(TodayStr,HBBS_GetWeekStartDate(tmpstr,HBBS_GetAmigaTime())) == 0)
      {
        HBBS_GetSetting(CfgFile,(void *)&BBSGlobal->CallsThisWeek,VTYPE_BIGNUM,"CallsThisWeek",OPT_SINGLE);
      }
      else
      {
        BBSGlobal->CallsThisWeek=0;
      } 
    }
    if (TodayStr) FreeStr(TodayStr);

    HBBS_FlushConfig(CfgFile);
  }

  if (!Loaded)
  {
    BBSGlobal->CallsToday=0;
    BBSGlobal->CallsThisWeek=0;

    for (nd=(struct NodeData *)BBSGlobal->NodeList->lh_Head;nd->node.ln_Succ;nd=(struct NodeData *)nd->node.ln_Succ)
    {
      nd->CallsToday=0;
    }
    HBBS_SaveCallsData();
  }
}

/****** HBBSCommon.library/HBBS_GetTime ******************************
*
*   NAME
*      HBBS_GetTime - Get time and store in a string
*
*   SYNOPSIS
*      timestr = HBBS_GetTime( timestr )
*                    A0
*      char * HBBS_GetTime( char *timestr )
*
*   FUNCTION
*      This function stores the current time in a string which must be
*      LEN_TIMESTR+1 characters or longer.
*
*      Example:
*
*      #include <hbbs/defines.h>
*
*      char mytimestr[LEN_TIMESTR+1];
*
*      puts(HBBS_GetTime(mytimestr));
*
*   INPUTS
*      timestr - string to store the time in.
*
*   RESULT
*      A pointer to timestr.
*
*   NOTES
*      timestr will be in the format HH:MM:SS and will be null terminated.
*
*   SEE ALSO
*      includes/hbbs/defines.h
*
*********************************************************************/

char  __asm __saveds *HBBS_GetTime(register __a0 char *timestr)
{

  struct ClockData MyClock;
  ULONG AmigaTime;

  AmigaTime=HBBS_GetAmigaTime();

  Amiga2Date(AmigaTime,&MyClock);

  sprintf(timestr,"%02ld:%02ld:%02ld",
          MyClock.hour,MyClock.min,MyClock.sec);
  return(timestr);
}

/****** HBBSCommon.library/HBBS_GetDateString ************************
*
*   NAME
*      HBBS_GetDateString - Convert a packed time to a string
*
*   SYNOPSIS
*      datestring = HBBS_GetDateString( datestr, AmigaTime)
*                                       A0       D0
*
*      char * HBBS_GetDateString( char *datestr, ULONG AmigaTime)
*
*   FUNCTION
*      This function converts the packed date in "AmigaTime" to
*      "datestr" using the format DD-MMM-YYYY
*
*   INPUTS
*      datestr - a string of length LEN_DATESTR+1
*      AmigaTime - a packed date, e.g like the ones returned by
*      ReadBattClock() or HBBS_GetAmigaTime() or
*      NodeData-> User. NormalData. LastCalledDate
*      (UserData->LastCalledDate)
*
*   RESULT
*      returns a pointer to datestr
*
*********************************************************************/

char __asm __saveds *HBBS_GetDateString(register __a0 char *datestr,register __d0 ULONG AmigaTime)
{
  struct ClockData MyClock;

  Amiga2Date(AmigaTime,&MyClock);

  sprintf(datestr,"%02ld-%s-%04ld",
          MyClock.mday,monthstr[MyClock.month-1],MyClock.year);

  return(datestr);
}

/*
void __asm __saveds HBBS_DateStrFromTM(register __a0 UBYTE *datestr, register __a1 struct tm *timestruct)
{
  if (datestr && timestruct)
  {
    if (timestruct->tm_mday<0)
    {
      strcpy(datestr,"01-JAN-1970"); // date limit!
    }
    else
    {
      sprintf(datestr,"%s%d-%s-%-04d",(timestruct->tm_mday < 10) ? "0" : "",timestruct->tm_mday,monthstr[timestruct->tm_mon],timestruct->tm_year+1900);
    }
  }

}

V_BOOL __asm __saveds  HBBS_DateStrToTM(register __a0 UBYTE *datestr, register __a1 struct tm *timestruct)
{
  UBYTE tmpstr[5];
  LONG tday,tmon=-1,tyear,loop;

  strNcpy(tmpstr,datestr,2);

  if (sscanf(tmpstr,"%ld",&tday))
  {
    strftcpy(tmpstr,datestr,3,5);
    for (loop=0;loop<=12 && tmon==-1;loop++)
    {
      if (stricmp(tmpstr,monthstr[loop])==0)
      {
        tmon=loop;
      }
    }
    if (tmon>=0)
    {
      strftcpy(tmpstr,datestr,7,10);
      if (sscanf(tmpstr,"%ld",&
      tyear))
      {
        tyear-=1900;
        if ((tyear>=70) && (tmon>=0) && (tmon<12) && (tday>=1) && (tday<=31))
        {
          // all data is ok, so opy it to the timestruct!
          timestruct->tm_mday=tday;
          timestruct->tm_year=tyear;
          timestruct->tm_mon=tmon;
          return(TRUE);
        }
      }
    }
  }
  return(FALSE);
}
*/

/****** HBBSCommon.library/HBBS_DateStrToAmigaTime *******************
*
*   NAME
*      HBBS_DateStrToAmigaTime - Convert a date to a packed date
*
*   SYNOPSIS
*      HBBS_DateStrToAmigaTime( datestr )
*                               A0
*
*      ULONG HBBS_DateStrToAmigaTime( char *datestr )
*
*   FUNCTION
*      This function convert a date string "datestr" in the format
*      DD-MMM-YYYY to an AmigaTime value.
*
*      This is the how dates are converted for use in the user data
*      file.
*
*   INPUTS
*      datestr - a string of length LEN_DATESTR or longer containing
*          a date in the format DD-MMM-YYYY e.g. 05-AUG-1999
*
*   RESULT
*      A packed date, or 0 if the date was invalid.
*
*   NOTES
*      See the file HBBS:Source/Doors_User/FileLister/Main.c for
*      an example usage.
*
*********************************************************************/

ULONG __asm __saveds HBBS_DateStrToAmigaTime(register __a0 char *datestr)
{
  ULONG AmigaTime=0;
  struct ClockData MyClock={0,0,0,0,0,0,0};

  UBYTE tmpstr[5];
  LONG tday,tmon=0,tyear,loop;

  strNcpy(tmpstr,datestr,2);

  tday=atol(tmpstr);
//  if (sscanf(tmpstr,"%ld",&tday))
//  {
    strftcpy(tmpstr,datestr,3,5);
    for (loop=0;loop<=12 && tmon==0;loop++)
    {
      if (stricmp(tmpstr,monthstr[loop])==0)
      {
        tmon=loop+1; // there's no month 0 :-)
      }
    }
    if (tmon>0)
    {
      strftcpy(tmpstr,datestr,7,10);

      tyear=atol(tmpstr);
//      if (sscanf(tmpstr,"%ld",&tyear))
//      {
        if ((tyear>=1970) && (tmon>=0) && (tmon<12) && (tday>=1) && (tday<=31))
        {
          // all data is ok, so opy it to the timestruct!
          MyClock.mday=tday;
          MyClock.year=tyear;
          MyClock.month=tmon;
          AmigaTime = Date2Amiga(&MyClock);
        }
//      }
    }
//  }
  return(AmigaTime);
}

// private function
void *AllocBuffer(ULONG *BufferSize,ULONG Flags) // min 10 bytes!
{
  void *retval=NULL;

  if (*BufferSize)
  {
    if (*BufferSize<10) *BufferSize=10;
    while ((*BufferSize>10) && ((retval=AllocVec(*BufferSize,Flags))==NULL))
    {
      *BufferSize/=2; // divide buffersize by 2!
    }

  }
  return(retval);
}

/****** HBBSCommon.library/HBBS_CopyFile *****************************
*
*   NAME
*      HBBS_CopyFile - Copy a file.
*
*   SYNOPSIS
*      HBBS_CopyFile( fromfile, tofile, BufSize )
*                     A0        A1      D0
*
*      BOOL HBBS_CopyFile(char *fromfile, char *tofile, ULONG BufSize)
*
*   FUNCTION
*      This function copies a file from one location to another.
*
*      The files names can be relative or absolute.
*
*      It is a good idea to use PathOK() or AssignOK() on the input
*      and output file names
*
*   INPUTS
*      fromfile - filename to copy
*      tofile - file to create
*      BufSize - buffer size, or 0 for default buffer size.
*          values of less than 16K (1024 * 16) will be set to 16K
*   RESULT
*      TRUE if sucessful
*
*   NOTES
*      The file's comment, date, time and attributes are not copied.
*
*   SEE ALSO
*      PathOK(), AssignOK()
*
*********************************************************************/

V_BOOL __asm __saveds HBBS_CopyFile(register __a0 char *fromfile, register __a1 char *tofile,register __d0 ULONG BufSize)
{
  BOOL retval=FALSE;
  BPTR FL,FromFH,ToFH;
  struct FileInfoBlock FB;
  BOOL error=TRUE;
  ULONG nr,nw,total=0;
  UBYTE *Buffer;
  ULONG MyBufSize=BufSize;

  if (MyBufSize==0) MyBufSize=BBSGlobal->CopyBufferSize;

  if (MyBufSize < 16 * 1024) MyBufSize= 16 * 1024;

  if (FL=Lock(fromfile,ACCESS_READ))
  {
    if (Examine(FL,&FB))
    {
      if (FromFH=OpenFromLock(FL))
      {
        if (ToFH=Open(tofile,MODE_NEWFILE))
        {
          if (FB.fib_Size>0)
          {
            if (MyBufSize>FB.fib_Size) MyBufSize=FB.fib_Size; // no sense allocating to much mem
            if (Buffer=AllocBuffer(&MyBufSize,MEMF_PUBLIC))
            {
              do
              {
                error=FALSE;
                if (nr=Read(FromFH,Buffer,MyBufSize-1))
                {
                  if (nr==(nw=Write(ToFH,Buffer,nr)))
                  {
                    total+=nw;
                  } else error=TRUE;
                } else error=TRUE;
              } while (!error);
              if (error && total>=FB.fib_Size) error=FALSE; //end of file rather than error...

              FreeVec(Buffer);
            }
          }
          else
          {
            error=FALSE;
          }
          Close(ToFH);
          SetComment(tofile,FB.fib_Comment);
          SetProtection(tofile,FB.fib_Protection);
        }
        Close(FromFH);
        FL=NULL;
      }
    }
    if (FL) UnLock(FL);
  }
  if (!error)
  {
    retval=TRUE;
//    if (FL=Lock(tofile,ACCESS_READ)) // is the new file the same as the old one ?
//    {
//      total=FB.fib_Size;
//      if (Examine(FL,&FB) && total==FB.fib_Size) retval=TRUE;
//      UnLock(FL);
//    }
  }

  if (!retval) // if the old file is different then delete it..
  {
    DeleteFile(tofile);
  }
  return(retval);
}

/****** HBBSCommon.library/HBBS_NodesInList **************************
*
*   NAME
*      HBBS_NodesInList - Count nodes in a list
*
*   SYNOPSIS
*      HBBS_NodesInList( list )
*                        A0
*
*      ULONG HBBS_NodesInList( struct List *list)
*
*   FUNCTION
*      This function counts the nodes in a list
*
*   INPUTS
*      list - pointer to a struct List
*
*   RESULT
*      Amount of nodes in the list
*
*********************************************************************/

ULONG __asm __saveds HBBS_NodesInList(register __a0 struct List *list)
{
  struct Node *node;
  ULONG Nodes=0;
  if (list)
  {
    for (node=list->lh_Head;node->ln_Succ;node=node->ln_Succ)
    {
      Nodes++;
    }
  }
  return(Nodes);
}

/****** HBBSCommon.library/HBBS_AppendStrToFile **********************
*
*   NAME
*      HBBS_AppendStrToFile - Append a string to a file.
*
*   SYNOPSIS
*      HBBS_AppendStrToFile( FileName, String)
*                            A0        A1
*
*      BOOL HBBS_AppendStrToFile( UBYTE *FileName, UBYTE *String)
*
*   FUNCTION
*      This function appends a string to a file.  Great for creating
*      log files etc..
*
*      If the file does not exist then it will be created.
*
*   INPUTS
*      Filename - filename to add the string to
*      String - the string to add to the end of the file.
*
*   RESULT
*      TRUE if successful.
*
*   NOTES
*      Try not to call this function multiple times in sucession
*      when using the same file. Instead open() your file, seek() to
*      the end of it and write() your data normally as it will be faster.
*      Especially on really long files.
*
*   SEE ALSO
*      None
*
*********************************************************************/

V_BOOL __asm __saveds HBBS_AppendStrToFile(register __a0 UBYTE *FileName, register __a1 UBYTE *String)
{
  BPTR FH;
  V_BOOL retval=FALSE;

  if (AssignOK(FileName))
  {
    if (FH=Open(FileName,MODE_READWRITE))
    {
      Seek(FH,0,OFFSET_END);
      if (FPuts(FH,String) == 0) retval=TRUE;

      Close(FH);
    }
  }
  return(retval);
}

/****** HBBSCommon.library/HBBS_CreateNode ***************************
*
*   NAME
*      HBBS_CreateNode - Create a node, for use in a linked list.
*
*   SYNOPSIS
*      HBBS_CreateNode( namestr, nodesize )
*                       A0       D0
*
*      struct Node *HBBS_CreateNode( char *namestr, ULONG nodesize )
*
*   FUNCTION
*      This function creates a node and returns a pointer to it.
*
*      All the allocated memory will be cleared, so all your other
*      fields will be intialized to NULL or 0.
*
*      This function is pretty handy, as it saves you having to
*      do quite a bit of error checking.
*
*   INPUTS
*      namestr - if namestr is not NULL then the string will be
*          duplicated and stored as the ln_Name field.
*      nodesize - if you specify 0 for the nodesize, then the size of
*          the node allocated will be the sizeof(struct Node),
*          otherwise it'll allocate however many bytes you specify.
*
*   RESULT
*      a pointer to the newly allocated node, or NULL if there
*      was a problem (out of memory)
*
*   NOTES
*      Use FreeVec() on the ln_Name field and the node itself.
*      Or pass it HBBS_FreeNode()
*
*   SEE ALSO
*      HBBS_CreateList(), HBBS_FreeNode()
*
*********************************************************************/

struct Node __asm __saveds *HBBS_CreateNode(register __a0 char *namestr,register __d0 ULONG nodesize)
{
  struct Node *newnode;

  if (nodesize==0) nodesize=sizeof(struct Node);

  if (newnode=AllocVec(nodesize,MEMF_CLEAR|MEMF_PUBLIC))
  {
    if (namestr)
    {
      if (!(newnode->ln_Name=DupStr(namestr)))
      {
        FreeVec(newnode);
        newnode=NULL; // failed!
      }
    }
  }
  return(newnode);  // NULL if not enough mem, or pointer to node otherwise..
}

/****** HBBSCommon.library/HBBS_FreeNode *****************************
*
*   NAME
*      HBBS_FreeNode - Free a node structure.
*
*   SYNOPSIS
*      HBBS_FreeNode( node, RemoveFromList )
*                     A0    D0
*
*      void HBBS_FreeNode( struct Node *node, BOOL RemoveFromList)
*
*   FUNCTION
*      This function free's a node created with NewStrNode() or
*      HBBS_CreateNode() or AllocVec()
*
*      It can remove the node from whatever list it may be in too.
*
*   INPUTS
*      node - the node to FreeVec(), if the ln_Name field is not NULL
*          then it will be FreeVec()'d
*      RemoveFromList - if TRUE the node will be Remove()'d from
*          whatever list it is in.
*
*   RESULT
*      None
*
*   NOTES
*      Don't pass it nodes that had an ln_Name field allocated in a
*      way other than AllocVec()
*      If the node has any other fields, then your are still
*      responsible for freeing them.
*
*   SEE ALSO
*      HBBS_FreeListNodes(), FreeVec(), AllocVec(), NewStrNode()
*      HBBS_CreateNode(), HBBS_CreateList()
*
*********************************************************************/

void __asm __saveds HBBS_FreeNode(register __a0 struct Node *node,register __d0 V_BOOL RemoveIt)
{
  if (node)
  {
    if (RemoveIt) Remove(node);
    if (node->ln_Name) FreeStr(node->ln_Name);
    FreeVec(node);
  }
}

/****** HBBSCommon.library/HBBS_FreeListNodes ************************
*
*   NAME
*      HBBS_FreeListNodes - Description
*
*   SYNOPSIS
*      HBBS_FreeListNodes( list )
*                          A0
*
*      void HBBS_FreeListNodes( struct List *list )
*
*   FUNCTION
*      This function free's all the nodes and their ln_Name fields
*      in the list specified using FreeVec(), but does not free the
*      list itself.  So it leaves you with an empty list.
*
*   INPUTS
*      list - a linked list.
*
*   RESULT
*      None
*
*   NOTES
*      Make sure the nodes were allocated using AllocVec() or
*      HBBS_CreateNode() or NewStrNode().  Each node is FreeVec()'d
*      along with it's ln_Name field.  You are still responsible for
*      freeing all other fields of the nodes.
*
*********************************************************************/

void __asm __saveds HBBS_FreeListNodes(register __a0 struct List *list)
{
  // free all ln_Names of each node in a list and remove all nodes from the list!
  // but we DON't free the list itself, we just leave it empty

  struct Node *node;
  if (list)
  {
    while (list->lh_Head->ln_Succ)
    {
      node=list->lh_Head;
      FreeStr(node->ln_Name);
      Remove(node);
      FreeVec(node);
    }
  }
}

/****** HBBSCommon.library/HBBS_SendOLM ******************************
*
*   NAME
*      HBBS_SendOLM - Send an OnLine Message
*
*   SYNOPSIS
*      HBBS_SendOLM( FromNode, FromPRG, ToNode, MessageStr, Pri )
*                    D0        A0       D1      A1          D2
*
*      BOOL HBBS_SendOLM( V_SMALLNUM FromNode, UBYTE *FromPRG,
*          V_SMALLNUM ToNode, UBYTE *MessageStr, BYTE Pri )
*
*   FUNCTION
*      Sends an OLM (OnLine Message) to another node.
*
*   INPUTS
*      FromNode - 0 unless you are calling this from a door that has
*          been started by the a node in which case set this to
*          NodeData->NodeNum+1
*      FromPRG - the name of your door or program in a short,
*          null terminatedstring.
*      ToNode - the node to send the message to, starting at 0.
*      MessageStr - the string to send to the other node.
*      Pri - 0, for a normal priority message. or 1 for a higher priority message.
*
*   RESULT
*      TRUE if an OLM was sent.
*
*   NOTES
*      The OLM will only be sent to the node specified if that node
*      exists and if a user is online.
*
*      Doors can delay the reception of OLM's by using the
*      DOOR_GetLine() flag of GL_NOOLM
*
*      OLM's are only displayed on other nodes when the other nodes
*      place a call to DOOR_GetLine(). So an OLM will never be
*      displayed (say) during the middle of a file listing.  This is
*      so that the user will ALWAYS see each OLM that is send
*
*********************************************************************/

V_BOOL __asm __saveds HBBS_SendOLM(register __d0 V_SMALLNUM FromNode,register __a0 UBYTE *FromPRG,register __d1 V_SMALLNUM ToNode,register __a1 UBYTE *MessageStr,register __d2 BYTE Pri)
{
  // frommnode should be 0 if something other than a door is sending the OLM
  // otheriwse fromnode should be set to your node number plus 1
  // e.g. N_ND->NodeNum+1, if fromnode


  V_BOOL retval=FALSE;
  struct OLMNode *NewOLM;
  struct NodeData *nd;
  struct NodeData *fnd;
  struct Message *NewMsg;

  // lets see if we can send an eall, node must be open and initalised with
  // a user loggined in and the status must be online
  if ((MessageStr) &&
      (FromNode>=0 ) && (FromNode<=BBSGlobal->BBSNodes) &&
      (nd=HBBS_NodeDataPtr(ToNode)) &&
      (nd->Status==STAT_ONLINE) &&
      (nd->OLMList) &&
      (nd->LoginType!=LOGIN_NONE) &&
      (nd->OnlineStatus==OS_ONLINE))
  {
    if (NewOLM=(struct OLMNode *)HBBS_CreateNode(MessageStr,sizeof(struct OLMNode)))
    {
      if (NewOLM->node.ln_Name=DupStr(MessageStr))
      {
        NewOLM->node.ln_Pri=Pri;
        HBBS_GetTime(NewOLM->Time);
        HBBS_GetDate(NewOLM->Date);
        NewOLM->FromNode=FromNode;
        if ((FromNode) && (fnd=HBBS_NodeDataPtr(FromNode-1)))
        {
          if (fnd->User->Valid) // NewOLD has already been blanked by CreateNode() so the string is already null terminated..
          {
            strNcpy(NewOLM->Handle,fnd->User->CallData->Handle,LEN_HANDLE);
          }
        }
        if (FromPRG)
        {
          strNcpy(NewOLM->FromPRG,FromPRG,LEN_HANDLE);
        }

        // *C* add semaphore checking to the nd->OLMList
        // maybe forbid()/permit() it ?

        Enqueue(nd->OLMList,(struct Node*)NewOLM);
        nd->OLMCount++;
        if (!(nd->NodeFlags & NFLG_OLMSWAITING)) nd->NodeFlags+=NFLG_OLMSWAITING;

        if (nd->OLMPort)
        {
          if (NewMsg=AllocVec(sizeof (struct Message),MEMF_PUBLIC|MEMF_CLEAR))
          {
            NewMsg->mn_Node.ln_Type = NT_MESSAGE;
            NewMsg->mn_ReplyPort=NULL;
            NewMsg->mn_Length=sizeof(struct Message);

            PutMsg(nd->OLMPort,NewMsg);
          }
        }
        retval=TRUE;
      }
    }
  }
  return(retval);
}

/****** HBBSCommon.library/FGetsR ************************************
*
*   NAME
*      FGetsR - Read a file backwards!
*
*   SYNOPSIS
*      FGetsR( FH, Buffer, BufferLen )
*              A0  A1      D0
*
*      BOOL FGetsR( BPTR FH, UBYTE *Buffer, LONG BufferLen )
*
*   FUNCTION
*      This function is the same as the dos.library/FGets() only it
*      works backwards from the current file position.
*
*      Example:
*
*      Use this to read a text file backwards!
*
*      Say you had a text file containing the following lines:
*
*      line one
*      line two
*      line three
*      line four
*      line five
*
*      And you Open()'d that file, then called FGetsR() 5 times and
*      printed each buffer to the screen, the output would be:
*
*      line five
*      line four
*      line three
*      line two
*      line one
*
*
*   INPUTS
*      FH - an Open()'ed file.
*      Buffer - pointer to a buffer to store a line of text in.
*      BufferLen - the size of the buffer.
*
*   RESULT
*      TRUE if a line of text was read.
*
*   NOTES
*      See the file HBBS:Source/Doors_User/FileLister/Main.c for an
*      example usage as it uses it to reverse scan a filelist!
*
*      This routine is not particualy fast, as quite alot of Seek()ing
*      has to be done, but is good enough for most purposes.
*      (it uses a 255 byte buffer)
*
*   SEE ALSO
*      dos.library/FGets(), dos.library/Open(), dos.library/Close()
*
*********************************************************************/

BOOL __asm __saveds FGetsR(register __a0 BPTR FH,register __a1 UBYTE *Buffer,register __d0 LONG BufferLen)
{

  // fucking amazing routine to read a file line by line but BACKWARDS from the current
  // position!! Whey hay!!

  LONG nr,where,back,foundpos=0;
  BOOL retval=TRUE,sof=FALSE,error=FALSE;

  UBYTE *strptr=NULL;


  Buffer[0]=0;
  BufferLen--;

  where=Seek(FH,0,OFFSET_CURRENT);  // start of file already ?
  if (where<=0) //start of file or error
  {
    retval=FALSE;
  }
  else
  {
    do
    {
      back=255;
      if (where<255) back=where;

      if (back>BufferLen) back=BufferLen;

      if ((where=Seek(FH,0-back,OFFSET_CURRENT))!=-1)
      {
        where-=back;

        if (nr=Read(FH,Buffer,back))
        {
          Buffer[nr]=0;
          if (strptr=strrchr(Buffer,'\n'))
          {
            foundpos=where+(strptr-Buffer);
            if (Seek(FH,foundpos+1,OFFSET_BEGINNING)==-1)
            {
              error=TRUE;
            }
          }
          else
          {
            if (where==0)
            {
              if (Seek(FH,0,OFFSET_BEGINNING)==-1)
              {
                error=TRUE;
              }
              else
              {
                sof=TRUE;
                foundpos=1;
              }
            }
            else
            {

              if (Seek(FH,0-nr,OFFSET_CURRENT)!=-1)
              {
                where-=nr;
              }
              else
              {
                error=TRUE;
              }
            }

          }
        }
        else
        {
          error=TRUE;
        }

      }
    } while (strptr==NULL && !error && !sof);

    Buffer[0]=0;
    if (strptr || sof )
    {
      FGets(FH,Buffer,BufferLen);
      Seek(FH,foundpos ? foundpos-1 : 0,OFFSET_BEGINNING);
    }
  }

  return(retval);

}

/****** HBBSCommon.library/HBBS_HandleNameOK *************************
*
*   NAME
*      HBBS_HandleNameOK - Check a handle name
*
*   SYNOPSIS
*      HBBS_HandleNameOK( checkstr )
*                         A0
*
*      BOOL HBBS_HandleNameOK( char *checkstr )
*
*   FUNCTION
*      This function checks to see if the users handle contains any
*      invalid characters that might cause HBBS problems.
*
*   INPUTS
*      checkstr - the string to check for invalid characters.
*
*   RESULT
*      TRUE if the handle is OK for HBBS to use.
*
*   NOTES
*      Currently the characters not allowed are ,;:()|#*?
*
*********************************************************************/

V_BOOL __asm __saveds HBBS_HandleNameOK(register __a0 char *checkstr)
{
  LONG loop;

  // check each char in the string against a list of char's that are
  // not allowed in a handle name.
  //
  // the list of chars contain characters that have meanings when
  // Loading Configs or Wildcards
  //
  // This should be used by anything that lets the user change their
  // handle, such as the account editor and user settings doors

  for (loop = 0;checkstr[loop];loop++)
  {
    if (strchr("=;?*#",checkstr[loop])) return(FALSE);
  }
  return(TRUE);
}

/****** HBBSCommon.library/HBBS_GetFileSize **************************
*
*   NAME
*      HBBS_GetFileSize - Get the size of a file, in bytes
*
*   SYNOPSIS
*      HBBS_GetFileSize( filename )
*                        A0
*
*      V_BIGNUM HBBS_GetFileSize( char *filename )
*
*   FUNCTION
*      This function returns the filesize of a given file in bytes.
*
*   INPUTS
*      filename - name of the file.
*
*   RESULT
*      size of the file, in bytes.
*
*********************************************************************/

V_BIGNUM __asm __saveds HBBS_GetFileSize(register __a0 char *filename)
{
  BPTR FL;
  struct FileInfoBlock FB;
  V_BIGNUM retval=0; // *C* this should really return -1 for an error... as
                     // a file size of 0 is valid!

  if (filename)
  {
    if (FL=Lock(filename,ACCESS_READ))
    {
      if (Examine(FL,&FB))
      {
        retval=FB.fib_Size;
      }
      UnLock(FL);
    }
  }
  return(retval);
}

/****** HBBSCommon.library/HBBS_FindConfFile *************************
*
*   NAME
*      HBBS_FindConfFile - File a file in a conference
*
*   SYNOPSIS
*      HBBS_FindConfFile( ConfNum, filename, fullname )
*                         D0       A0        A1
*
*      BOOL HBBS_FindConfFile( V_BIGNUM ConfNum, char *filename,
*          char *fullname)
*
*   FUNCTION
*      This function searches the download paths of the conf for the
*      filename specified and returns TRUE if the file is found.
*      The full path and filename are then stored in fullname
*      if fullname is not a null pointer
*
*   INPUTS
*      ConfNum - conference number, starting at 1.
*      filename - name of the file to find
*      fullname - NULL or a pointer to store the full path and
*          filename of the file, if it is found.
*
*   RESULT
*      TRUE if the file was found.
*
*********************************************************************/

V_BOOL __asm __saveds HBBS_FindConfFile(register __d0 V_BIGNUM ConfNum,register __a0 char *filename,register __a1 char *fullname)
{
  V_BOOL retval=FALSE;
  struct ConfData *Conf;
  char tmpstr[1024];
  struct Node *node;


  // valid conf num ?
  if (ConfNum>=1 && ConfNum<=BBSGlobal->Conferences)
  {
    Conf=(struct ConfData *)GetNode(BBSGlobal->ConfList,ConfNum-1);

    // any download paths to search ??
    if (Conf->Download)
    {
      for (node=Conf->Download->lh_Head;!retval && node->ln_Succ;node=node->ln_Succ)
      {
        strcpy(tmpstr,node->ln_Name);
        strcat(tmpstr,filename);
        if (PathOK(tmpstr))
        {
          retval=TRUE;
          if (fullname)
          {
            strcpy(fullname,tmpstr);
          }
        }
      }
    }
  }
  return(retval);

}

char __asm __saveds *HBBS_AccessName(register __d0 V_SMALLNUM Access)
{
  struct Node *node;
  V_BIGNUM loop;
  char tmpstr[10];
  sprintf(tmpstr,"%d",Access);

  // check all access levels!
  for (loop=0;loop<BBSGlobal->AcsLevels;loop++)
  {
    // get the level number node.
    if(node=GetNode(BBSGlobal->AcsLevelList,loop))
    {
      // same as the level we want to find ?
      if (stricmp(tmpstr,node->ln_Name)==0)
      {
        // yup, so get the name
        if(node=GetNode(BBSGlobal->AcsLevelNames,loop))
        {
          // and return it!
          return(node->ln_Name);
        }
      }
    }
  }
  return(NULL);
}

/****** HBBSCommon.library/HBBS_ApplyAccountDefaults *****************
*
*   NAME
*      HBBS_ApplyAccountDefaults - Apply user account defaults.
*
*   SYNOPSIS
*      HBBS_ApplyAccountDefaults( User )
*                                 A0
*
*      void HBBS_ApplyAccountDefaults( struct UserData *User )
*
*   FUNCTION
*      This function applies defaults to a user account.
*
*   INPUTS
*      User - pointer to an allocated and initialised user data
*          structure.
*          The UserData->Access record must be correct and verified
*          for this function to work.
*
*   RESULT
*      None
*
*   SEE ALSO
*      HBBS:Docs/Configuration/AccountDefaults.Guide
*      HBBS_InitUserData(), HBBS_Adduser()
*
*********************************************************************/

void __asm __saveds HBBS_ApplyAccountDefaults( register __a0 struct UserData *User )
{
  char tmpstr[BIG_STR];
  struct CfgFileData *Cfg;
  char *nameptr;
  char *pathname="HBBS:System/Data/AccountDefaults/";
  V_BIGNUM FreeUploadBytes=0;
  V_STRING UserType=NULL;

  // try loading a config file from
  // HBBS:System/Data/AccountDefaults/<acslevel>.CFG
  // and if that files then
  // HBBS:System/Data/AccountDefaults/<acsname>.CFG

  sprintf(tmpstr,"%Level_s%ld.CFG",pathname,User->Access);
  if (!(Cfg = HBBS_LoadConfig(tmpstr,LCFG_NONE)))
  {
    //get the actual name for the access level.
    if (nameptr = HBBS_AccessName(User->Access))
    {
      sprintf(tmpstr,"%s%s.CFG",pathname,nameptr);
      if (!(Cfg = HBBS_LoadConfig(tmpstr,LCFG_NONE)))
      {

        sprintf(tmpstr,"%s%s.CFG",pathname, User -> ConfAcsDataFile );
        if (!(Cfg = HBBS_LoadConfig(tmpstr,LCFG_NONE)))
        {
          sprintf(tmpstr,"Could not open any file in %s for access level %ld while attempting to apply account defaults",pathname,User->Access);
          HBBS_LogError(BBSGlobal->ConfigLogFile,ERR_INFO,tmpstr,TYPE_WARNING);
        }
      }
    }
  }

  if (Cfg)
  {
    HBBS_GetSetting(Cfg,(void *)&User->TimeAllowed,VTYPE_BIGNUM,"TimeAllowed",OPT_SINGLE);
    HBBS_GetSetting(Cfg,(void *)&User->ChatAllowed,VTYPE_BIGNUM,"ChatAllowed",OPT_SINGLE);
    HBBS_GetSetting(Cfg,(void *)&User->CallsAllowed,VTYPE_BIGNUM,"CallsAllowed",OPT_SINGLE);
    HBBS_GetSetting(Cfg,(void *)&User->BytesAllowed,VTYPE_BIGNUM,"BytesAllowed",OPT_SINGLE);
    HBBS_GetSetting(Cfg,(void *)&User->Language,VTYPE_BIGNUM,"LanguageNumber",OPT_SINGLE);
    HBBS_GetSetting(Cfg,(void *)&User->PreferredConf,VTYPE_BIGNUM,"PreferredConf",OPT_SINGLE);
    HBBS_GetSetting(Cfg,(void *)&User->LastConf,VTYPE_BIGNUM,"LastConf",OPT_SINGLE);
    HBBS_GetSetting(Cfg,(void *)&User->BytesRatio,VTYPE_BIGNUM,"BytesRatio",OPT_SINGLE);
    HBBS_GetSetting(Cfg,(void *)&User->FilesRatio,VTYPE_BIGNUM,"FilesRatio",OPT_SINGLE);
    HBBS_GetSetting(Cfg,(void *)&FreeUploadBytes,VTYPE_BIGNUM,"FreeUploadBytes",OPT_SINGLE);
    HBBS_GetSetting(Cfg,(void *)&UserType,VTYPE_STRING,"UserType",OPT_SINGLE);

    if (UserType)
    {
      if (stricmp(UserType,"NORMAL") == 0 ) User->UserType = USERTYPE_NORMAL;
      if (stricmp(UserType,"EXPERT") == 0 ) User->UserType = USERTYPE_EXPERT;

      FreeVec(UserType);
    }

    // add the free upload bytes if the user hasn't uploaded anything yet!
    if (FreeUploadBytes && !User->UploadBytes)
    {
      User->UploadBytes = FreeUploadBytes;
    }

    HBBS_FlushConfig(Cfg);
  }
}

/****** HBBSCommon.library/HBBS_OkToUpload ***************************
*
*   NAME
*      HBBS_OkToUpload - checks to see if it's ok to upload a file
*
*   SYNOPSIS
*      HBBS_OkToUpload( filename )
*                       A0
*
*      ULONG HBBS_OkToUpload( char *filename )
*
*   FUNCTION
*      this checks to see if the filename in question (filepart only)
*      is currently being uploaded on another node, or is in another,
*      open, node's playpen directory that is waiting to be checked.
*
*   INPUTS
*      filename - null terminated filename (may contain a path, which
*          will be ignored)
*
*   RESULT
*      This function returns the following flags:
*
*      SKIPF_NONE - if it's OK to upload this file.
*      SKIPF_INVALIDORNONAME - if there is a problem with the filename
*      SKIPF_ONANOTHERNODE - if another using is uploading or has just
*          uploaded this file.
*
*********************************************************************/

ULONG __asm __saveds HBBS_OkToUpload(register __a0 char *filename)
{
  struct NodeData *nd;
  ULONG SkipReason=SKIPF_NONE;
  char *tmpname;

  if (!filename)
  {
    SkipReason = SKIPF_INVALIDORNONAME;
  }
  else
  {
    for (nd = (struct NodeData *)BBSGlobal->NodeList->lh_Head ; (SkipReason == SKIPF_NONE) && nd->node.ln_Succ ; nd =(struct NodeData *) nd->node.ln_Succ)
    {
      if (nd->Status == STAT_ONLINE || nd->Status == STAT_READY)
      {
        if (nd->OnlineStatus == OS_ONLINE && nd->TransferringFile && stricmp(filename,nd->TransferReceiveName)==0)
        {
          SkipReason = SKIPF_ONANOTHERNODE;
        }
        else
        {
          if (tmpname = AllocVec(strlen(filename) + strlen(nd->NodeSettings.NodePlayPen) + 2,MEMF_PUBLIC))
          {
            strcpy(tmpname,nd->NodeSettings.NodePlayPen);
            strcat(tmpname,filename);

            if (PathOK(tmpname))
            {
              SkipReason = SKIPF_ONANOTHERNODE;
            }
            FreeVec(tmpname);
          }
        }
      }
    }
  }
  return(SkipReason);
}

/****** HBBSCommon.library/HBBS_ClearCommentsFromList ****************
*
*   NAME
*      HBBS_ClearCommentsFromList - Clears coments from a list
*
*   SYNOPSIS
*      HBBS_ClearCommentsFromList( list )
*                                  A0
*
*      void HBBS_ClearCommentsFromList( struct List *list )
*
*   FUNCTION
*      This routine removes comments and trailing spaces from the
*      ln_Name fields of each node in the specified list.  The memory
*      allocated for ln_Name is unchanged, just the contents of it are
*      modified.
*
*      A comment is anything after and including a semicolon (;)
*      character.
*
*      E.G.
*
*      Say you had a couple of nodes in a list with the ln_Name files
*      like this:
*
*      "Information1  ; this is a comments     "
*      "Information2; So is this.   "
*
*      In the above case, each ln_Name would be stripped to the
*      following:
*
*      "Information1"
*      "Information2"
*
*      Quite a handy routing for config and list files.. :-)
*
*      This is used by the MakeFreeDL door, check the source for an
*      example.
*
*   INPUTS
*      list - a linked list.
*
*   RESULT
*      None
*
*********************************************************************/

void __asm __saveds HBBS_ClearCommentsFromList(register __a0 struct List *list)
{
  struct Node *node;

  if (list)
  {
    for ( node = list->lh_Head ; node->ln_Succ ; node = node->ln_Succ )
    {
      StripComments(node->ln_Name);
      RemoveSpaces(node->ln_Name);
    }
  }
}


struct List __asm __saveds *HBBS_LoadKey( void )
{
  BPTR FH;
  BPTR FL;
  BPTR KEYLOCK;
  BPTR NFL;
  struct FileInfoBlock *FB;
  UBYTE buffer[bufflen];
  struct List *FileList=NULL;
  BOOL error = FALSE,
       Done  = FALSE;
  LONG More;

  if (FL = Lock(SECKEYDIR, ACCESS_READ))
  {

    NFL=CurrentDir(FL);
//    UnLock(NFL);

    // This next lock is just to throw crackers off!
    // as a snoopdos log will give you
    // lock SECKEYDIR
    // CurrentDir SECKEYDIR
    // Lock <wrongdir>
    // Lock <keyfile>
    // which makes people think the keyfile is in <wrongdir>

    if (KEYLOCK = Lock("HBBS:System/KeyFiles",ACCESS_READ))
    {
      UnLock(KEYLOCK);
    }

    if (KEYLOCK = Lock("HydraBBS.Key",ACCESS_READ)) // hahaha!!!
    {
      UnLock(KEYLOCK);
    }

    if (FB=AllocVec(sizeof(struct FileInfoBlock),MEMF_PUBLIC))
    {
      if (Examine(FL,FB))
      {
        do
        {
          if (More=ExNext(FL,FB))
          {
            if (FB->fib_DirEntryType<0) // File ? or DIR
            {
              if (stricmp(FB->fib_FileName,SECKEYNAME) == 0)
              {
                if (KEYLOCK = Lock(FB->fib_FileName,ACCESS_READ)) // assumes last directory is the SECKEYDIR
                {
                  if (FH=OpenFromLock(KEYLOCK))
                  {
                    if (FileList=HBBS_CreateList())
                    {
                      while (FGets(FH,buffer,bufflen) && !error)
                      {
                        stripcr(buffer);
                        if (NewStrNode(buffer,FileList))
                        {
                          error=TRUE;
                        }
                      }
                    }
                    Close(FH);
                  }
                  else
                  {
                    UnLock(KEYLOCK);
                  }
                }
                Done=TRUE;
              }
            }
          }
        }
        while (More && !Done);
      }
      FreeVec(FB);
    }
//    UnLock(NFL);
  }
  if (error)
  {
    FreeStrList(FileList);
    FileList=NULL;
  }
  return(FileList);
}

/****** HBBSCommon.library/HBBS_AddCfgList ***************************
*
*   NAME
*      HBBS_AddCfgList - Add an item to a config file, overwriting old
*          items with the same name.
*
*   SYNOPSIS
*      HBBS_AddCfgList( cfgfile, list, itemname)
*                       A0       A1    A2
*
*      BOOL HBBS_AddCfgList(struct CfgFileData *cfgfile,
*          struct List *list, UBYTE *itemname)
*
*   FUNCTION
*      This function adds items to a configfile (structure in memory)
*      from a linked list, using Itemname as the base name and
*      each ln_Name field from the supplied list as the paramater
*
*      Each item that is added the the config has it's name created
*      like this: "<itemname>_<num>" where num starts at 1
*
*      e.g.  say mylist has three nodes and you call
*
*      HBBS_AddCfgList( cfg, mylist, "fred");
*
*      the items will appear in the config file as:
*
*      Fred_1=<ln_Name field of first node in mylist>
*      Fred_2=<ln_Name field of second node in mylist>
*      Fred_3=<ln_Name field of third node in mylist>
*
*   INPUTS
*      cfgfile - pointer to a config file created or loaded using
*          HBBS_CreateConfig() or HBBS_LoadConfig()
*      list - a linked list.
*      itemname - Base name of item (null terminated string)
*
*   RESULT
*      TRUE if all the items were added to the config file.
*      If not ALL of the items could be added then all the ones
*      that were added will be removed.
*
*   NOTES
*      Make sure each node has a non-null ln_Name field.
*
*   SEE ALSO
*      HBBS_AddCfgItem(), HBBS_AddCfgItemQuick()
*
*********************************************************************/

BOOL __asm __saveds HBBS_AddCfgList(register __a0 struct CfgFileData *cfgfile,register __a1 struct List *list,register __a2 char *itemname)
{
  struct Node *node;
  int count=1;
  BOOL error=FALSE;
  char tmpstr[BIG_STR];

  sprintf(tmpstr,"%s_#?",itemname);
  HBBS_RemoveCfgItem(cfgfile,tmpstr);

  for (node=list->lh_Head;node->ln_Succ && !error ;node=node->ln_Succ)
  {
    sprintf(tmpstr,"%s_%d",itemname,count++);
    if (!HBBS_AddCfgItem(cfgfile,tmpstr,node->ln_Name))
    {
      error=TRUE;
    }
  }

  if (error)
  {
    sprintf(tmpstr,"%s_#?",itemname);
    HBBS_RemoveCfgItem(cfgfile,tmpstr);
  }
  return((BOOL)!error); // return TRUE if all OK, or FALSE if there was an error
}

/****** HBBSCommon.library/HBBS_GetWeekStartDate *********************
*
*   NAME
*      HBBS_GetWeekStartDate - Get the date of the week start in
*          string format.
*
*   SYNOPSIS
*      datestring = HBBS_GetWeekStartDate( datestr, AmigaTime)
*                                          A0       D0
*
*      char * HBBS_GetWeekStartDate( char *datestr, ULONG AmigaTime)
*
*   FUNCTION
*      This function converts the packed date in "AmigaTime" to
*      "datestr" using the format DD-MMM-YYYY
*      And "rounds it down" to the nearest monday
*
*      e.g. if today is Wednesday August 3rd, (03-AUG-1999)
*      this datestr would contain "01-AUG-1999"
*
*      This function works happily over month and year boundaries too.
*
*   INPUTS
*      datestr - a string of length LEN_DATESTR+1
*      AmigaTime - a packed date, e.g like the ones returned by
*          ReadBattClock() or HBBS_GetAmigaTime() or
*          NodeData-> User. NormalData. LastCalledDate
*          (UserData->LastCalledDate)
*
*   RESULT
*      returns a pointer to datestr
*
*********************************************************************/

char __asm __saveds *HBBS_GetWeekStartDate(register __a0 char *datestr, register __d0 ULONG AmigaTime)
{
  ULONG todaytime;
  struct ClockData todayDT;

  todaytime = HBBS_GetAmigaTime();
  Amiga2Date(todaytime,&todayDT);

  /* .wday 0 = sunday, but a BBS week start is MONDAY because people trade all weekend..
   * so we set the .wday to (an invalid) 7 so that when we take -1 from it
   * we can remove the right amount of seconds from the todaytime to give us
   * the nearest previous monday to today
   */

  if (todayDT.wday == 0) todayDT.wday = 7;

  todaytime -= 86400 * (todayDT.wday-1);

  return(HBBS_GetDateString(datestr,todaytime));
}

