/*
	Remove 36.5 -- Copyright (c) 1993 Ralph Seichter. All rights reserved.

	Remove is freely distributable software -- it is *NOT* public domain!
	You may copy it for yourself and give copies to your friends, but please
	don't modify the program.  If you have enhancement suggestions, contact
	rseichter@darkness.gun.de (Usenet) or R.SEICHTER@DARKNESS.ZER (Z-Net).

	Remove was designed as a safer replacement for AmigaDOS' Delete function.
	Safer in this case means that Remove will ask you before a file is really
	deleted (which you may override), which results in unwantedly deleting
	files less often.  Did you ever execute "delete dh1: all quiet force" and
	then realised it was drive DH2: you wanted to wipe?  That's why I decided
	to write a tool like Remove.

	The command template for Remove is "FILE/A/M,ALL/S,QUIET/S,FORCE/S", which
	is exactly the same as the template for Delete.

		FILE		File name or directory pattern(s); may include wildcards.
		ALL		Remove subdirectories aswell.
		QUIET	Don't ever ask, delete every file that matches FILE.
		FORCE	Override delete protection (if possible).

	Example usage:

		Remove foobar
		Remove foo:bar/#?.info t:#?.c
		Remove s: all quiet

	Note that if applied to deeply nested directories, Remove will need lots
	of program stack, so set the stack high enough to avoid crashes!
*/

#include	<dos/exall.h>
#include	<proto/dos.h>
#include	<proto/exec.h>
#include	<string.h>

#define	ARG_FILE		0
#define	ARG_ALL		1
#define	ARG_QUIET		2
#define	ARG_FORCE		3
#define	ARG_TOTAL		4
#define	DATASIZE		512
#define	PATRNSIZE		256
#define	DIRSIZE		128



/* function prototypes */

VOID printf (STRPTR, ...);
VOID printName (LONG, STRPTR, BOOL);
BOOL ask (BOOL *, STRPTR);
VOID deleteDir (LONG, STRPTR, LONG, STRPTR, BOOL);
BOOL delete (STRPTR);
LONG hereWeGo (VOID);



/* global data */

UBYTE ver[] = "$VER: Remove 36.5 (1.9.93) Copyright \xA9 Ralph Seichter";
STRPTR arg[ARG_TOTAL] = {0, 0, 0, 0};
BOOL keepGoing = TRUE;
BPTR input, output;
struct DosLibrary *DOSBase;



/* the main program (argument parsing) */

LONG __saveds hereWeGo (VOID)
{
	LONG rc = RETURN_ERROR;

	if (DOSBase = (struct DosLibrary *)OpenLibrary (DOSNAME, 36))
	{
		LONG err;
		struct RDArgs *rda;

		input = Input ();
		output = Output ();
		if (rda = ReadArgs ("FILE/A/M,ALL/S,QUIET/S,FORCE/S", (LONG *)arg, NULL))
		{
			WORD i;
			BOOL dontAsk = (arg[ARG_QUIET] != 0);
			UBYTE directory[DIRSIZE], pattern[PATRNSIZE];
			STRPTR s, pat, current;
			STRPTR *list = (STRPTR *)arg[ARG_FILE];

			for (i = 0; current = list[i]; ++i)
			{
				s = current + strlen (current);
				while (s > current && *s != '/' && *s != ':')
					--s;
				if (*s == '/' || *s == ':')
				{
					pat = s + 1;
					if (*s == ':')
						++s;
					stccpy (directory, current, 1 + s - current);
				}
				else
				{
					directory[0] = 0;
					pat = current;
				}
				if (ParsePatternNoCase (pat, pattern, PATRNSIZE) >= 0)
					deleteDir (0, directory, SHARED_LOCK, pattern, dontAsk);
			}
			FreeArgs (rda);
		}
		err = IoErr ();
		if (err > 0 && err != ERROR_NO_MORE_ENTRIES)
			PrintFault (err, NULL);
		else
			rc = RETURN_OK;
		CloseLibrary ((struct Library *)DOSBase);
	}
	return (rc);
}



/* ANSI-C function replacement */

VOID printf (STRPTR ctl, ...)
{
	UBYTE buf[256];

	RawDoFmt (ctl, &ctl + 1, (VOID (*))"\x16\xc0\x4e\x75", buf);
	PutStr (buf);
}



/* print file/directory name (indented) */

VOID printName (LONG indent, STRPTR name, BOOL isDir)
{
	LONG i;
	STRPTR eol;

	for (i = 0; i < indent; ++i)
		printf ("   ");
	eol = (isDir ? "  (Dir)" : "");
	printf ("%s%s\n", name, eol);
}



/* ask the user if he wants to delete a file */

BOOL ask (BOOL *dontAsk, STRPTR name)
{
	BOOL delete;

	printf ("Delete %s (yes/no/all/stop)? ", name);
	Flush (output);
	Flush (input);
	switch (FGetC (input))
	{
		case 'a':
			/* delete all further files without asking */
			*dontAsk = TRUE;
		case 'y':
			/* delete one file */
			delete = TRUE;
			break;
		case 's':
			/* exit from program */
			keepGoing = FALSE;
		default:
			/* any other key: don't delete */
			delete = FALSE;
	}
	return (delete);
}



/* delete a complete directory */

VOID deleteDir (LONG indent, STRPTR dir, LONG locktype, STRPTR match, BOOL dontAsk)
{
	BPTR lock;

	if (!(arg[ARG_QUIET]) && locktype == EXCLUSIVE_LOCK)
		printName (indent, dir, TRUE);
	if (lock = Lock (dir, locktype))
	{
		BPTR oldCurDir;

		if (oldCurDir = CurrentDir (lock))
		{
			struct ExAllControl *eac;

			if (eac = AllocDosObject (DOS_EXALLCONTROL, NULL))
			{
				BOOL more;
				UBYTE data[DATASIZE];
				struct ExAllData *ed;

				if (*match)
					eac->eac_MatchString = match;
				do
				{
					more = ExAll (lock, (struct ExAllData *)data, DATASIZE, ED_TYPE, eac);
					if (eac->eac_Entries > 0)
					{
						ed = (struct ExAllData *)data;
						do
						{
							if (dontAsk || ask (&dontAsk, ed->ed_Name))
							{
								if (ed->ed_Type < 0)
								{
									if (delete (ed->ed_Name))
									{
										if (!(arg[ARG_QUIET]))
											printName (indent + 1, ed->ed_Name, FALSE);
									}
									else
										PrintFault (IoErr (), ed->ed_Name);
								}
								if (ed->ed_Type > 0 && (arg[ARG_ALL]))
									deleteDir (indent + 1, ed->ed_Name, EXCLUSIVE_LOCK, "", dontAsk);
							}
							ed = ed->ed_Next;
						}
						while ((ed) && keepGoing);
					}
				}
				while (more && keepGoing);
				FreeDosObject (DOS_EXALLCONTROL, eac);
			}
			CurrentDir (oldCurDir);
		}
		UnLock (lock);
		if (locktype == EXCLUSIVE_LOCK && !delete (dir))
			PrintFault (IoErr (), dir);
	}
}



/* delete one file and deprotect it if necessary */

BOOL delete (STRPTR name)
{
	BOOL ok;

	if (!arg[ARG_FORCE] || SetProtection (name, 0))
		ok = DeleteFile (name);
	else
		ok = FALSE;
	return (ok);
}

/* end of file */
