utils.h
=============================================================================

    Various utility functions prototyped in the file UTILS.H


	BEEP
	-------------------------------------------------------------------------

		Synopsis  Emit a short beep through the PC speaker.

		Syntax    void beep();

		Remarks   Emit a short beep through the PC speaker. This does not
		          produce a sound on the remote computer. The duration and
		          the frequency are preset.

		Return    Nothing.

		See also  bleep(), sound(), nsound(), delay()

		Example   if( there_is_error ){
		              printf("error!");  /* visual indication */
		              beep();            /* alert the user!   */
		          }


	BLEEP
	-------------------------------------------------------------------------

		Synopsis  Emit a bleeping sound through the PC speaker.

		Syntax    void bleep();

		Remarks   Plays a bleeping sound through the PC speaker. Note that
		          this does not work on the remote end (if any), but only
		          on the local computer. Unlike beep(), this sound is a bit
		          more elaborate, however, its exact nature defies depiction;
		          you have to hear it on your own.

		Return    Nothing.

		See also  beep(), sound(), nosound(), delay()

		Example   if( needs_attention ){
		              printf("Hello, World!");  /* say hi to user  */
		              bleep();                  /* and say hi too! */
		          }


	C2PSTR
	-------------------------------------------------------------------------

		Synopsis  Convert a C-style string to Pascal.

		Syntax    char* c2pStr(char *dest, const char *src);

		Remarks   Convert a C-style NUL-terminated string to a Pascal string.
		          Pascal strings are limited to 255 bytes and they store the
		          length of the string as the first character in the array.
		          'dest' should be large enough to hold the result. Note that
		          if 'src' is longer than the maximum of 255 characters, it
		          will be truncated to fit within those limits.

		Return    dest

		See also  p2cStr()


    ISWHITE
    -------------------------------------------------------------------------

        Synopsis  Checks if a character is whitespace

        Syntax    int iswhite(int c);

        Remarks   Unlike the standard isspace() which in some implementations
                  fails on high ASCII characters, this function works just
                  fine with them. It checks for spaces, tabs (horizontal and
                  vertical), new lines, and carriage returns. Even though
                  the parameter 'c' is declared of int type, only the low
                  order 8 bits are examined.

        Return    Non-zero if whitespace; 0 any other character


    HASH
    -------------------------------------------------------------------------

        Synopsis  Computes the ELF hash of a string

        Syntax    ulong hash(const char *s);

        Remarks   This function computes the hash value of the string 's'
                  using the popular UNIX ELF algorithm.

        Return    The 32-bit hash value


    FILECRC32
    -------------------------------------------------------------------------

        Synopsis  Compute the 32-bit CRC of a file.

        Syntax    int fileCRC32(const char *path, ulong *CRC32);

		Remarks   Compute the 32-bit CRC of the file 'path'. The CRC is
		          based on the standard polynomial 0x14C11DB7, which is
		          the same used in PKZIP and Ethernet (ANSI X3.66). This
		          is a fast table-driven implementation. The resulting
		          CRC will be stored in the location pointed to by 'crc32'.

		Return	  on file I/O error, returns -1
		          on success, returns 0

		See also  bufCRC32()

		Example   char  fileName[] = "C:\\AUTOEXEC.BAT";
		          ulong crc;
		          if( -1 != fileCRC32(fileName, &crc) ){
		              printf("32-bit CRC is %08lX\n", crc);
		          }


	BUFCRC32
	-------------------------------------------------------------------------

		Synopsis  Calculate the 32-bit of a buffer.

		Syntax    ulong bufCRC32(const char *buf, size_t buflen);

		Remarks   Compute the 32-bit CRC of a buffer 'buf' with length of
		          'buflen' bytes. Note that the buffer does not have to
		          be a string. The calculation is based on the standard
		          polynomial 0x14C11DB7, which is the same used in PKZIP
		          and Ethernet (ANSI X3.66). This is a fast table-driven
		          implementation.

		Return    the 32-bit CRC of the buffer.

		See also  fileCRC32()

		Example   char *buf = "This is a sample buffer";
		          printf("CRC: %08lX\n", bufCRC32(buf, strlen(buf)));


    P2CSTR
    -------------------------------------------------------------------------

        Synopsis  Convert Pascal to C style string

        Syntax    char* p2cStr(char *dest, const char *src, int *len = 0);

        Remarks   This function is the reverse of c2pStr(). It converts the
                  'src' string which is Pascal-style (up to 255 characters
                  long, with the length byte preceding the string, no NUL
                  terminator) to a regular C string in 'dest'. Note that
                  'dest' must be large enough to accomodate the result. The
                  last parameter, 'len' is optional. If this pointer is not
                  NULL (default), the length of the string will be stored
                  there.

        Return    dest


    MTOL
    -------------------------------------------------------------------------

        Synopsis  Converts a MegaNum into a long

        Syntax    long mtol(const char *s);

        Remarks   This function is useful for programming with the Remote
                  Imaging Protocol (RIP) which uses the so-called MegaNums
                  (base 36) numbers. The string 's' is a MagaNum which this
                  function converts to a regular long integer.

        Return    Converted MegaNum value

        See also  ltom()


    LTOM
    -------------------------------------------------------------------------

        Synopsis  Convert a long interger to MegaNum

        Syntax    char* ltom(long value);

        Remarks   This function is the reverse of mtol(). It converts the
                  long integer 'value' into a base-36 MegaNum string. Useful
                  for programming with RIP. Note that this routine uses an
                  internal static buffer which is overwritten with each call.

        Return    base-36 number (string representation)

        See also  mtol()


	CSTRLEN
	-------------------------------------------------------------------------

		Synopsis  Compute length of string without special characters.

		Syntax    size_t cstrlen(const char *string);

		Remarks	  Compute the length of the string 'string' without the
		          tilde ('~') characters. These are used to mark the
		          highlighted portion of the string and therefore are not
		          displayable. One class that uses these strings, is the
		          zCommandBar, for the command display items.

		Return    Length of the displayable string.

		See also  zCommandBar, zCommandItem

		Example	  assert( 6 == cstrlen("[~Q~uit]") );


	CTRLTOARROW                                                     CTL2ARR.C
	-------------------------------------------------------------------------

		Synopsis  Convert Ctrl+letter to keyboard constant code.

		Syntax    ushort CtrlToArrow(ushort aCode);

		Remarks   Convert the Ctrl+letter combinations to the appropriate
		          kb???? constant. The following conversions are performed
		          (all other keyboard codes are passed untouched):

		                    Original |  New value
		                 ------------+---------------
		                     Ctrl+S  |  Left arrow
		                     Ctrl+D  |  Right arrow
		                     Ctrl+E  |  Up arrow
		                     Ctrl+X  |  Down arrow
		                     Ctrl+A  |  Ctrl+Left
		                     Ctrl+F  |  Ctrl+Right
		                     Ctrl+G  |  Delete
		                     Ctrl+V  |  Insert
		                     Ctrl+R  |  Page Up
		                     Ctrl+C  |  Page Down
		                     Ctrl+H  |  Backspace
		                 ------------+---------------

                  These conversions are useful especially in the case of
                  remote key processing which will send the Ctrl+letter
                  combination and you might want to use it as a direction
                  key code. Note that the keyboard handler class zKeyboard
                  already does that conversion for you.

		Return    The converted value, if applicable or the original code.

		See also  zKeyboard


    NEWSTR
    -------------------------------------------------------------------------

        Synopsis  Duplicate a string

        Syntax    char* newStr(const char *src);

        Remarks   This function allocates space and then copies the contents
                  of the string 'src'. Since the allocation is done via the
                  'new' operator, the caller can and must use the 'delete[]'
                  operator to dispose of the dynamically allocated memory.
                  This routine handles the cases when 'src' is NULL. In this
                  case it returns NULL as well.

        Return    On success, pointer to allocated string; NULL otherwise


    HEXCOLOR
    -------------------------------------------------------------------------

        Synopsis  Create PC color attribute from hexadecimal specs

        Syntax    int hexColor(uchar bg, uchar fg);

        Remarks   This routine creates a PC-compatible (see conio.h) color
                  attribute from two numbers, given in their hex digits.
                  'bg' is the background character. Values > 7 will result
                  in the blinking attribute being set. The 'fg' argument is
                  the foreground color. Note that both parameters are
                  actually characters representing the hexadecimal digits.

        Return    PC color attribute


    SETCURSORTYPE
    -------------------------------------------------------------------------

        Synopsis  Change the size of the cursor

        Syntax    void setCursorType(cursortype_t type);

        Remarks   This function changes the size of the cursor. There are
                  three different cursor types that can be used:

                      typedef enum
                      {
                          CURSOR_HIDDEN = 0x2000,
                          CURSOR_NORMAL = 0x0D0E,
                          CURSOR_BLOCK  = 0x010E
                      } cursortype_t;

                  The 'type' argument can be any of the above constants.
                  The first one hides the cursor completely. The second one
                  restores the default VGA text mode cursor. The last one
                  will cause the function to change the cursor to full block.
                  This is sometimes used when overtype mode is ON.

        Return    Nothing


    UPDATECRC32
    -------------------------------------------------------------------------

        Synopsis  Update the current CRC value with a new byte

        Syntax    ulong UpdateCRC32(char aByte, ulong crc);

        Remarks   This macro calculates the new 32-bit CRC from the 'aByte'
                  byte and the current 'crc'. It returns the updated value.
                  This macro should be used to continuously update the CRC
                  value from a stream of bytes. The correct setup for the
                  process is as follows:

                      (1) initialize the crc variable to -1
                      (2) continuously call crc = UpdateCRC32(byte, crc)
                      (3) complement the result (crc ~= crc)

                  This will produce the correct 32-bit CRC value for the
                  stream of bytes. This implementation uses a high-speed
                  table lookup. The table with the pre-calculated values
                  is defined as:

                                 extern ulong tblCRC32[];

                  You can access it as well if need be. The values that
                  are in this table were generated by a separate program
                  which is also available.

        Return    The updated CRC value


    KEY_POKE, KEY_READ, KEY_PEEK, KEY_STAT, KEY_ISHIT, KEY_FLUSH, KEY_INKEY
    -------------------------------------------------------------------------

        Synopsis  Keyboard access and manipulation functions

        Syntax    Boolean key_poke(ushort aCode);
                  ushort  key_read();
                  ushort  key_peek();
                  ushort  key_stat();
                  Boolean key_ishit();
                  void    key_flush();
                  ushort  key_inkey();

        Remarks	  These functions provide convenient access to the keyboard.
                  While not high-performance as a low-level library might be,
                  they do have most, if not all, the functionality needed in
                  modern applications.

                  key_poke() puts the 'aCode' character code in the local
                  buffer. Since the size of the buffer is limited, this
                  function can easily fail. 'aCode' can be the ASCII value
                  of the code or any of the kb??? constants defined in the
                  KBCODES.H header file.

                  key_read() waits for the next available key and then
                  retrieves it. The code returned can be ASCII (for the
                  regular low-order characters) or one of the kb??? constants
                  defined in the KBCODES.H header file.

                  key_peek() returns 0 if a key is not waiting in the buffer
                  or the key code if a key is available. The key code is not
                  removed from the buffer.

                  key_stat() returns the status of the shift keys (Alt, Shift
                  and Control). You can use the following bitmap to test the
                  individual status bits:

                      76543210  40:17  Keyboard Flags Byte 0
                               right shift key depressed
                              left shift key depressed
                             CTRL key depressed
                            ALT key depressed
                           scroll-lock is active
                          num-lock is active
                         caps-lock is active
                        insert is active

                  key_ishit() simply checks if a key is available in the
                  keyboard buffer. It returns True if at least one key code
                  is found, and False if the buffer is empty.

                  key_flush() empties the keyboard buffer and discards all
                  the keyboard codes currently there.

                  key_inkey() checks the buffer for available keys. If there
                  is a key waiting, it retrieves it and returns the code as
                  key_read() does. If no key is waiting, this function will
                  not wait, but will return immediately with 0 as the code.

        Return    key_poke returns True if key was stuffed; False otherwise
                  key_read returns ASCII or extended keycode (KBCODES.H)
                  key_peek returns ASCII or extended keycode (KBCODES.H)
                  key_stat returns the status of the Shift keys
                  key_ishit returns True if a key is available; False if not
                  key_flush empties the keyboard buffer
                  key_inkey return 0 if no key available, otherwise the code


    _DOS_ANSI_LOADED
    -------------------------------------------------------------------------

        Synopsis  Check if ANSi.SYS (or compatible) is installed

        Syntax    int _dos_ansi_loaded(void);

		Remarks   Checks the local system if ANSi.SYS (or compatible)
		          device driver is installed. Note that this does not
		          utilize the ANSi protocol for checking the terminal
		          capability. Rather, it queries the driver directly.
		          Some device drivers may not identify themselves here.

		Return    on success (ANSi.SYS installed), returns 1
		          on error (no dirver present), returns 0

		See also  n/a

		Example   if( _dos_ansi_loaded() ) printf("ANSi.SYS loaded");


    _DOS_SHARE_LOADED
    -------------------------------------------------------------------------

        Synopsis  Checks if file sharing is available

        Syntax    int _dos_share_loaded(void);

        Remarks   This function will check if file sharing is supported
                  in the current environment. While under MS-DOS it will
                  check the presence of SHARE.EXE, multitasking systems
                  running Win95 or OS/2 will also report file sharing as
                  available without this program as it is built into the
                  Operating System itself. File sharing allows for the
                  low-level record-based file locking.

        Return    On success, returns 1; 0 otherwise


    _DOS_DETECT_OS, _DOS_IDLE
    -------------------------------------------------------------------------

        Synopsis  Release time slices to multitaskers

        Syntax    int  _dos_detect_os(void);
                  void _dos_idle(void);

        Remarks   These routines enable your program to support some of the
                  more popular multitasking environments by releasing time
                  slices when idle. The caller must invoke _dos_detect_os()
                  once at the beginning of your program and then call its
                  partner, _dos_idle(), every time a slice needs to be
                  released. Note that _dos_detect_os() actually	returns the
                  type of OS detected. You can use any of the following
                  constants to examine its value, if necessary:

                              __DOS   : MS-DOS or compatible
                              __OS2   : OS/2 DOS box
                              __WIN   : Windows, Win95, Win/NT
                              __DV    : DESQview

                  There is generally no need to use these explicitly unless
                  you want to do some special processing yourself.

        Return    _dos_detect_os() returns the OS type detected


	_DOS_DRVREADY
	-------------------------------------------------------------------------

		Synopsis  Check status of removable media drive (floppy).

		Syntax	  int _dos_drvready(int drive);

		Remarks   This routine should only be used on removable media
		          (floppy disks). Its behavior on any other type is
		          undefined. It tests whether there is a disk in the
		          floppy drive and whether the disk is readable or
		          writable (implies readable). The test will not
		          trigger the critical handler. You can use _dos_drvtype()
		          to determine the type of the drive. The 'drive'
		          parameter specifies the disk drive to test. These are
		          numbered as A=1, B=2, and so on.

		Return    dtNotReady, if there is no disk in the drive;
		          dtReadOK, if the disk is readable;
		          dtWriteOK, if the disk is writable (implies readable);
		          dtError, if there was a low-level controller failure;

		See also  _dos_drvtype(), _dos_drvnum()

		Example   See the example for _dos_drvtype()


	_DOS_DRVTYPE
	-------------------------------------------------------------------------

		Synopsis  Determine the type of the disk drive.

		Syntax    int _dos_drvtype(int drive);

		Remarks   Determines the type of the disk drive without triggering
		          the critical error handler if there is no disk in the
		          drive. A wide range of drives is recognized. Note that
		          since the test is benign, an I/O error will not result
		          in an ugly message on the screen. The 'drive' parameter
		          specifies the drive you want to query, with A=1, B=2, etc.

		Return	  dtError, if there was any I/O error;
		          dtFixed, if the drive is a hard disk;
		          dtRemovable, if the drive is a floppy;
		          dtRemote, if it is a network drive;
		          dtCDROM, if it is a CD-ROM drive driven by MSCDEX;
		          dtSubst, if it is a fake drive created by SUBST;
		          dtRAM, if it is a RAM disk (RAMDRIVE.SYS, VDISK.SYS);
		          dtDblSpace, if the drive is DoubleSpace compressed;

		See also  _dos_drvready(), _dos_drvnum()

		Example
				#include <stdio.h>
				#include "utils.h"

				int
				main()
				{
					for( int i = 'A'; i <= 'Z'; ++i )
					{
						printf( "\ndrive %c: ", i );
						switch( _dos_drvtype(i - '@') )
						{
							case dtError:
								printf( "invalid or unknown type" );
								break;
							case dtFixed:
								printf( "fixed media" );
								break;
							case dtRemovable:
								printf( "removable media ... " );
								switch( _dos_drvready(i - '@') )
								{
									case dtError:
										printf( "controller error" );
										break;
									case dtNotReady:
										printf( "not ready" );
										break;
									case dtReadOK:
										printf( "ready for reading" );
										break;
									case dtWriteOK:
										printf( "ready for read/write" );
										break;
									default:
										printf( "unknown error" );
								}
							break;
							case dtRemote:
								printf( "network drive" );
								break;
							case dtRAM:
								printf( "RAM drive" );
								break;
							case dtCDROM:
								printf( "MSCDEX 2.00+ CD-ROM" );
								break;
							case dtSubst:
								printf( "SUBST'ed drive" );
								break;
							case dtDblSpace:
								printf( "DoubleSpace compressed" );
								break;
							default:
								printf( "unknown error" );
						}
					}
					return 0;
				}


	_DOS_DRVNUM
	-------------------------------------------------------------------------

		Synopsis  Retrieve the number of valid drives in the system.

		Syntax    int _dos_drvnum();

		Remarks   This routine simply returns the number of drives that
		          have been recognized as valid by a _dos_drvtype() query.

		Return    The number of drives.

		See also  _dos_drvtype(), _dos_drvready()

		Example   See the example for _dos_drvtype()


    _DOS_VERSION
    -------------------------------------------------------------------------

        Synopsis  Returns the MS-DOS version number

        Syntax    int _dos_version(void);

        Remarks   Returns the true MS-DOS version number, major version in
                  the low-order byte, and the minor revision - in the high-
                  order byte.

        Return    MS-DOS version number


    _DOS_LOCK, _DOS_LOCKNOW, _DOS_UNLOCK
    -------------------------------------------------------------------------

        Synopsis  Record-based file locking support

        Syntax    int _dos_lock(int fd, long whence, long len);
                  int _dos_locknow(int fd, long whence, long len);
                  int _dos_unlock(int fd, long whence, long len);

        Remarks   These functions provide low-level record-based file locking
                  support. You must verify that the operating system can
                  actually perform these functions by examining the return
                  value of _dos_share_loaded() first. The file descriptors
                  'fd' must be returned by calls to open() or creat(). The
                  'whence' parameter determines the position of the file
                  that the lock must begin at; the 'len' parameter specifies
                  the number of bytes to lock, beginning at 'whence'.

                  _dos_lock() attempts to lock a region of a file. It will
                  try for up to 15 seconds to perform the locking.

                  _dos_locknow() tries to lock the specified region only
                  once and immediately returns, regardless of the result.

                  _dos_unlock() should be used to remove a lock placed on
                  a region in the file. Note that all locks must be removed
                  before the program terminates or the files will remain
                  inaccessible until a system reboot. Also note that the
                  parameters passed to this routine must match the ones used
                  for the locking exactly.

        Return	  On success, all return 0; non-zero otherwise


    EFFECT_FADE
    -------------------------------------------------------------------------

        Synopsis  Creates a fading text effect

        Syntax    void effect_fade(char *s, int col, int row);

        Remarks   This function simply prints the string 's' at the (col,row)
                  location and then cycles through the user-definable color
                  palette, thus simulating fading. The delay between the
                  steps is definable. The default pallette is shown below:

                    int   fade_delay = 125;
                    uchar fade_palette[EFFECT_PALSIZE] = {8,7,15,7,8};

                  The delay is secified in milliseconds. Larger values will
                  result in slower effects.


        Return    Nothing


    EFFECT_FLASH
    -------------------------------------------------------------------------

        Synopsis  Runs a flash through a string

        Syntax    void effect_flash(char *s, int col, int row);

        Remarks   This function will display the string 's' at the (col,row)
                  position on the screen and then run a three step flash
                  through it (the parts of the string will gradually light
                  up and become dark, from left to right). The speed of the
                  effect is controlled by the variable 'flash_delay' which
                  is defined as follows:

                                 int flash_delay = 12;

                  The higher its value, the slower the effect. The pallete
                  is also user-definable. The default is as follows:

                      uchar flash_palette[EFFECT_PALSIZE] = {8,7,15,7,8};

                  The values represent the PC color attributes to use for
                  the ripple.

        Return    Nothing


    EFFECT_BOUNCE
    -------------------------------------------------------------------------

        Synopsis  Bounces a string within a window

        Syntax    char effect_bounce(char *s, int x1, int y1, int x2, int y2,
                                     Boolean cycle);

        Remarks   This function displays the string 's' in the window with
                  coordinates (x1,y1) (top, left); and (x2,y2) (lower right).
                  If the string is longer than the display window, it will be
                  truncated by this function. The routine will then bounce
                  the string off the imaginary walls of this window. The
                  effect's speed is controlled by the

                                 int bounce_delay = 21;

                  variable. The higher its value, the slower the bounce. If
                  the 'cycle' parameter is True, the effect will also apply
                  the fade effect to the string as it moves along. For more
                  information about this effect, read the section on the
                  effect_fade() routine. The palette used for the fade is:

                     uchar bounce_palette[EFFECT_PALSIZE] = {8,7,15,7,8};

                  This effect will execute until a key is pressed, at which
                  point it will terminate and return the key. Alternatively,
                  the caller can use the

                                 int bounce_loops = -1;

                  variable to determine the number of times to loop through
                  the bouncer. The default value of -1 means loop until a
                  key is pressed.

        Return    Code of key pressed


    EFFECT_SCROLL_LEFT, EFFECT_SCROLL_RIGHT
    -------------------------------------------------------------------------

        Synopsis  Scroll text left or right

        Syntax    void effect_scroll_left(char *s, int x, int y, int endx,
                                          Boolean early);
                  void effect_scroll_right(char *s, int x, int y, int endx,
                                           Boolean early);

        Remarks   These functions operate very similarly, except the first
                  one scrolls the text from right to left, and the other
                  one scrolls it from left to right. The string is provided
                  via the 's' parameter. The (x,y) coordinates provide the
                  beginning location of the scroll, and the 'endx' parameter
                  defines the ending column. Both functions expect that the
                  'x' column is less than the 'endx' column. You can use
                  strings which are longer than the specified width (that
                  is, strlen(s) > (endx - x). The speed of the scroll is
                  controlled by the

                                    int scroll_delay = 80;

                  variable. The higher the value, the slower the scroll.
                  The 'early' parameter will cause the function to stop the
                  scroll before the string starts disappearing (scrolling
                  off he edge of the window). This is useful if you want
                  to continue displaying the string.

        Return    Nothing


    EFFECT_TWIRL
    -------------------------------------------------------------------------

        Synopsis  Continuously displays a set of strings

        Syntax    char effect_twirl(char **s, char *keys, int col, int row);

        Remarks   This routine will display the set of strings in the 's'
                  array at the location (col, row) on the screen. Note that
                  the last element in the string array must be a NULL pointer
                  or the function will send your program into oblivion. You
                  can use this routine to create simple text animations,
                  where each string in the array represents a successive
                  step in the animation. The function will run until one of
                  the hotkeys in the 'keys' list is detected (the comparison
                  is not case-sensitive), at which point it will terminate.
                  The speed of the effect is controlled by the

                                 int twirl_delay = 4;

                  variable. Higher values will slow the effect down. The
                  'keys' list is simply a string which lists all keys that
                  should cause the function to terminate.

        Return    The key pressed (case preserved)


    FAIL, MSGFAIL, LOGFAIL
    -------------------------------------------------------------------------

        Synopsis  Display error message and abort program

        Syntax    void fail(const char *fmt, ...);
                  void msgfail(const char *logfile, const char *fmt, ...);
                  void logfail(const char *logfile, const char *fmt, ...);

        Remarks   These routines all either display or log an error message
                  and then abort the program. The fail() function will
                  write the message to the standard error output. msgfail()
                  will write the message to the specified file 'logfile'
                  (the caller must provide the CR/LF newlines, etc.) and
                  will also display it on the screen. And, finally, logfail()
                  will only write the message to the file 'logfile' without
                  any screen output. If 'logfile' exists, both functions will
                  append to it. The file is used in text mode. All three
                  routines accept a variable number of arguments and use the
                  same conventions as the standard printf() syntax. The
                  format string 'fmt' uses type and format specifiers for
                  all additional arguments, if any.

        Return    Nothing


    LOG, MSGLOG
    -------------------------------------------------------------------------

        Synopsis  Display and optionally log an error message

        Syntax    void log(const char *logfile, const char *fmt, ...);
                  void msglog(const char *logfile, const char *fmt, ...);

        Remarks   These routines are close to the logfail() and msgfail()
                  counterparts. The only difference is that these versions
                  will not terminate the program after writing out the
                  messages. Again, if 'logfile' exists, these routines will
                  append the new messages to it. If it does not exist, it
                  will be created. No special formatting is performed when
                  writing to the file. The caller is responsible for all
                  newline and carriage control characters in the strings.
				  The format string 'fmt' allows for a variable number of
				  arguments to be passed to these functions. The syntax and
				  setup is the same as for the printf() family of routines.
				  log() will only write the message to the log file; while
				  msglog() will write and display the message.

        Return    Nothing


