/*
** $VER: Kill.c 1.97 (31.7.94)
**
** Beinahe dasselbe wie C:delete, nur daß man zusätzlich wie bei
** List SINCE und UPTO angeben kann
** (und das Programm informativer ist (bei löschgeschützten Files))
**
** © Copyright 1994 by Walter Dörwald
**   Alle Rechte vorbehalten
*/

#include <exec/memory.h>
#include <dos/datetime.h>
#include <clib/macros.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>

#include <stdio.h>
#include <stdlib.h>

#include <privat/Defs.h>
#include <privat/Auto.h>
#include <privat/Proto.h>

#include <debug/debug.h>

// !!! Wo ist denn dieses FORMAT_DEF definiert? (in dos.library/StrToDate() ist's jedenfalls dokumentiert)

#include "rev.h"

#define PROGRAM "Kill"

#define ARG_TEMPLATE "FILE/M/A,ALL/S,QUIET/S,FORCE/S,SINCE/K,UPTO/K,SINCEDAYS/K/N,UPTODAYS/K/N,BIGGER/K/N,SMALLER/K/N"
#define ARG_FILE         0
#define ARG_ALL          1
#define ARG_QUIET        2
#define ARG_FORCE        3
#define ARG_SINCE        4
#define ARG_UPTO         5
#define ARG_SINCEDAYS    6
#define ARG_UPTODAYS     7
#define ARG_BIGGER       8
#define ARG_SMALLER      9
#define ARG_sizeof       10

#define ISDIR(AP) (((AP)->ap_Info.fib_DirEntryType >= 0) && ((AP)->ap_Info.fib_DirEntryType != ST_SOFTLINK))

static const char Version[] = "$VER: " PROGRAM " " VERSION_STR "." REVISION_STR " (" HEUTE_STR ")";

struct RDArgs *ArgsPtr;
UBYTE *ArgArray[ARG_sizeof];

struct DateStamp Jetzt;
struct DateStamp Since;
struct DateStamp Upto;
struct DateStamp SinceDays;
struct DateStamp UptoDays;

void DoExit(ULONG result)
{
	if (ArgsPtr)
		FreeArgs(ArgsPtr);

	exit(result);
}

void PrintError(LONG Error,STRPTR Text)
{
	D2(bug("Error=%ld, Text=»%s«\n",Error,Text));

	if (!ArgArray[ARG_QUIET])
	{
		if (!PrintFault(Error,Text))
			MyPrintf("%s: Error %ld",Text,Error);
	}
}

LONG Kill(struct AnchorPath *AP)
{
	LONG Error;

	D2(bug("AP.ap_Buf=»%s«\n",AP->ap_Buf));
	if (!ArgArray[ARG_QUIET])
		MyPrintf("%s",AP->ap_Buf);

	if (AP->ap_Info.fib_Protection & FIBF_DELETE) // Beim löschgeschützten Files vorher entriegeln
	{
		if (ArgArray[ARG_FORCE]) // ... aber nur wenn es der Benutzer auch will
		{
			if (SetProtection(AP->ap_Info.fib_FileName,AP->ap_Info.fib_Protection & ~FIBF_DELETE))
			{
				if (!ArgArray[ARG_QUIET])
					MyPrintf(" " CSI_STR "1munprotected" CSI_STR "0m and");
			}
			else
			{
				D(bug("SetProtection() fehlgeschlagen\n"));
				PrintError(IoErr()," " CSI_STR "1mnot" CSI_STR "0m unprotected");
				return (RETURN_ERROR);
			}
		}
		else
		{
			SetIoErr(ERROR_DELETE_PROTECTED);
			PrintError(ERROR_DELETE_PROTECTED," " CSI_STR "1mnot" CSI_STR "0m deleted (use FORCE/S)");
			return (RETURN_WARN);
		}
	}

	if (DeleteFile(AP->ap_Info.fib_FileName))
	{
		if (!ArgArray[ARG_QUIET])
			MyPrintf(" deleted\n");
		return (RETURN_OK);
	}
	else
	{
		D(bug("DeleteFile() fehlgeschlagen\n"));
		Error = IoErr();
		PrintError(Error," " CSI_STR "1mnot" CSI_STR "0m deleted");
		return ((Error == ERROR_DIRECTORY_NOT_EMPTY) ? RETURN_WARN : RETURN_ERROR);
	}
}

LONG Do(struct AnchorPath *AP)
{
	if (ArgArray[ARG_SINCE] && (CompareDates(&Since,&AP->ap_Info.fib_Date)<0))
	{
		D2(bug("AP.ap_Buf=»%s« to old (SINCE/K) (DateStamp=={%ld,%ld,%ld})\n",AP->ap_Buf,AP->ap_Info.fib_Date.ds_Days,AP->ap_Info.fib_Date.ds_Minute,AP->ap_Info.fib_Date.ds_Tick));
		return (RETURN_OK);
	}
	if (ArgArray[ARG_UPTO] && (CompareDates(&Upto,&AP->ap_Info.fib_Date)>0))
	{
		D2(bug("AP.ap_Buf=»%s« to new (UPTO/K) (DateStamp=={%ld,%ld,%ld})\n",AP->ap_Buf,AP->ap_Info.fib_Date.ds_Days,AP->ap_Info.fib_Date.ds_Minute,AP->ap_Info.fib_Date.ds_Tick));
		return (RETURN_OK);
	}
	if (ArgArray[ARG_SINCEDAYS] && (CompareDates(&SinceDays,&AP->ap_Info.fib_Date)<0))
	{
		D2(bug("AP.ap_Buf=»%s« to old (SINCEDAYS/K) (DateStamp=={%ld,%ld,%ld})\n",AP->ap_Buf,AP->ap_Info.fib_Date.ds_Days,AP->ap_Info.fib_Date.ds_Minute,AP->ap_Info.fib_Date.ds_Tick));
		return (RETURN_OK);
	}
	if (ArgArray[ARG_UPTODAYS] && (CompareDates(&UptoDays,&AP->ap_Info.fib_Date)>0))
	{
		D2(bug("AP.ap_Buf=»%s« to new (UPTODAYS/K) (DateStamp=={%ld,%ld,%ld})\n",AP->ap_Buf,AP->ap_Info.fib_Date.ds_Days,AP->ap_Info.fib_Date.ds_Minute,AP->ap_Info.fib_Date.ds_Tick));
		return (RETURN_OK);
	}
	if (ArgArray[ARG_BIGGER] && !ISDIR(AP) && (AP->ap_Info.fib_Size<=*(LONG *)ArgArray[ARG_BIGGER]))
	{
		D2(bug("AP.ap_Buf=»%s« to small (BIGGER) (Size==%ld)\n",AP->ap_Buf,AP->ap_Info.fib_Size));
		return (RETURN_OK);
	}
	if (ArgArray[ARG_SMALLER] && !ISDIR(AP) && (AP->ap_Info.fib_Size>=*(LONG *)ArgArray[ARG_SMALLER]))
	{
		D2(bug("AP.ap_Buf=»%s« to big (SMALLER) (Size==%ld)\n",AP->ap_Buf,AP->ap_Info.fib_Size));
		return (RETURN_OK);
	}

	return (Kill(AP));
}

// Alle Unterverzeichnisse durchlaufen
#define SB_ALL 0
#define SF_ALL (1L<<SB_ALL)

// Alle Ausgaben unterdrücken
#define SB_QUIET 1
#define SF_QUIET (1L<<SB_QUIET)

LONG Scan(STRPTR *Files,LONGBITS BreakBits,LONGBITS Flags,LONG NameLen)
{
	struct AnchorPath *AP;
	LONG               Error;
	LONG               Level;
	LONG               TestLevel;

	Level = RETURN_OK;

	if (AP = AllocVec(sizeof(struct AnchorPath)+NameLen,MEMF_ANY|MEMF_CLEAR))
	{
		AP->ap_BreakBits = BreakBits;
		AP->ap_FoundBreak = 0;
		AP->ap_Flags = APF_DOWILD;
		AP->ap_Strlen = NameLen;

		for (;*Files;Files++)
		{
			for (Error = MatchFirst(*Files,AP);!Error;Error = MatchNext(AP))
			{
				BPTR OldLock;

				D2(bug("File »%s«, DateStamp {%ld,...}\n",AP->ap_Buf,AP->ap_Info.fib_Date.ds_Days));

				if (AP->ap_Flags & APF_DIDDIR) // Directory fertig?
					AP->ap_Flags &= ~APF_DIDDIR;
				else if (ISDIR(AP)) // Ein Directory?
				{
					if (Flags & SF_ALL) // Rein in's Directory aber noch nicht löschen sondern erst bei Rückkehr
					{
						AP->ap_Flags |= APF_DODIR;
						continue;
					}
				}

				OldLock = CurrentDir(AP->ap_Current->an_Lock);

				TestLevel = Do(AP);
				Level = MAX(Level,TestLevel);

				CurrentDir(OldLock);
				if (Level>RETURN_WARN)
					goto fertig;
			}
			MatchEnd(AP);
			// Den Fehlerlevel zurückgeben
			if (Error!=ERROR_NO_MORE_ENTRIES)
				Level = RETURN_ERROR;
		}
	}
	else
	{
		D(bug("AllocVec() fehlgeschlagen: Kein Speicher für struct AnchorPath\n"));
		return (RETURN_FAIL);
	}
	fertig:
	FreeVec(AP);
	return (Level);
}

void main()
{
	LONG Level;
	LONG Flags;
	struct DateTime Zeit;

	D2(bug(CSI_STR "1mLos geht's" CSI_STR "0m\n"));

	DateStamp(&Jetzt);

	Zeit.dat_Format = FORMAT_DOS;
	Zeit.dat_Flags = 0; // DTF_SUBST braucht laut dos.library/StrToDate() nicht gesetzt zu sein

	if (!(ArgsPtr = ReadArgs(ARG_TEMPLATE,(LONG *)&ArgArray,NULL)))
	{
		D(bug("ReadArgs() fehlgeschlagen\n"));
		PrintFault(IoErr(),PROGRAM "(parsing arguments)");
		DoExit(RETURN_ERROR);
	}

	Flags = 0L;
	if (ArgArray[ARG_ALL])
		Flags |= SF_ALL;
	if (ArgArray[ARG_QUIET])
		Flags |= SF_QUIET;
	if (ArgArray[ARG_SINCE])
	{
		Zeit.dat_StrDate = ArgArray[ARG_SINCE];
		if (!StrToDate(&Zeit))
		{
			D(bug("StrToDate() fehlgeschlagen (bei ARG_SINCE)\n"));
			PrintFault(ERROR_OBJECT_WRONG_TYPE,PROGRAM "(parsing SINCE/K)");
			DoExit(RETURN_ERROR);
		}
		Zeit.dat_Stamp.ds_Minute = 0;
		Zeit.dat_Stamp.ds_Tick = 0;
		Since = Zeit.dat_Stamp;

		D2(bug("Since=={%ld,%ld,%ld}\n",Since.ds_Days,Since.ds_Minute,Since.ds_Tick));
	}
	if (ArgArray[ARG_UPTO])
	{
		Zeit.dat_StrDate = ArgArray[ARG_UPTO];
		if (!StrToDate(&Zeit))
		{
			D(bug("StrToDate() fehlgeschlagen (bei ARG_UPTO)\n"));
			PrintFault(ERROR_OBJECT_WRONG_TYPE,PROGRAM "(parsing UPTO/K)");
			DoExit(RETURN_ERROR);
		}
		Zeit.dat_Stamp.ds_Minute = 24*60-1;
		Zeit.dat_Stamp.ds_Tick = TICKS_PER_SECOND-1;
		Upto = Zeit.dat_Stamp;

		D2(bug("Upto=={%ld,%ld,%ld}\n",Upto.ds_Days,Upto.ds_Minute,Upto.ds_Tick));
	}
	if (ArgArray[ARG_SINCEDAYS])
	{
		SinceDays = Jetzt;
		SinceDays.ds_Days += *(LONG *)ArgArray[ARG_SINCEDAYS];

		D2(bug("SinceDays=={%ld,%ld,%ld}\n",SinceDays.ds_Days,SinceDays.ds_Minute,SinceDays.ds_Tick));

	}
	if (ArgArray[ARG_UPTODAYS])
	{
		UptoDays = Jetzt;
		UptoDays.ds_Days += *(LONG *)ArgArray[ARG_UPTODAYS];

		D2(bug("UptoDays=={%ld,%ld,%ld}\n",UptoDays.ds_Days,UptoDays.ds_Minute,UptoDays.ds_Tick));
	}

	if ((Level = Scan((STRPTR *)ArgArray[ARG_FILE],SIGBREAKF_CTRL_C,Flags,256L))>RETURN_WARN)
		PrintFault(IoErr(),PROGRAM);

	DoExit(Level);
}
