
/*
 * Delete.c -- for BlackHole.c.
 * $Revision: 1.1
 */

#include "header.h"
#include "prog-protos.h"

/* Prototypes */
Prototype LONG delete (struct HoleArg *);

Local LONG delete_file (struct WBArg *);
Local LONG delete_icon (struct WBArg *);
Local LONG delete_dir (struct WBArg *);
Local LONG delete_contents (BPTR , BOOL );

/* Globals */
BOOL ignore_protection;

#define EXALL_NUM 16		    /* examine 16 files at a time (I think) */

/* Entry point */
LONG delete (struct HoleArg *harg)
{
    set_current (NULL, SET_START);  /* reset current file */
    ignore_protection = harg->prefs.ignore_prot;
    return (delete_file (&(harg->wbarg)));
}

/* Local functions */

/* delete_contents -- delete the contents of a directory with lock lock.    */
/* If byicons is FALSE, search for ~(#?.info) & delete both name found and  */
/* name.info, if byicons is true, delete any file of pattern #?.info	    */
LONG delete_contents (BPTR lock, BOOL byicons)
{
    BPTR old_lock = CurrentDir (lock);

    struct ExAllControl *eac = 0;
    struct ExAllData *eadata = 0;
    struct WBArg *arg = 0;
    LONG error = 0;
    BOOL more;
    BYTE pattern[50];

    if (!(eac = (struct ExAllControl *) AllocDosObject (DOS_EXALLCONTROL, NULL)))
	goto fail;
    if (!(eadata = (struct ExAllData *)
	malloc (EXALL_NUM * sizeof (struct ExAllData)))) goto fail;
    if (!(arg = (struct WBArg *)malloc (sizeof (struct WBArg)))) goto fail;

    if (byicons) ParsePatternNoCase ("#?.info", pattern, 49);
    else ParsePatternNoCase ("~(#?.info)", pattern, 49);

    eac->eac_LastKey = 0;
    eac->eac_MatchString = pattern;
    eac->eac_MatchFunc = NULL;

    do {
	register struct ExAllData *ead;

	more = ExAll (lock, eadata, EXALL_NUM * sizeof (struct ExAllData),
		     ED_TYPE, eac);
	if (!more)
	{
	    if ((error = IoErr()) != ERROR_NO_MORE_ENTRIES) break;
	    else error = 0;
	}

	if (eac->eac_Entries == 0) continue;    /* have we finished? */

	ead = eadata;

	do {
	    if (ead->ed_Type >=0)       /* is it a directory? */
	    {
		arg->wa_Lock = Lock (ead->ed_Name, SHARED_LOCK);
		arg->wa_Name = NULL;
		if (arg->wa_Lock)
		{
		    if (error = delete_file (arg))
		    {
			more = FALSE; break;
			UnLock (arg->wa_Lock);
		    }
		}
	    }
	    else			/* it must be a file. */
	    {
		arg->wa_Lock = lock;
		arg->wa_Name = ead->ed_Name;

		if (byicons)
		{
		    if (error = delete_icon (arg))
		    {
			more = FALSE; break;
		    }
		}
		else
		{
		    if (error = delete_file (arg))
		    {
			more = FALSE; break;
		    }
		}
	    }
	} while (ead = ead->ed_Next);   /* go to next entry */

    } while (more);                     /* take next read from ExAll */

    fail:

    if (arg) free (arg);
    else error = ERROR_NO_FREE_STORE;
    if (eadata) free (eadata);
    if (eac) FreeDosObject (DOS_EXALLCONTROL, eac);
    CurrentDir (old_lock);

    return (error);
}


/* delete_dir -- delete a directory from lock. lock MUST be a directory     */
/* or else! Returns an error from IoErr(), or 0 for ok.                     */
LONG delete_dir (struct WBArg *wa)
{
    LONG error = 0;
    char buf[256];
    struct WBArg *parent = 0;

    if (error = delete_contents (wa->wa_Lock, FALSE)) goto fail;   /* delete files with/without icons */
    if (error = delete_contents (wa->wa_Lock, TRUE)) goto fail;    /* delete any lone .info files */
    if (!(parent = (struct WBArg *)malloc (sizeof (struct WBArg)))) goto fail;

    if (parent->wa_Lock = ParentDir (wa->wa_Lock))
    {
	if (NameFromLock (wa->wa_Lock, buf, 254))
	{
	    UnLock (wa->wa_Lock);   /* UnLock the dir as it is about to be deleted */
	    wa->wa_Lock = NULL;     /* Tell everyone we've unlocked it. */

	    parent->wa_Name = FilePart (buf);
	    set_current (NULL, SET_EXIT_DIR);
	    error = delete_file (parent);   /* delete ourselves */
	    if (error)              /* if failed, restore lock to directory */
	    {
		BPTR olddir = CurrentDir (parent->wa_Lock);
		wa->wa_Lock = Lock (parent->wa_Name, SHARED_LOCK); /* fingers crossed! */
		CurrentDir (olddir);
	    }
	} else error = IoErr();
	UnLock (parent->wa_Lock);
    }
    else error = IoErr();

    fail:

    if (parent) free (parent);

    return (error);
}

/* delete_icon -- delete an icon with name (MUST have a .info extension)    */
/* which lives in dir locked in lock					    */
LONG delete_icon (struct WBArg *wa)
{
    LONG error = 0;
    char *dot;

    if (error = set_current (wa, SET_NEWFILE)) goto fail;

    if (dot = strrchr (wa->wa_Name, '.'))
    {
	*dot = '\0';    /* temporarily remove .info extension */
	error = delete_file (wa);
	*dot = '.';     /* restore .info, in case it is needed later */
    }
    else error = ERROR_OBJECT_WRONG_TYPE;

    fail:

    return (error);
}

/* delete_file -- delete a file or dir given a lock to the dir + a name */
/* Returns an error code from IoErr() if failed, or 0 for ok.           */
LONG delete_file (struct WBArg *wa)
{
    LONG error = 0;
    BOOL nofile = FALSE;

    if (error = set_current (wa, SET_NEWFILE)) goto fail;

    if ((wa->wa_Name[0] == '\0') || !(wa->wa_Name)) /* is it a directory? */
    {
	error = delete_dir (wa);
    }
    else
    {
	/* cd to lock and store old lock */
	BPTR old_lock = CurrentDir (wa->wa_Lock);

	if (!DeleteFile (wa->wa_Name))       /* if delete file fails */
	{
	    error = IoErr();

	    if ((error == ERROR_DELETE_PROTECTED) && ignore_protection)
	    {
		error = 0;
		if (SetProtection (wa->wa_Name, 0))
		{   /* try again to delete it */
		    if (!DeleteFile (wa->wa_Name)) error = IoErr();
		}
		else error = IoErr();
	    }

	    if (error == ERROR_OBJECT_NOT_FOUND)
	    {
		error = 0;
		nofile = TRUE;	 /* object is just an icon */
	    }
	}

	if (!error)
	{
	    char dotinfo[100];
	    strcpy (dotinfo, wa->wa_Name);
	    strcat (dotinfo, ".info");      /* create name of icon */

	    if (!DeleteDiskObject (wa->wa_Name))    /* delete icon */
	    {
		error = IoErr();

		if ((error == ERROR_DELETE_PROTECTED) && ignore_protection)
		{
		    error = 0;
		    if (SetProtection (dotinfo, 0))
		    {	/* try again */
			if (!DeleteDiskObject (wa->wa_Name)) error = IoErr();
		    }
		    else error = IoErr();
		}

		/* If no icon was found and there was a file that was deleted */
		if ((error == ERROR_OBJECT_NOT_FOUND) && !nofile)
		{
		    BPTR fh = Open (dotinfo, MODE_NEWFILE); /* make pseudo icon */
		    if (fh)
		    {
			Close (fh);
			if (!DeleteDiskObject (wa->wa_Name))   /* now delete it! */
			error = IoErr();
			else error = 0;
		    }
		    else error = IoErr();
		}
	    }
	}
	CurrentDir (old_lock);      /* restore old cd */
    }

    fail:

    return (error);
}

