[mod.amiga.sources] DirUtil II

doc@pucc-j.UUCP (08/12/86)

Reply-To: David Jobusch <jobusch%iowa-state.csnet@CSNET-RELAY.ARPA>

/***********
	D I R U T I L   I I   - a file/disk utility program
		Orignal Version by Chris Nicotra
		Revisions/Enhancements/Fixes by Dave Jobusch
					...umn-cs!isucs1!jobusch

	This program is in the public domain. That means you can't
	sell the damn thing to anyone, and further more, I doubt
	you'd get much for it. As a matter of fact, I beat this
	thing to death for 2 weeks, and it still isn't perfect,
	but it works. I got the original program from a BBS 
	in Omaha, and do not know who Chris Nicotra is.

	Enhancements, suggestions, thanks, money should be sent
	to isucs1!jobusch (Dave Jobusch). Flames, nasty comments
	to /dev/null.

	Instructions:
	Click on file or directory names in the file window to 
	select a file to take action on. Click on one of the
	many gadgets to take the selected action on the selected
	files/directory (yes, only one directory at a time).
	The three string gadgets at the bottom are:
		C: The current directory. Mess with this. If you
		   enter an existing path, you just selected the
		   new current directory.
		D: The destination gadget. This one is used to enter
		   destination directories, and is also where you
		   enter requests for directories to create.
	    3rd  : Status window. Error descriptions and DOS error
		   numbers appear here. Entries of text into this
		   gadget will result in the destruction of the world.


	df0:,df1:,ram:,hd0: --->
		Set current directory to the root of the selected device.

	ALL --->
		Select ALL files in the current directory, including
		the ones "scrolled off" the window.
	CLEAR --->
		De-select all selected files
	COPY --->
		Copy selected files to the directory specified in the
		second string gadget. Mind you, you CANNOT enter a
		filename here. It must be an existing directory 
		path and CAN go across devices. You should be able
		to enter paths relative to the current directory.
		(I think I fixed that...) 
	DELETE --->
		Delete selected files. Will not delete directories.
	RENAME --->
		Renames first selected file or directory to the name
		or path (on the same device) in the second string
		gadget.
	GETDIR --->
		If you want to go to a new directory, select
		a directory in the file window, and click on this
		gadget. DO NOT USE this to get a directory that is
		listed in the first string gadget! If you must type
		it in, you hit return while in the string gadget -
		then that will be your new current directory.
	MAKEDIR --->
		This will create the directory that you have typed in
		the second string gadget. You MAY use relative paths.
	DELETEDIR --->
		This will delete the non-empty diretory that you have
		selected in the file window. You will get and error
		if the directory is not empty.
	PARENT --->
		Go to the parent directory
	ROOT --->
		Go to the root of the current device
	LIST --->
		Use this to generate lists of files on your disks. It
		will write out to stdout the files that you have selected.
		If anyone has a neat way to retrieve the volume name of
		a device, please fix this so that the volume name will
		be added to the beginning of each file listed.
		Remember, you need to redirect output to a file if you
		want to save the listings.
	SHOWILBM --->
		If you select this to be included, the program will
		copy a SHOWIBLM command into the ram disk, and will lock
		it (to save it frlom accidental deletion), and then
		will perform consecutive Execute's to show the selected
		files. The show command that has been floating around
		here needs you to click the upper left hand corner
		after showing each file - so there is no loop to wait
		for a picture to show. NOTE: Beware of the 1.1 Execute
		bug that eats 30+ bytes every time.

	NEW NOTES: 6-Aug
		Because of a problem with locking and unlocking the
		current directory on a disk that had been removed,
		I had to make a change to what the program defaulted
		to (for a current directory).
		Changes made:
			- Initial current directory is ALWAYS set to RAM: 
			- if you really confuse it, it will go to RAM:
			  rather than SYS:
			- PWD routine no longer needed, so I combined the
			  dirutil and qsort source into one file. (Follows)
			- so now, to compile, simply

			cc +f +l -s dirutil.c
			ln dirutil.o -lc32


		I forgots :
			I neglected to mention a rather important feature.
			If you did something stupid like select all files
			and then hit delete, and suddenly realize that
			you REALLY didnt want to do that, there is a
			STOP IDIOT feature. When you are doing operations
			on a group of files, CLICKING ANYWHERE IN THE
			WINDOW WILL ABORT THE OPERATION.

	Sorry about any problems this has caused. Please don't sue me
	if this program wipes out a disk of yours. I have been using
	the program for about 2 weeks now, and have not munged anything.
	Guru reports would be appreciated.

	Revised program source follows.

#-----This is NOT a shell archive.---#-------------#-------------#----------#
#-----but cut here anyway------------#-------------#-------------#----------#
/* DIRectory UTILity II -
	Original Version (Directory Utilitity 1.0) by Chris Nicotra
	Revisions/Enhancements/Fixes by David L. Jobusch
				   ...umn-cs!isucs1!jobusch

	This program is in the public domain. Nicotra source downloaded
	from local BBS and modified by Jobusch. Word is Nicotra recently
	released an improved version (1.3). Hope his code is better than
	it was in 1.0. NO guarentees...if you blow up a disk with this
	program, it's your own damn fault.				*/

#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <graphics/gfxbase.h>
#include <stdio.h>
#include <libraries/dos.h>

#define INTUITION 		 0x00000001
#define GRAPHICS  		 0x00000002
#define WINDOW    		 0x00000004
#define INITDIRLOCKOK	 	0x00000008

#ifdef SHOW
#define SHOWDIR	"sys:c"		/* What directory its in	*/
#define SHOWCMD	"show"		/* Name of the actual program	*/
#define SHOWNAMELENGTH 10       /* Make changes? Set this!	*/
#define RAMSHOW	"ram:DIRUTIL.SHOW" /* Where it will end up	*/
#define RAMSHOWLEN  16		/* CHANGE THIS IF YOU CHANGE NAME!*/
#endif

#define FBSIZE  (long)(sizeof (struct FileInfoBlock))
#define MAXFILE 100		/* Maximum files to look at in directory */
#define MAXFNAME 10		/* Maximum files to show at once	 */
#define MAXCHAR	 30 		/* Max file name length			 */
#define MAXCD 	 255		/* Max current dir name length		 */

#define SLIDE_GADGET 	0
#define ERR_GADGET 	3
#define CURDIR_GADGET	4	/* Gadget labels			 */
#define CPDIR_GADGET	5
#define DF0_GADGET	6
#define DF1_GADGET	7
#define HD0_GADGET	8
#define RAM_GADGET	9
#define ALL_GADGET	10
#define CLEAR_GADGET	11
#define COPY_GADGET	12
#define DELETE_GADGET	13
#define RENAME_GADGET	14
#define GETDIR_GADGET	15
#define MAKEDIR_GADGET	16
#define DELETEDIR_GADGET 17
#define PARENT_GADGET	18
#define ROOT_GADGET	19
#define LIST_GADGET	20
#define BUT_FIRST DF0_GADGET

#ifdef SHOW
#define SHOW_GADGET	21
#define FIRST_GADGET	But_gad[15]
#define KILL_SHOW	But_gad[15]
#define BUT_LAST	SHOW_GADGET
#endif
#ifndef SHOW
#define FIRST_GADGET	But_gad[14]
#define BUT_LAST	LIST_GADGET
#endif

#define WinIDCMP  CLOSEWINDOW | REFRESHWINDOW | GADGETUP | MOUSEBUTTONS

extern APTR AllocMem();
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct IntuiMessage *message;
struct RastPort *rp;
struct Window *w;

/************************************************************************/
/* 		Source/Destination string gadgets			*/
/************************************************************************/
USHORT str_pairs [] = { 0,0,  590,0,  590,9,  0,9,  0,0,};
struct Border str_border =
{
    -1,-1,              /* Leftedge, Topedge */
     1, 1, JAM1,        /* FrontPen, BackPen, Drawmode */
     5,                 /* Number of Pairs */
    (APTR) &str_pairs,  /* XY, Pointer to XY pairs */
    NULL,               /* More borders */
};

/* Error display */
char err_buf[MAXCD+1];
struct StringInfo err_str = 
{ &err_buf, NULL, 0, MAXCD+1,   0, 0, 0, 0, 2,0, NULL, 0, NULL, };

struct Gadget Err_gad =
{
	NULL, /* last in the list of gadgets */
	21,122,
	588, 9,
	GADGHCOMP, GADGIMMEDIATE | RELVERIFY | STRINGCENTER,
	STRGADGET,
    	(APTR) &str_border,
	NULL,
	NULL,	/* Intuitext pointer */
	0,
	&err_str,
	ERR_GADGET,
	NULL,
};

struct IntuiText Curdir_text = {1,0,JAM1, -10,0,NULL,"S",NULL,};
char cd_buf[MAXCD+1];
struct StringInfo Curdir_str = 
{ &cd_buf, NULL, 0, MAXCD+1,   0, 0, 0, 0, 2,0, NULL, 0, NULL, };
struct Gadget Curdir_gad =
{
	&Err_gad,
	21,100,
	588, 9,
    	GADGHCOMP, GADGIMMEDIATE | RELVERIFY,
	STRGADGET,
    	(APTR) &str_border,
	NULL,
	&Curdir_text,
	0,
	&Curdir_str,
	CURDIR_GADGET,
	NULL,
};

/* Destination definitions */
char cp_buf[MAXCD+1];
struct IntuiText Cpdir_text = {1,0,JAM1,-10,0,NULL,"D",NULL,};
struct StringInfo Cpdir_str = 
{ &cp_buf, NULL, 0, MAXCD+1,   0, 0, 0, 0, 2,0, NULL, 0, NULL, };

struct Gadget Cpdir_gad =
{
    	&Curdir_gad,
	21,111,
	588, 9,
    	GADGHCOMP, GADGIMMEDIATE | RELVERIFY,
	STRGADGET,
    	(APTR) &str_border,
	NULL,
	&Cpdir_text,
	0,
	&Cpdir_str,
	CPDIR_GADGET,
	NULL,
};

/************************************************************************/
/*		File Select Gadget Declarations				*/
/************************************************************************/
struct Image slide_img;
struct PropInfo slide_prop;
struct Gadget Slide_gad =
{
    &Cpdir_gad,         /* Next gadget */
    20, 14,             /* Left, Top edge */
    20, 84, 		/* Width Hieght */
    GADGHNONE,          /* Flags */
    GADGIMMEDIATE       /* More flags */
    | RELVERIFY,
    PROPGADGET,         /* Gadget type */
    (APTR)&slide_img,   /* Border definition */
    NULL,               /* Select render */
    NULL,               /* Gadget text */
    0,                  /* MutualExclude */
    (APTR) &slide_prop, /* Special Info - proportional specification */
    SLIDE_GADGET,
    NULL,
};

/* Button Gadget IDs */

struct IntuiText But_text[] =
{
	3,2,JAM2,0,0,NULL,"  df0:   ",NULL,
	3,2,JAM2,0,0,NULL,"  df1:   ",NULL,
	3,2,JAM2,0,0,NULL,"  hd0:   ",NULL,
	3,2,JAM2,0,0,NULL,"  ram:   ",NULL,
	3,2,JAM2,0,0,NULL,"   ALL   ",NULL,
	3,2,JAM2,0,0,NULL,"  CLEAR  ",NULL,
	3,2,JAM2,0,0,NULL,"  COPY   ",NULL,
	3,2,JAM2,0,0,NULL," DELETE  ",NULL,
	3,2,JAM2,0,0,NULL," RENAME  ",NULL,
	3,2,JAM2,0,0,NULL," GETDIR  ",NULL,
	3,2,JAM2,0,0,NULL," MAKEDIR ",NULL,
	3,2,JAM2,0,0,NULL,"DELETEDIR",NULL,
    	3,2,JAM2,0,0,NULL," PARENT  ",NULL,
	3,2,JAM2,0,0,NULL,"  ROOT   ",NULL,
	3,2,JAM2,0,0,NULL,"  LIST   ",NULL,
#ifdef SHOW
	3,2,JAM2,0,0,NULL,"SHOW ILBM",NULL,
#endif
};

struct Gadget But_gad[] =
{
    NULL,5,174,72,8,GADGHCOMP,GADGIMMEDIATE | RELVERIFY,BOOLGADGET,
	    NULL,NULL,NULL,NULL,NULL,DF0_GADGET,NULL,
    NULL,5,174,72,8,GADGHCOMP,GADGIMMEDIATE | RELVERIFY,BOOLGADGET,
	    NULL,NULL,NULL,NULL,NULL,DF1_GADGET,NULL,
    NULL,5,174,72,8,GADGHCOMP,GADGIMMEDIATE | RELVERIFY,BOOLGADGET,
	    NULL,NULL,NULL,NULL,NULL,HD0_GADGET,NULL,
    NULL,5,174,72,8,GADGHCOMP,GADGIMMEDIATE | RELVERIFY,BOOLGADGET,
	    NULL,NULL,NULL,NULL,NULL,RAM_GADGET,NULL,
    NULL,5,174,72,8,GADGHCOMP,GADGIMMEDIATE | RELVERIFY,BOOLGADGET,
	    NULL,NULL,NULL,NULL,NULL,ALL_GADGET,NULL,
    NULL,5,174,72,8,GADGHCOMP,GADGIMMEDIATE | RELVERIFY,BOOLGADGET,
	    NULL,NULL,NULL,NULL,NULL,CLEAR_GADGET,NULL,
    NULL,5,174,72,8,GADGHCOMP,GADGIMMEDIATE | RELVERIFY,BOOLGADGET,
	    NULL,NULL,NULL,NULL,NULL,COPY_GADGET,NULL,
    NULL,5,174,72,8,GADGHCOMP,GADGIMMEDIATE | RELVERIFY,BOOLGADGET,
	    NULL,NULL,NULL,NULL,NULL,DELETE_GADGET,NULL,
    NULL,5,174,72,8,GADGHCOMP,GADGIMMEDIATE | RELVERIFY,BOOLGADGET,
	    NULL,NULL,NULL,NULL,NULL,RENAME_GADGET,NULL,
    NULL,5,174,72,8,GADGHCOMP,GADGIMMEDIATE | RELVERIFY,BOOLGADGET,
	    NULL,NULL,NULL,NULL,NULL, GETDIR_GADGET,NULL,
    NULL,5,174,72,8,GADGHCOMP,GADGIMMEDIATE | RELVERIFY,BOOLGADGET,
	    NULL,NULL,NULL,NULL,NULL,MAKEDIR_GADGET,NULL,
    NULL,5,174,72,8,GADGHCOMP,GADGIMMEDIATE | RELVERIFY,BOOLGADGET,
	    NULL,NULL,NULL,NULL,NULL,DELETEDIR_GADGET,NULL,
    NULL,5,174,72,8,GADGHCOMP,GADGIMMEDIATE | RELVERIFY,BOOLGADGET,
	    NULL,NULL,NULL,NULL,NULL,PARENT_GADGET,NULL,
    NULL,5,174,72,8,GADGHCOMP,GADGIMMEDIATE | RELVERIFY,BOOLGADGET,
	    NULL,NULL,NULL,NULL,NULL,ROOT_GADGET,NULL,
    NULL,5,174,72,8,GADGHCOMP,GADGIMMEDIATE | RELVERIFY,BOOLGADGET,
	    NULL,NULL,NULL,NULL,NULL,LIST_GADGET,NULL,
#ifdef SHOW
    NULL,5,174,72,8,GADGHCOMP,GADGIMMEDIATE | RELVERIFY,BOOLGADGET,
	    NULL,NULL,NULL,NULL,NULL,SHOW_GADGET,NULL,
#endif
};

/* BORDER DEFINITION FOR FILE SELECT AREA */
USHORT Fil_pairs [] = { 40,15,  356,15,  356,98, 40,98, 40,15, };
struct Border Fil_border =
{
	-1,-1,              /* Leftedge, Topedge */
	1, 0, JAM1,        /* FrontPen, BackPen, Drawmode */
	5,                 /* Number of Pairs */
	(APTR) Fil_pairs,   /* XY, Pointer to XY pairs */
	NULL,               /* More borders */
};


struct NewWindow nw =
{
	0,0,            	/* Start position */
	640,134,        	/* Width, height */
	0,1,     	        /* Detail, block pens */
	WinIDCMP,		/* IDCMP flags */
	WINDOWSIZING
	| WINDOWDEPTH
	| WINDOWDRAG
	| REPORTMOUSE
	| WINDOWCLOSE
	| ACTIVATE
	| RMBTRAP
	| SMART_REFRESH,
	&FIRST_GADGET,      /* First gadget in the list */
	NULL,               /* User checkmark */
	"D I R U T I L    I I: Chris Nicotra / Dave Jobusch",
	NULL,   
	NULL,               /* Pointer to superbitmap */
	100, 40, 640, 125, 	/* Now sizeable */
	WBENCHSCREEN,       /* Using the Workbench screen */
};

/* The following structure is used to hold the directory entries */
struct DirTable
{
	char  dt_fname[MAXCHAR];	/* File name */
	short dt_select;		/* Select flag */
	short dt_dir;			/* Directory flag */
	long  dt_size;			/* File size */
};

struct DirTable dirtable[MAXFILE+1];
struct FileLock *InitialCurrentDirLock;
struct FileLock *CurrentDirLock;
struct FileLock *RAMSHOWLOCK;
int cmp_dt();
int numdir;
int cur_index;
int NILFD = 0;
unsigned int mask = 0;
char curdir[MAXCD+1],requestedcd[MAXCD+1];

main(argc,argv)
char *argv[];
{
	struct Gadget *gad_ptr;
	struct IntuiText *text_ptr;
	ULONG MessageClass;
	USHORT code;
	int i;
#ifdef SHOW
	char SHOWNAME[SHOWNAMELENGTH];
#endif

	if(argc>2) {
		printf("usage: %s [SHOW]\n",*argv);
		exit(1);
	}

#ifdef SHOW
	if((argc==0)||((argc==2)&&(!strcmp(argv[1],"SHOW")))) {
	/*  WBENCH      CLI AND SHOW OPTION									*/
		if(!(NILFD = Open("nil:",MODE_OLDFILE))) {
			puts("DirUtil FATAL: Can't open NIL: in main\n");
			exit(2);
		}
	/* If SHOW option was given, the default show command is loaded */
	/* into the RAMdisk. It will will show up in DIRUTIL directory	*/
	/* listings of RAM: but it cant be deleted while you are	*/
	/* still in DIRUTIL. Create the path name for your show cmd,	*/
	/* and put a copy of it into RAMDisk. Then rename it to RAMSHOW */
	/* Sure its a kludge, but this way I didn't have to change copy */
	/* or do an Execute. If for some reason you are running two	*/
	/* DirUtil's, the Rename will fail, but there will still be	*/
	/* a RAM:DIRUTIL.SHOW file...I hope.				*/
		sprintf(SHOWNAME,"%s/%s",SHOWDIR,SHOWCMD);
		copy(SHOWNAME,"ram:");
		sprintf(SHOWNAME,"%s%s","ram:",SHOWCMD);
		Rename(SHOWNAME,RAMSHOW);
		if(!(RAMSHOWLOCK = Lock(RAMSHOW,ACCESS_READ))) {
			printf("DirUtil FATAL: Can't get Lock on %s\n",RAMSHOW);
			Close(NILFD);
			exit(3);
		}
	} 
#endif

	*cp_buf = 0;
	strcpy(curdir,"ram:");
	strcpy(requestedcd,curdir);
	if(!(CurrentDirLock = Lock(curdir,ACCESS_READ))) {
		printf("DirUtil FATAL: Could not lock RAMdisk!\n");
		close_things();
		exit(4);
	}
	mask |= INITDIRLOCKOK;
	InitialCurrentDirLock = CurrentDir(CurrentDirLock);

	/* Gadget initialization */
	slide_prop.Flags = FREEVERT | AUTOKNOB;
	slide_prop.VertBody = 0;
	slide_prop.VertPot = 0x8000;

	/* Setup the button gadgets */
	for (i=0;i<(BUT_LAST-BUT_FIRST)+1;i++) {
		gad_ptr = &But_gad[i];
		text_ptr = &But_text[i];

		gad_ptr->LeftEdge = ((i%3) * 90) + 360;
		gad_ptr->TopEdge = ((i/3) * 15) + 14;

		if (i)
		    gad_ptr->NextGadget = &But_gad[i-1];
		else
		    gad_ptr->NextGadget = &Slide_gad;

		gad_ptr->GadgetText = text_ptr;

		text_ptr->LeftEdge = 0;
		text_ptr->TopEdge = 0;
	}

    if (!(GfxBase = (struct GfxBase *) OpenLibrary("graphics.library",0))) {
        puts("DirUtil FATAL : no graphics library!!!\n");
        close_things();
        exit(5);
    }
    mask |= GRAPHICS;

    if (!(IntuitionBase = (struct IntuitionBase *)
        OpenLibrary("intuition.library",0))) {
        puts("DirUtil FATAL : no intuition here!!\n");
        close_things();
        exit(6);
    }
    mask |= INTUITION;

    if (!(w = (struct Window *) OpenWindow(&nw) )) {
        puts("DirUtil FATAL : Could not open the window\n");
        close_things();
        exit(7);
    }
    mask |= WINDOW;

    /************ INITIALIZATION BEFORE main() loop *****************/
	rp = w->RPort;
	DrawBorder(rp,&Fil_border,0,0);

	getdir(curdir);
	new_dir();

	display_error("O.K.",0);
	RefreshGadgets(&FIRST_GADGET,w,NULL);
#ifdef SHOW
	if(!NILFD) OffGadget(&KILL_SHOW,w,NULL);
#endif

	for (;;) {
		WaitPort(w->UserPort); 
        	message = (struct IntuiMessage *) GetMsg(w->UserPort);
		MessageClass = message->Class;
		code = message->Code;
		ReplyMsg(message);
		switch (MessageClass) {
			case GADGETUP:
			case GADGETDOWN:
				display_error("O.K.",0);
		                do_gadgets(message,w);
		                break;

			case REFRESHWINDOW:
				BeginRefresh(w); EndRefresh(w);
				dis_files(cur_index);
				DrawBorder(rp,&Fil_border,0,0);
				break;

			case CLOSEWINDOW:
				close_things();
				exit(0);
		                break;

			case MOUSEBUTTONS:
				if (code == SELECTDOWN)
				    sel_file();
		                break;

			default:
				break;
		} /*end switch*/
    	} /*end for*/
}

do_gadgets(mes,win)
struct IntuiMessage *mes;
struct Window *win;
{
	struct FileLock *MakeDirLock;
	struct Gadget *igad;
	int gadgid;                 /* ID Code identifying which gadget */
	int i;

	igad = (struct Gadget *) mes->IAddress;
	gadgid = igad->GadgetID;
	switch (gadgid) {
		case CURDIR_GADGET:
    			ModifyIDCMP(w,0);
			nullterm(cd_buf,MAXCD);
			Curdir_str.NumChars = strlen(cd_buf);
			Curdir_str.BufferPos = strlen(cd_buf);
			getdir(cd_buf);
			new_dir();
			ModifyIDCMP(w,WinIDCMP);
			break;

		case ERR_GADGET:
			nullterm(err_buf,MAXCD);
			err_str.NumChars = strlen(err_buf);
			err_str.BufferPos = strlen(err_buf);
			break;

		case CPDIR_GADGET:	
			nullterm(cp_buf,MAXCD);
			Cpdir_str.NumChars = strlen(cp_buf);
			Cpdir_str.BufferPos = strlen(cp_buf);
			break;

		case SLIDE_GADGET:
			rdis_files();
			break;

		case ROOT_GADGET:
		case PARENT_GADGET:
			par_dir(gadgid);
			break;

#ifdef SHOW
		case SHOW_GADGET:
			display_error("SHOW : Now showing selected pictures.",0);
#endif
		case DELETE_GADGET:
		case COPY_GADGET:
		case LIST_GADGET:
		case RENAME_GADGET:
		case DELETEDIR_GADGET:
			action_on_selected(gadgid);
			break;

		case MAKEDIR_GADGET:
		/* Attempt to create the named directory. Note that since
		   the CurrentDir is whatever is in the CurrentDir gadget,
		   if you don't give a complete path name, it will assume
		   to create the directory in the current directory.
		   Also, 1.1 CreateDir does not care if you try to create
		   a directory entry on top of a file of the same name.
		   Code around this...				       */
			if(MakeDirLock = Lock(cp_buf,ACCESS_READ)) {
				UnLock(MakeDirLock);
				display_error("MAKEDIR ERROR : DIRECTORY / FILE ALREADY EXISTS. " ,IoErr());
				break;
			} 
			if (!(MakeDirLock = CreateDir(cp_buf))) {
				display_error("MAKEDIR ERROR : CreateDir FAILED. ", IoErr());
				break;
			}
			UnLock(MakeDirLock);
			getdir(curdir);
			new_dir();
			break;

		case DF0_GADGET:
			getdir("df0:"); new_dir(); break;
		case DF1_GADGET:
			getdir("df1:"); new_dir(); break;
		case RAM_GADGET:
			getdir("ram:"); new_dir(); break;
		case HD0_GADGET:
			getdir("hd0:"); new_dir(); break;

		case GETDIR_GADGET: 
			getdir(requestedcd);
			new_dir();
			break;

		case CLEAR_GADGET:
			for (i=0;i<numdir;i++)
			    dirtable[i].dt_select = 0;
			dis_files(cur_index);
			break;

		case ALL_GADGET:
			for (i=0;i<numdir;i++)
				if (!dirtable[i].dt_dir)
					dirtable[i].dt_select = 1;
			dis_files(cur_index);
			break;

		default:
		        break;
	}
}


getdir(DirToGet) 
char *DirToGet;
{
	struct FileLock *filelock;
	struct FileInfoBlock *fb;
	char EffectiveDir[MAXCD+1];

	if (!(*DirToGet))
		strcpy(EffectiveDir,":");
	else
		strcpy(EffectiveDir,DirToGet);

	/* If we cant open requested dir, don't change */
TRYAGAIN:
	if (!(filelock = Lock(EffectiveDir,ACCESS_READ))) {
		strcpy(EffectiveDir,curdir);
		display_error("ERROR : CAN'T LOCK REQUESTED DIRECTORY. ", IoErr());
		if(!(filelock = Lock(EffectiveDir,ACCESS_READ))) {
			display_error("FATAL : CAN'T LOCK CURRENT DIR. SWITCHING TO RAM: ", IoErr());
			strcpy(EffectiveDir,"RAM:");
			goto TRYAGAIN;
		}
	}
	/* Allocate the file info block */
	fb = AllocMem(FBSIZE,MEMF_CLEAR | MEMF_PUBLIC);
	if (!Examine(filelock,fb)) {
		FreeMem(fb,FBSIZE);
		UnLock(filelock);
		display_error("FATAL : CAN'T READ INFO BLOCK. SWITCHING TO RAM: ",IoErr());
		strcpy(EffectiveDir,"RAM:");
		goto TRYAGAIN;
	}
	if(fb->fib_DirEntryType < 0) { 	/* ITS A FILE- NOT A DIRECTORY	*/
		strcpy(EffectiveDir,curdir);/* Forget request, use current*/
		FreeMem(fb,FBSIZE);
		UnLock(filelock);
		display_error("WARNING : DIRECTORY REQUESTED WAS REGULAR FILE - NO CHANGES. ", IoErr());
		goto TRYAGAIN;
	}

	/* Copy directory string into gadget structure */
	strcpy(cd_buf,EffectiveDir);
	Curdir_str.NumChars = strlen(EffectiveDir);
	Curdir_str.BufferPos = strlen(EffectiveDir);
	strcpy(curdir,EffectiveDir);
	strcpy(requestedcd,EffectiveDir);
	RefreshGadgets(&Cpdir_gad,w,NULL);

	numdir = 0;
	cur_index = 0;

	/* Loop through each of the files in this directory */
	while (ExNext(filelock,fb)) {
	        if (fb->fib_DirEntryType < 0)
			dirtable[numdir].dt_dir = 0;
		else
		    dirtable[numdir].dt_dir = -1;
		dirtable[numdir].dt_select = 0;
		dirtable[numdir].dt_size = fb->fib_Size;
		strncpy(dirtable[numdir].dt_fname,fb->fib_FileName,MAXCHAR);
		nullterm(dirtable[numdir].dt_fname,MAXCHAR);

		if (++numdir == MAXFILE)
		    break;
	}

	/* Sort the table */
	qsort(dirtable,numdir,(sizeof (struct DirTable)),cmp_dt); 
	FreeMem(fb,FBSIZE);
	UnLock(CurrentDirLock);		/* Release old CD lock	    */
	UnLock(filelock);		/* Release new request lock */
	CurrentDirLock = Lock(EffectiveDir);
	CurrentDir(CurrentDirLock);	/* MAKE IT THE CURRENT DIRECTORY */
}



rdis_files()
{
    USHORT Vpot;
	int numd;
	int pos;

	if (numdir > MAXFNAME) {
    	Vpot = slide_prop.VertPot;
		Vpot >>= 1;
		Vpot &= 0x7fff;
		numd = numdir - MAXFNAME;
		pos = ((float) Vpot / (float) 0x7fff) * numd;
		cur_index = pos;
		dis_files(pos);
	}
}

/**********************************************************************
*    new_dir - New directory
*    Purpose: To display a new directory and initialize the slide gadget.
**********************************************************************/
new_dir()
{
	USHORT Vbody;

	if (numdir <= MAXFNAME) 
		Vbody = 0xffff;
	else {
	    Vbody = ((float) 0x7fff) / (((float) numdir) / ((float) MAXFNAME));
		Vbody <<= 1;
	}
	cur_index = 0;
	ModifyProp(&Slide_gad,w,NULL,slide_prop.Flags,0,0,0,Vbody);
	dis_files(0);
	return();
}

/**********************************************************************
*    dis_files - Display files
*    Purpose: 	 To display a page of file names
*    Parameters: pos - The position in the file list
**********************************************************************/
dis_files(pos)
int pos;
{
	int i;

	for (i=0;i<MAXFNAME;i++)
		dis_name(i+pos,i);
}

/***********************************************************************
*    dis_name - Display a file name
*    Purpose: To display a file name
*    Parameters:
*        file - File index (into dirtable) or -1 for blank entry
*        pos - The position in the file list
**********************************************************************/
dis_name(file,pos)
int file;
int pos;
{
	static char file_name[MAXCHAR+8];
	static struct IntuiText file_text = {
	    2,0,JAM2,45,15,NULL,(UBYTE *)file_name, NULL };

	/* Set the top position */
	file_text.TopEdge = (pos*8)+16;

	if (file == -1 || file >= numdir) {
		sprintf(file_name,"%-38.38s","");
		file_text.FrontPen = 2;
		file_text.BackPen = 2;	/* black */
	} else {
		if (dirtable[file].dt_dir) {
			sprintf(file_name,"%-30.30s%-8.8s",dirtable[file].dt_fname,"");
			if (dirtable[file].dt_select) {
				file_text.FrontPen = 2; /* Black on Orange */
				file_text.BackPen = 3;
			} else {
				file_text.FrontPen = 3; /* Orange on Black */
				file_text.BackPen = 2;
			}
		} else {
			sprintf(file_name,"%-30.30s %7ld",dirtable[file].dt_fname,
			dirtable[file].dt_size);
			if (dirtable[file].dt_select) {
				file_text.FrontPen = 2; /* Black on White */
				file_text.BackPen = 1;
			} else {
				file_text.FrontPen = 1; /* White on Black  */
				file_text.BackPen = 2;
			}
		}
	}

	/* Display the text */
	PrintIText(rp,&file_text,0,0);
}

/**********************************************************************
*    sel_file - Select file
*
*    Purpose: To check if a file has been selected and the select
*        or unselect it
**********************************************************************/
sel_file()
{
	int file,i;
	char *ptr;

	if (message->MouseX < 45 || message->MouseX > 351)
	    return;
	if (message->MouseY < 16 || message->MouseY >= (MAXFNAME*8+16))
	    return;
	file = message->MouseY - 16;
	file /= 8;
	if((!numdir) || (file > MAXFNAME) || ((file+cur_index) > numdir))
	    return;

	/* Check to see if selection is a directory, or regular file	*/
	/* For now, only allow one directory to be selected at a time	*/
	if (dirtable[file+cur_index].dt_dir) {
		for(i=0;i<numdir;i++)
			if((dirtable[i].dt_dir)&&(i != file+cur_index))
				dirtable[i].dt_select = 0;
		ptr = curdir + strlen(curdir) - 1;
		if(dirtable[file+cur_index].dt_select ^= 1) 
			sprintf(requestedcd,"%s%s%s",curdir,((*ptr == ':') ? "":"/"),
			dirtable[file+cur_index].dt_fname);
		else
			strcpy(requestedcd,curdir);
		dis_files(cur_index);
		return();
	}
	dirtable[file+cur_index].dt_select ^= 1;
	dis_name(file+cur_index,file);
}

action_on_selected(gadgid) /* Copy,Delete,ShowILBM,List,DELETEDIR */
int gadgid;
{
	struct IntuiMessage *message;
	int i;
	int j,k;
	char filename[MAXCD+MAXCHAR+2];
	char *ptr;
#ifdef SHOW
	char command[MAXCD+MAXCHAR+2+RAMSHOWLEN];/* See comments in #defines*/
#endif

	/* At each iteration, check for any IntuiMessages that would signal
	   user wants to halt operation: GADGETUP(he hit a gadget) or
	   MOUSEBUTTONS(button press on something we dont recognize) */

	ModifyIDCMP(w,GADGETUP | MOUSEBUTTONS);
	for (i=0;i<numdir;i++) {
		if(message = (struct IntuiMessage *) GetMsg(w->UserPort)) {
			ReplyMsg(message);
			display_error("INTERRUPT : OPERATION HALTED.",0);
			break; 
		}

		if (dirtable[i].dt_select) {
			ptr = curdir + strlen(curdir)-1;
			if (*ptr == ':')
				sprintf(filename,"%s%s",curdir,dirtable[i].dt_fname);
			else
				sprintf(filename,"%s/%s",curdir,dirtable[i].dt_fname);

			switch (gadgid) {
				case DELETE_GADGET:
					if(dirtable[i].dt_dir) {
						display_error("DELETE ERROR : FILE IS A DIRECTORY.",0);
						break;
					}
					if(!(DeleteFile(filename)))
					display_error("DELETE ERROR : COULD NOT DELETE REQUESTED FILE. ", IoErr());
					break;

				case DELETEDIR_GADGET:
					if(!dirtable[i].dt_dir) {
						display_error("DELETEDIR ERROR : FILE IS NOT A DIRECTORY. ",0);
						break;
					}
					if(!(DeleteFile(filename)))
						display_error("DELETEDIR ERROR : COULD NOT DELETE REQUESTED DIRECTORY. ", IoErr());
					break;

				case COPY_GADGET:
					copy(filename,cp_buf);
					break;

#ifdef SHOW
				case SHOW_GADGET:
					if(NILFD) {
						sprintf(command,"%s %s",RAMSHOW,filename);
						Execute(command,0,NILFD);
						dirtable[i].dt_select = 0;
						dis_files(cur_index);
					}
					break;
#endif

				case LIST_GADGET:
				/* K L U D G E */
					ptr = &filename;
					while((*ptr != ':')&&(*ptr != '\0')) ptr++;
					printf("%s\n",ptr);
					dirtable[i].dt_select = 0;
					break;

				case RENAME_GADGET:
				/* NOTE: you must select the file to be renamed -
				   this doesnt check cd_buf for a filename!	*/
					if(!(Rename(filename,cp_buf))) 
						display_error("RENAME ERROR : CANNOT RENAME TO REQUESTED PATH. ", IoErr());
					break;

				default:
				break;
			} /*switch*/
			if(RENAME_GADGET == gadgid)
				break; /* RENAME FIRST FILE SELECTED ONLY! */
		} /* if selected */
	} /* for all */

#ifdef SHOW
	if((SHOW_GADGET == gadgid)||(LIST_GADGET == gadgid)) {
#endif
#ifndef SHOW
	if(LIST_GADGET == gadgid) {
#endif
		dis_files(cur_index);
	} else {
		getdir(curdir);
		new_dir();
	}
	ModifyIDCMP(w, WinIDCMP);
}

par_dir(gadgid) /* Parent and Root */
int gadgid;
{
	char *ptr;		   /* With this, we may change curdir  */

	ModifyIDCMP(w,0);
	ptr = curdir+strlen(curdir)-1;
	if(PARENT_GADGET == gadgid)
		for (;ptr >= curdir;ptr--) {
 			if (*ptr == ':') { 
				*(ptr+1) = 0; 
				break;
			}
			if (*ptr == '/') { 
				*ptr = 0;
				break;
			}
		}
	else 	/* Gadget was root. Scan back to :  */
		for (;ptr >= curdir;ptr--) 
			if (*ptr == ':') {
				*(ptr+1) = 0;
				break;
			}

	getdir(curdir);
	new_dir();
	ModifyIDCMP(w,WinIDCMP);
	return;
}

/* The following function is used to compare two directory entries */
cmp_dt(dt1,dt2)
struct DirTable *dt1,*dt2;
{
   char *ptr1,*ptr2;
   int c1,c2;

   for (ptr1=dt1->dt_fname,ptr2=dt2->dt_fname;*ptr1 && *ptr2;ptr1++,ptr2++) {
	c1 = *ptr1;
	c2 = *ptr2;

	if (c1 >= 'A' && c1 <= 'Z') {
		c1 -= 'A';
		c1 += 'a';
   	}

   	if (c2 >= 'A' && c2 <= 'Z') {
		c2 -= 'A';
		c2 += 'a';
   	}

   	if (c1 > c2)
		return (-1);

   	if (c1 < c2)
		return (1);
   }

   if (*ptr1)
   	return (-1);

   if (*ptr2)
	return (1);

   return (0);
}

copy(s,pc)
char *s;
char *pc;
{
	struct FileHandle  *copyin;
	struct FileHandle  *copyout;
	int iosize;
	int actual;
	char *copybuf;
	char work[300];
	struct FileLock *fl;
	char *ptr;
	char *ptr1;

	if (!(copyin = Open(s,MODE_OLDFILE)))
		return;

	if (!*pc) {
		display_error("COPY ERROR : NO DESTINATION PATH GIVEN.",0);
		return();
	}

	if ((fl = Lock(pc,ACCESS_READ))) {
		UnLock(fl);
		ptr = strlen(pc) + pc - 1;
		for (ptr1=s+strlen(s)-1;ptr1 > s;ptr1--) {
			if (*ptr1 == '/' || *ptr1 == ':')
				break;
		}
		if (*ptr1 == '/' || *ptr1 == ':')
			ptr1++;
		if (*ptr == ':')
			sprintf(work,"%s%s",pc,ptr1);
		else
		    sprintf(work,"%s/%s",pc,ptr1);
	} else {
		display_error("COPY ERROR : ILLEGAL DESTINATION PATH. ", IoErr());
		Close(copyin);
		return;
	}

	if (!strcmp(work,s)) {
		display_error("COPY ERROR : CANNOT COPY FILE TO ITSELF. ",0);
		Close(copyin);
		return;
	}

	if (!(copyout = Open(work, MODE_NEWFILE))) {
		display_error("COPY ERROR : COPY CANNOT WRITE TO REQUESTED PATH. ", IoErr());
		Close(copyin);
		return;
	} else {
		/* Determine the length of the file */
		iosize = Seek(copyin, 0, OFFSET_END);
		iosize = Seek(copyin, 0, OFFSET_BEGINING);

		/* Allocate memory for the copy buffer */
		do {
			copybuf = AllocMem(iosize, MEMF_PUBLIC|MEMF_CLEAR);
			if (copybuf == 0)
				iosize = iosize/2;
		} while (copybuf == 0 & iosize > 512);

		/* Copy the file */
		do {
			actual = Read(copyin, copybuf, iosize);
			if (Write(copyout, copybuf, actual) != actual)
				break;
		} while (actual == iosize);

		/* Free the copy buffer */
		FreeMem(copybuf, iosize);

		/* Close the input and output buffers */
		Close(copyout);
		Close(copyin);
	}
}

nullterm(str,len)	/* End a string with a null */
char *str;
int len;
{
	int i;

	str[len] = 0; 
	for (i=len;len >= 0;len--) {
	    if (str[i] != ' ' && str[i])
		    break;
		str[i] = 0;
	}
}

display_error(errormsg,doserrnum)
char *errormsg;
long doserrnum;
{
	strcpy(err_buf,errormsg);
	if(doserrnum)
		sprintf(err_buf,"%s (Dos Error %d)",err_buf,doserrnum);
	err_str.NumChars = strlen(err_buf);
	err_str.BufferPos = strlen(err_buf);
	RefreshGadgets(&Err_gad,w,NULL);
}

close_things()
{
    if (mask & WINDOW)
        CloseWindow(w);

    if (mask & GRAPHICS)
        CloseLibrary(GfxBase);

    (void) OpenWorkBench();

    if (mask & INTUITION)
        CloseLibrary(IntuitionBase);

#ifdef SHOW
	if(NILFD) {
		UnLock(RAMSHOWLOCK);
		DeleteFile(RAMSHOW);
		Close(NILFD);
	}
#endif

    if(mask & INITDIRLOCKOK) {
	CurrentDir(InitialCurrentDirLock);
	UnLock(CurrentDirLock);
    }
}

qsort( v, n, size, comp)
char *v;
int n;
int size;
int (*comp)();
{
	int gap, i, j, x, cnt;
	char temp, *p1, *p2;

	cnt = 0;
	for (gap=n/2; gap > 0 ; gap /= 2)
		for (i=gap; i<n; i++)
			for (j = i-gap; j >= 0; j -= gap)
				if ( (*comp) ( (p1=v+j*size), (p2=v+(j+gap)*size) ) < 0) {
					cnt++;
					/* exchange them */
					for (x=0; x<size; x++) {
						temp = *p1;
						*p1++ = *p2;
						*p2++ = temp;
					}
				}
	return(cnt);
}