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