/*
 *  FILE
 *	funcs.c
 *
 *  DESCRIPTION
 *	All the functions for actually read and write the MANX
 *	environment variables.
 *
 *  AUHOR
 *	Anders `ALi' Lindgren
 *	Marlarblick 8
 *	S-161 51 Bromma
 *	Sweden
 *
 *  STATUS
 *	This file is in the Public Domain.
 *
 *  CODE HISTORY
 *	26-Sep-89   (10:51) Created this file.
 *	27-Sep-89   (18:56) Read works ok.
 *		    (19:27) ReAlloc written.
 *		    (12:00) Both Read and Write works ok.
 *	28-Sep-89   (14:00) Locks implemented.
 *
 *	 1-Oct-89	    No hacking. I was employed by Commodore,
 *	   ...		    Sweden at the Alvsjo copmuter fair.
 *	 6-Oct-89
 *	 6-Oct-89	    (Gee, today I bougth a 2000 :-) )
 *
 *	 6-Oct-89   (23:30) Implemented Directory functions. Some
 *			    people wanted it badly.
 *	 8-Oct-89   (12:00) Directory functions works ok.
 *
 *	   ...		    (I got my hands on MicroGNUEmacs 3. It`s GREAT!)
 *
 *	17-Oct-89	    I threw out the old directory functions, becauce
 *			    they didn`t function the way I liked. 
 *
 *	20-Jan-90	    I`m sorry, but I have been up to my neck into
 *			    other things so I haven`t been able to work
 *			    on the project. Well, now it`s released, enjoy!
 *
 *  DISCUSSION
 *	Change __builtin_memcpy to CopyMem???
 */

#include "funcs.h"

/*
 *  FUNCTION
 *	action_input
 *
 *  DESCRIPTION
 *	Read an MANX variable. This function copies the variable
 *	value to a temporary buffer.
 *
 *	The valuebuf structure conatisn the following fields:
 *	    vb_buf - Pointer to the temporary buffer.
 *	    vb_pos - Index, pointing to the next char to be read.
 *	    vb_end - Size of the Buffer.
 *
 *	When vb_pos and vb_end is equal, all data was been read.
 */

void
action_input (register struct DosPacket * pkt, struct MsgPort * port)
{
    register struct FileHandle * fh;
    register struct valuebuf   * vb;
    register	    char       * var;
    register	    int 	 len;
		    char	 name[NAMESIZE];
		    char       * varname;

    pkt->dp_Res1 = DOS_FALSE;

    fh = (struct FileHandle *)BTOC(pkt->dp_Arg1);

    BtoCStr(name, pkt->dp_Arg3, NAMESIZE-1);
    varname = BaseName ( name );

    if ( * varname ) {

	Forbid();

	if (var = mygetenv(varname)) {

	    if (vb = (struct valuebuf *) AllocMem( sizeof(struct valuebuf),
					MEMF_PUBLIC | MEMF_CLEAR ) ) {

		fh->fh_Type  = port;
		fh->fh_Arg1  = (long) vb;

		len = __builtin_strlen(var);

		if (vb->vb_buf = AllocMem(len, MEMF_PUBLIC | MEMF_CLEAR)) {
		    vb->vb_end = len;
		    vb->vb_pos = 0;

		    __builtin_memcpy(vb->vb_buf, var, len);

		    vb->vb_flags = ENVF_READ;

		    pkt->dp_Res1 = DOS_TRUE;
		}
		else {
		    pkt->dp_Res2 = ERROR_NO_FREE_STORE;
		    FreeMem((char *)vb, sizeof(struct valuebuf));
		}
	    }
	    else {
		pkt->dp_Res2 = ERROR_NO_FREE_STORE;
	    }
	}
	else {
	    pkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND;
	}

	Permit();
    }
    else {
	pkt->dp_Res2 = ERROR_BAD_STREAM_NAME;
    }

    return;
}


/*
 *  FUNCTION
 *	action_read
 *
 *  DESCRIPTION
 *	Writes the buffer to the fh.
 */

void
action_read (register struct DosPacket * pkt)
{
    register struct valuebuf * vb;
    register	    int        num;

    vb = (struct valuebuf *)pkt->dp_Arg1;

    if (vb->vb_flags & ENVF_READ) {

		    /* Already read all? */
	if (vb->vb_pos == vb->vb_end) {
	    pkt->dp_Res1 = 0;
	}
    	else {
		/* Get the smallest, buffer or remaining chars */
	    if ( (vb->vb_end - vb->vb_pos) <= pkt->dp_Arg3 ) {
	    	num = vb->vb_end - vb->vb_pos;
	    }
	    else {
		num = pkt->dp_Arg3;
	    }

	    __builtin_memcpy( (void *)pkt->dp_Arg2,
			    (void *)(vb->vb_buf + vb->vb_pos), num );

	    vb->vb_pos += num;

	    pkt->dp_Res1 = num;
	}
    }
    else {
	pkt->dp_Res1 = 0;
	pkt->dp_Res2 = ERROR_OBJECT_WRONG_TYPE;
    }
    return;
}


/*
 *  FUNCTION
 *	action_output
 *
 *  DESCRIPTION
 *	Open the env: for writing a variable.
 *	This function allocates memory for the namestring.
 *	It doesn`t allocate memory for the buffer, because if the
 *	vb_buf is zero (ie. no write has been made) the Setenv
 *	function removes the variable.
 */

void
action_output (register struct DosPacket * pkt, struct MsgPort * port)
{
    register struct valuebuf   * vb;
    register	    char       * varname;
    register        int		 namelen;
	     struct FileHandle * fh;
		    char	 name[NAMESIZE];

    pkt->dp_Res1 = DOS_FALSE;

    if (vb = (struct valuebuf *) AllocMem( sizeof(struct valuebuf),
					    MEMF_PUBLIC | MEMF_CLEAR ) ) {

	fh = (struct FileHandle *)BTOC(pkt->dp_Arg1);

	BtoCStr(name, pkt->dp_Arg3, NAMESIZE-1);
	varname = BaseName( name );

	if ( * varname ) {
	    namelen = __builtin_strlen(varname) + 1;

	    if (vb->vb_name = DosAllocMem(namelen)) {
		__builtin_memcpy(vb->vb_name, varname, namelen);

		fh->fh_Type  = port;
		fh->fh_Arg1  = (long) vb;

		vb->vb_flags = ENVF_WRITE;

		pkt->dp_Res1 = DOS_TRUE;
		return; 			/* Correct exit */
	    }
				/* Error hancdling code */
	    else {
		pkt->dp_Res2 = ERROR_NO_FREE_STORE;
	    }
	}
	else {
	    pkt->dp_Res2 = ERROR_BAD_STREAM_NAME;
	}
	FreeMem((char *)vb, sizeof(struct valuebuf));
    }
    else {
	pkt->dp_Res2 = ERROR_NO_FREE_STORE;
    }
    return;	/* Error Exit! */
}


/*
 *  FUNCTION
 *	action_write
 *
 *  DESCRIPTON
 *	Called when a client writes the contents of a environment
 *	variable.
 *	The data is stored at at buffer. If the buffer is smaler
 *	than the data, the buffer is expanded.
 *	BUFSIZE is the amount of extra memory which shall be
 *	allocated at a ReAlloc call.
 *	(If BUFSIZE is zero, a ReAlloc call will be made at
 *	every write.)
 *
 *  NOTE
 *	No errorchecking is made on the ReAlloc function, yet....
 */

void
action_write (register struct DosPacket * pkt)
{
    register struct valuebuf	* vb;
    register char		* newbuf;

    vb = (struct valuebuf *)pkt->dp_Arg1;

    if (vb->vb_flags & ENVF_WRITE) {

	if (vb->vb_pos + pkt->dp_Arg3 + 1 >= vb->vb_end) {
	    newbuf = ReAlloc(vb->vb_buf, vb->vb_end,
			vb->vb_end + pkt->dp_Arg3 + BUFSTEP, MEMF_CLEAR);
	    if (newbuf == NULL) {
		FreeMem (vb->vb_buf, vb->vb_end);
		DosFreeMem (vb->vb_name);
		FreeMem ((char *)vb, sizeof(struct valuebuf));
		pkt->dp_Res1 = 0;
		pkt->dp_Res2 = ERROR_NO_FREE_STORE;
		return;
	    }
	    vb->vb_buf = newbuf;
	    vb->vb_end += pkt->dp_Arg3 + BUFSTEP;
	}
	__builtin_memcpy(vb->vb_buf + vb->vb_pos, (void *)pkt->dp_Arg2,
							pkt->dp_Arg3);
	vb->vb_pos += pkt->dp_Arg3;

	pkt->dp_Res1 = pkt->dp_Arg3;
    }
    else {
	pkt->dp_Res1 = 0;
	pkt->dp_Res2 = ERROR_OBJECT_WRONG_TYPE;
    }
    return;
}


/*
 *  FUNCTION
 *	action_end
 *
 *  DESCRIPTION
 *	This function is executed when someone Close()`s the filehandler.
 *
 *	On read, it simply frees the memory. On Write, the actual
 *	writing takes place here.
 */

void
action_end (register struct DosPacket * pkt)
{
    register struct valuebuf * vb;

    pkt->dp_Res1 = DOS_FALSE;

    if (vb = (struct valuebuf *)pkt->dp_Arg1) {

	if (vb->vb_flags & ENVF_READ) {
	    if (vb->vb_buf && vb->vb_end) {
		FreeMem( (char *)vb->vb_buf, vb->vb_end);
		FreeMem( (char *)vb, sizeof(struct valuebuf) );
	    }
	    pkt->dp_Res1 = DOS_TRUE;
	}
	else if (vb->vb_flags & ENVF_WRITE) {
	    if (vb->vb_name) {
		Setenv(vb->vb_name, vb->vb_buf);
		DosFreeMem(vb->vb_name);
	    }
	    if (vb->vb_buf) {
		FreeMem(vb->vb_buf, vb->vb_end);
	    }
	    FreeMem((char *)vb, sizeof(struct valuebuf));

	    pkt->dp_Res1 = DOS_TRUE;
	}
	else {	/* Error */
	    pkt->dp_Res2 = ERROR_OBJECT_WRONG_TYPE;
	}
    }
    else {
	pkt->dp_Res2 = ERROR_OBJECT_WRONG_TYPE;
    }
    return;
}


/*
 *  FUNCTION
 *	action_lock
 *
 *  DESCRIPTION
 *	Lock the env: device, or a `file'.  If a file is locked, the filename
 *	is copied to a aread pointed to by lock->fl_Name.
 *
 *  ARGUMENTS
 *	Arg1 : Lock
 *	Arg2 : Name
 *	Arg3 : Mode
 */

void
action_lock (register struct DosPacket * pkt,
		      struct MsgPort   * port,
		      struct DosList   * node)
{
    register struct myFileLock  * lock;
		    char	  name[NAMESIZE];
		    char	* namebase;

    pkt->dp_Res1 = NULL;

    if (lock = (struct myFileLock *)AllocMem(sizeof(struct myFileLock),
						MEMF_PUBLIC | MEMF_CLEAR)) {
	pkt->dp_Res1 = CTOB(lock);

	lock->fl_Access = pkt->dp_Arg3;
	lock->fl_Task   = port;
	lock->fl_Volume = CTOB(node);

	BtoCStr(name, pkt->dp_Arg2, NAMESIZE-1);
	if (* (namebase = BaseName( name ))) {
	    if ( (lock->fl_Name = getenvname( namebase )) == NULL ) {
		FreeMem ((char *)lock, sizeof(struct myFileLock));
		pkt->dp_Res1 = NULL;
		pkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND;
	    }
	}
    }
    else {
	pkt->dp_Res2 = ERROR_NO_FREE_STORE;
    }

    return;
}


/*
 *  FUNCTION
 *	action_copy_lock
 *
 *  DESCRIPTION
 *	Implement the DupLock function.
 */

void
action_copy_lock (register struct DosPacket * pkt)
{
    register struct myFileLock	* newlock;
    register struct myFileLock	* oldlock;
    register int		  namelen;

    pkt->dp_Res1 = NULL;

    if (oldlock = (struct nyFileLock *)BTOC(pkt->dp_Arg1)) {

	if (newlock = (struct myFileLock *)AllocMem(sizeof(struct myFileLock),
						MEMF_PUBLIC | MEMF_CLEAR)) {
	    pkt->dp_Res1 = CTOB(newlock);

	    newlock->fl_Access = oldlock->fl_Access;
	    newlock->fl_Task   = oldlock->fl_Task;
	    newlock->fl_Volume = oldlock->fl_Volume;

	    if (oldlock->fl_Name) {
		namelen = __builtin_strlen(oldlock->fl_Name);
		if (newlock->fl_Name = DosAllocMem( namelen + 1 )) {
		    __builtin_memcpy (newlock->fl_Name, oldlock->fl_Name, namelen );
		}
		else {
		    pkt->dp_Res1 = NULL;
		    pkt->dp_Res2 = ERROR_NO_FREE_STORE;
		    FreeMem ((char *) newlock, sizeof(struct myFileLock));
		}
	    }
	}
	else {
	    pkt->dp_Res2 = ERROR_NO_FREE_STORE;
	}
    }

    return;
}


/*
 *  FUNCTION
 *	action_freelock
 *
 *  DESCRIPTION
 *	Frees the lock.
 */

void
action_freelock(register struct DosPacket * pkt)
{
    register struct myFileLock * lock;

    if (lock = BTOC(pkt->dp_Arg1)) {
	if (lock->fl_Name) {
	    DosFreeMem((char *) lock->fl_Name);
	}
	FreeMem((char *)lock, sizeof(struct myFileLock));
    }
    pkt->dp_Res1 = DOS_TRUE;

    return;
}


/*
 *  FUNCTION
 *	action_delete
 *
 *  DESCRIPTION
 *	Delete a MANX variable.
 */

void
action_delete(register struct DosPacket * pkt)
{
    char   name[NAMESIZE];
    char * varname;

    BtoCStr(name, pkt->dp_Arg2, NAMESIZE-1);
    varname = BaseName( name );

    if (* varname) {
	if (Setenv(varname, NULL)) {
	    pkt->dp_Res1 = DOS_TRUE;
	}
	else {
		/* As close at is can be. */
	    pkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND;
	}
    }
    else {
	pkt->dp_Res2 = ERROR_BAD_STREAM_NAME;
    }
    return;
}


/*
 *  FUNCTION
 *	action_examine_object
 *
 *  DESCRIPTION
 *	This function returns information about either ENV: itself
 *	os a single varable. It fills in the FileInfoBlock with
 *	appropriate values.
 */

void
action_examine_object(register struct DosPacket * pkt)
{
    register struct myFileLock	  * lock;
    register struct FileInfoBlock * fib;

    if (lock = (struct myFileLock *) BTOC(pkt->dp_Arg1)) {
	if (fib = (struct FileInfoBlock *)BTOC(pkt->dp_Arg2)) {

	    if (lock->fl_Name == NULL) {

		fib->fib_DiskKey	= FIRST_KEY;
		fib->fib_DirEntryType   = 2;	      /* Directory  */
		fib->fib_EntryType	= 2;
		fib->fib_Size		= 0;
		fib->fib_NumBlocks	= 0;

		CtoBStr( "ENV", CTOB(fib->fib_FileName), 4);
	    }
	    else {
		fib->fib_DiskKey	= FIRST_KEY;
		fib->fib_DirEntryType	= -3;
		fib->fib_EntryType	= -3;
		Forbid();
		fib->fib_Size		=
				__builtin_strlen(mygetenv(lock->fl_Name));
		Permit();
		fib->fib_NumBlocks	= 1;

		CtoBStr ( lock->fl_Name, CTOB(fib->fib_FileName), 30);
	    }

	    fib->fib_Protection     = 0x00;	      /* ----rwed   */
	    fib->fib_Comment[0]     = 0;
	    /* DateStamp( (long *) & fib->fib_Date );    /* Edit later! */

	    pkt->dp_Res1 = DOS_TRUE;

	}
	else {	/* No fib */
	    pkt->dp_Res2 = ERROR_OBJECT_WRONG_TYPE;
	}
    }
    else {  /* No lock */
	pkt->dp_Res2 = ERROR_INVALID_LOCK;
    }
    return;
}


/*
 *  FUNCTION
 *	action_examine_next
 *
 *  DESCTIPTION
 *	This function:
 *	    - handles the `examine next' packet.
 *	    - is called as part of `examine object' funcetion.
 *
 *	It fills in the `FileInfoBlock' with appropriate values.
 */

void
action_examine_next (register struct DosPacket * pkt)
{
    register struct myFileLock	  * lock;
    register struct FileInfoBlock * fib;
    register	    char	  * newname;
    register	    LONG	    namelen;

    if (lock = (struct myFileLock *) BTOC(pkt->dp_Arg1)) {
	if (fib = (struct FileInfoBlock *)BTOC(pkt->dp_Arg2)) {

	    Forbid();

	    if (newname = getnextenv(fib, pkt) ) {
		for (namelen = 0; newname[namelen] != '='; namelen++)
		    ;

		fib->fib_DiskKey	+= 1;
		fib->fib_DirEntryType	= -3;
		fib->fib_Protection	= 0x00;		/* ----rwed */
		fib->fib_EntryType	= -3;
		fib->fib_Size		=
				__builtin_strlen(newname) - namelen - 1;
		fib->fib_NumBlocks	= 1;		
		/* DateStamp( (long *) & fib->fib_Date );  /* Edit later! */
		fib->fib_Comment[0]     = 0;

		__builtin_memcpy( fib->fib_FileName + 1, newname, namelen);
		fib->fib_FileName[0] = namelen;

		pkt->dp_Res1 = DOS_TRUE;
	    }
	    Permit();
	}
	else {
	    pkt->dp_Res2 = ERROR_OBJECT_WRONG_TYPE;
	}
    }
    else {
	pkt->dp_Res2 = ERROR_INVALID_LOCK;
    }
    return;
}


/*
 *  FUNCTION
 *	mygetenv
 *
 *  DESCRIPTION
 *	Get a MANX environment variable.
 *	I can`t use the Arp function `getenv', because it will call
 *	ENV: if the variable doesn`t exist in the MANX area, resulting
 *	in a loop loop loop loop loop.....
 *
 *	The EnvSpace is builtup by strings in the form:
 *	     name=value
 *	They are separated by one \0 and they are ended by two.
 *
 *  NOTE
 *	The task MUST be Forbid()`ed before this function is called!
 */

static char *
mygetenv(register char * name)
{
    register char * p;
    register int    len;

    if ( p = ((struct EnvBase*)ArpBase->EnvBase)->EnvSpace ) {

	len = __builtin_strlen(name);

	while (*p) {
	    if (Strncmp(p, name, len) == 0) {
		if ( p[len] == '=' ) {
		    return (& p[len+1]);
		}
	    }
	    p += __builtin_strlen(p) + 1;
	}
    }
    return(NULL);
}


/*
 *  FUNCTION
 *	getnextenv
 *
 *  DESCRIPTION
 *	Get the next environment variable. If an error accured the correct
 *	errorcode is placed in pkt->dp_Res2 and a NULL is returned.
 *
 *  NOTE
 *	This function MUST be called in Forbid()`ed state.
 */

static char *
getnextenv (register struct FileInfoBlock * fib, register struct DosPacket * pkt)
{
    register char * p;
    register int    len;

    if ( p = ((struct EnvBase*)ArpBase->EnvBase)->EnvSpace ) {

	if (fib->fib_DirEntryType > 0) {
	    if (*p) {
		return (p);
	    }
	    else {
		pkt->dp_Res2 = ERROR_NO_MORE_ENTRIES;
		return (NULL);
	    }
	}

	while (*p) {
	    len = __builtin_strlen(p);

	    if ( (Strncmp(p, fib->fib_FileName + 1,
				* fib->fib_FileName) == 0)
			&& (p[* fib->fib_FileName] == '=') ) {
		
		if ( * (p + len + 1) ) {
		    return (p + len + 1);
		} 
		else {
		    pkt->dp_Res2 = ERROR_NO_MORE_ENTRIES;
		    return (NULL);
		}
	    }
	    p += len + 1;
	}
	pkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND;	/* Correct??? */
    }
    return (NULL);	/* No environment at all, or entry not found */
}


/*
 *  FUNCTION
 *	getenvname
 *
 *  DESCRIPTION
 *	Get the name of an environment variable.
 *
 *  NOTE
 *	This function MUST be called in Forbid()`ed state!
 */

static char *
getenvname( register char * orgname )
{
    register char * p;
    register int    len;
    register char * newname;
    register int    namelen;

    if ( p = ((struct EnvBase*)ArpBase->EnvBase)->EnvSpace ) {

	namelen = __builtin_strlen( orgname );

	while (*p) {
	    len = __builtin_strlen(p);

	    if ( (Strncmp(p, orgname, namelen) == 0) &&
			(p[namelen] == '=') ) {
		newname = DosAllocMem (namelen+1);
		__builtin_memcpy( newname, p, namelen );
		return ( newname );
	    }
	    p += len + 1;
	}
    }
    return (NULL);
}


/*
 *  FUNCTION
 *	ReAlloc
 *
 *  DESCRIPTION
 *	Alloctes a biger memory area and copies the contens of
 *	the original block. It then releases the original
 *	block.
 *
 *  RETURNS
 *	A pointer to the new memory block. Or a NULL is
 *	AllocMem failed, in which case the original
 *	memory block is NOT released.
 */

static void *
ReAlloc ( void * oldbuf, long oldsize, long newsize, long flags )
{
    void * newbuf;

    if (newbuf = AllocMem(newsize, flags)) {

	if ((oldbuf != NULL) && (oldsize != 0L)) {
	    __builtin_memcpy( newbuf, oldbuf, MAX(oldsize, newsize) );
	    FreeMem( oldbuf, oldsize );
	}
    }
    return (newbuf);
}
