/*
**	patch.library
**
**	Copyright © 1993-1997 by Stefan Fuchs
**		Freely distributable.
*/

#ifndef _PATCH_INCLUDES_H
#include "patch_includes.h"
#endif


/* internal functions: */
ULONG RemovePatchInternalEntry( struct Patch *, struct TagItem *);


/****** patch.library/RemovePatchTagsA ***************************************
*
*   NAME
*        RemovePatchTagsA -- Removes an installed patch. (V2)
*        RemovePatchTags -- varargs stub for RemovePatchTagsA(). (V2)
*
*   SYNOPSIS
*        Error = RemovePatchTagsA( patch, taglist )
*        D0                        A0     A1
*
*        ULONG Error RemovePatchTagsA( struct Patch *, struct TagItem *);
*
*        Error = RemovePatchTags( patch, ...)
*
*        ULONG Error RemovePatchTags( struct Patch *, ...);
*
*   FUNCTION
*        Removes a patch from a library function.
*
*        All allocated resources for that specific patch will
*        be deallocated.
*
*   INPUTS
*        patch = pointer to a patch structure or NULL for no action
*        taglist = pointer to array of tags
*
*   TAGS
*        PATT_TimeOut (ULONG) - The number of ticks (1/50 seconds) the
*                function keeps trying to remove the patch, if another task
*                is running in the patchcode.
*                If the patch.library does not succeed in the given time the
*                function will return PATERR_PatchInUse.
*                Defaults to NULL, which means no retry.
*        PATT_DelayedExpunge (BOOL) - If this tag is not set to FALSE and a
*                non-patch.library patch was installed after the patch.library
*                patch for a specific library function the specified patch
*                will nevertheless be removed.
*                BUT some resources will be kept allocated by patch.library
*                (e.g.: the Library Offset Vector will not be restored to its
*                old state). Patch.library will try to deallocate these
*                resources automatically, if the system is getting low on
*                memory or if a call to a patch.library function that
*                removes or installs patches is made.
*                The default is TRUE !!!
*
*   RESULT
*        Error = errorcode as defined in patch.h.
*                PATERR_Ok
*                        Indicates success of the operation.
*                PATERR_PatchInUse
*                        Indicates that some other task is using the
*                        installed function and the patch can't be removed.
*                        Your task may wait and try again later.
*                PATERR_PatchInstalled
*                        Indicates that a patchcode has been installed for
*                        that function after your patch.library patch has
*                        been installed and your patch is the only
*                        patch.library patch for that function.
*                        If patch.library would remove your patch
*                        tasks would jump into deallocated memory
*                        Result:  blinking borders.
*                        Never occurs, if you pass in the tag
*                        PATT_DelayedExpunge with TRUE.
*                PATERR_InvalidHandle
*                        Indicates that the pointer to the patch passed
*                        to the function was not or is no longer valid.
*                        This might happen, if you pass a wrong pointer or
*                        you got the pointer via FindPatch() and another
*                        task has removed the patch before this task called
*                        RemovePatchTags().
*                        Also keep in mind that ln_Type must be PS_TYPE_USER.
*
*   NOTES
*        Removing a patch routine can never be made absolutely safe.
*        Although patch.library does anything possible to provide methods
*        to minimize the chance of a crash, there will always be a
*        slight chance.
*        So minimize the number of install and remove operations.
*
*        This function may implicitly call dos.library functions, so do not
*        call it from tasks.
*
*   BUGS
*
*   SEE ALSO
*        InstallPatchTags(), RemovePatchProject(), patch.h, patchtags.h
*
******************************************************************************
*
*/

ULONG LIBFUNC RemovePatchTagsA( REGA0 struct Patch *patch GNUC_REGA0, REGA1 struct TagItem *taglist GNUC_REGA1)
{
LONG timeout;
ULONG result = NULL;


	RemovePatchHandler( );

	if( patch)
	{
		timeout = GetTagData(PATT_TimeOut, 0L, taglist);

		for(;;)
		{
			result = RemovePatchInternalEntry( patch, taglist);
			if( result == PATERR_Ok)
			{
				SendNotify( PATCOD_PatchRemoved, NULL);
				break;
			}
			if( result == PATERR_PatchInstalled) break;
			if( result == PATERR_InvalidHandle) break;
			if( result == PATERR_NoSemaphore) break;
			timeout -= 30;
			MyDelay( 30L);
			if( timeout < 0) break;
		}

	}
	return( result);
}
/* ------------------------------------------------------------------------------ */
ULONG RemovePatchInternalEntry( struct Patch *patch, struct TagItem *taglist)
{
ULONG Result;
struct MasterPatch *master;
struct Patch *orig;

	Result = SAVEObtainSemaphore();
	if (Result == 0L)
	{
/* Tests, if handle is still valid */
		Result = PATERR_InvalidHandle;
		if( (patch->PS_Node.ln_Type == PS_TYPE_SYSTEM) || (patch->PS_Node.ln_Type == PS_TYPE_USER))
		{
			Result = TestPatchHandle( patch);
			if( Result == PATERR_Ok)
			{
				if( patch->PS_Node.ln_Type == PS_TYPE_USER) patch->PS_Node.ln_Type = PS_TYPE_DELUSER;
				if( patch->PS_Node.ln_Type == PS_TYPE_SYSTEM) patch->PS_Node.ln_Type = PS_TYPE_DELSYSTEM;
				master = GetMasterPatch( patch);
				Disable();
				LinkPatch( master);
				CacheClearU();
				Enable();
				if( GetTagData(PATT_DelayedExpunge, 1L, taglist))
				{
					Result = RemStruct( patch);
					if( Result == PATERR_Ok) 
					{
						ReCalcStackSize( master);
						Result = RemoveSystemNodeWithTest( master);
						if( Result == PATERR_PatchInstalled) /* Ignore PATERR_PatchInstalled errors */
							Result = PATERR_Ok;
						goto ende;
					}
				}
				else
				{
					if( FindType( (struct List *)(&(master->MPS_PatchHeader)), PS_TYPE_USER) || FindType( (struct List *)(&(master->MPS_PatchHeader)), PS_TYPE_SYSTEM))
					{
/* --- here follows the code called, when there exists another UserPatch */
						Result = RemStruct( patch);
						ReCalcStackSize( master);
					}
					else
					{
/* --- here follows the code called, when the UserPatch was the last one */
						Forbid();
						Result = DisconnectStartType( master);
						if( Result == PATERR_Ok)
						{
							orig = (struct Patch *)FindType( (struct List *)(&(master->MPS_PatchHeader)), PS_TYPE_ORIG);
							Disable();
							if( (TestUsage( patch) == 0L) && (TestUsage( orig) == 0L))
							{
								RemStruct( patch);
								RemoveSystemNode( master);
							}
	 						else
							{
								ConnectStartType( master);
								Result = PATERR_PatchInUse;
							}
							Enable();
						}
						Permit();
					}
					if( Result == PATERR_Ok) goto ende;
				}
				if( patch->PS_Node.ln_Type == PS_TYPE_DELUSER) patch->PS_Node.ln_Type = PS_TYPE_USER;
				if( patch->PS_Node.ln_Type == PS_TYPE_DELSYSTEM) patch->PS_Node.ln_Type = PS_TYPE_SYSTEM;
				Disable();
				LinkPatch( master);
				CacheClearU();
				Enable();
			}
		}
ende:		ReleaseSemaphore(&(PatchBase->PB_Semaphore));
	}
	return( Result);
}
