 /*
  * Khoros: $Id: transport.c,v 1.4 1992/03/25 17:30:44 dkhoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: transport.c,v 1.4 1992/03/25 17:30:44 dkhoros Exp $";
#endif

 /*
  * $Log: transport.c,v $
 * Revision 1.4  1992/03/25  17:30:44  dkhoros
 * VirtualPatch5
 *
  */ 


/*
 *----------------------------------------------------------------------
 *
 * Copyright 1990, University of New Mexico.  All rights reserved.
 * Permission to copy and modify this software and its documen-
 * tation only for internal use in your organization is hereby
 * granted, provided that this notice is retained thereon and
 * on all copies.  UNM makes no representations as too the sui-
 * tability and operability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 * 
 * UNM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
 * NESS.  IN NO EVENT SHALL UNM BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY OTHER DAMAGES WHAT-
 * SOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PER-
 * FORMANCE OF THIS SOFTWARE.
 * 
 * No other rights, including for example, the right to redis-
 * tribute this software and its documentation or the right to
 * prepare derivative works, are granted unless specifically
 * provided in a separate license agreement.
 *---------------------------------------------------------------------
 */

#include "unmcopyright.h"	 /* Copyright 1990 by UNM */
#include "transport.h"	
#include "machdefs.h"	


/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>								<<<<
   >>>>	    file name: transport.c				<<<<
   >>>>								<<<<
   >>>>   description: Transport system routines		<<<<
   >>>>								<<<<
   >>>>      routines:  ktempnam()				<<<<
   >>>>		 	kopen()					<<<<
   >>>>			kclose()				<<<<
   >>>>			kread()					<<<<
   >>>>			kwrite()				<<<<
   >>>>			kaccess()				<<<<
   >>>>			klseek()				<<<<
   >>>>			ktell()					<<<<
   >>>>			kunlink()				<<<<
   >>>>			kflock()				<<<<
   >>>>								<<<<
   >>>>			kpopen()				<<<<
   >>>>			kpclose()				<<<<
   >>>>								<<<<
   >>>>			kfopen()				<<<<
   >>>>			kfclose()				<<<<
   >>>>			kfread()				<<<<
   >>>>			kfwrite()				<<<<
   >>>>			kfputc()				<<<<
   >>>>			kfgetc()				<<<<
   >>>>			kfputs()				<<<<
   >>>>			kfgets()				<<<<
   >>>>			kfseek()				<<<<
   >>>>			kftell()				<<<<
   >>>>			krewind()				<<<<
   >>>>			kfprintf()				<<<<
   >>>>			kfscanf()				<<<<
   >>>>								<<<<
   >>>>			kmachtype()				<<<<
   >>>>			kgethostname()				<<<<
   >>>>			kfork()					<<<<
   >>>>			ksystem()				<<<<
   >>>>			kexecvp()				<<<<
   >>>>			kfileno()				<<<<
   >>>>			kdescriptors()				<<<<
   >>>>								<<<<
   >>>> modifications:						<<<<
   >>>>								<<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */




/**************************************************************
*
* MODULE NAME: ktempnam
*
*     PURPOSE: This module is used to create temporary files.
*	       The application program calls "ktempnam"
*	       with a template filename which is used to help
*	       identify the application routine.  The type of
*	       temporary created depends on the identifier.  If
*	       a unique temporary shared memory key is desired
*	       then the template would look like:
*
*			"shm=XXXX"
*
*	       If a file was desired then either:
*
*			"XXXXXXXXX"
*			"file=XXXX"
*
*	       would work.  The convention should be as follows,
*	       an identifier followed by an "=" equals indicator,
*	       and finally followed by an optional template.
*
*
*       INPUT: 	machine - optional machine name in which to create
*		          the template file.
*		template - template of the file to be created.
*
*      OUTPUT:  returns a unique temporary filename
*
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*
*
**************************************************************/


char *ktempnam(machine, template)

char *machine;
char *template;
{
	TransportInformation *routines;
	char	*identifier, *filename, itemp[LENGTH], ftemp[LENGTH],
		result[LENGTH];


	/*
	 *  According to the transport routines
	 */
	identifier = transport_identifier(template, itemp);
	routines = transport_routines(identifier);
	if (routines == NULL)
	{
	   (void) fprintf(stderr,"ktempnam:  Unsupported transport....\n\n");
           (void) fprintf(stderr,"  Unable to find support for the following \
transport (%s).\n", identifier);
	   return(NULL);
	}

	/*
	 *  Get the filename component
	 */
	if ((filename = transport_token(template, ftemp)) == NULL)
	{
	   (void) fprintf(stderr,"ktempnam: No filename component....\n\n");
	   (void) fprintf(stderr,"  Unable to find any template for the path \
(%s).\n", template);
	   return(NULL);
	}

	if (routines->tempnam == NULL)
	   return(NULL);

	if (routines->tempnam(identifier, filename, result) == -1)
	   return(NULL);
	else
	   return(VStrcpy(result));
}



/**************************************************************
*
* MODULE NAME: kopen(path, flags, mode)
*
*     PURPOSE: This function is a replacement for the system "open"
*	       call.  The only difference is that kopen() supports
*	       more than just files, it supports other data transports
*	       as well.  The path should be in the form of an identifier,
*	       followed by an "=" equal indicator, and then the transport
*	       token. ie) a Shared memory path would look like:
*
*			"shm=1032"
*
*	       If a file was desired then either:
*
*			"/usr/tmp/vadd1235"
*			"file=/usr/tmp/vadd1235"
*
*
*       INPUT: path - is the string containing the path name to the
*		      desired file to be open.  The path name identifies
*		      the file to be opened, just like the regular UNIX
*		      open() command.
*
*	       flags - how the file is to be opened.
*
*			O_RDONLY - Open file for only reading
*			O_WRONLY - Open file for only writing
*			O_RDWR   - Open file for both reading & writing
*
*			O_NONBLOCK -
*			O_NDELAY - Whether to block when reading and
*				   writing to the file.
*			O_APPEND - Append to the file (writing).
*			O_TRUNC  - Truncate the file for writing.
*			O_EXCL   - Error if file exists
*			
*
*	       mode -  the permissions to be used when creating a new
*		       file.
*
*      OUTPUT:  returns the kfile id or -1 upon failure
*
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*
*
**************************************************************/


int kopen(path, flags, mode)

char *path;
int  flags;
int  mode;
{
	int	id;
	kfile   *file;
	TransportInformation *routines;
	char	*identifier, *filename, temp[LENGTH];


	/*
	 *  Before opening the transport check to see if we should
	 *  negotiate a new transport for this file, or if not to
	 *  actually negotiate to inform the "negotiator" of our
	 *  attentions to open this transport.
	 */
	(void) negotiate_open(path, path);

	/*
	 *  According to the transport routines
	 */
	identifier = transport_identifier(path, temp);
	routines = transport_routines(identifier);
	if (routines == NULL)
	{
	   (void) fprintf(stderr,"kopen:  Unsupported transport....\n\n");
           (void) fprintf(stderr,"  Unable to find support for the following \
transport (%s).\n", identifier);
	   return(-1);
	}

	/*
	 *  Get the filename component
	 */
	if ((filename = transport_token(path, temp)) == NULL)
	{
	   (void) fprintf(stderr,"kopen: No filename component....\n\n");
	   (void) fprintf(stderr,"  Unable to find any filename for the path \
(%s).\n", path);
	   return(-1);
	}

	/*
	 *  Malloc the file structure.
	 */
	if ((file = (kfile *) calloc(1, sizeof(kfile))) == NULL)
	{
	   (void) fprintf(stderr,"kopen:  Not enough memory....\n\n");
	   (void) fprintf(stderr,"  Unable to malloc (%d) bytes for the khoros \
file structure.\n", sizeof(kfile));
	   return(-1);
	}

	file->mode   = mode;
	file->flags  = flags;
	file->path   = VStrcpy(path);
	file->routines = routines;

	if (routines->open(filename, flags, mode, file) == -1)
	{
	   kfile_free(file);
	   return(-1);
	}

	/*
	 *  add the file entry to the kfile table
	 */
	id = kfile_add_entry(file);
	return(id);
}



/**************************************************************
*
* MODULE NAME: kclose
*
*     PURPOSE: This function is a replacement for the system "close"
*	       call.  The only difference is that kclose() supports
*	       more than just files, it supports other data transports
*	       as well, such as shared memory, pipes, files, etc.
*
*
*       INPUT: id - the file to be close which was opened earlier
*		    with kopen().
*
*      OUTPUT: returns -1 or 0 depending on whether the
*	       operation failed or succeeded.
*
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*
*
**************************************************************/


int kclose(id)

int	id;
{
	kfile	*file;
	int	status;
	TransportInformation *routines;


	/*
	 *  Get the file entry associated with the id
	 */
	if ((file = kfile_get_entry(id)) == NULL)
	{
	   errno = EBADF;
	   return(-1);
	}
	kfile_delete_entry(id);

	/*
	 *  Call the "close()" routine to close the transport
	 */
	routines = file->routines;
	status = routines->close(file);

	/*
	 *  After closing the transport check to see if we should
	 *  inform the negotiator of the fact that the transport for
	 *  this file has been closed.
	 */
	(void) negotiate_close(file->path);

	kfile_free(file);
	return(status);
}



/**************************************************************
*
* MODULE NAME: kread
*
*     PURPOSE: This function is a replacement for the system "read"
*	       call.  The only difference is that kread() supports
*	       more than just files, it supports other data transports
*	       as well, such as shared memory, pipes, files, etc.
*
*	       The routine will read nbytes into the character array
*	       "buf".  If not all nbytes can be read then the kread()
*	       routine returns the number of bytes actually read.
*
*
*       INPUT: id - the file id to be read which was opened earlier
*		    with kopen().
*
*	       buf - the buffer to read the data into
*
*	       nbytes - the number of bytes to be read
*
*      OUTPUT: returns the number of bytes read, 0 when end
*	       of file is encountered, or -1 when an error
*	       is encountered.
*
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*
*
**************************************************************/


int kread(id, buf, nbytes)

int	id;
char	*buf;
int	nbytes;
{
	int	num;
	kfile	*file;
	TransportInformation *routines;


	/*
	 *  Get the file entry associated with the id
	 */
	if ((file = kfile_get_entry(id)) == NULL)
	{
	   errno = EBADF;
	   return(-1);
	}


	/*
	 *  Call the "read()" routine to read from the transport
	 */
	routines = file->routines;
	num = routines->read(file, buf, nbytes);
	return(num);
}



/**************************************************************
*
* MODULE NAME: kwrite
*
*     PURPOSE: This function is a replacement for the system "write"
*	       call.  The only difference is that kwrite() supports
*	       more than just files, it supports other data transports
*	       as well, such as shared memory, pipes, files, etc.
*
*	       The routine will write nbytes from the character array
*	       "buf" to the appropriate transport mechanism specified by
*	       id.  If not all nbytes can be read then the kread()
*	       routine returns the number of bytes actually read.
*
*
*       INPUT: id - the file id to be read which was opened earlier
*		    with kopen().
*
*	       buf - the buffer to write the data from
*
*	       nbytes - the number of bytes to be written
*
*      OUTPUT: returns the number of bytes written, or -1 when
*	       an error is encountered.
*
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*
*
**************************************************************/


int kwrite(id, buf, nbytes)

int	id;
char	*buf;
int	nbytes;
{
	int	num;
	kfile	*file;
	TransportInformation *routines;


	/*
	 *  Get the file entry associated with the id
	 */
	if ((file = kfile_get_entry(id)) == NULL)
	{
	   errno = EBADF;
	   return(-1);
	}

	/*
	 *  Call the "write()" routine to write to the transport
	 */
	routines = file->routines;
	num = routines->write(file, buf, nbytes);
	return(num);
}



/**************************************************************
*
* MODULE NAME: kaccess
*
*     PURPOSE: This function is a replacement for the system "access"
*	       call.  The only difference is that kaccess() checks
*	       more than just files, it will also check other data
*	       transports as well, such as shared memory, pipes, files,
*	       etc.
*
*	       The routine will check to see if the token specified by
*	       the transport is either readable (R_OK), writeable (W_OK),
*	       or accessible (X_OK), or any possible combination by
*	       or'ing the flags together.
*
*       INPUT: path - is the string containing the path name to the
*		      desired file to be accessed.  The path name ident-
*		      ifies the file/token to be tested, just like the
*		      regular UNIX access() command.
*
*	       mode - the mode in which kaccess() should test for the
*		      file.  The mode is comprised of an or'ing of the
*		      following flags:
*
*				R_OK - the file readable
*				W_OK - the file writeable
*				X_OK - the file executable
*				F_OK - the directory leading to the
*				       file accessible.
*        
*      OUTPUT:  return -1 for failure otherwise 0 for success
*
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*
*
**************************************************************/


int kaccess(path, mode)

char *path;
int  mode;
{
	TransportInformation *routines;
	char	*identifier, *filename, temp[LENGTH];


	/*
	 *  According to the transport routines
	 */
	identifier = transport_identifier(path, temp);
	routines = transport_routines(identifier);
	if (routines == NULL)
	{
	   (void) fprintf(stderr,"kaccess:  Unsupported transport....\n\n");
           (void) fprintf(stderr,"  Unable to find support for the following \
transport (%s).\n", identifier);
	   return(-1);
	}

	/*
	 *  Get the filename component
	 */
	if ((filename = transport_token(path, temp)) == NULL)
	{
	   (void) fprintf(stderr,"kaccess: No filename component....\n\n");
	   (void) fprintf(stderr,"  Unable to find any filename for the path \
(%s).\n", path);
	   return(-1);
	}

	if (routines->access != NULL)
	   return(routines->access(path, filename, mode));
	else if (routines->open == NULL)
	   return(access(filename, mode));
	else
	   return(0);
}



/**************************************************************
*
* MODULE NAME: kunlink
*
*     PURPOSE: This function is used to unlink the kfile.  Depending
*	       on the different transport being used the behavior
*	       will be different.  For a file it unlinks the file, but
*	       for a shared memory segment it initializes it before
*	       using the shmctl() to destroy it.
*
*        
*       INPUT: path - the path to the object ot be unlinked.
*
*      OUTPUT: returns -1 or 0 depending on whether the
*	       operation failed or succeeded.
*        
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*        
*
**************************************************************/


int kunlink(path)

char *path;
{
	TransportInformation *routines;
	char	*identifier, *filename, itemp[LENGTH], ftemp[LENGTH];


	/*
	 *  According to the transport routines
	 */
	identifier = transport_identifier(path, itemp);
	routines = transport_routines(identifier);
	if (routines == NULL)
	{
	   (void) fprintf(stderr,"kunlink:  Unsupported transport....\n\n");
           (void) fprintf(stderr,"  Unable to find support for the following \
transport (%s).\n", identifier);
	   return(-1);
	}

	/*
	 *  Get the filename component
	 */
	if ((filename = transport_token(path, ftemp)) == NULL)
	{
	   (void) fprintf(stderr,"kunlink: No filename component....\n\n");
	   (void) fprintf(stderr,"  Unable to find any filename for the path \
(%s).\n", path);
	   return(-1);
	}

	if (routines->unlink == NULL)
	   return(unlink(filename));
	else
	   return(routines->unlink(identifier, filename));
}



/**************************************************************
*
* MODULE NAME: kflock
*
*     PURPOSE: This function is used to lock the transport. Depending
*	       on the different transport being used the behavior
*	       will be different.  For a file it calls flock() to
*	       lock the file, but for a shared memory segment it
*	       it calls shmctl() to lock & unlock the shared memory
*	       segment.
*
*        
*       INPUT: id	 - the id of the object to be flock.
*
*	       operation - the mode in which kflock() should lock the
*		      file.  The operation is comprised of an or'ing of the
*		      following flags:
*
*				LOCK_SH - lock file shareable (when reading)
*				LOCK_EX - lock file execlusive (when writing)
*				LOCK_NB - lock file for no block (don't block)
*				LOCK_UN - unlock the file (free previous lock)
*        
*      OUTPUT: returns -1 or 0 depending on whether the
*	       operation failed or succeeded.
*        
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*        
*
**************************************************************/


int kflock(id, operation)

int	id;
int	operation;
{
	kfile	*file;
	TransportInformation *routines;


	/*
	 *  Get the file entry associated with the id
	 */
	if ((file = kfile_get_entry(id)) == NULL)
	{
	   errno = EBADF;
	   return(-1);
	}

	/*
	 *  Call the "lock()" routine to close the transport
	 */
	routines = file->routines;
	if (routines->lock != NULL)
	{
	   return(routines->lock(file, operation));
	}
	return(0);
}



/**************************************************************
*
* MODULE NAME: klseek
*
*     PURPOSE: This function is used to seek the transport. Depending
*	       on the different transport being used the behavior
*	       will be different.  For a file it calls lseek() to
*	       lock the file, but for a shared memory segment it
*	       it simply resets the internal offset pointer.
*
*        
*       INPUT:	id	 - the id of the object to be seeked.
*
*		offset - the offset in which to seek
*
*		whence - the control of how the offset will be applied
*
*        
*      OUTPUT: returns -1 or 0 depending on whether the
*	       operation failed or succeeded.
*        
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*        
*
**************************************************************/


int klseek(id, offset, whence)

int	id;
int	offset;
int	whence;
{
	kfile	*file;
	TransportInformation *routines;


	/*
	 *  Get the file entry associated with the id
	 */
	if ((file = kfile_get_entry(id)) == NULL)
	{
	   errno = EBADF;
	   return(-1);
	}

	/*
	 *  Call the "lseek()" routine to close the transport
	 */
	routines = file->routines;
	if (routines->lseek != NULL)
	   return(routines->lseek(file, offset, whence));
	else
	   return(-1);
}



/**************************************************************
*
* MODULE NAME: ktell
*
*     PURPOSE: This function is used to tell the current position
*	       within the transport. Depending on the different
*	       transport being used the behavior will be different.
*	       For a file it calls tell() to locate the offset
*	       within the file, but for a shared memory segment it
*	       it simply indicates the internal offset.
*
*        
*       INPUT:	id	 - the id of the object to be seeked.
*
*      OUTPUT: returns -1 or the current offset depending on whether the
*	       operation failed or succeeded.
*        
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*        
*
**************************************************************/


int ktell(id)

int	id;
{
	kfile	*file;
	TransportInformation *routines;


	/*
	 *  Get the file entry associated with the id
	 */
	if ((file = kfile_get_entry(id)) == NULL)
	{
	   errno = EBADF;
	   return(-1);
	}

	/*
	 *  Call the "tell()" routine to close the transport
	 */
	routines = file->routines;
	if (routines->tell != NULL)
	   return(routines->tell(file));
	else
	   return(-1);
}



/**************************************************************
*
* MODULE NAME: kpopen
*
*     PURPOSE: Not available as of yet
*
*
*       INPUT: 
*
*      OUTPUT: 
*        
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*        
*
**************************************************************/


kfile *kpopen(command, type)

char *command;
int  type;
{
}



/**************************************************************
*
* MODULE NAME: kpclose
*
*     PURPOSE: Not available as of yet
*
*
*       INPUT: 
*
*      OUTPUT: 
*        
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*        
*
**************************************************************/


int kpclose(file)

kfile *file;
{
}



/**************************************************************
*
* MODULE NAME: kfopen(path, type)
*
*     PURPOSE: This function is a replacement for the system "fopen"
*	       call.  The only difference is that kfopen() supports
*	       more than just files, it supports other data transports
*	       as well.  The path should be in the form of an identifier,
*	       followed by an "=" equal indicator, and then the transport
*	       token. ie) a Shared memory path would look like:
*
*			"shm=1032"
*
*	       If a file was desired then either:
*
*			"/usr/tmp/vadd1235"
*			"file=/usr/tmp/vadd1235"
*
*
*       INPUT: path - is the string containing the path name to the
*		      desired file to be open.  The path name identifies
*		      the file to be opened, just like the regular UNIX
*		      fopen() command.
*
*	       type - how the file is to be opened.
*
*			"r"  - Open file for only reading
*			"w"  - Open file for only writing
*			"a"  - apend file for only writing
*			"A"  - apend file for writing; non overwrite
*			"r+" - Open file for both reading & writing
*			"w+" - Open file for both reading & writing
*			"a+" - apend file for only writing
*			"A+" - apend file for writing; non overwrite
*
*      OUTPUT:  returns the kfile structure or NULL upon failure
*        
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*        
*
**************************************************************/


kfile *kfopen(path, type)

char *path;
char *type;
{
	kfile   *file;
	int	flags = 0;
	TransportInformation *routines;
	char	*identifier, *filename, temp[LENGTH];


	/*
	 *  Before opening the transport check to see if we should
	 *  negotiate a new transport for this file, or if not to
	 *  actually negotiate to inform the "negotiator" of our
	 *  attentions to open this transport.
	 */
	(void) negotiate_open(path, path);

	/*
	 *  According to the transport routines
	 */
	identifier = transport_identifier(path, temp);
	routines = transport_routines(identifier);
	if (routines == NULL)
	{
	   (void) fprintf(stderr,"kopen:  Unsupported transport....\n\n");
           (void) fprintf(stderr,"  Unable to find support for the following \
transport (%s).\n", identifier);
	   return(NULL);
	}

	/*
	 *  Get the filename component
	 */
	if ((filename = transport_token(path, temp)) == NULL)
	{
	   (void) fprintf(stderr,"kopen: No filename component....\n\n");
	   (void) fprintf(stderr,"  Unable to find any filename for the path \
(%s).\n", path);
	   return(NULL);
	}

	/*
	 *  Malloc the file structure.
	 */
	if ((file = (kfile *) calloc(1, sizeof(kfile))) == NULL)
	{
	   (void) fprintf(stderr,"kopen:  Not enough memory....\n\n");
	   (void) fprintf(stderr,"  Unable to malloc (%d) bytes for the khoros \
file structure.\n", sizeof(kfile));
	   return(NULL);
	}
	flags = open_flags(type);

	file->mode   = 0664;
	file->flags  = flags;
	file->path   = VStrcpy(path);
	file->routines = routines;

	if (routines->open(filename, file->flags, file->mode, file) == -1)
	{
	   kfile_free(file);
	   return(NULL);
	}

	/*
	 *  add the file entry to the kfile table
	 */
	(void) kfile_add_entry(file);
	return(file);
}



/**************************************************************
*
* MODULE NAME: kfclose
*
*     PURPOSE: This function is a replacement for the system "fclose"
*	       call.  The only difference is that kfclose() supports
*	       more than just files, it supports other data transports
*	       as well, such as shared memory, pipes, files, etc.
*
*
*       INPUT: id - the file to be close which was opened earlier
*		    with kfopen().
*
*      OUTPUT: returns -1 or 0 depending on whether the
*	       operation failed or succeeded.
*
*        
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*        
*
**************************************************************/

int kfclose(file)

kfile *file;
{
	int	status;
	TransportInformation *routines;


	/*
	 *  Make sure the entry is within our table.  If not then it
	 *  has probably been closed already.
	 */
	if (file == NULL || (file = kfile_get_entry(file->id)) == NULL)
	{
	   errno = EBADF;
	   return(-1);
	}
	kfile_delete_entry(file->id);



	/*
	 *  Call the "close()" routine to close the transport
	 */
	routines = file->routines;
	status = routines->close(file);

	/*
	 *  After closing the transport check to see if we should
	 *  inform the negotiator of the fact that the transport for
	 *  this file has been closed.
	 */
	(void) negotiate_close(file->path);

	kfile_free(file);
	return(status);
}



/**************************************************************
*
* MODULE NAME: kfread
*
*     PURPOSE: This function is a replacement for the system "fread"
*	       call.  The only difference is that kfread() supports
*	       more than just files, it supports other data transports
*	       as well, such as shared memory, pipes, files, etc.
*
*	       The routine will read nbytes into the character array
*	       "ptr".  If not all nitems can be read then the kfread()
*	       routine returns the number of items actually read.
*
*
*       INPUT: ptr  - the character pointer to read the data into
*
*	       size - the number of bytes to be read for each element
*
*	       nitems - the number of elements to be read
*
*	       file - the kfile pointer to be read which was opened earlier
*		    with kfopen().
*	          
*      OUTPUT: returns the number of items read, 0 when end
*	       of file is encountered or when an error is encountered.
*        
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*        
*
**************************************************************/


int kfread(ptr, size, nitems, file)

void  *ptr;
int   size, nitems;
kfile *file;
{
	int	num;
	TransportInformation *routines;


	/*
	 *  Make sure the entry is within our table.  If not then it
	 *  has probably been closed already.
	 */
	if (file == NULL || (file = kfile_get_entry(file->id)) == NULL)
	{
	   errno = EBADF;
	   return(-1);
	}


	/*
	 *  Call the "read()" routine to read from the transport
	 */
	routines = file->routines;
	num = routines->read(file, ptr, size*nitems);
	return(num/nitems);
}



/**************************************************************
*
* MODULE NAME: kfwrite
*
*     PURPOSE: This function is a replacement for the system "fwrite"
*	       call.  The only difference is that kfwrite() supports
*	       more than just files, it supports other data transports
*	       as well, such as shared memory, pipes, files, etc.
*
*	       The routine will write nitems from the character array
*	       "ptr".  If not all nitems can be written then the kfwrite()
*	       routine returns the number of items actually written.
*
*
*       INPUT: ptr  - the character pointer to write the data from
*
*	       size - the number of bytes to be write for each element
*
*	       nitems - the number of elements to be written
*
*	       file - the kfile pointer to be written which was opened earlier
*		    with kfopen().
*	          
*      OUTPUT: returns the number of items written, 0 when end
*	       of file is encountered or when an error is encountered.
*        
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*        
*
**************************************************************/


int kfwrite(ptr, size, nitems, file)

void  *ptr;
int   size, nitems;
kfile *file;
{
	int	num;
	TransportInformation *routines;


	/*
	 *  Make sure the entry is within our table.  If not then it
	 *  has probably been closed already.
	 */
	if (file == NULL || (file = kfile_get_entry(file->id)) == NULL)
	{
	   errno = EBADF;
	   return(-1);
	}


	/*
	 *  Call the "write()" routine to write to the transport
	 */
	routines = file->routines;
	num = routines->write(file, ptr, size*nitems);
	return(num/size);
}



/**************************************************************
*
* MODULE NAME: kfputc
*
*     PURPOSE: This function is a replacement for the system "fputc"
*	       call.  The only difference is that kfputc() supports
*	       more than just files, it supports other data transports
*	       as well, such as shared memory, pipes, files, etc.
*
*	       The routine will writes a single character to the
*	       specified transport.  If the character could not be
*	       written EOF is returned, otherwise the character
*	       that is written is returned.
*
*
*       INPUT: character - the character to be written
*
*	       file - the kfile pointer to be written which was opened earlier
*		    with kfopen().
*	          
*      OUTPUT: returns the character written, EOF when end
*	       of file is encountered or when an error is encountered.
*        
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*        
*
**************************************************************/


int kfputc(character, file)

int	character;
kfile	*file;
{
	int	 num;
	unsigned char ptr[1]; 
	TransportInformation *routines;


	/*
	 *  Make sure the entry is within our table.  If not then it
	 *  has probably been closed already.
	 */
	if (file == NULL || (file = kfile_get_entry(file->id)) == NULL)
	{
	   errno = EBADF;
	   return(-1);
	}


	/*
	 *  Call the "write()" routine to write to the transport
	 */
	routines = file->routines;
	ptr[0] = (unsigned char) character;
	num    = routines->write(file, (caddr_t) ptr, 1);
	return((num == 1) ? character : EOF);
}



/**************************************************************
*
* MODULE NAME: kfputs
*
*     PURPOSE: This function is a replacement for the system "fputs"
*	       call.  The only difference is that kfputs() supports
*	       more than just files, it supports other data transports
*	       as well, such as shared memory, pipes, files, etc.
*
*	       The routine will writes a single line of characters
*	       to the specified transport.  If the characters could
*	       not be written EOF is returned, otherwise the number of
*	       characters is returned
*
*
*       INPUT: buffer - the characters to be written
*
*	       file - the kfile pointer to be written which was opened earlier
*		    with kfopen().
*	          
*      OUTPUT: returns the character written, EOF when end
*	       of file is encountered otherwise the number of
*	       characters is returned.
*        
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*        
*
**************************************************************/


int kfputs(buffer, file)

char	*buffer;
kfile	*file;
{
	int	 num;
	TransportInformation *routines;


	/*
	 *  Make sure the entry is within our table.  If not then it
	 *  has probably been closed already.
	 */
	if (file == NULL || (file = kfile_get_entry(file->id)) == NULL)
	{
	   errno = EBADF;
	   return(-1);
	}


	/*
	 *  Call the "write()" routine to write to the transport
	 */
	num = VStrlen(buffer);
	routines = file->routines;
	if (routines->write(file, (caddr_t) buffer, num) != num)
	   return(EOF);

	return(num);
}



/**************************************************************
*
* MODULE NAME: kfgetc
*
*     PURPOSE: This function is a replacement for the system fgetc()
*	       routine.  The only difference is that kfgetc() supports
*	       all available transport mechanisms.  The routine will get
*	       a single character from the specified transport.  If the
*	       character could not be written, EOF is returned; otherwise,
*	       the character that is written is returned.
*
*       INPUT: file - the kfile pointer to be read which was opened earlier
*                   with kfopen().
*
*      OUTPUT: returns the character read or EOF upon error.
*
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*
*
**************************************************************/


int kfgetc(file)

kfile *file;
{
	int	 num, character;
	unsigned char ptr[1]; 
	TransportInformation *routines;


	/*
	 *  Make sure the entry is within our table.  If not then it
	 *  has probably been closed already.
	 */
	if (file == NULL || (file = kfile_get_entry(file->id)) == NULL)
	{
	   errno = EBADF;
	   return(-1);
	}


	/*
	 *  Call the "write()" routine to write to the transport
	 */
	routines  = file->routines;
	num       = routines->read(file, (caddr_t) ptr, 1);
	character = ptr[0];
	return((num == 1) ? character : EOF);
}



/**************************************************************
*
* MODULE NAME: kfgets
*
*     PURPOSE: This function is a replacement for the system "fgets"
*	       call.  The only difference is that kfgets() supports
*	       more than just files, it supports other data transports
*	       as well, such as shared memory, pipes, files, etc.
*
*	       The routine will reads a single line of characters
*	       from the specified transport.  If the characters could
*	       not be read then NULL is returned, otherwise the input
*	       character array is returned.  kfgets() reads characters
*	       from the stream into the array pointed to by buffer
*	       until num-1 characters are read or a '\n' (newline character)
*	       or a EOF is encountered.  The characters read are returned
*	       or if EOF or an error is encountered and no characters
*	       have been read then NULL is returned.
*
*
*       INPUT: buffer - the characters to be written
*
*	       num  - maximum number of characters (sizeof character array).
*
*	       file - the kfile pointer to be written which was opened earlier
*		    with kfopen().
*	          
*      OUTPUT: returns the character read, if EOF or an error is encountered
*	       and no characters have been read then NULL is returned.
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*
*
**************************************************************/


char *kfgets(buffer, num, file)

char  *buffer;
int   num;
kfile *file;
{
	int  i = num;
	char *buf = buffer;
	TransportInformation *routines = file->routines;


	/*
	 *  Make sure the entry is within our table.  If not then it
	 *  has probably been closed already.
	 */
	if (file == NULL || (file = kfile_get_entry(file->id)) == NULL)
	{
	   errno = EBADF;
	   return(NULL);
	}

	do
	{
	    if (routines->read(file, buf, 1) == 0 || *buf++ == '\n')
	       break;
	} while (--i > num);
	*buf = '\0';

	/*
	 *  Check why we terminated and return the appropriate value
	 */
	if (buffer == buf)
	   return(NULL);
	else
	   return(buffer);
}



/**************************************************************
*
* MODULE NAME: kfseek
*
*     PURPOSE: This function is used to seek the transport. Depending
*	       on the different transport being used the behavior
*	       will be different.  For a file it calls fseek() to
*	       lock the file, but for a shared memory segment it
*	       it simply resets the internal offset pointer.
*
*        
*       INPUT:	file - the kfile pointer to be written which was opened earlier
*                      with kfopen().
*
*		offset - the offset in which to seek
*
*		whence - the control of how the offset will be applied
*
*        
*      OUTPUT: returns -1 or 0 depending on whether the
*	       operation failed or succeeded.
*        
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*        
*
**************************************************************/


int kfseek(file, offset, whence)

kfile	*file;
long	offset;
int	whence;
{
	TransportInformation *routines;


	/*
	 *  Make sure the entry is within our table.  If not then it
	 *  has probably been closed already.
	 */
	if (file == NULL || (file = kfile_get_entry(file->id)) == NULL)
	{
	   errno = EBADF;
	   return(-1);
	}

	/*
	 *  Call the "fseek()" routine to seek the transport
	 */
	routines = file->routines;
	if (routines->lseek != NULL)
	   return(routines->lseek(file, (int) offset, whence));
	else
	   return(-1);
}



/**************************************************************
*
* MODULE NAME: kftell
*
*     PURPOSE: This function is used to tell the current position
*	       within the transport. Depending on the different
*	       transport being used the behavior will be different.
*	       For a file it calls ftell() to locate the offset
*	       within the file, but for a shared memory segment it
*	       it simply indicates the internal offset.
*
*        
*       INPUT:	file - the kfile pointer to be written which was opened earlier
*                      with kfopen().
*
*      OUTPUT: returns -1 or the current offset depending on whether the
*	       operation failed or succeeded.
*        
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*        
*
**************************************************************/


int kftell(file)

kfile	*file;
{
	TransportInformation *routines;


	/*
	 *  Make sure the entry is within our table.  If not then it
	 *  has probably been closed already.
	 */
	if (file == NULL || (file = kfile_get_entry(file->id)) == NULL)
	{
	   errno = EBADF;
	   return(-1);
	}

	/*
	 *  Call the "ftell()" routine to close the transport
	 */
	routines = file->routines;
	if (routines->tell != NULL)
	   return(routines->tell(file));
	else
	   return(-1);
}



/**************************************************************
*
* MODULE NAME: krewind
*
*     PURPOSE: This function is used to seek the transport to the
*	       beginning of a file.  In essence it performs the
*	       following command:
*
*			kfseek(file, 0L, 0);
*
*	       Depending on the different transport being used the
*	       behavior will be different.  For a file it calls fseek() to
*	       seek the file, but for a shared memory segment it
*	       it simply resets the internal offset pointer.
*
*        
*       INPUT:	file - the kfile pointer to be written which was opened earlier
*                      with kfopen().
*
*      OUTPUT: returns nothing
*        
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*        
*
**************************************************************/


void krewind(file)

kfile	*file;
{
	TransportInformation *routines;


	/*
	 *  Make sure the entry is within our table.  If not then it
	 *  has probably been closed already.
	 */
	if (file == NULL || (file = kfile_get_entry(file->id)) == NULL)
	{
	   errno = EBADF;
	   return;
	}

	/*
	 *  Call the "fseek()" routine to seek the transport
	 */
	routines = file->routines;
	if (routines->lseek != NULL)
	   (void) routines->lseek(file, (int) 0L, 0);
}



/***********************************************************************
*
* MODULE NAME: kfprintf
*
*     PURPOSE: This function is a replacement for the system "fprintf"
*	       call.  The only difference is that kfprintf() supports
*	       more than just files, it supports other data transports
*	       as well.  This function is used to print a formatted text
*	       strings into the data transport.  
*
*			kfprintf(file, format, arguments);
*
*	       Depending on the different transport being used the
*	       behavior will be different.  Essentially this call
*	       uses the vsprintf() and calls kfputs() to then print
*	       the text to the appropriate transport.
*
*        
*       INPUT:	file - the kfile pointer to be written which was opened earlier
*                      with kfopen().
*		format - the format in which to the arguments will be
*			 formatted.
*		args   - a variable argument list which are used as the
*			 the inputs to the format command.
*
*      OUTPUT: returns nothing
*        
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Jeremy Worley and Mark Young
*        
*
***********************************************************************/


#if !defined(__STDC__)
int kfprintf(file, format, va_alist)

kfile *file;
char  *format;
va_dcl
#else
int kfprintf(kfile *file, char *format, ...)
#endif
{
	int     num = 0;
	va_list args;
	char    temp[LENGTH*5];


	/*
	 *  Attach the "args" to the variable argument list.
	 */
	VA_START(args, format);

	/*
	 *  Call vsprintf() to do the actual formating of the arguments
	 *  into the temporary character array.  If the number of arguments
	 *  printed does not equal 0 then call kfputs() to write the string
	 *  to the transport.  The return value of kfprintf() represents
         *  the number of values actually written to the transport device.
	 */
	if (vsprintf((char *) temp, format, args) != 0)
	{
	   if ((num = kfputs(temp, file)) == EOF)
	      return(0);
	}

	VA_END(args);

	return(num);
}



/**************************************************************
*
* MODULE NAME: kfscanf
*
*     PURPOSE: Not implemented as of yet
*
*
*
*       INPUT: 
*
*      OUTPUT: 
*
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*
*
**************************************************************/




/**************************************************************
*
* MODULE NAME: kfork
*
*     PURPOSE: This routine is used to run a process which can
*	       be communicated via the returned kfile identifier.
*
*
*       INPUT: command - the command to be executed which can be in
*			 the form of
*
*      OUTPUT: 
*
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*
*
**************************************************************/


int kfork(command)

char *command;
{
	RemoteInformation *remote;
	TransportInformation *routines;
	char	*identifier, temp[LENGTH];


	/*
	 *  According to the transport routines
	identifier = transport_identifier(arg0, temp);
	routines = transport_routines(identifier);
	if (routines == NULL)
	{
	   (void) fprintf(stderr,"kfork:  Unsupported transport....\n\n");
           (void) fprintf(stderr,"  Unable to find support for the following \
transport (%s).\n", identifier);
	   return(-1);
	}
	 */

	/*
	 *  Should never return...
	if (routines->remote != NULL)
	{
	   if (routines->remote->exec == NULL)
	      return(-1);

	   remote = routines->remote;
	   if (remote->exec(arg0, args) == -1)
	      return(-1);
	   else
	      _exit(0);
	}
	else
	{
	   execvp(arg0, args);
	}
	return(-1);
	 */
}



/**************************************************************
*
* MODULE NAME: kmachtype
*
*     PURPOSE: Same as the Khoros routine machtype(), except that it 
*	       supports all machines available for distributed processing.
*
*       INPUT: machine  - machine to inquire about
*
*      OUTPUT: hosttype - returns string describing the type of machine
*
* CALLED FROM:  by application programmer
*  WRITTEN BY:  Mark Young
*
**************************************************************/


long kmachtype(machine, hosttype)

char	*machine;
char	*hosttype;
{
	int	type;
	RemoteInformation *remote;
	TransportInformation *routines;
	char	temp[LENGTH], *identifier;


	/*
	 *  According to the transport routines
	 */
	identifier = transport_machine(machine, temp);
	routines = transport_routines(identifier);
	if (routines == NULL)
	{
	   (void) fprintf(stderr,"kmachtype:  Unsupported transport....\n\n");
           (void) fprintf(stderr,"  Unable to find support for the following \
transport (%s).\n", identifier);
	   return(-1);
	}

	if (routines->remote != NULL)
	{
	   remote = routines->remote;
	   if (remote->machtype != NULL)
	      type = remote->machtype(machine, hosttype);
	   else
	      return(-1);
	}
	else
	{
	   type = LocalDef.order;
	   if (hosttype != NULL)
	      (void) strcpy(hosttype, LocalDef.hosttype);
	}
	return(type);
}



/**************************************************************
*
* MODULE NAME: kgethostname
*
*     PURPOSE: 
*
*
*       INPUT: 
*
*      OUTPUT: 
*
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*
*
**************************************************************/

int kgethostname(machine, hostname, namelen)

char *machine;
char *hostname;
int  namelen;
{
	RemoteInformation *remote;
	TransportInformation *routines;
	char	temp[LENGTH], *identifier;


	/*
	 *  According to the transport routines
	 */
	identifier = transport_machine(machine, temp);
	routines = transport_routines(identifier);
	if (routines == NULL)
	{
	   (void) fprintf(stderr,"kgethostname:  Unsupported transport...\n\n");
           (void) fprintf(stderr,"  Unable to find support for the following \
transport (%s).\n", identifier);
	   return(-1);
	}

	if (routines->remote != NULL)
	{
	   remote = routines->remote;
	   if (remote->gethostname != NULL)
	   {
	      if (remote->gethostname(machine, hostname, namelen) == -1)
		 return(-1);
	   }
	}
	else
	{
	   if (gethostname(hostname, namelen) == -1)
	      return(-1);
	}
	return(0);
}



/**************************************************************
*
* MODULE NAME: ksystem
*
*     PURPOSE: This function is a replacement for the "system" library
*	       call.  The only difference is that ksystem() supports
*	       more than just files, it supports other data transports
*	       in order to allow remote execution of a job.
*
*
*       INPUT:  command - the command to be systemed
*
*      OUTPUT:  returns the job status
*
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*
*
**************************************************************/

int ksystem(command)

char *command;
{
	RemoteInformation *remote;
	TransportInformation *routines;
	char	*identifier, temp[LENGTH], *args[MAX_ARGS];


	/*
	 *  According to the transport routines
	 */
	identifier = transport_identifier(command, temp);
	routines = transport_routines(identifier);
	if (routines == NULL)
	{
	   (void) fprintf(stderr,"ksystem:  Unsupported transport....\n\n");
           (void) fprintf(stderr,"  Unable to find support for the following \
transport (%s).\n", identifier);
	   return(-1);
	}

	/*
	 *  If there is no remote exec routine that simply call system() to
	 *  execute the command.
	 */
	if (routines->remote == NULL || routines->remote->exec == NULL)
	   return(system(command));
	else
	   remote = routines->remote;

	/*
	 *  Exec the command...
	 */
	command_args(command, args);
	if (remote->exec(args[0], args) == -1)
	   return(127);
	else
	   return(0);
}



/**************************************************************
*
* MODULE NAME: kexecvp
*
*     PURPOSE: This function is a replacement for the system "execvp"
*	       call.  The only difference is that kexecvp() supports
*	       executing processes on remote machines.
*
*	       The routine will execute another process (specified by
*	       arg0) and replace the current core image with the
*	       specified core.  For example, the command:
*
*			"vfileinfo -i1 file"
*
*	       would be specified as:
*
*			arg0 = "vfileinfo" 
*
*			args[0] = "vfileinfo" 
*			args[1] = "-i1" 
*			args[2] = "file" 
*			args[3] = NULL
*
*	       If the command is to be executed on a different machine such
*	       as "borris" then the same command would look like:
*
*			"vfileinfo@borris -i1 file"
*
*	       would be specified as:
*
*			arg0 = "vfileinfo@borris" 
*
*			args[0] = "vfileinfo@borris" 
*			args[1] = "-i1" 
*			args[2] = "file" 
*			args[3] = NULL
*
*
*	       If the command is to be executed on a different machine such
*	       as "borris", and the file to be read is on "natasha" then
*	       the command would look like this:
*
*			"vfileinfo@borris -i1 file@natasha"
*
*	       would be specified as:
*
*			arg0 = "vfileinfo@borris" 
*
*			args[0] = "vfileinfo@borris" 
*			args[1] = "-i1" 
*			args[2] = "file@natasha" 
*			args[3] = NULL
*
*
*
*       INPUT: arg0 - the routine to be executed
*
*	       args - the arguments, include arg0, in which execute
*		      the command
*
*      OUTPUT: none...  (Note: this call overlays the calling process
*			 with the named file, then transfers control to
*			 the new core image.  If the command is successfully
*			 executed then there will be no return from the
*			 new process.  If the core cannot be loaded or found
*			 then we return with an error of -1).
*
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young & Steve Jorgensen
*
*
**************************************************************/


kexecvp(arg0, args)

char *arg0;
char *args[];
{
	RemoteInformation *remote;
	TransportInformation *routines;
	char	*identifier, temp[LENGTH];


	/*
	 *  Make a quick check to see if we are to be execing this locally
	 *  or remotely.  If local then simply call execvp()
	 */
	if (strchr(arg0, '@') == NULL)
	{
	   execvp(arg0, args);
	   return(-1);
	}

	/*
	 *  According to the transport routines
	 */
	identifier = transport_identifier(arg0, temp);
	routines = transport_routines(identifier);
	if (routines == NULL)
	{
	   (void) fprintf(stderr,"kexec:  Unsupported transport....\n\n");
           (void) fprintf(stderr,"  Unable to find support for the following \
transport (%s).\n", identifier);
	   return(-1);
	}

	/*
	 *  Should never return...
	 */
	if (routines->remote == NULL || routines->remote->exec == NULL)
	   return(-1);

	remote = routines->remote;
	if (remote->exec(arg0, args) == -1)
	   return(-1);
	else
	   _exit(0);
}



/**************************************************************
*
* MODULE NAME: kread_alloc
*
*     PURPOSE: This function is a replacement for the system "read"
*	       call.  The only difference is that kread() supports
*	       more than just files, it supports other data transports
*	       as well, such as shared memory, pipes, files, etc.
*
*	       The routine will read nbytes into the character array
*	       "buf".  If not all nbytes can be read then the kread()
*	       routine returns the number of bytes actually read.
*
*
*       INPUT: id - the file id to be read which was opened earlier
*		    with kopen().
*
*	       nbytes - the number of bytes to be read
*
*
*      OUTPUT: num_read - the number of bytes actally read
*
*	       also returns the data pointer or NULL
*
* CALLED FROM:  by application programmer
*
*  WRITTEN BY:  Mark Young
*
*
**************************************************************/


char *kread_alloc(id, nbytes, num_read)

int	id;
int	nbytes;
int	*num_read;
{
	int	num;
	kfile	*file;
	caddr_t data;
	TransportInformation *routines;

	/*
	 *  Get the file entry associated with the id
	 */
	if ((file = kfile_get_entry(id)) == NULL)
	{
	   errno = EBADF;
	   return(NULL);
	}


	/*
	 *  Call the "read()" routine to read from the transport
	 */
	if (file->addr == NULL)
	{
	   /*
	    *  Since the data is not available directly we better malloc space
	    *  and read the data into it.
	    */
	   if ((data = (char *) kmalloc((unsigned) nbytes)) == NULL)
	      return(NULL);

	   routines = file->routines;
	   num = routines->read(file, data, nbytes);
	}
	else
	{
	   data = &file->addr[file->offset];
	   num = MIN(nbytes, file->size - file->offset);
	   file->offset += num;
	}

	/*
	 *  If the num_read is not NULL then return the number of bytes read
	 *  into the data array.
	 */
	if (num_read != NULL)
	   *num_read = num;

	return(data);
}



/**************************************************************
*
* MODULE NAME: kfileno(file)
*
*     PURPOSE: This function is used to retrieve the corresponding
*	       kfile entry descriptor, which can be used with the
*	       (int fid) routines like: kread(), kwrite().
*
*       INPUT: file - the kfile structure to be referenced.
*
*      OUTPUT: returns the entry descriptor (or -1 if it doesn't exist)
*
* CALLED FROM: various kfile utilities
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int kfileno(file)

kfile *file;
{
	/*
	 *  Make sure the entry is within our table.  If not then it
	 *  has probably been closed already.
	 */
	if (file == NULL || (file = kfile_get_entry(file->id)) == NULL)
	{
	   errno = EBADF;
	   return(-1);
	}
	else
	   return(file->id);
}



/**************************************************************
*
* MODULE NAME: kdescriptors(file, inum, onum)
*
*     PURPOSE: This function is used to retrieve the input and
*	       output file descriptor associated with a transport.
*	       Note:  this routine should not be used without
*		      extreme caution, since certain transports
*		      don't have file descriptors (such as shared
*		      memory).
*
*       INPUT: file - the kfile structure to be referenced.
*
*      OUTPUT: inum - the input descriptor if exists (or if non exists -1)
*	       onum - the output descriptor if exists (or if non exists -1)
*
* CALLED FROM: various kfile utilities
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int kdescriptors(file, inum, onum)

kfile *file;
int   *inum;
int   *onum;
{
	int	status;
	TransportInformation *routines;


	/*
	 *  Make sure the entry is within our table.  If not then it
	 *  has probably been closed already.
	 */
	if (file == NULL || (file = kfile_get_entry(file->id)) == NULL)
	{
	   errno = EBADF;
	   return(-1);
	}


	/*
	 *  Call the "filedescriptor()" routine to see if a input and output
	 *  file descriptor exist
	 */
	routines = file->routines;
	if (routines->descriptors != NULL)
	{
	   status = routines->descriptors(file, inum, onum);
	}
	else
	{
	   if (inum != NULL) *inum = -1;
	   if (onum != NULL) *onum = -1;
	   status = -1;
	}
	return(status);
}
