[comp.sources.amiga] v02i024: MRBackUp

mark@s.cc.purdue.edu.UUCP (09/17/87)

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# Xshar: Extended Shell Archiver.
# This is part  2 out of  3.
# This archive created: Wed Sep 16 20:51:21 1987
# By: Craig Norborg (Purdue University Computing Center)
#	Run the following text with /bin/sh to create:
#	BackUp.c
#	PathRequest.c
cat << \SHAR_EOF > BackUp.c
/************************* Main *********************/
/*                                                  */
/*  Hard Disk Backup  Usage Backup dhx:             */
/*  where x is the hard drive number                */
/*                                                  */
/****************************************************/

#include <exec/memory.h>
#include <exec/types.h>
#include <intuition/intuition.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <stdio.h>
#include <ctype.h>
#include <setjmp.h>
#include <time.h>
#include <functions.h>

#include "gadget.h"
#include "menu.h"

/* Constants */

#define false	0		/* for short parameter requirements */
#define true	1		/* for short parameter requirements */
#define LINES_PER_PAGE  60

#define VOLUME_MAX	32	/* max characters in volume name */
#define PATH_MAX	256 /* max characters in pathname (very arbitrary) */

/* Define error recovery constants.  Note that these are all powers
 * of 2 to allow creating 'sets' of allowable options during the
 * recovery prompt.
 */

#define NERRCODE			5	/* number of error recovery codes */

#define ERR_NONE			0	/* what we want ALL the time :-) */
#define ERR_ABORT			1	/* give up the ship */
#define ERR_RETRY_FILE		2	/* let's try that file one more time */
#define ERR_RESTART_VOLUME 	4	/* for media errors on output floppy */
#define ERR_IGNORE			8	/* ignore this error and trudge on */

/* Macros */

/* determine if a menu item is "checked" */

#define GadgetString(g) ((struct StringInfo *) g->SpecialInfo)->Buffer
#define IsChecked(item) (item->Flags & CHECKED == CHECKED)

/* The following structure is used to link file and directory node
 * information into a doubly-linked list.  This provides a way to
 * defer processing of sub-directory nodes until all files in a
 * current directory are processed.  As nodes are "consumed", they 
 * are returned to free memory.
 */

typedef struct t_file {
	struct t_file  *previous,*next;
	char   			*filename;
	USHORT			blocks;
	BOOL 			is_dir;			/* TRUE => it's a directory */
	} T_FILE;

/* The following structure links lists of T_FILE nodes. */

typedef struct t_file_list {
	T_FILE *first_file;
	T_FILE *last_file;
	} T_FILE_LIST;

/* External and forward function declarations */

struct Window 	*GetMyWindow();
extern char   	*calloc(), *index(), *rindex();
extern long   	DiskBlocks();
extern int	  	errno;
T_FILE 		  	*FindFile();

/* External data */

extern struct Gadget StopGad;
extern struct Menu Titles[];
extern struct MenuItem Items[];
extern struct Window *pathwindow;

/* Global data */

#ifdef DEBUG
struct FileHandle *debugconsole;
char debugmsg[512];
#endif

int back = 0, size = 0;
char backpath[81] = "DF0:";			/* where backups go and restores
									   come from */

struct FileHandle *console;			/* for informative messages */
char conmsg[512];
T_FILE *current_dir = NULL;			/* current directory node */

char    destdrive[5] = "DF0:";
char	destpath[PATH_MAX+1];
char    destvol[VOLUME_MAX+1];

USHORT	do_compress = 1;			/* compression flag */
USHORT  do_listing = 1;				/* listing flag */
USHORT  do_speech = 1;				/* speech flag */

char *erropts[NERRCODE] = {			/* error recovery options */
	"No error", 
	"Abort processing",
	"Retry this file",
	"Restart the output volume",
	"Ignore this error"
	};

char homepath[81] = "DH0:";			/* where files are backed up from and
									   restored to */
struct IntuitionBase *IntuitionBase;

USHORT level;						/* file nesting level */
USHORT linecount;					/* number of lines in listing */
FILE *listing;
char listpath[81] = "PRT:";			/* where we send all of that vital
									   information about backups */
T_FILE_LIST main_list;
struct Window *mywindow;

/* New window structure */

static struct NewWindow    nw = {
	0,0,640,200,0,1,

/* IDCMP Flags */

	MENUPICK | MOUSEBUTTONS | DISKINSERTED |
	CLOSEWINDOW | GADGETDOWN | GADGETUP | REQSET,

/* Flags */
	WINDOWCLOSE | WINDOWDEPTH | ACTIVATE ,

	NULL,							/* First gadget */
	NULL,							/* Checkmark */
	(UBYTE *)"MRBackup Version 1.0",/* Window title */
	NULL,							/* No custom streen */
	NULL,							/* Not a super bitmap window */
	0,0,640,200,					/* Not used, but set up anyway */
	WBENCHSCREEN
};
struct 	DateStamp *now, *since;		/* for date comparisons */
char	srcpath[PATH_MAX];
char	srcvol[VOLUME_MAX+1];		/* source volume name */
char    temp[256];

/* The following flags suppress repetition of spoken
 * messages.  After all, let's not over-do it.
 */

UBYTE at_your_service;
^L
/* Main program  - N.S.D.T.! */

main(argc,argv)
int     argc;
char   *argv[];
{

	Initialize();
	User();					/* it's in the user's hands */
	CleanUp(NULL, 0);
}
^L
/* Initialize the program. */

Initialize()
{

	if (! (IntuitionBase = (struct IntuitionBase *)
			OpenLibrary("intuition.library", 33L ) ) ) {
		CleanUp("Can't open Intuition library!", 20);
	}

	if (!( mywindow = OpenWindow(&nw) ) )
		CleanUp("Can't open program window!", 20);

	SetMenuStrip(mywindow, Titles);

	InitPathRequest();

	AddGadget(mywindow, &StopGad, -1L);
	OnGadget(&StopGad, mywindow, NULL);

	now = (struct DateStamp *) 
		AllocMem((long) sizeof(struct DateStamp), MEMF_PUBLIC);

	since = (struct DateStamp *)
		AllocMem( (long) sizeof(struct DateStamp), MEMF_PUBLIC);

#ifdef DEBUG
	if (!(debugconsole = 
		Open("CON:0/100/640/40/Debug Info", MODE_NEWFILE))) 
		CleanUp("Can't open debug console!", 20);
#endif

	if (!(console = 
		Open("CON:0/140/640/60/Progress Report", MODE_NEWFILE)))
		CleanUp("Can't open console!", 20);

	SetSpeech();
}
^L
/* Handle program termination.
 * Called with:
 *		msg:		termination message or NULL
 *		code:	 	exit code (0 => normal termination)
 */

CleanUp(msg, code)
	char *msg; int code;
{
	if (msg)
		puts(msg);

#ifdef DEBUG
	if (debugconsole) Close(debugconsole);
#endif

	if (console) Close(console);

	if (listing) fclose(listing);

	if (mywindow) {
		ClearMenuStrip(mywindow);
		CancelPathRequest();
		CloseWindow(mywindow);
	}

	if (IntuitionBase) CloseLibrary(IntuitionBase);
	if (now) FreeMem( now, (long) sizeof(struct DateStamp));
	if (since) FreeMem( since, (long) sizeof(struct DateStamp));
	exit(code);
}
^L
/* Add a file/directory name to the list.
 * Called with:
 *		name:		filename string
 *		FIB:		pointer to FileInfoBlock for this file or NULL.
 *					If NULL, file is starting directory.
 *		list:		address of list header
 * Returns:
 *		0	=> success, failure status otherwise
 * Notes:
 *		The filename string MUST NOT contain the volume component,
 *		since it is used to build both source and destination
 *		pathnames.  Also note that this routine inserts the name
 *		into the list in sorted order (case sensitive).  Though this
 *		changes the "natural order" of the files, it makes them 
 *		easier to locate on the backup disks.
 */

int 
AddFile(name, FIB, list)
	char *name; struct FileInfoBlock *FIB; T_FILE_LIST *list;
{
	USHORT blks;
	T_FILE *fnode, *tnode;


	if (!(fnode = (T_FILE *) calloc(1,sizeof(T_FILE)))) {
nomem:
		TypeAndSpeak("AddFile: Out of memory!\n");
		return ERROR_NO_FREE_STORE;
	}
	if (!(fnode->filename = calloc(1, strlen(name)+1))) 
		goto nomem;
	strcpy(fnode->filename, name);		/* copy the filename */

	if (!FIB) {
		blks = 1;
		fnode->is_dir = true;
	}
	else if (fnode->is_dir = (FIB->fib_DirEntryType >= 0) )
		blks = 1;				/* assume 1 block for directory */
	else {
		blks = ((FIB->fib_Size/488)+2);	/* compute file size in blocks */
		blks += (blks/70);
	}
	fnode->blocks = blks;

	if (!list->first_file) {			/* file list is empty? */
		list->first_file = fnode;		/* this is the new head */
	}
	else {
		/* Find the insertion point for this file. */

		for (tnode = list->first_file; tnode; tnode = tnode->next) {
			if (strcmp(fnode->filename, tnode->filename) <= 0) {
				fnode->next = tnode;	/* insert it here */
				if (tnode->previous)
					tnode->previous->next = fnode;
				fnode->previous = tnode->previous;
				tnode->previous = fnode;
				if (list->first_file == tnode)
					list->first_file = fnode;
				return 0;
			}
		}

		/* append to the end of the list */

		fnode->previous = list->last_file;
		list->last_file->next = fnode;
	}
	list->last_file = fnode;
	return 0;
}
^L
/* Main backup routine. */

Backup()
{
	int compare_flag, status = 0;

	Speak("O K,  I'm ready.  Lets back up your disk!");
	DateStamp(now);
	main_list.first_file = main_list.last_file = current_dir = NULL;

	if (do_listing) 
		if ( OpenList() ) return;

	/* Get the file comparison date.  Only files created on or after
	 * this date are backed up.
	 */
	do {
		*since = *now;
		since->ds_Days -= 7;		/* default interval is 1 week */
		since->ds_Minute = 0;
		since->ds_Tick = 0;
		Speak("Enter the date since your last backup.");
		DateRequest(mywindow, "Backup files since:",since, since);
		if ( (compare_flag = CompareDS(since, now) ) >= 0) 
			DisplayBeep(NULL);
	} while (compare_flag >= 0);

	BreakPath(homepath, srcvol, srcpath);
	BreakPath(backpath, destdrive, destpath);

#ifdef DEBUG
	sprintf(debugmsg, "destdrive = %s, destpath = %s\n",destdrive,destpath);
	DebugWrite(debugmsg);
	sprintf(debugmsg, "srcvol = %s, srcpath = %s\n", srcvol,srcpath);
	DebugWrite(debugmsg);
#endif

	if (*destpath) {
		TypeAndSpeak(
			"On a backup, the backup path must only be a device name");
		status = ERR_ABORT;
		goto done;
	}
	strcat(destdrive,":");

	level = 0;
	size = 0;
	if (*srcpath) {				/* starting path is a directory */
		status = AddFile(srcpath, NULL, &main_list);
	}
	else						/* starting path is a device */
		status = CollectFiles(srcpath,&main_list);

	if (!status) {
		while (main_list.first_file) {	/* while something in the list */
			if (status = BackupFiles(&main_list)) 	break;
			if (status = BackupDirs(&main_list)) 	break;
		}
	}
done:
	if (status == 0) {
		TypeAndSpeak("That was a piece of cake.\n");
	}
	else {
		sprintf(conmsg,"Oops!  Backup terminated with error %d\n",status);
		TypeAndSpeak(conmsg);
	}
}

^L
/* Process the next directory in the file list.
 * This routine is recursive.
 * Called with:
 *		list:		file list to be processed
 * Returns:
 *		status code	(0 => success)
 */

int
BackupDirs(list)
	T_FILE_LIST *list;
{
	T_FILE *dirnode;
	T_FILE *saved_current_dir;
	int 	status = 0;
	T_FILE_LIST sublist;				/* subdirectory list header */

	sublist.first_file = sublist.last_file = NULL;
	saved_current_dir = current_dir;	/* remember current context */

/* There are a couple of things to note here.  The first is that once
 * we have reached here, there should be NO simple file nodes in "list".
 * That currently is not handled as an error, but probably should be.
 * Second, since this scan modifies the list, a removal of a directory
 * node starts the scan at the beginning of the list since we shouldn't
 * reference the links in the node we're removing.  Since we should be
 * removing the first node in the list anyway, who cares?
 */
	for (dirnode = list->first_file; dirnode; ) {
		if (dirnode->is_dir) {			/* found one */
			current_dir = dirnode;		/* set current directory */
			RemFile(dirnode, list);
			if (status = NewDir(current_dir->filename)) break;
			if (status = CollectFiles(current_dir->filename,&sublist)) 
				break;
			if (status = BackupFiles(&sublist)) break;
			if (status = BackupDirs(&sublist))  break;
			dirnode = list->first_file;
		}
		else							/* should never get here !!! */
			dirnode = dirnode->next;
	}
	current_dir = saved_current_dir;
	return status;
}
^L
/* Backup all simple files in the current list.
 * Called with:
 *		list:		file list header
 * Returns:
 *		status code (0 => success)
 */
int
BackupFiles(list)
	T_FILE_LIST *list;
{
	T_FILE *primary, *secondary;
	int status = 0;

/* The following loop continually scans the file list (from the front),
 * looking for more file entries to process.  If the primary choice
 * fails, an attempt is made to find a file which will fit in the
 * space remaining.  If that attempt fails, then a new disk is formatted.
 */

	while (primary = FindFile(list,false)) {/* find next file to process */
		if (primary->blocks >= size) { 	/* file doesn't fit */
			if (!(secondary = FindFile(list,true))) {
				/* At this point, we know that there's at least one
				 * file to back up, but none that fit.  Start a new
				 * disk.
				 */
				if (status = NewDisk(true)) return status;
				continue;				/* try that file again */
			}
			primary = secondary;		/* use second choice */
		}
		if (status = DoFile(primary)) return status;
		RemFile(primary,list);			/* delete the node */
	}
	if (current_dir) {					/* forget current directory */
		FreeFile(current_dir);
		current_dir = NULL;
	}
	return status;
}

/* Break a full file specification into its volume and pathname components.
 * Called with:
 *		fullpath:	full pathname string (input)
 *		volume:		volume name component, sans colon (output)
 *		path:		pathname component (output)
 * Returns:
 *		status code: 1 => no volume name specified, 0 otherwise
 */

BreakPath(fullpath,volume,path)
	char *fullpath, *volume, *path;
{
	char c, *fp, *s;
	unsigned has_volume = 1;			/* assume it has volume component */

	fp = fullpath;
	s = volume;
	if ( index(fp, ':') ) {				/* volume name specified? */
		s = volume;
		while ((c = *fp++) != ':') *s++ = c;
	}
	else
		has_volume = 0;

	*s = '\0';							/* terminate volume */
	strcpy(path, fp);					/* the rest is pathname stuff */
	return has_volume;
}

/* Check the current number of disk blocks (size) available.  If
 * less than 1, it's time to format a new disk.
 * Called with:
 *		create_dir:		true => OK to create continuation directory
 * Returns:
 *		0 => success
 *		1 => failure
 */

int
CheckSize(create_dir)
	int create_dir;
{
	if (size > 0) return 0;
	return NewDisk(create_dir);
}
^L
/* Do a quick poll on the STOP gadget.
 * Returns 1 if STOP gadget hit, 0 otherwise.
 */

int
CheckStop()
{
	ULONG class;
	struct Gadget *gadget;
	struct IntuiMessage *msg;
	int status = 0;

	if (msg = (struct IntuiMessage *) GetMsg(mywindow->UserPort)) {
		class = msg->Class;
		gadget = (struct Gadget *) msg->IAddress;
		ReplyMsg(msg);
		if (class == GADGETUP && gadget->GadgetID == STOPGAD) {
			TypeAndSpeak("I am stopping, as you requested.\n");
			status = ERR_ABORT;		/* quit */
		}
	}
	return status;
}
^L
/* Collect file names from a starting path.
 * Called with:
 *		name:		starting pathname
 *						Note that name may be a null string when copying
 *						the entire home device.
 *		list:		pointer to file list header
 * Returns:
 *		status code (0 => success)
 * Notes:
 *		CollectFiles attempts to collect all file and directory names
 *		for a given level, starting with "name".  If a simple filename
 *		is given as a starting path, only that name will be collected.
 *		If a directory name is given, then all pathnames contained
 *		within that directory (only) will be collected.  For each
 *		directory name collected, CollectFiles will be called again to
 *		collect files for that particular directory.  This iterative
 *		approach (vs. recursive) saves memory and allows us to maintain
 *		order at each directory level.
 */

int
CollectFiles(name, list)
	char *name; T_FILE_LIST *list;
{
	int     status = 0;
	struct FileInfoBlock   *FIB = NULL;
	T_FILE *fnode;			/* file descriptor node */
    struct Lock *lock = NULL;	
	char path[PATH_MAX+1];
	USHORT top_level;

#ifdef DEBUG
	sprintf(debugmsg,"Collecting files from %s\n",name);
	DebugWrite(debugmsg);
#endif

	top_level = (*name == '\0');	/* empty name implies top level */

	if (!(FIB =
		AllocMem((long)sizeof(struct FileInfoBlock),
				 MEMF_CHIP|MEMF_CLEAR))) {
		TypeAndSpeak("CollectFiles: Can not allocate memory for FIB\n");
		return ERROR_NO_FREE_STORE;
	}

	strcpy(path,srcvol);			/* rebuild current home path */
	strcat(path,":");
	strcat(path, name);

	if (!(lock=Lock(path,SHARED_LOCK))) {
		status = IoErr();
		sprintf(conmsg,"CollectFiles can not lock %s; error %d\n",
			path, status);
		TypeAndSpeak(conmsg);
		goto out2;
	}

	if ((Examine(lock,FIB))==0){
		status = IoErr();
		sprintf(conmsg,"CollectFiles can not examine %s; error: %d\n",
				name, status);
		TypeAndSpeak(conmsg);
		goto out2;
	}

	if (FIB->fib_DirEntryType > 0){	/* "name" is a directory */

		while(!status && ExNext(lock,FIB)) {
			if (FIB->fib_DirEntryType < 0) {
				if (CompareDS(&FIB->fib_Date, since) < 0) {
#ifdef DEBUG
					sprintf(debugmsg,"Skipping %s\n",
							&FIB->fib_FileName[0]);
					DebugWrite(debugmsg);
#endif
					continue;
				}
			}
			if (top_level)
				*path = '\0';
			else {
				strcpy(path,name);
				strcat(path,"/");
			}
			strcat(path,&FIB->fib_FileName[0]);
			status = AddFile(path, FIB, list);
		}
		/* !!! Need check here for ERROR_NO_MORE_ENTRIES */
	}
	else {
		if ( CompareDS(&FIB->fib_Date, since ) >= 0 ) 
			status = AddFile(name, FIB, list);
		}
out2: 
	UnLock(lock);
out1: 
	FreeMem(FIB, (long)sizeof(struct FileInfoBlock) );

	/* Don't give up if somebody else is using the file - just
	 * ignore the error.  The user can cancel us if there's a
	 * problem.
	 */

	if (status == ERROR_OBJECT_IN_USE)
		status = 0;
	return status;
}

#ifdef DEBUG
DebugWrite(msg)
	char *msg;
{
	Write(debugconsole, msg, (long) strlen(msg));
}
#endif
^L
/* Process one file.
 * Called with:
 *		fnode:			node describing file
 * Returns:
 *		0	=> success
 *		1	=> failure
 */
int
DoFile(fnode)
	T_FILE *fnode;
{
	int     status = ERR_NONE;
	char    destname[PATH_MAX+1];
	int		oldsize;
	char	srcname[PATH_MAX+1];

	oldsize = size;
	size -= fnode->blocks;			/* deduct blocks for file */
	if (CheckSize(true))			/* check disk space */
		return ERR_ABORT;

	if (size > oldsize)				/* we just formatted */
		oldsize = size;

/*#define NOCOPY*/						/* for fast debugging */
	do {
		if (status = CheckStop())	/* does user want out? */
			return status;

		sprintf(srcname,"%s:%s",srcvol,fnode->filename);
		sprintf(conmsg,"Blocks left: %5d   ",oldsize);
		WriteConsole(conmsg);
		if ( !do_compress || IsCompressed(srcname) || fnode->blocks < 4){
			sprintf(destname,"%s%s",destvol,fnode->filename);
			sprintf(conmsg,"Copying %s\n",srcname);
			WriteConsole(conmsg);
#ifndef NOCOPY
			status = CopyFile(srcname,destname);
#endif
		}
		else {
			sprintf(destname,"%s%s.Z",destvol,fnode->filename);
			sprintf(conmsg,"Compressing %s\n",srcname);
			WriteConsole(conmsg);
#ifndef NOCOPY
			if (!(status = compress(srcname,destname)))
				status = CopyFileDate(srcname,destname);
#endif
		}
		if (status) {
			status = GetErrOpt("I/O error during copy or compress",
						ERR_ABORT | ERR_IGNORE | 
						ERR_RETRY_FILE | ERR_RESTART_VOLUME);
		}
		if (status == 0) ListLine(destname);

	} while (status == ERR_RETRY_FILE);
	if (status == ERR_IGNORE)
		status = ERR_NONE;

#ifndef NOCOPY
	if (!status){
		if ((size = DiskBlocks(destvol)) < 0)	/* update blocks left */
			++status;
	}
#endif
	return status;
}
^L
/* Handle a gadget action.
 * Called with:
 *		class:		message class (GADGETUP/GADGETDOWN/?)
 *		addr:		pointer to gadget structure
 */
DoGadget(class, addr)
	ULONG class; struct Gadget *addr;
{
	USHORT id;					/* gadget identifier */
	if (class == GADGETUP) {
		id = addr->GadgetID;
#ifdef DEBUG
		sprintf(debugmsg,"GADGETUP: %d\n",id);
		DebugWrite(debugmsg);
#endif
		switch (id) {
			case HOMEPATHGAD:
				GetHomePath(addr);
				break;
			case BACKPATHGAD:
				GetBackPath(addr);
				break;
			case LISTPATHGAD:
				GetListPath(addr);
				break;
			case STOPGAD:
				TypeAndSpeak(
	"Use the STOP gadget during backup and restore operations.\n");
				break;
			default:
				WriteConsole("Unknown gadget - program error!\n");
				break;
		}
	}
}
^L
/* Attempt to find a file node in the file list.  If the check_size
 * parameter is true, only look at files which will fit in the space
 * remaining on the disk.
 * Called with:
 *		list:		pointer to file list to be searched
 *		check_size: false => find first file in the list
 *					true  => test space available
 * Returns:
 *		file node or NULL (not found)
 */

T_FILE *FindFile(list,check_size)
	T_FILE_LIST *list; int check_size;
{
	T_FILE *fnode;

	for (fnode = list->first_file; fnode; fnode = fnode->next) {
		if (!fnode->is_dir) {		/* don't consider directory nodes */
			if (!check_size) break;	/* take this one */
			if (fnode->blocks < size) break;
		}
	}
	return fnode;
}

/* Free memory allocated to a file node.
 * Called with:
 *		node:	file node
 */
FreeFile(node)
	T_FILE *node;
{

	free(node->filename);
	free(node);
}
^L
/* Get the backup device pathname specification. 
 * Called with:
 * Called with:
 *		gadget:		pointer to relevant string gadget
 *
 * Side-effects:
 *		If the pathname specification passes its requirements tests,
 *		the global string backpath will be set to the pathname.
 *		Otherwise, homepath is left unchanged and the gadget's string
 *		is reset to the current contents of homevolume.
 */

GetBackPath(gadget)
	struct Gadget *gadget;
{
	char volume[VOLUME_MAX+1];
	UBYTE *fullpath, path[PATH_MAX+1];

	fullpath = (UBYTE *) GadgetString(gadget);

	BreakPath(fullpath,volume,path);
	if (!IsDir(fullpath)) {
		TypeAndSpeak("Backup path must be a device or directory name!\n");
badpath:
		strcpy(fullpath, backpath);	/* restore previous value */
		return;
	}
	if (strlen(volume) != 3 ||
		tolower(volume[0]) != 'd' ||
		tolower(volume[1]) != 'f' ||
		volume[2] < '0' || volume[2] > '3') {
		TypeAndSpeak("Backup device must be DF0 through DF3\n");
		goto badpath;
	}
	strcpy(backpath, fullpath);		/* use new value */
}
^L
/* An error has occurred.  Find out what the user wants to do about it.
 * Called with:
 *		msg:		message string
 *		options:	the "set" of options available
 *					0 => sorry - no options, ERR_ABORT returned
 * Returns:
 *		error recovery option
 */

int
GetErrOpt(msg, options)
	char *msg; int options;
{
	int i, mask, option_count = 0, select;
	int option_list[NERRCODE];

	sprintf(conmsg,"An error has interrupted processing:\n%s\n\n",msg);
	TypeAndSpeak(conmsg);
	return ERR_ABORT;				/* ...implement recovery... */
}
^L
/* Get the home pathname specification (volume and pathname).
 * Called with:
 *		gadget:		pointer to relevant string gadget
 *
 * Side-effects:
 *		If the pathname specification passes its requirements tests,
 *		the global string homepath will be set to the pathname.
 *		Otherwise, homevolume is left unchanged and the gadget's string
 *		is reset to the current contents of homevolume.
 */

GetHomePath(gadget)
	struct Gadget *gadget;
{
	UBYTE *fullpath, path[PATH_MAX+1], volume[VOLUME_MAX+1];

	fullpath = (UBYTE *) GadgetString(gadget);

	BreakPath(fullpath, volume, path);
	if (!IsDir(fullpath)) {
		TypeAndSpeak(
	"Home path must be a hard disk device with optional directory name!\n");
badpath:
		strcpy(fullpath, homepath);	/* restore previous value */
	}
	else if (strlen(volume) != 3 ||
			tolower(volume[0]) != 'd' ||
			tolower(volume[1]) != 'h' ||
			!isdigit(volume[2])) {
#ifdef DEBUG
			sprintf(debugmsg,"home device = \"%s\" ? \n",volume);
			DebugWrite(debugmsg);
#endif
			TypeAndSpeak("Home device must be DH0: through DH9:\n");
			goto badpath;
	}
	strcpy(homepath, fullpath);
}	

/* Get the listing pathname.
 * Called with:
 *		gadget:		pointer to relevant string gadget
 *
 * Side-effects:
 */

GetListPath(gadget)
	struct Gadget *gadget;
{
	UBYTE *path;

	if (!do_listing) {
		TypeAndSpeak("Listing mode is not enabled.  ");
		TypeAndSpeak("Your change has been ignored.\n");
badpath:
		strcpy(path, listpath);
	}
	else {
		path = (UBYTE *) GadgetString(gadget);
		if (!strcmp(path, listpath)) {	/* same pathname */
			return;						/* ignore the request */
		}
		if (OpenList()) goto badpath;
		strcpy(listpath, path);
	}
}
^L
/* Output a new header to the listing file. */

Header()
{
	if (do_listing) {
		fprintf(listing,"\f    MRBackup Listing of Volume %s\n\n", destvol);
		linecount = 2;
	}
}
^L
/* Determine if a file is compressed.  This is currently done by looking
 * at the file name for a ".Z" or ".z" extension.  Since we use the
 * "magic header" form of compression, we could also check for that as
 * well...maybe later.
 * 
 * Called with:
 *		filename:	file name string
 * Returns:
 *		1 => file is compressed, 0 => file is not compressed
 */

int     IsCompressed(filename)
char   *filename;
{
	char *s;

	s = filename + strlen(filename)-2;
	if (!strcmp(s,".z")|| !strcmp(s,".Z"))
		return 1;
	else
		return 0;
}
^L
/* Output a line to the listing file.  Start a new line if necessary.
 * The output string is assumed to have no form control characters.
 * Called with:
 *		str:		string to be output
 */
ListLine(str)
	char *str;
{	
	if (do_listing) {
		if (linecount >= LINES_PER_PAGE) Header();
		fprintf(listing,"  %s\n",str);
		++linecount;
	}
}
^L
/* Create a new directory on the destination disk.
 * Called with:
 *		name:		directory pathname
 * Returns:
 *		false => success
 *		true  => failure
 * Notes:
 *		NewDir may be called by NewDisk() or BackupDirs().  You should
 *		note that a condition of mutual recursion between CheckSize and
 *		NewDir exists, due to the following:
 *
 *		When NewDir is called from BackupDirs(), it calls CheckSize(),
 *		which in turn calls NewDisk() if there is no space left.
 *		The create_dir parameter to CheckSize() (false) prevents a
 *		secondary call to NewDir() since the job is already being
 *		performed.
 *
 *		When CheckSize() is called as a result of BackupFiles()
 *		processing, the create_dir parameter is true so that NewDir()
 *		will be called if NewDisk() is called, thus creating a
 *		continuation of the current directory.  Confused?  Me too :-).
 */
int
NewDir(name)
	char   *name;
{
	char c;
	struct Lock *dirlock;
	int dirleng;
	int errnum;
	char dirname[256];
	int nameindx = 0, nameleng;

	size--;							/* takes a block for a directory */
	if (CheckSize(false))			/* have room on disk? */
		return 1;

	strcpy(dirname,destvol);		/* start with volume name */
	dirleng = strlen(dirname);
	nameleng = strlen(name);

	/* Parse the pathname, one directory node at a time, creating
	 * directories as needed.
	 */

	while (nameindx < nameleng) {
		if (nameindx)				/* 2nd - nth pass? */
			dirname[dirleng++] = '/'; /* directory separator */
		while ((c = name[nameindx++]) && c != '/')
			dirname[dirleng++] = c;
		dirname[dirleng] = '\0';	/* terminate with null */
		if (dirlock = Lock(dirname,SHARED_LOCK)) /* subdir exists? */
			UnLock(dirlock);
		else {						/* create subdirectory */
			if ((dirlock = CreateDir(dirname))== NULL){
				if ((errnum = IoErr())== ERROR_DIRECTORY_NOT_EMPTY){
					sprintf(conmsg,
						"Directory %s already exists!\n",dirname);
					TypeAndSpeak(conmsg);
				}
				else {
					sprintf(conmsg,
						"ERROR %d: Unable to create directory %s\n",
						errnum,dirname);
					TypeAndSpeak(conmsg);
					return 1;
				}
			}
			else
				UnLock(dirlock);
		}
	}								/* endwhile */
	return 0;
}
^L
/* Format a new diskette.
 * Called with:
 *		create_dir:	true => create continuation directory, if necessary
 * Returns:
 *		false => success
 *		true  => failure
 */

int
NewDisk(create_dir)
	int create_dir;
{
	char datestr[20];
	int status = 0;

	if (back)						/* not first disk? */
		Delay(TICKS_PER_SECOND * 5L);	/* let disk buffers flush */

	++back;					/* Increment the volume ID */
	Speak("Attention!  I need a disk for formatting.");
	do {
		if (!RequestDisk(mywindow, destdrive))
			return ERR_ABORT;

	 	/* Don't put the colon in the volume name - 
		 * FormatDisk will happily use it as part of 
		 * the name rather than as a delimiter. 
		 */

		DS2Str(datestr, "%02m-%02d-%02y", now);
		sprintf(destvol,"Backup %s.%d", datestr, back);

		Inhibit(destdrive,1);
		if (status = FormatDisk(destdrive,destvol)) {
			TypeAndSpeak("I failed to format the disk.  Sorry.\n");
		}
	} while (status);

	strcat(destvol, ":");			/* add colon */

 	Delay(TICKS_PER_SECOND * 2L); 	/* let disk validate */

	if ((size = DiskBlocks(destvol)) < 0)
		status = -size;
	else
		if (create_dir && (current_dir != NULL) )
			status = NewDir(current_dir->filename);

	return status;
}
^L
/* Skip 'n' lines in the listing file.
 * Called with:
 *		n:		number of lines to skip
 */

NewLine(n)
	int n;
{
	if (do_listing) {
		if (n + linecount >= LINES_PER_PAGE)
			Header();
		else while (n--) {
			fputc('\n', listing);
			++linecount;
		}
	}
}
^L
/* Open the listing file.
 * Returns:
 *		status (0 => success)
 */
int
OpenList()
{
	if (listing) fclose(listing);		/* prior listing file open? */
	if (!(listing = fopen(listpath, "w"))) {
		sprintf(conmsg,
				"I can't open the listing file %s, error %d\n",
				listpath, errno);
		TypeAndSpeak(conmsg);
	}
	else
		linecount = LINES_PER_PAGE;
	return errno;
}

^L
/* Remove a file node from the list.
 * Called with:
 *		node:		file node pointer
 *		list:		file list header
 * Returns:
 *		nothing
 */
RemFile(node,list)
	T_FILE *node; T_FILE_LIST *list;
{
	if (node->previous)
		node->previous->next = node->next;

	if (node->next)
		node->next->previous = node->previous;

	if (node == list->first_file)
		list->first_file = node->next;

	if (node == list->last_file)
		list->last_file = node->previous;

	if (!node->is_dir)
		FreeFile(node);
}

/* Enable/disable speech capability, based on global 'do_speech'. */

SetSpeech()
{
	if (do_speech) {
		if (!SpeechOn()) 
			Say("Ahhh, that feels good!  Thanks for turning me on!");
	}
	else
		SpeechOff();
}

Speak(msg)
	char *msg;
{
	if (do_speech) Say(msg);
}

/* Type a message to the console and optionally "speak" it.
 * Called with:
 *		msg:		message string
 */
TypeAndSpeak(msg)
	char *msg;
{
	WriteConsole(msg);
	if (do_speech) Say(msg);
}

/* Handle IDCMP messages generated by user actions. */

User()
{
	ULONG class;				/* message class */
	USHORT code;				/* message code */
	USHORT gadgid;				/* gadget ID */
	APTR Iadr;					/* address field from message */
	struct IntuiMessage *msg;	/* Intuition message pointer */
	USHORT quit = 0;
	SHORT x,y;					/* mouse x and y position */
	ULONG waitbits;

	waitbits = (1L << mywindow->UserPort->mp_SigBit) |
			   (1L << pathwindow->UserPort->mp_SigBit);
#ifdef DEBUG
	sprintf(debugmsg,"User: waitbits = %08lx\n", waitbits);
	DebugWrite(debugmsg);
#endif

	if (++at_your_service == 1)
			Speak("At your servis."); /* SIC! SIC! SIC! */

	while (!quit) {
		Wait(waitbits);
		while (!quit) {
			if (!(msg = (struct IntuiMessage *) 
				GetMsg(mywindow->UserPort)))
				if (!(msg = (struct IntuiMessage *)
					  GetMsg(pathwindow->UserPort)))
					break;

			class = msg->Class;
			code = msg->Code;
			Iadr = msg->IAddress;
			x = msg->MouseX;
			y = msg->MouseY;
			ReplyMsg(msg);		/* acknowledge the message */
#ifdef DEBUG
			sprintf(debugmsg,"Message class: 0x%lx, code: 0x%x\n",
				class, code);
#endif
			switch (class) {
			case CLOSEWINDOW:
				++quit;
				break;

			case GADGETUP:
			case GADGETDOWN:
				DoGadget(class,Iadr);
				break;

			case MENUPICK:
				quit = UserMenu(code);
				break;

			default:
				break;			/* ignore the rest */
			}					/* end switch(class) */
		}
	}
}

/* Handle a menu selection. 
 * Called with:
 *		code:		menu selection code
 * Returns:
 *		status code (1 => Quit was selected)
 */

int
UserMenu(code)
	USHORT code;
{
	struct MenuItem *item;
	USHORT itemnum,menunum;

	if (code != MENUNULL) {
		menunum = MENUNUM(code);	/* only 1 in this appl. */
		itemnum = ITEMNUM(code);

#ifdef DEBUG
		sprintf(debugmsg,"menu = %d, item = %d\n",menunum,itemnum);
		DebugWrite(debugmsg);
#endif
		item = (struct MenuItem *) ItemAddress(Titles, menunum);
		switch (menunum) {
		case MENU_PROJECT:			/* Project Menu */
			switch (itemnum) {
			case ITEM_BACKUP:		/* Backup */
				Backup();
				break;
			case ITEM_RESTORE:		/* Restore */
				Restore();
				break;
			case ITEM_ABOUT:		/* About */
				About();
				break;
			case ITEM_QUIT:			/* Quit */
				return 1;			
			default:
				DisplayBeep(NULL);
				break;
			}
			break;

		case MENU_FLAGS:			/* Flags Menu */
			switch (itemnum) {
			case ITEM_COMPRESS:		/* Compression */
				do_compress = IsChecked(item); 
				break;
			case ITEM_NOCOMPRESS:	/* No Compression */
				do_compress = !IsChecked(item);
				break;
			case ITEM_LIST:			/* Listing */
				do_listing = IsChecked(item);
				break;
			case ITEM_NOLIST:		/* No Listing */
				do_listing = !IsChecked(item);
				break;
			case ITEM_SPEECH:		/* Speech */
				do_speech = IsChecked(item);
				SetSpeech();
				break;
			case ITEM_NOSPEECH:		/* No Speech */
				do_speech = !IsChecked(item);
				SetSpeech();
				break;
			default:
				DisplayBeep(NULL);
				break;
			}
		}
	}
	return 0;
}


/* Write a message to the console window.
 * Called with:
 *		msg:	message string
 */
WriteConsole(msg)
	char *msg;
{
	Write(console, msg, (long) strlen(msg));
}
SHAR_EOF
cat << \SHAR_EOF > PathRequest.c

/**********************************************************************
 *                    Gadget Structure Definitions
 * 
 * The following structures were defined using the Gadget Editor created 
 * by the Programmer's Network.
 * The credits for the Gadget Editor are:
 * 
 *     John Draper    - Initial design, coordination, and integration.
 *     Ray Larson     - Images and Intuitext. 
 *     Brent Southard - Saving and restoring gadgets in binary form.
 *     Dave Milligan  - Gadget Editor Main menu.
 * 
 * 
 **********************************************************************/

/*  The header files needed for gadget definitions  */ 
#include <exec/memory.h>
#include <intuition/intuition.h> 
#include <intuition/intuitionbase.h> 
#include <libraries/dosextens.h> 
#include <graphics/gfxbase.h> 
#include <graphics/gfx.h> 
#include <graphics/display.h> 
#include <graphics/text.h> 
#include <functions.h>
#include <ctype.h> 

#include "gadget.h"

/**********************************************************************
 *  Text attribute structures used in rendering IntuiTexts
 **********************************************************************/


char def_font[] ="topaz.font";

struct TextAttr TxtAt_Plain = { (UBYTE *)def_font, 8,
        FS_NORMAL, FPF_ROMFONT};

struct TextAttr TxtAt_BIU = {(UBYTE *)def_font, 8, 
        FSF_BOLD | FSF_ITALIC | FSF_UNDERLINED, FPF_ROMFONT};

struct TextAttr TxtAt_BU = {(UBYTE *)def_font, 8, 
        FSF_BOLD | FSF_UNDERLINED, FPF_ROMFONT};

struct TextAttr TxtAt_BI = {(UBYTE *)def_font, 8, 
       FSF_BOLD | FSF_ITALIC, FPF_ROMFONT};

struct TextAttr TxtAt_B ={(UBYTE *)def_font, 8, 
       FSF_BOLD, FPF_ROMFONT};

struct TextAttr TxtAt_IU ={(UBYTE *)def_font, 8,
       FSF_ITALIC | FSF_UNDERLINED, FPF_ROMFONT};

struct TextAttr TxtAt_I ={(UBYTE *)def_font, 8, 
       FSF_ITALIC, FPF_ROMFONT};

struct TextAttr TxtAt_U ={(UBYTE *)def_font, 8, 
       FSF_UNDERLINED, FPF_ROMFONT};

/* STOP gadget definition */

/***************************************************************/
/*  The following data structure contains the image data */
/***************************************************************/
USHORT StopGadImg_dat[]=  {
  0x0000,
  0x0000,  0x0000,  0x0000,  0x0000,  0x0020,  0x0000,  0x0000,  0x0000,
  0x0000,  0x0000,  0x0020,  0x0000,  0x00ff,  0xffff,  0xffe0,  0x0000,
  0x0006,  0x0000,  0x03ff,  0xffff,  0xfff8,  0x0000,  0x0006,  0x0000,
  0x07ff,  0xffff,  0xfffc,  0x0000,  0x0027,  0x0000,  0x1fff,  0xffff,
  0xffff,  0x0000,  0x0021,  0x0000,  0x3fff,  0xffff,  0xffff,  0x8000,
  0x0000,  0x0000,  0xffff,  0xffff,  0xffff,  0xe000,  0x0026,  0x0001,
  0xffff,  0xffff,  0xffff,  0xf000,  0x0000,  0x0007,  0xffff,  0xffff,
  0xffff,  0xfc00,  0x0020,  0x000f,  0xffff,  0xffff,  0xffff,  0xfe00,
  0x0038,  0x000f,  0xffff,  0xffff,  0xffff,  0xfe00,  0x0010,  0x000f,
  0xffff,  0xffff,  0xffff,  0xfe00,  0x0000,  0x000f,  0xffff,  0xffff,
  0xffff,  0xfe00,  0x0000,  0x000f,  0xffff,  0xffff,  0xffff,  0xfe00,
  0x0000,  0x000f,  0xffff,  0xffff,  0xffff,  0xfe00,  0x0000,  0x000f,
  0xffff,  0xffff,  0xffff,  0xfe00,  0x0000,  0x000f,  0xffff,  0xffff,
  0xffff,  0xfe00,  0x0000,  0x000f,  0xffff,  0xffff,  0xffff,  0xfe00,
  0x0036,  0x000f,  0xffff,  0xffff,  0xffff,  0xfe00,  0x0020,  0x000f,
  0xffff,  0xffff,  0xffff,  0xfe00,  0x0006,  0x000f,  0xffff,  0xffff,
  0xffff,  0xfe00,  0x003c,  0x000f,  0xffff,  0xffff,  0xffff,  0xfe00,
  0x0006,  0x0007,  0xffff,  0xffff,  0xffff,  0xfc00,  0x0031,  0x0001,
  0xffff,  0xffff,  0xffff,  0xf000,  0x0000,  0x0000,  0xffff,  0xffff,
  0xffff,  0xe000,  0x0000,  0x0000,  0x3fff,  0xffff,  0xffff,  0x8000,
  0x000f,  0x0000,  0x1fff,  0xffff,  0xffff,  0x0000,  0x0000,  0x0000,
  0x07ff,  0xffff,  0xfffc,  0x0000,  0x0000,  0x0000,  0x03ff,  0xffff,
  0xfff8,  0x0000,  0x0000,  0x0000,  0x00ff,  0xffff,  0xffe0,  0x0000,
  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,
  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,
  0x0000,  0x0000,  0x0020,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,
  0x0006,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,
  0x0000,  0x0000,  0x0000,  0x0000,  0x0007,  0x0000,  0x0000,  0x0000,
  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,
  0x0006,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,
  0x0000,  0x0000,  0x0000,  0x0000,  0x0001,  0x0000,  0x00ff,  0xffff,
  0xffe0,  0x0000,  0x0000,  0x0000,  0x03ff,  0xffff,  0xfff8,  0x0000,
  0x0000,  0x0000,  0x07ff,  0xffff,  0xfffc,  0x0000,  0x0000,  0x0000,
  0x1fff,  0xffff,  0xffff,  0x0000,  0x0020,  0x0000,  0x3fff,  0xffff,
  0xffff,  0x8000,  0x0000,  0x0000,  0xffff,  0xffff,  0xffff,  0xe000,
  0x003f,  0x0001,  0xffff,  0xffff,  0xffff,  0xf000,  0x0006,  0x0007,
  0xffff,  0xffff,  0xffff,  0xfc00,  0x0001,  0x0007,  0xffff,  0xffff,
  0xffff,  0xfc00,  0x0026,  0x0007,  0xffff,  0xffff,  0xffff,  0xfc00,
  0x0000,  0x0007,  0xffff,  0xffff,  0xffff,  0xfc00,  0x0001,  0x0007,
  0xffff,  0xffff,  0xffff,  0xfc00,  0x0006,  0x0007,  0xffff,  0xffff,
  0xffff,  0xfc00,  0x0006,  0x0007,  0xffff,  0xffff,  0xffff,  0xfc00,
  0x0003,  0x0007,  0xffff,  0xffff,  0xffff,  0xfc00,  0x0001,  0x0007,
  0xffff,  0xffff,  0xffff,  0xfc00,  0x0000,  0x0007,  0xffff,  0xffff,
  0xffff,  0xfc00,  0x0024,  0x0007,  0xffff,  0xffff,  0xffff,  0xfc00,
  0x0000,  0x0007,  0xffff,  0xffff,  0xffff,  0xfc00,  0x0006,  0x0007,
  0xffff,  0xffff,  0xffff,  0xfc00,  0x0000,  0x0001,  0xffff,  0xffff,
  0xffff,  0xf000,  0x0001,  0x0000,  0xffff,  0xffff,  0xffff,  0xe000,
  0x0000,  0x0000,  0x3fff,  0xffff,  0xffff,  0x8000,  0x0001,  0x0000,
  0x1fff,  0xffff,  0xffff,  0x0000,  0x0000,  0x0000,  0x07ff,  0xffff,
  0xfffc,  0x0000,  0x0000,  0x0000,  0x03ff,  0xffff,  0xfff8,  0x0000,
  0x0000,  0x0000,  0x00ff,  0xffff,  0xffe0,  0x0000,  0x0000,  0x0000,
  0x0000,  0x0000,  0x0000,  0x0000,  0x003f,  0x0000,  0x0000,  0x0000,
  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,
  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,
  0x0000,  0x0000,  0x0000,  0x0000,  0x0007,  0x0000,  0x0000,  0x0000,
  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,
  0x0006,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0000,  0x0002
     };

/***************************************************************/
/*  The following data structure defines the image  */
/***************************************************************/

struct Image StopGadImg =  {
       0, 0, /* Left, Top */
       90, 38, /* Width, Height */
       2,     /* Depth */ 
       (USHORT *)&StopGadImg_dat, /* ImageData */
       0xff,  /* PlanePick */
       0x00,  /* PlaneOnOff */
       NULL     /* Next Image */
      }; 

/**********************************************************************
 *  IntuiTexts for the StopGad gadget.
 **********************************************************************/

struct IntuiText StopGad_Text_0 = {
   0, 3,     	/* FrontPen, BackPen */
   JAM2,       	/* DrawMode */
   26, 13,     	/* LeftEdge, TopEdge */
   &TxtAt_B, 	/* ITextFont Pointer - bold */ 
   /* The IText */
   (UBYTE *)"STOP",
   NULL
 };



/**********************************************************************
 *  Gadget Structure definition for the StopGad gadget.
 **********************************************************************/

struct Gadget StopGad = {
  NULL,     /* NextGadget pointer */
  76, 30,    /* LeftEdge, TopEdge  */
  90, 38,    /* Width, Height      */
  /* Gadget Flags */
   GADGIMAGE,
  /* Activation Flags */
  RELVERIFY,
  /* GadgetType */
  BOOLGADGET,
  (APTR)&StopGadImg,    /*  GadgetRender */
  NULL,    /* SelectRender */
   &StopGad_Text_0,  /* GadgetText */
  0x0,    /* MutualExclude */
  NULL,   /* SpecialInfo */
  STOPGAD,    /* GadgetID         */
  0x4     /* UserData Pointer */
};
^L
#define REQUESTWIDTH	240	
#define REQUESTHEIGHT	50

/* New window structure */

static struct NewWindow pathreqnw = {
	640 - REQUESTWIDTH - 8,			/* LeftEdge */
	12,								/* TopEdge */
	REQUESTWIDTH + 4,				/* Width */
	REQUESTHEIGHT + 10,				/* Height */
	0,								/* DetailPen */
	1,								/* BlockPen */

/* IDCMP Flags */

	GADGETDOWN | GADGETUP | REQSET,

/* Flags */
	 WINDOWDEPTH | WINDOWDRAG | ACTIVATE ,

	NULL,							/* First gadget */
	NULL,							/* Checkmark */
	(UBYTE *)"Pathname Specifications",	/* Window title */
	NULL,							/* No custom streen */
	NULL,							/* Not a super bitmap window */
	REQUESTWIDTH+4,					/* MinWidth */
	REQUESTHEIGHT + 10,				/* MinHeight */
	640,							/* MaxWidth */
	200,							/* MaxHeight */
	WBENCHSCREEN
};


/**********************************************************************
 *  Border Definitions for listpath gadget
 **********************************************************************/

SHORT listpath_Pairs_1[] = {
  0,     0,   
  240,     0,   
  240,     30,   
  0,     30,   
  0,     0    
};
struct Border listpath_bord_1 = {
  -1,  -1,       /* LeftEdge, TopEdge */
  1,  3,  JAM2,  /* FrontPen, BackPen, DrawMode*/
  5,             /* Count of XY pairs */  
  (SHORT *)&listpath_Pairs_1, /* XY pairs */
  NULL           /* Next Border */
};


/**********************************************************************
 *  IntuiTexts for the listpath gadget.
 **********************************************************************/

struct IntuiText listpath_Text_0 = {
   3, 0,     /* FrontPen, BackPen */
   JAM2,       /* DrawMode */
   -112, 0,     /* LeftEdge, TopEdge */
   &TxtAt_Plain, /* ITextFont Pointer */ 
   /* The IText */
   (UBYTE *)"Listing Path:",
   NULL
 };



/**********************************************************************
 *  String information for the listpath string gadget.
 **********************************************************************/

UBYTE listpath_sbuf_1[81] = "PRT:";
UBYTE listpath_ubuf_1[81];

struct StringInfo listpath_txstr_1 = {
  listpath_sbuf_1, listpath_ubuf_1, /* Buffer, UndoBuffer  */
  0, 80, 0,  /* BufferPos, MaxChars, DispPos */
  0, 0,      /* UndoPos, NumChars */
  0, 0, 0,  /* DispCount, CLeft, CTop */
  0x0, 0,   /* LayerPtr, LongInt */
  0x0        /* AltKeyMap */
};


/**********************************************************************
 *  Gadget Structure definition for the listpath gadget.
 **********************************************************************/

struct Gadget listpathgad = {
  NULL,     /* NextGadget pointer */
  120, 36,    /* LeftEdge, TopEdge  */
  100, 9,    /* Width, Height      */
  /* Gadget Flags */
  GADGHCOMP,
  /* Activation Flags */
 GADGIMMEDIATE | RELVERIFY,
  /* GadgetType */
  REQGADGET | STRGADGET,
  /*(APTR)&listpath_bord_1, */  /*  GadgetRender */
  NULL,						/* GadgetRender */
  NULL,    /* SelectRender */
   &listpath_Text_0,  /* GadgetText */
  0x0,    /* MutualExclude */
  (APTR)&listpath_txstr_1,   /* SpecialInfo */
  LISTPATHGAD,    /* GadgetID         */
  NULL						/* UserData Pointer */
};

/**********************************************************************
 *  Border Definitions for backpath gadget
 **********************************************************************/

SHORT backpath_Pairs_1[] = {
  0,     0,   
  240,     0,   
  240,     9,   
  0,     9,   
  0,     0    
};
struct Border backpath_bord_1 = {
  -1,  -1,       /* LeftEdge, TopEdge */
  1,  3,  JAM2,  /* FrontPen, BackPen, DrawMode*/
  5,             /* Count of XY pairs */  
  (SHORT *)&backpath_Pairs_1, /* XY pairs */
  NULL           /* Next Border */
};


/**********************************************************************
 *  IntuiTexts for the backpath gadget.
 **********************************************************************/

struct IntuiText backpath_Text_0 = {
   3, 0,     /* FrontPen, BackPen */
   JAM2,       /* DrawMode */
   -104, 0,     /* LeftEdge, TopEdge */
   &TxtAt_Plain, /* ITextFont Pointer */ 
   /* The IText */
   (UBYTE *)"Backup Path:",
   NULL
 };



/**********************************************************************
 *  String information for the backpath string gadget.
 **********************************************************************/

UBYTE backpath_sbuf_1[81] = "DF0:";
UBYTE backpath_ubuf_1[81];

struct StringInfo backpath_txstr_1 = {
  backpath_sbuf_1, backpath_ubuf_1, /* Buffer, UndoBuffer  */
  0, 80, 0,  /* BufferPos, MaxChars, DispPos */
  0, 0,      /* UndoPos, NumChars */
  0, 0, 0,  /* DispCount, CLeft, CTop */
  0x0, 0,   /* LayerPtr, LongInt */
  0x0        /* AltKeyMap */
};


/**********************************************************************
 *  Gadget Structure definition for the backpath gadget.
 **********************************************************************/

struct Gadget backpathgad = {
  &listpathgad,    				/* NextGadget pointer */
  120, 20,    					/* LeftEdge, TopEdge  */
  100, 9,    					/* Width, Height      */
  GADGHCOMP,					/* Flags */
  GADGIMMEDIATE | RELVERIFY,	/* Activation */
  REQGADGET | STRGADGET,		/* GadgetType */
  /* (APTR)&backpath_bord_1, */ /*  GadgetRender */
  NULL,							/* GadgetRender */
  NULL,    						/* SelectRender */
   &backpath_Text_0,  			/* GadgetText */
  0x0,    						/* MutualExclude */
  (APTR)&backpath_txstr_1,   	/* SpecialInfo */
  BACKPATHGAD,    				/* GadgetID         */
  NULL							/* UserData Pointer */
};

/**********************************************************************
 *  Border Definitions for homepath gadget
 **********************************************************************/

SHORT homepath_Pairs_1[] = {
  0,     0,   
  240,     0,   
  240,     9,   
  0,     9,   
  0,     0    
};
struct Border homepath_bord_1 = {
  -1,  -1,       /* LeftEdge, TopEdge */
  1,  3,  JAM2,  /* FrontPen, BackPen, DrawMode*/
  5,             /* Count of XY pairs */  
  (SHORT *)&homepath_Pairs_1, /* XY pairs */
  NULL           /* Next Border */
};


/**********************************************************************
 *  IntuiTexts for the homepath gadget.
 **********************************************************************/

struct IntuiText homepath_Text_0 = {
   3, 0,     /* FrontPen, BackPen */
   JAM2,       /* DrawMode */
   -88, 0,     /* LeftEdge, TopEdge */
   &TxtAt_Plain, /* ITextFont Pointer */ 
   /* The IText */
   (UBYTE *)"Home Path:",
   NULL
 };



/**********************************************************************
 *  String information for the homepath string gadget.
 **********************************************************************/

UBYTE homepath_sbuf_1[81] = "DH0:";
UBYTE homepath_ubuf_1[81];

struct StringInfo homepath_txstr_1 = {
  homepath_sbuf_1, homepath_ubuf_1, /* Buffer, UndoBuffer  */
  0, 80, 0,  /* BufferPos, MaxChars, DispPos */
  0, 8,      /* UndoPos, NumChars */
  0, 0, 0,  /* DispCount, CLeft, CTop */
  0x0, 0,   /* LayerPtr, LongInt */
  0x0        /* AltKeyMap */
};


/**********************************************************************
 *  Gadget Structure definition for the homepath gadget.
 **********************************************************************/

struct Gadget homepathgad = {
  &backpathgad,     /* NextGadget pointer */
  120, 4,    	/* LeftEdge, TopEdge  */
  100, 9,    /* Width, Height      */
  /* Gadget Flags */
  GADGHCOMP,
  /* Activation Flags */
  GADGIMMEDIATE | RELVERIFY,
  /* GadgetType */
  REQGADGET | STRGADGET,
  /*(APTR)&homepath_bord_1, */  /*  GadgetRender */
  NULL,						/* GadgetRender */
  NULL,    /* SelectRender */
   &homepath_Text_0,  /* GadgetText */
  0x0,    /* MutualExclude */
  (APTR)&homepath_txstr_1,   /* SpecialInfo */
  HOMEPATHGAD,    			/* GadgetID         */
  NULL						/* UserData Pointer */
};

struct Requester *pathrequest;
struct Window *pathwindow;

/* Initialize the pathname requester. */
int
InitPathRequest()
{
	int status = 0;
	pathrequest = (struct Requester *)
		AllocMem((long) sizeof(struct Requester), MEMF_PUBLIC|MEMF_CLEAR);

	if (pathrequest) {
		InitRequester(pathrequest);
		pathrequest->Width = REQUESTWIDTH;
		pathrequest->Height = REQUESTHEIGHT;
		pathrequest->TopEdge = 12;
		pathrequest->LeftEdge = 4;
		pathrequest->BackFill = 2;
		pathrequest->ReqGadget = &homepathgad;
		if (pathwindow = OpenWindow(&pathreqnw))
			Request(pathrequest, pathwindow);
		else ++status;
	}
	else ++status;
	return status;
}

/* Shut down the path requester mechanisms. */

CancelPathRequest()
{
	if (pathrequest) {
		if (pathwindow) {
			EndRequest(pathrequest, pathwindow);
			CloseWindow(pathwindow);
		}
		FreeMem(pathrequest, (long) sizeof(struct Requester));
	}
}
SHAR_EOF