/*
 *	Actions.c - Copyright © 1991 by S.R. & P.C.
 *
 *	Created:	02 Mar 1991  15:50:36
 *	Modified:	29 Jul 1991  07:10:23
 *
 *	Make>> make
 */

#include "Global.h"
#include "DosVar.h"
#include "FileList.h"
#include "ActionBack.h"
#include "Process.h"
#include "proto/Actions.h"
#include "proto/Copy.h"
#include "proto/ActionBack.h"
#include "proto/Request.h"
#include "proto/File.h"
#include "proto/FileList.h"


extern void setmem(void *mem, size_t size, long value);

extern struct ExecBase *SysBase;
extern char *ReqTitle;


/* just request cmd can't be done on volumes/devices/assigns */

static void TellNoVolumes(char *cmd)
{
	SimpleRequest(ReqTitle, "Can't %s volumes.", cmd);
}


/* Get the only element of list */

static struct SuperFileInfo *GetSelected(struct HeadFileList *hfl)
{
	struct BrowserDir *bd;

	bd = (struct BrowserDir *)hfl->DirList.mlh_Head;
	return (struct SuperFileInfo *)bd->SuperFileList.mlh_Head;
}


/* Tell if str has a single star in it. if yes, return a pointer to it */

static char *has_star(char *str)
{
	char *s;

	while(*str && *str != '*') str++;
	if (!*str)
		return NULL;	/* no '*' in str */
	else
		s = str++;		/* '*' found so keep pointer and skip it */
	while(*str && *str != '*') str++;
	/* now, if *str != '\0', another star was found, so return NULL */
	return (*str) ? NULL : s;
}


/*
 *	Check for difference between the two names to make sure rename/duplicate
 *	can be done. The new name mustn't be a single star, that is to say "*".
 *	If the old one have a star '*' in it, the new one MUST have one too and
 *	only one. And if the old name has no star, the two names must be different.
 *	If ok, then preparse new name as a format for SPrintf(). 
 */

static BOOL ValidRenDup(char *NewName, char *OldName)
{
	char buf[32];
	char *s;

	if (!(*NewName) || !strcmp(NewName, "*") || !strcmp(NewName, OldName))
		return FALSE;
	if (has_star(OldName)) {
		if (!(s = has_star(NewName))) {
			SimpleRequest(ReqTitle, "You MUST keep a single '*' in name.");
			return FALSE;
		}
		else {
			strcpy(buf, s+1);	/* save prefix in buf */
			strcpy(s, "%s");	/* replace '*' with "%s" */
			strcpy(s+2, buf);	/* restore prefix */
		}
	}
	return TRUE;
}


/* Build new name with name and pattern given by Init rename/duplicate */

short MakeNewName(char *NewName, char *fmt, char *OldName)
{
	char buffer[64];

	if (SPrintf(buffer, fmt, OldName) > 30) {
		/* multiply result by 2 because "Skip" take place of usual "Retry" in
		 * requester and no retry is provided here. */
		return ThreeGadRequest("Skip", NULL, "Filename to long: \"%s\"", buffer) << 1;
	}
	else {
		/* give back newname only if no trouble */
		strcpy(NewName, buffer);
		return A_RETRY;
	}
}


static BOOL InitRename(struct HeadFileList *hfl)
{
	struct SuperFileInfo *sfi;
	BPTR VolumeLock;
	char *Title, *NewName, *s;
	char tmpbuf[32];
	short Ok = A_RETRY;

	if (hfl->Vols > 1 || hfl->Vols && hfl->Vols < hfl->NumEntries) {
		SimpleRequest(ReqTitle, "Can't relabel multiple volumes.");
		return FALSE;
	}
	if (hfl->NumEntries > 1 || (hfl->Select.si_Flags & SI_AFFECT_SUBDIRS)) {
		strcpy(tmpbuf, "*");
		Title = "Add suffix and/or prefix...";
	}
	else if (hfl->NumEntries == 1) {
		Title = "Enter new name...";
		sfi = GetSelected(hfl);
		strcpy(tmpbuf, sfi->FileInfo.fi_Name);
		switch(sfi->FileInfo.fi_Type) {
		case DLX_ASSIGN:
			SimpleRequest(ReqTitle, "Can't rename an assign.");
			return FALSE;
		case DLX_DEVICE:	/* for a device, obtain mounted volume name, and continue to volume handling routine */
			while(Ok == A_RETRY && !(VolumeLock = Lock(tmpbuf, ACCESS_READ)))
				Ok = ThreeGadRequest("Retry", NULL, "Couldn't access \"%s\"\n%s.", tmpbuf, StrIoErr());
			if (Ok != A_RETRY)
				return FALSE;
			PathName(VolumeLock, tmpbuf, 31);
			UnLock(VolumeLock);
		case DLX_VOLUME:	/* for a volume, remove ending ':' */
		case DLX_UNMOUNTED:
			s = &tmpbuf[strlen(tmpbuf)-1];
			if (*s == ':') *s = '\0';
			break;
		}
	}
	else
		return FALSE;
	NewName = hfl->ActionArgs.NewName;
	strcpy(NewName, tmpbuf);
	if (GetString(NewName, Title, NULL, 38, 31) && ValidRenDup(NewName, tmpbuf))
		return TRUE;
	else
		return FALSE;
}


static BOOL InitTouch(struct HeadFileList *hfl)
{
	if (hfl->Vols) {
		TellNoVolumes("touch");
		return FALSE;
	}
	DateStamp(&hfl->ActionArgs.DateStamp);
	return TRUE;
}


static BOOL InitSetComment(struct HeadFileList *hfl)
{
	char buf[82];
	char *s;

	if (hfl->Vols) {
		TellNoVolumes("comment");
		return FALSE;
	}
	if (hfl->NumEntries == 0)
		return FALSE;
	buf[0] = '\0';
	if (hfl->NumEntries == 1 && (s = GetSelected(hfl)->FileInfo.fi_Comment))
		strcpy(buf, s);
	if (GetString(buf, "Set Comment...", NULL, 50, 80)) {
		strcpy(hfl->ActionArgs.Comment, buf);
		return TRUE;
	}
	return FALSE;
}


static BOOL InitSetProtect(struct HeadFileList *hfl)
{
	struct SelectInfo SelectInfo;

	if (hfl->Vols) {
		TellNoVolumes("protect");
		return FALSE;
	}
	if (hfl->NumEntries == 0)
		return FALSE;
	else if (hfl->NumEntries == 1 && !(hfl->Select.si_Flags & SI_AFFECT_SUBDIRS))
		SelectInfo.si_PosProtect = GetSelected(hfl)->FileInfo.fi_Protection;
	else
		SelectInfo.si_PosProtect = 0x0F;	/* four lower bits (rwed) */
	SelectInfo.si_NegProtect = 0;
	if (FiltersReq(&SelectInfo, PROTECT_REQ)) {
		hfl->ActionArgs.Protection.Pos = SelectInfo.si_PosProtect;
		hfl->ActionArgs.Protection.Neg = SelectInfo.si_NegProtect;
		return TRUE;
	}
	return FALSE;
}


static BOOL InitDelete(struct HeadFileList *hfl)
{
	char *MiddleText = NULL;
	short Flags;

	if (hfl->Vols) {
		TellNoVolumes("delete");
		return FALSE;
	}
	if (hfl->NumEntries == 0)
		return FALSE;
	Flags = hfl->Select.si_Flags;
	/* Don't ask for Delete All if AFFECT_SUBDIRS and DIRS isn't selected */
	if (hfl->Dirs)
		MiddleText = "Delete All";
	switch(ThreeGadRequest("Delete", MiddleText, "Really delete all marked files ?")) {
	case 0:		/* CANCEL */
		return FALSE;
	case 1:		/* DELETE */
		/* Delete files and non-empty dirs */
		break;
	case 2:		/* DELETE ALL */
		if (!(hfl->Select.si_Flags & SI_AFFECT_SUBDIRS))
			hfl->Select.si_Flags = SI_AFFECT_SUBDIRS|SI_ALL_FILES|SI_ALL_DIRS;
	}
	return TRUE;
}


static BOOL InitAndDoDuplicate(struct HeadFileList *hfl)
{
	char tmpbuf[40];
	char *Title, *NewName;

	NewName = hfl->ActionArgs.NewName;
	if (hfl->Vols) {
		TellNoVolumes("duplicate");
		return FALSE;
	}
	if (hfl->NumEntries > 1 || (hfl->Select.si_Flags & SI_AFFECT_SUBDIRS)) {
		strcpy(tmpbuf, "*");
		Title = "Add suffix and/or prefix...";
	}
	else if (hfl->NumEntries == 1) {
		strcpy(tmpbuf, GetSelected(hfl)->FileInfo.fi_Name);
		Title = "Duplicate...";
	}
	else
		return FALSE;
	strcpy(NewName, tmpbuf);
	if (GetString(NewName, Title, NULL, 38, 31) && ValidRenDup(NewName, tmpbuf)) {
		if (BaseName(NewName) == NewName) {
			hfl->CopyMode = (hfl->CopyMode & ~(CM_ALLWAYS_MOVE|CM_CONTEXT)) | CM_ALLWAYS_COPY;	/* Tell CopyMove() to copy ! */
			CopyMove(hfl);
		}
		else
			SimpleRequest(ReqTitle, "Full path name not allowed for Duplicate.");
	}
	return FALSE;	/* Allways return FALSE since the action is done in InitAction */
}


static short MakeDir(struct HeadFileList *hfl, char *Name)
{
	struct SuperFileInfo sfi;
	struct FileInfoBlock *fib;
	BPTR NewDir;
	short Ok = A_RETRY;

	if (!(fib = AllocMem(sizeof(struct FileInfoBlock), MEMF_PUBLIC|MEMF_CLEAR)))
		return FALSE;
	setmem(&sfi, sizeof(struct SuperFileInfo), 0);
	/* Prevent CreateDir() bug which create a dir even if a file with the same name already exists */
	while(Ok == A_RETRY && (NewDir = Lock(Name, ACCESS_READ))) {
		UnLock(NewDir);
		Ok = ThreeGadRequest("Retry", "Skip", "Couldn't create dir \"%s\"\n%s.", Name, "Object already exists");
	}
	while(Ok == A_RETRY && !(NewDir = CreateDir(Name)))
		Ok = ThreeGadRequest("Retry", "Skip", "Couldn't create dir \"%s\"\n%s.", Name, StrIoErr());
	if (Ok == A_RETRY) {
		UnLock(NewDir);
		if (Name == BaseName(Name) && (Ok = GetFib(Name, fib, TRUE)) == A_RETRY) {
			Fib2Fi(&sfi.FileInfo, fib);
			strcpy(sfi.OldName, sfi.FileInfo.fi_Name);
			sfi.ActionBack |= AB_NEW_ENTRY;
			SendActionBack(&sfi, NULL, hfl->DestDir);
		}
	}
	FreeMem(fib, sizeof(struct FileInfoBlock));
	return Ok;
}


#define MAKEDIR_BUFSIZE  46

static BOOL InitAndDoMakeDir(struct HeadFileList *hfl)
{
	BPTR CD;
	char *s, *dir, c;
	char Buffer[MAKEDIR_BUFSIZE];
	BOOL quote;
	short Ok = A_RETRY;

	if (!hfl->DestDir) {	/* this is the main window */
		SimpleRequest(ReqTitle, "Can't create a dir in this window.");
		return FALSE;
	}
	setmem(Buffer,  MAKEDIR_BUFSIZE, 0);
	if (GetString(Buffer, "Makedir(s)...", NULL, MAKEDIR_BUFSIZE-1, MAKEDIR_BUFSIZE-1)) {
		CD = CurrentDir(hfl->DestDir);
		s = Buffer;
		while (Ok != A_STOP && *s) {
			while ((c = *s) && (c == ' ' || c == '\t'))
				s++;		/* skip leading spaces */
			if (!c)
				break;
			if (c == '"') {
				quote = TRUE;
				s++;
			}
			else
				quote = FALSE;
			dir = s;
			while((c = *s) && ((quote && c != '"') || (!quote && c != ' ')))
				s++;
			*s++ = '\0';
			Ok = MakeDir(hfl, dir);
		}
		SendUpdateDir(hfl->DestDir);
		CurrentDir(CD);
	}
	return FALSE;	/* Allways return FALSE since action is done in init */
}


static short DoRelabel(char *Dev, char *Name)
{
	long args[7];
	char *str;
	struct MsgPort *task;
	short Ok = A_RETRY;

	while(Ok == A_RETRY && !(task = (struct MsgPort *)DeviceProc(Dev)))
		Ok = ThreeGadRequest("Retry", NULL, "Couldn't access \"%s\"\n%s.", Dev, StrIoErr());
	if (Ok == A_RETRY) {
		str = AllocMem(65L, MEMF_PUBLIC);
		CtoBStr(Name, (ULONG)str>>2, 64);
		args[0] = (ULONG)str>>2;
		while (Ok == A_RETRY && !SendPacket(ACTION_RENAME_DISK, args, task))
			Ok = ThreeGadRequest("Retry", NULL, "Couldn't relabel \"%s\"\n%s.", Name, StrIoErr());
		FreeMem(str, 65L);
		if (Ok == A_RETRY) {
			args[0] = 1;
			SendPacket(ACTION_INHIBIT, args, task);
			args[0] = 0;
			SendPacket(ACTION_INHIBIT, args, task);
		}
	}
	return Ok;
}


static short DoRename(struct SuperFileInfo *sfi, union ActionArgs *Args)
{
	short Ok = A_RETRY;
	char *OldName, *NewName;

	OldName = sfi->OldName;
	NewName = sfi->FileInfo.fi_Name;
	switch(sfi->FileInfo.fi_Type) {
	case DLX_DIR:
	case DLX_FILE:
		if ((Ok = MakeNewName(NewName, Args->NewName, OldName)) == A_RETRY) {
			while(Ok == A_RETRY && !Rename(OldName, NewName))
				Ok = ThreeGadRequest("Retry", "Skip", "Couldn't rename \"%s\" as \"%s\"\n%s.", OldName, NewName, StrIoErr());
			if (Ok == A_RETRY) {
				/* if newname hasn't the same directory path, just remove old file from window, else, update string */
				if (NewName == BaseName(NewName))
					sfi->ActionBack |= AB_RENAME_ENTRY;
				else
					sfi->ActionBack |= AB_DELETE_SOURCE;
			}
		}
		break;
	case DLX_DEVICE:
	case DLX_VOLUME:
		if ((Ok = DoRelabel(OldName, Args->NewName)) == A_RETRY)
			sfi->ActionBack |= AB_SCANDEVS;
		break;
	}
	return Ok;
}


short Touch(char *Name, struct DateStamp *ds)
{
	struct MsgPort *task;
	long args[7];
	char *str;
	struct Process *pp;
	short Ok = A_RETRY;

	pp = (struct Process *)SysBase->ThisTask;
	while(Ok == A_RETRY && !(task = (struct MsgPort *)DeviceProc(Name)))
		Ok = ThreeGadRequest("Retry", "Skip", "Couldn't access \"%s\"\n%s.", Name, StrIoErr());
	if (Ok == A_RETRY) {
		str = AllocMem(65, MEMF_PUBLIC);
		CtoBStr(Name, (BSTR)str>>2L, 64);
		args[1] = pp->pr_CurrentDir;
		args[2] = (BSTR)str>>2L;
		args[3] = (long)ds;
		while (Ok == A_RETRY && !SendPacket(ACTION_SET_DATE, args, task))
			Ok = ThreeGadRequest("Retry", "Skip", "Couldn't touch \"%s\"\n%s.", Name, StrIoErr());
		FreeMem(str, 65);
	}
	return Ok;
}


static short DoTouch(struct SuperFileInfo *sfi, union ActionArgs *Args)
{
	short Ok;

	if ((Ok = Touch(sfi->FileInfo.fi_Name, &Args->DateStamp)) == A_RETRY) {
		sfi->FileInfo.fi_Date = Args->DateStamp;
		sfi->ActionBack |= AB_UPDATE_STRING;
	}
	return Ok;
}


short Comment(char *Name, char *FileNote)
{
	short Ok = A_RETRY;

	while (Ok == A_RETRY && !SetComment(Name, FileNote))
		Ok = ThreeGadRequest("Retry", "Skip", "Couldn't set comment of \"%s\"\n%s.", Name, StrIoErr());
	return Ok;
}


static short DoSetComment(struct SuperFileInfo *sfi, union ActionArgs *Args)
{
	short Ok = A_RETRY;

	if (Ok = Comment(sfi->FileInfo.fi_Name, Args->Comment)) {
		CleanFileInfo(&sfi->FileInfo);
		if (strlen(Args->Comment)) {
			sfi->FileInfo.fi_Comment = CopyStr(Args->Comment);
			sfi->FileInfo.fi_Protection |= FIBF_COMMENT;
		}
		else
			sfi->FileInfo.fi_Protection &= ~FIBF_COMMENT;
		sfi->ActionBack |= AB_UPDATE_STRING;
	}
	return Ok;
}


short Protect(char *Name, long Protection)
{
	short Ok = A_RETRY;

	Protection = (Protection & 0x00FF) ^ 0x0F;	/* clear comment bit and complement four lower bits. strange AmigaDOS !! */
	while (Ok == A_RETRY && !SetProtection(Name, Protection))
		Ok = ThreeGadRequest("Retry", "Skip", "Couldn't protect \"%s\"\n%s.", Name, StrIoErr());
	return Ok;
}


static short DoSetProtect(struct SuperFileInfo *sfi, union ActionArgs *Args)
{
	long Protection;
	short Ok = A_RETRY;

	if (Args->Protection.Neg & FIBF_COMMENT) {	/* Protect -c "file" means remove comment */
		if ((Ok = Comment(sfi->FileInfo.fi_Name, "")) == A_RETRY)
			CleanFileInfo(&sfi->FileInfo);
	}
	Protection = (sfi->FileInfo.fi_Protection | Args->Protection.Pos) & ~Args->Protection.Neg;
	if (Ok == A_RETRY && (Ok = Protect(sfi->FileInfo.fi_Name, Protection))) {
		sfi->FileInfo.fi_Protection = Protection;
		sfi->ActionBack |= AB_UPDATE_STRING;
	}
	return Ok;
}


/*
 *	Don't request for a directory not empty since user may already
 *	has answered after a file couldn't be deleted in this dir.
 */

static short DoDelete(struct SuperFileInfo *sfi)
{
	char *Name;
	short err=0, Ok = A_RETRY;

	Name = sfi->FileInfo.fi_Name;
	while (Ok == A_RETRY && !DeleteFile(Name) && (err = IoErr()) != ERROR_DIRECTORY_NOT_EMPTY) {
		Ok = ThreeGadRequest("Retry", "Skip", "Couldn't delete \"%s\"\n%s.", Name, DosError(err));
		err = 0;
	}
	if (Ok == A_RETRY && err == 0)
		sfi->ActionBack |= AB_DELETE_SOURCE;
	return Ok;
}


static short DoRecurse(struct HeadFileList *hfl, struct SuperFileInfo *StartSFI, struct FileInfoBlock *StartFib, short (*Action)(struct SuperFileInfo *, union ActionArgs *))
{
	struct SuperFileInfo *sfi;
	struct FileInfoBlock *OldFib, *fib;
	struct SelectInfo *Select;
	char *DirName;
	BPTR DirLock, CD;
	short err = 0, Ok = A_RETRY;
	long next;

	fib = AllocMem(sizeof(struct FileInfoBlock), MEMF_PUBLIC|MEMF_CLEAR);
	OldFib = AllocMem(sizeof(struct FileInfoBlock), MEMF_PUBLIC|MEMF_CLEAR);
	sfi = AllocMem(sizeof(struct SuperFileInfo), MEMF_PUBLIC|MEMF_CLEAR);
	if (fib && OldFib && sfi) {
		Select = &hfl->Select;
		DirName = StartFib->fib_FileName;
		while(Ok == A_RETRY && !(DirLock = Lock(DirName, ACCESS_READ)))
			Ok = ThreeGadRequest("Retry", "Skip", "Couldn't access \"%s\"\n%s.", DirName, StrIoErr());
		if (Ok == A_RETRY && DirLock) {
			CD = CurrentDir(DirLock);
			*fib = *StartFib;
			if (!(next = ExNext(DirLock, fib)) && (err = IoErr()) != ERROR_NO_MORE_ENTRIES)
				Ok = ThreeGadRequest("Skip", NULL, "Error reading directory \"%s\"\n%s.", DirName, DosError(err)) << 1;
			if (Ok == A_RETRY) {
				while(Ok != A_STOP && next) {
					*OldFib = *fib;
					Fib2Fi(&sfi->FileInfo, fib);
					/* Examine next entry before possible delete (need for Commodore RAM disk) */
					next = ExNext(DirLock, fib);
					err = IoErr();
					strcpy(sfi->OldName, sfi->FileInfo.fi_Name);	/* save name for actions which change it */
					switch(sfi->FileInfo.fi_Type) {
					case DLX_FILE:
						if (MatchFilters(&sfi->FileInfo, Select))
							Ok = Action(sfi, &hfl->ActionArgs);
						break;
					case DLX_DIR:
						Ok = DoRecurse(hfl, sfi, OldFib, Action);
					}
					if (Ok != A_RETRY)
						sfi->ActionBack = AB_SELECT;
					SendActionBack(sfi, DirLock, NULL);
				}
				if (Ok == A_RETRY && err != ERROR_NO_MORE_ENTRIES)
					Ok = ThreeGadRequest("Skip", NULL, "Error reading directory \"%s\"\n%s.", DirName, DosError(err)) << 1;
				CleanFileInfo(&sfi->FileInfo);
			}
			CurrentDir(CD);
			SendUpdateDir(DirLock);
			UnLock(DirLock);
		}
		if (Ok == A_RETRY && MatchFilters(&StartSFI->FileInfo, Select))
			Ok = Action(StartSFI, &hfl->ActionArgs);
	}
	else
		Ok = A_STOP;
	if (sfi) FreeMem(sfi, sizeof(struct SuperFileInfo));
	if (OldFib) FreeMem(OldFib, sizeof(struct FileInfoBlock));
	if (fib) FreeMem(fib, sizeof(struct FileInfoBlock));
	return Ok;
}


static BOOL (*InitActionArray[])(struct HeadFileList *) = {
	InitRename,
	InitAndDoMakeDir,
	InitAndDoDuplicate,
	InitTouch,
	InitSetComment,
	InitSetProtect,
	InitDelete
};


static BOOL (*DoActionArray[])() = {
	DoRename,
	NULL,
	NULL,
	DoTouch,
	DoSetComment,
	DoSetProtect,
	DoDelete
};


void DoAction(struct HeadFileList *hfl, short ActionNum)
{
	struct BrowserDir *bd;
	struct SuperFileInfo *sfi;
	struct FileInfoBlock *fib;
	short (*Action)(struct SuperFileInfo *, union ActionArgs *);
	short Ok = A_RETRY;

	if (!(fib = AllocMem(sizeof(struct FileInfoBlock), MEMF_PUBLIC|MEMF_CLEAR)))
		return;
	Action = DoActionArray[ActionNum];
	bd = (struct BrowserDir *)hfl->DirList.mlh_Head;
	while( Ok != A_STOP && bd->Node.mln_Succ ) {
		if (bd->DirLock)
			CurrentDir(bd->DirLock);
		sfi = (struct SuperFileInfo *)bd->SuperFileList.mlh_Head;
		while(Ok != A_STOP && sfi->Node.mln_Succ) {
			/* Examine entry only if necessary */
			if (sfi->FileInfo.fi_Type == DLX_DIR && (hfl->Select.si_Flags & SI_AFFECT_SUBDIRS)) {
				if ((Ok = GetFib(sfi->FileInfo.fi_Name, fib, TRUE)) == A_RETRY)
					Fib2Fi(&sfi->FileInfo, fib);
			}
			else
				Ok = A_RETRY;
			if (Ok == A_RETRY) {
				switch(sfi->FileInfo.fi_Type) {
				case DLX_FILE:
					Ok = Action(sfi, &hfl->ActionArgs);
					break;
				case DLX_DIR:
					if (hfl->Select.si_Flags & SI_AFFECT_SUBDIRS)
						Ok = DoRecurse(hfl, sfi, fib, Action);
					else
						Ok = Action(sfi, &hfl->ActionArgs);
					break;
				default:	/* Assign, Device, or Volume */
					Ok = Action(sfi, &hfl->ActionArgs);
				}
			}
			if (Ok != A_RETRY)
				sfi->ActionBack = AB_SELECT;
			SendActionBack(sfi, bd->DirLock, NULL);
			sfi = (struct SuperFileInfo *)sfi->Node.mln_Succ;
		}
		SendUpdateDir(bd->DirLock);
		bd = (struct BrowserDir *)bd->Node.mln_Succ;
	}
	CurrentDir(((struct TaskData *)SysBase->ThisTask->tc_UserData)->td_InitialDir);
	FreeMem(fib, sizeof(struct FileInfoBlock));
}


void InitAndDoAction(struct HeadFileList *hfl, short ActionNum)
{
	if (InitActionArray[ActionNum](hfl))
		DoAction(hfl, ActionNum);
}


