home *** CD-ROM | disk | FTP | other *** search
- /*######################################################################################
- ## compare.module by Leo 'Nudel' Davidson for Gods'Gift Utilities. ##
- ## A plug-in module for Directory Opus 5.5 to compare the contents of two files. ##
- ## ##
- ## ##
- ## Until July 1998 you should be able to contact me via any of the following: ##
- ## ##
- ## email: leo.davidson@keble.oxford.ac.uk ##
- ## www: http://users.ox.ac.uk/~kebl0364 ##
- ## IRC: Nudel in #Amiga on Effnet or Undernet (very rarely). ##
- ## ##
- ## Comments, suggestions, questions, offers, and chats all welcome. ##
- ## ##
- ## ##
- ## Tabsize: 4 -- 88 Columns (sorry) -- Amiga-specific -- Compile with SAS/C. ##
- ## ##
- ## Credit is due to Nick Christie, Jonathan Potter, and Greg Perry for their advice, ##
- ## examples, and general help beyond the call of duty. Thanks guys! ##
- ##************************************************************************************##
- ## There appears to be a bug in DOpus 5.5: Each call to AsyncRequestTags() looses ##
- ## 32 bytes of memory. This also happens in the example module which comes with the ##
- ## OpusSDK and has been reported to GPSoftware. ##
- ########################################################################################
- ## This program rarely allocates more than about 30k at a time. Since all error ##
- ## messages are output by requesters (which take quite a bit of mem to display), ##
- ## memory allocation failures result in a silent abort. Perhaps it would be better ##
- ## to at least call DisplayBeep() -- maybe in the future. ##
- ## In the places where large allocations are possible (while generating the full ##
- ## report, mostly), error requesters ARE implimented. ##
- ########################################################################################
- ## I almost added the option to specify the files to compare on the command-line, but ##
- ## I can't see any point or use for it. ##
- ######################################################################################*/
-
- #include "compare.module.h"
-
- /**************************************************************************************/
-
- #define PROGNAME "compare.module"
- #define PROGVERS "1.2"
- #define PROGDATE __AMIGADATE__
- static char version_str[] = "\0$VER: " PROGNAME " " PROGVERS " " PROGDATE "\0";
- // Above line should be double null-terminated.
-
- /*= Definition of the module =========================================================*/
- ModuleInfo module_info =
- {
- 1, // Version
- "compare.module", // Module name
- "compare.catalog", // Catalog name
- 0, // Flags
- 1, // Number of functions
- {0,"Compare",MSG_COMPARE_DESC,\
- FUNCF_NEED_SOURCE|FUNCF_NEED_FILES|FUNCF_SINGLE_SOURCE,\
- 0} // First function.
- };
-
- /**************************************************************************************/
-
- /*= L_Module_Entry() =================================================================-.
- || Main entry point to the module. The L_ is to identify this as a library ||
- || function (specified by the "libprefix" option in the makefile) ||
- ||------------------------------------------------------------------------------------||
- || This kinda evolved into one huge routine and at this stage isn't really worth ||
- || cutting down. I'd definately split things up if I did it all again, though. ||
- `-====================================================================================*/
- int __asm __saveds L_Module_Entry(
- register __a0 char *args, // User-supplied arguments
- register __a1 struct Screen *screen, // Screen to open on
- register __a2 IPCData *ipc, // Our IPC pointer
- register __a3 IPCData *main_ipc, // Main Opus IPC pointer
- register __d0 ULONG mod_id, // ID of module function
- register __d1 EXT_FUNC(func_callback)) // Opus callback function
- {
- Compare_Data *data; // Pseudo-global variables.
- FuncArgs *fa;
- LONG diffbytes; // Number of differences between the files.
-
- if (data = AllocVec(sizeof(Compare_Data),MEMF_CLEAR))
- {
- data->ipc = ipc;
- data->func_callback = func_callback;
-
- if (data->rnd.poolhead = \
- NewMemHandle(PUDDLESIZE,THRESHSIZE,MEMF_CLEAR) )
- {
- data->rnd.data = data;
- if (DOpusBase->lib_Version < MIN_OPUS_VERSION)
- informUser(data,dgs(MSG_VERSREQ_FMT),FALSE,NULL,MIN_OPUS_VERSION);
- else
- {
- fa = parseArgs(data,args); // Parse command-line arguments.
-
- if ((get_files_to_compare(data,data->file1path,data->file1name,
- data->file2path,data->file2name)) \
- && (data->file1rn = createResNode(&data->rnd)) \
- && (data->file2rn = createResNode(&data->rnd)) \
- && (open_files_to_compare(data)) \
- && ( (diffbytes = compare_files(data)) >= 0 ) )
- {
- // Note: The files being compared remain open for later.
- // (They'll be closed by ResNodes automatically on abort.)
-
- // Report the number of differences, and, if there were any, ask
- // if the user wants a full display of them.
- if (report_num_diffs(data,diffbytes))
- {
- // If they asked for a full display, generate one.
- report_full_display(data);
- }
- // If more is done before the call to deleteAllResNodes()
- // just below, file1rn and file2rn should be deleted unless
- // the files are still wanted (remember they are still open).
- }
- freeArgs(fa); // Free FuncArgs structure, if any.
- }
- // Free all remainding resources allocated with ResNodes.
- deleteAllResNodes(&data->rnd); // Safe to call even if no ResNodes.
-
- // Free our memory pool.
- FreeMemHandle(data->rnd.poolhead);
- data->rnd.poolhead = NULL;
- }
- // Free our pseudo-global variables.
- FreeVec(data);
- }
- // Currently, functions should always return 1.
- return(1);
- }
-
- /*= InformUser() =====================================================================-.
- || Send the user a requester with printf-style formatted text. ||
- || Requester is centered on the Opus screen and has just an "OK" gadget. ||
- || If window is TRUE it'll try to open over the lister window. ||
- ||------------------------------------------------------------------------------------||
- || Flags: IU_DISPLAY -- changes gadgets to "Display" and "OK" and returns 1 or 0 resp.||
- `-====================================================================================*/
- BOOL informUser(Compare_Data *data,char *format,BOOL window,ULONG flags,...)
- {
- BOOL iu_return;
- struct Window *win;
- struct Screen *screen;
- ResNode *rn1;
- va_list ap;
-
- if (rn1 = allocNewResNode(&data->rnd,INFORMUSERBUFFERSIZE))
- {
- // Build requester text
- va_start(ap,flags);
- vsprintf(rn1->rn_Mem,format,ap);
- va_end(ap);
-
- if (window)
- {
- win = getListerWindow(data);
- screen = NULL;
- }
- else
- {
- screen = getDOpusScreen(data);
- win = NULL;
- }
-
- // Display requester over window.
- iu_return = AsyncRequestTags(data->ipc, REQTYPE_SIMPLE, 0, 0, 0,
- TAGIF(win,AR_Window), win,
- TAGIF((!win) && screen,AR_Screen), screen,
- AR_Message, rn1->rn_Mem,
- AR_Title, dgs(MSG_TITLE),
- TAGIF(flags & IU_DISPLAY,AR_Button), dgs(MSG_DISPLAY_GAD),
- TAGIF(flags & IU_DISPLAY,AR_Button), dgs(MSG_NODISPLAY_GAD),
- TAGNOT(flags & IU_DISPLAY,AR_Button), dgs(MSG_OK_GAD),
- TAG_END);
-
- // Free memory allocated for buffer.
- deleteResNode(&data->rnd,rn1);
- }
- return(iu_return);
- }
-
- /*= Get_Files_To_Compare() ===========================================================-.
- || Fills in the file paths and names of the two files to be compared. ||
- || If two files are selected in the Source lister they will be chosen. Otherwise, ||
- || the file selected in the Souce lister is chosen along with the first one in the ||
- || Destination lister. ||
- || Returns boolean success. ||
- || Does *not* report any errors (e.g. not enough selected files) to the user to be ||
- || be in keeping with how the other Opus commands behave. ||
- `-====================================================================================*/
- BOOL get_files_to_compare(Compare_Data *data,char *path1,char *name1,\
- char *path2,char *name2)
- {
- struct path_node *pn1;
- ResNode *rn1;
- LONG src_entries;
- BOOL gftc_return = FALSE;
-
- // Allocate a temporary path buffer.
- if (rn1 = allocNewResNode(&data->rnd,PATHBUFFSIZE))
- {
- // path_node to pn1 and path string to ResNode's allocated buffer.
- // If no source lister is returned silently abort. There is not much
- // point in an error message because Opus won't let this function run
- // without a source lister, so something really bad has happened.
- if (pn1 = (struct path_node *)\
- data->func_callback(EXTCMD_GET_SOURCE,IPCDATA(data->ipc),rn1->rn_Mem) )
- {
- // Store the handle of our lister for later use.
- (data->listerhandle) = (pn1->lister);
-
- src_entries =\
- data->func_callback(EXTCMD_ENTRY_COUNT,IPCDATA(data->ipc),NULL);
-
- switch(src_entries)
- {
- // If there are no files selected something really bad has
- // happened as Opus shouldn't have started this function at all.
- case 0: break;
-
- // If there's only one file selected we have to find the other
- // in the Destination lister.
- case 1: gftc_return = get_s_and_d(data,rn1,path1,name1,path2,name2);
- break;
-
- // If there's 2 or more selected, use the first two.
- default: gftc_return = get_s_twice(data,rn1,path1,name1,path2,name2);
- break;
- }
- }
- // Free memory allocated for buffer.
- deleteResNode(&data->rnd,rn1);
- }
- return(gftc_return);
- }
-
- /*= get_s_twice() ====================================================================-.
- || Used by Get_Files_To_Compare() to get two file from the source lister. ||
- || Returns boolean success. ||
- `-====================================================================================*/
- BOOL get_s_twice(Compare_Data *data,ResNode *rn1,char *path1,char *name1,
- char *path2,char *name2)
- {
- BOOL gst_return = FALSE;
- struct function_entry *fentry;
- struct endentry_packet eep;
-
- // Put the source lister path at the front of both path strings.
- strcpy(path1,rn1->rn_Mem);
- strcpy(path2,rn1->rn_Mem);
-
- // Get first entry from source lister.
- if (fentry = (struct function_entry *)\
- data->func_callback(EXTCMD_GET_ENTRY,IPCDATA(data->ipc),NULL))
- {
- strcat(path1,fentry->name);
- strcpy(name1,fentry->name);
-
- // Finish with the entry and deselect it.
- eep.entry = fentry;
- eep.deselect = TRUE;
- data->func_callback(EXTCMD_END_ENTRY,IPCDATA(data->ipc),&eep);
-
- // Now get the second entry and do the same thing with it.
- if (fentry = (struct function_entry *)\
- data->func_callback(EXTCMD_GET_ENTRY,IPCDATA(data->ipc),NULL))
- {
- strcat(path2,fentry->name);
- strcpy(name2,fentry->name);
-
- // Finish with the entry and deselect it.
- eep.entry = fentry;
- eep.deselect = TRUE;
- data->func_callback(EXTCMD_END_ENTRY,IPCDATA(data->ipc),&eep);
-
- // We got our filenames/paths okay.
- gst_return = TRUE;
- }
- }
- return(gst_return);
- }
-
- /*= get_s_and_d() ====================================================================-.
- || Used by Get_Files_To_Compare() to get one file from the source and one from the ||
- || destination lister. Returns boolean success. ||
- `-====================================================================================*/
- BOOL get_s_and_d(Compare_Data *data,ResNode *rn1,char *path1,char *name1,
- char *path2,char *name2)
- {
- BOOL gsad_return = FALSE;
- struct function_entry *fentry;
- struct endentry_packet eep;
- struct command_packet cp;
- char destlister[24];
- ResNode *combufrn;
-
- // Allocate a temporary buffer to build ARexx commands in.
- if (combufrn = allocNewResNode(&data->rnd,COMMBUFFSIZE))
- {
- // Put the source lister path at the front of the first path string.
- strcpy(path1,rn1->rn_Mem);
-
- // Get entry from source lister.
- if (fentry = (struct function_entry *)\
- data->func_callback(EXTCMD_GET_ENTRY,IPCDATA(data->ipc),NULL))
- {
- strcat(path1,fentry->name);
- strcpy(name1,fentry->name);
-
- // Finish with the entry and deselect it.
- eep.entry = fentry;
- eep.deselect = TRUE;
- data->func_callback(EXTCMD_END_ENTRY,IPCDATA(data->ipc),&eep);
-
- /* There are not callback functions to deal with entries in the dest lister, so
- we have to do it with ARexx. -- There are callback functions to GET the
- dest lister but if we used them it would not be safe to unbusy it before
- the operation is completed. */
-
- // First, get the dest lister's handle.
- // If we can't get a dest, silently fail to be consistent with what
- // Opus does internally.
-
- strcpy(combufrn->rn_Mem,"lister query dest");
- cp.command = combufrn->rn_Mem;
- cp.flags = COMMANDF_RESULT;
- if ( (data->func_callback(EXTCMD_SEND_COMMAND,IPCDATA(data->ipc),&cp)) && \
- (cp.result) && (cp.result[0]) )
- {
- copyword(destlister,cp.result);
- FreeVec(cp.result);
-
- // As fast as possible, make the lister busy.
- sprintf(combufrn->rn_Mem,"lister set %s busy %s wait",
- destlister,"on");
- sendExtCmd_nr(data,combufrn->rn_Mem,&cp);
-
- // Get the lister's path and put it at start of second filepath.
- sprintf(combufrn->rn_Mem,"lister query %s path",destlister);
- cp.command = combufrn->rn_Mem;
- cp.flags = COMMANDF_RESULT;
- if ( (data->func_callback(EXTCMD_SEND_COMMAND,IPCDATA(data->ipc),&cp))\
- && (cp.result) && (cp.result[0]) )
- {
- strcpy(path2,cp.result);
- FreeVec(cp.result);
-
- // Get the name of the first selected file, if any.
- sprintf(combufrn->rn_Mem,"lister query %s selfiles",destlister);
- cp.command = combufrn->rn_Mem;
- cp.flags = COMMANDF_RESULT;
- if ( (data->func_callback(EXTCMD_SEND_COMMAND,IPCDATA(data->ipc),
- &cp)) && (cp.result) && (cp.result[0]) )
- {
- copywordquoted(name2,cp.result);
- strcat(path2,name2);
-
- // Now we just have to deselect the entry.
- // Filename already has quotes around it.
- sprintf(combufrn->rn_Mem,"lister select %s %s 0",
- destlister,cp.result);
-
- FreeVec(cp.result);
-
- sendExtCmd_nr(data,combufrn->rn_Mem,&cp);
-
- sprintf(combufrn->rn_Mem,"lister refresh %s",destlister);
- sendExtCmd_nr(data,combufrn->rn_Mem,&cp);
-
- // We got our filenames/paths okay.
- gsad_return = TRUE;
- }
- else if (cp.result)
- {
- FreeVec(cp.result);
- }
- }
- else if (cp.result)
- {
- FreeVec(cp.result);
- }
-
- // Free-up the destination lister.
- sprintf(combufrn->rn_Mem,"lister set %s busy %s wait",destlister,"off");
- sendExtCmd_nr(data,combufrn->rn_Mem,&cp);
- }
- else if (cp.result)
- {
- FreeVec(cp.result);
- }
- }
- // Free memory allocated for command buffer.
- deleteResNode(&data->rnd,combufrn);
- }
- return(gsad_return);
- }
-
- /*= open_files_to_compare() ==========================================================-.
- || Attempts to open the two files in preparation for comparison. ||
- || Returns boolean success and will fail if the files cannot be openned, or cannot be ||
- || be examined. ||
- `-====================================================================================*/
- BOOL open_files_to_compare(Compare_Data *data)
- {
- BOOL oftc_return = FALSE;
-
- // Give the ResNodes filenames.
- (data->file1rn->rn_Name) = (data->file1path);
- (data->file2rn->rn_Name) = (data->file2path);
-
- // Attempt to get the size of each file.
- if ( !( (examineResNode(&data->rnd,data->file1rn)) && \
- (examineResNode(&data->rnd,data->file2rn)) ) )
- {
- // Error message and abort if we couldn't get either size.
- informUser(data,dgs(MSG_EXAMFAIL),TRUE,NULL);
- }
- else
- {
- if ((data->file1rn->rn_FIB->fib_Size) != (data->file2rn->rn_FIB->fib_Size))
- {
- (data->minsize) = min((data->file1rn->rn_FIB->fib_Size),
- (data->file2rn->rn_FIB->fib_Size));
-
- // Warn them that the files are of different sizes and only the first
- // xxx bytes will be compared.
- if (!(data->nosizewarn))
- informUser(data,dgs(MSG_DIFFSIZES_FMT),TRUE,NULL,data->minsize);
- }
- else
- (data->minsize) = (data->file1rn->rn_FIB->fib_Size);
-
- if (!( (openFileResNode(&data->rnd,data->file1rn,MODE_OLDFILE))
- && (openFileResNode(&data->rnd,data->file2rn,MODE_OLDFILE)) ))
- {
- // Error message and abort if we couldn't actually open either file.
- informUser(data,dgs(MSG_OPENFAIL),TRUE,NULL);
- }
- else
- oftc_return = TRUE;
- }
- return(oftc_return);
- }
-
- /*= compare_files() ==================================================================-.
- || Actually compares the files which have been made ready by previous routines. ||
- || Builds a linked list of DiffRange's which describe where the differences are. ||
- || Returns the number of different bytes, or (-1) on failure/user-abort. ||
- `-====================================================================================*/
- LONG compare_files(Compare_Data *data)
- {
- LONG cf_return = -1; // We return the number of different bytes, or -1 on error.
- APTR progwin; // Handle of progress window.
- LONG remfsize; // Bytes remaining to be compared.
- LONG donebytes; // Total bytes done.
- LONG diffbytes; // Total bytes different.
- LONG dobytes; // Bytes remaining in current loop.
- BOOL comperror; // If true there has been an error while comparing.
- LONG infotxtid; // Used for choosing one out of a set of locale strings.
- LONG lastdiff; // Offset from start of file to the last different byte.
- LONG thisdiff; // Offset from start of file to the current different byte.
- LONG thisdiffstart; // -._ Form an interval around the current
- LONG thisdiffend; // -' different byte (offsets from start of file).
- struct DiffRange *drlast; // Last dr in linked list. (Base is data->drbase)
- struct DiffRange *drnew; // New/current DiffRange structure.
- char *cp1; // Pointer to current posn. in compare buffer1.
- char *cp2; // Pointer to current posn. in compare buffer2.
- char *cpe; // Pointer to end of compare buffer1.
- char *cpo; // Pointer to start of compare buffer1.
- ResNode *progbrn; // ResNode for memory allocated for building progress msg.
-
- remfsize = (data->minsize);
-
- // Open progress window.
- if (progwin = OpenProgressWindowTags(\
- PW_Window, getListerWindow(data),
- PW_Title, dgs(MSG_PROGTITLE),
- PW_FileName, data->file1name,
- PW_FileCount, remfsize,
- PW_Flags, PWF_INFO|PWF_GRAPH|PWF_ABORT|PWF_FILENAME,
- TAG_END))
- {
- // Allocate comparison buffers. Error message and abort if we can't.
- if (!(allocMemResNode(&data->rnd,data->file1rn,data->buffsize) && \
- allocMemResNode(&data->rnd,data->file2rn,data->buffsize) && \
- (progbrn = allocNewResNode(&data->rnd,NAMEBUFFSIZE+50)) ))
- {
- informUser(data,dgs(MSG_CMPBUFFAIL),TRUE,NULL);
- }
- else
- {
- // Initialize some variables (remfsize initialized above).
- (data->drbase) = drlast = NULL;
- // Make sure first difference starts a new new DiffRange
- lastdiff = (-(4*DR_EXTEND));
- donebytes = diffbytes = 0;
- comperror = FALSE;
-
- while( (remfsize > 0) && (!(CheckProgressAbort(progwin))) && \
- (!(data->func_callback(EXTCMD_CHECK_ABORT,IPCDATA(data->ipc),NULL))) \
- && (!comperror) )
- {
- // Use a different string depending on how many bytes are different.
- // No crappy "xxx error(s)" messages -- using the correct
- // singular/plural form is quick'n'easy and not doing so is just lazy.
- switch (diffbytes)
- {
- case 0: infotxtid = MSG_DIFFPROG0_FMT; break;
- case 1: infotxtid = MSG_DIFFPROG1_FMT; break;
- default: infotxtid = MSG_DIFFPROGP_FMT; break;
- }
- sprintf(progbrn->rn_Mem,dgs(infotxtid),data->file2name,diffbytes);
- SetProgressWindowTags(progwin,
- PW_FileNum,donebytes,
- PW_Info,progbrn->rn_Mem,
- TAG_END);
-
- // If we filled the compare buffers, compare the entire buffers,
- // otherwise compare until the end of the file.
- dobytes = min(remfsize,(data->buffsize));
-
- // Update the number of bytes remaining to be compared after this
- // block is done. remfsize will go <= 0 when we're done.
- remfsize -= (data->buffsize);
-
- // Read the data for the next comparison from the two files.
- if ((0>=Read(data->file1rn->rn_FHandle,data->file1rn->rn_Mem,dobytes))\
- || (0>=Read(data->file2rn->rn_FHandle,data->file2rn->rn_Mem,dobytes)))
- {
- // If we couldn't read for some reason, report the error
- // and flag that we should abort.
- comperror = TRUE;
- informUser(data,dgs(MSG_READFAIL),TRUE,NULL);
- }
-
- // Setup the buffer pointers.
- cp1 = (data->file1rn->rn_Mem);
- cp2 = (data->file2rn->rn_Mem);
- cpo = cp1;
- cpe = cp1 + dobytes;
-
- // While there isn't an error, compare the two buffers, storing
- // the ranges in which differences occur.
-
- for (; (!comperror) && (cp1 < cpe); cp1++,cp2++)
- {
- if ((*cp1) != (*cp2))
- {
- diffbytes++;
- thisdiff = (cp1 - cpo) + donebytes;
-
- // We don't have to worry about pointing outside the
- // file here because the routine which deals with the
- // DiffRanges handles that.
- thisdiffstart = thisdiff - (thisdiff%8);
- thisdiffend = thisdiff + ((8 - (thisdiff%8)) - 1);
-
- // If this difference is within 3 * DR_EXTEND bytes of
- // the previous one, just extend the previous difference range.
- // Otherwise, create a new difference range of just this byte.
-
- if ((thisdiffend - lastdiff) <= (3*DR_EXTEND))
- (drlast->end) = thisdiffend;
- else
- {
- // We don't bother tracking the allocations of the
- // Difference Ranges: they'll be freed when the pool is
- // destroyed and don't need to be freed before that.
- if (!(drnew = AllocMemH(data->rnd.poolhead,
- sizeof(struct DiffRange)) ))
- {
- comperror = TRUE;
- informUser(data,dgs(MSG_OUTOFMEM),TRUE,NULL);
- DisplayBeep(NULL);
- }
- else
- {
- if (!(data->drbase))
- (data->drbase) = drlast = drnew;
- else
- {
- (drlast->next) = drnew;
- drlast = drnew;
- }
- (drnew->start) = thisdiffstart;
- (drnew->end) = thisdiffend;
- }
- }
- lastdiff = (thisdiffend + 1);
- }
- }
- donebytes += dobytes;
- }
-
- // Only signal that the rest of the program should continue if there
- // wasn't an error and the user didn't abort before the end of the files.
- if ((!comperror) && (remfsize <= 0))
- {
- cf_return = diffbytes;
-
- // Make sure the progress window reaches 100% done.
- switch (diffbytes)
- {
- case 0: infotxtid = MSG_DIFFPROG0_FMT; break;
- case 1: infotxtid = MSG_DIFFPROG1_FMT; break;
- default: infotxtid = MSG_DIFFPROGP_FMT; break;
- }
- sprintf(progbrn->rn_Mem,dgs(infotxtid),data->file2name,diffbytes);
- SetProgressWindowTags(progwin,
- PW_FileNum,donebytes,
- PW_Info,progbrn->rn_Mem,
- TAG_END);
- }
-
- // Free memory for the comparison buffers.
- deleteResNode(&data->rnd,progbrn);
- freeMemResNode(&data->rnd,data->file1rn);
- freeMemResNode(&data->rnd,data->file2rn);
- }
- CloseProgressWindow(progwin);
- }
- return(cf_return);
- }
-
- /*= report_num_diffs() ===============================================================-.
- || Reports the number of differences to the user and gives them the option of a full ||
- || display of them. Returns TRUE if they do, FALSE if they don't. (Note that they ||
- || cannot ask for a full display when the files are identical.) ||
- `-====================================================================================*/
- BOOL report_num_diffs(Compare_Data *data,LONG diffbytes)
- {
- BOOL rnd_return = FALSE;
-
- switch (diffbytes)
- {
- case 0:
- rnd_return = FALSE;
- informUser(data,dgs(MSG_NUMDIFFS0),TRUE,NULL);
- break;
- case 1:
- rnd_return = \
- informUser(data,dgs(MSG_NUMDIFFS1_FMT),TRUE,IU_DISPLAY,diffbytes);
- break;
- default:
- rnd_return = \
- informUser(data,dgs(MSG_NUMDIFFSP_FMT),TRUE,IU_DISPLAY,diffbytes);
- break;
- }
- return(rnd_return);
- }
-
- /*= getListerWindow() ================================================================-.
- || Gets the window of a lister from just the lister handle (in ASCII). ||
- || Returns the handle, or NULL. This should not be stored for later use as the window ||
- || may be different next time (for example, Opus has reopenned on another screen). ||
- || This is a bit of a hack, but Jonathan Potter has said it should be okay. ||
- `-====================================================================================*/
- struct Window *getListerWindow(Compare_Data *data)
- {
- struct path_node pn;
-
- // Setup some semi-sensible defaults (and hope they'll do!)
- pn.buffer[0] = '\0';
- pn.path = pn.buffer;
- pn.flags = NULL;
-
- pn.lister = (data->listerhandle);
-
- return((struct Window *)\
- data->func_callback(EXTCMD_GET_WINDOW,IPCDATA(data->ipc),(APTR)&pn));
- }
-
- /*= getDOpusScreen() =================================================================-.
- || Attempts to get the Opus screen, returns it or NULL. ||
- || The return should not be stored for later use as the screen may be different next ||
- || time if the user has changed it. ||
- `-====================================================================================*/
- struct Screen *getDOpusScreen(Compare_Data *data)
- {
- struct Screen *screen = NULL;
- struct DOpusScreenData *dsd;
-
- if (dsd = (struct DOpusScreenData *)\
- data->func_callback(EXTCMD_GET_SCREENDATA,IPCDATA(data->ipc),NULL))
- {
- screen = dsd->screen;
-
- data->func_callback(EXTCMD_FREE_SCREENDATA,IPCDATA(data->ipc),(APTR)dsd);
- }
- return(screen);
- }
-
- /*= report_full_display() ============================================================-.
- || Based on the linked list of DiffRanges produced by compare_files(), this routine ||
- || builds a side-by-side Hex/ASCII dump of the differences between the two files. ||
- ||------------------------------------------------------------------------------------||
- || Nasty long routine alert! ||
- ||------------------------------------------------------------------------------------||
- || Unlike the comparison routine, there is no fixed buffer size for this routine: it ||
- || will attempt to allocate enough memory to hold two versions of each DiffRange. ||
- || Only one DiffRange is dealt with at a time, but it's always possible that every ||
- || byte is different and the files are large. This is hardly a concern as: ||
- || a) If there are millions of differences it's unlikely that the user will want to ||
- || see them all. ("Duh, It's no case of just a different version string, Bob, ||
- || these files really are different and the filesize is coincidence!"). ||
- || b) The report file goes to T: and the DOpus text viewer loads it all at once, and ||
- || the report file is several times larger than any one DiffRange in the set which ||
- || produces it, so if there isn't enough memory for a DiffRange there isn't going ||
- || to be enough to display the final report anyway. (And I'm not about to rewrite ||
- || the Opus text viewer or faff about with offering destinations other than T:, ||
- || given than point (a) is pretty stand-alone anyway.) ||
- || If you're a complete psycho, feel free to think this is a bad way of doing it, be ||
- || my guest and recode it. Then see if it makes the blindest bit of difference in ||
- || The Real World(tm). ||
- || (Anyone would think I was feeling inadequate, or something...) Ahh-hu-hu-huh-hem...||
- `-====================================================================================*/
- void report_full_display(Compare_Data *data)
- {
- LONG errkeep;
- struct DiffRange *drnew; // New/current DiffRange structure.
- ResNode *dislinern;
- ResNode *tempfilern;
- ResNode *hexbuf1rn;
- ResNode *hexbuf2rn;
- ResNode *ascbuf1rn;
- ResNode *ascbuf2rn;
- BPTR outfile; // -.
- char *disline; // |_ Copies of things from ResNodes
- char *f1block; // | for shorter/simpler lines of code.
- char *f2block; // -'
- LONG donebytes; // How far into the file we are. (start of current DiffRng)
- LONG dobytes; // How many bytes to do in the current DiffRange.
- APTR progwin;
- ULONG tsecs; // -._ Used to get a unique filename
- ULONG tmics; // -' for the tempfile in T:.
- ResNode *combufrn;
- struct command_packet cp;
-
- // Open progress window.
- if (progwin = OpenProgressWindowTags(\
- PW_Window, getListerWindow(data),
- PW_Title, dgs(MSG_PROGTITLE),
- PW_FileName, dgs(MSG_GENERATING),
- PW_FileCount, data->minsize,
- PW_Flags, PWF_GRAPH|PWF_ABORT|PWF_FILENAME,
- TAG_END))
- {
- if ( (dislinern = allocNewResNode(&data->rnd,DISPLAYLINEBUFFSIZE)) && \
- (hexbuf1rn = allocNewResNode(&data->rnd,HEXBUFFSIZE)) && \
- (hexbuf2rn = allocNewResNode(&data->rnd,HEXBUFFSIZE)) && \
- (ascbuf1rn = allocNewResNode(&data->rnd,ASCBUFFSIZE)) && \
- (ascbuf2rn = allocNewResNode(&data->rnd,ASCBUFFSIZE)) && \
- (tempfilern = allocNewResNode(&data->rnd,PATHBUFFSIZE)) )
- {
- disline = (dislinern->rn_Mem);
- donebytes = 0;
-
- SetProgressWindowTags(progwin,
- PW_FileNum,donebytes,
- TAG_END);
-
- // Generate a unique filename. Virtually impossible to get two reports
- // from the same lister within one second of each other. Using the micros
- // value in the filename runs the risk of generating a name over 30 chars.
- CurrentTime(&tsecs,&tmics);
- sprintf(tempfilern->rn_Mem,"t:cmp_%lu_%lu.tmp",data->listerhandle,tsecs);
- (tempfilern->rn_Name) = (tempfilern->rn_Mem);
-
- // This is the line identifying which file is on which side of the output.
- sprintf(disline,dgs(MSG_LEFTRIGHT),data->file1path,data->file2path);
-
- // Attempt to open the temp file for writting to.
- outfile = openFileResNode(&data->rnd,tempfilern,MODE_NEWFILE);
-
- // Flag that the file should be deleted automatically.
- // (This flag will be removed if the file gets to the Opus viewer
- // successfully as it will be left to Opus to delete it after we
- // have exited.) -- Safe to set flag even if file didn't open.
- (tempfilern->rn_TempFile) = TRUE;
-
- // Now check the open and write the header line.
- if (!( (outfile) && (0 < Write(outfile,disline,strlen(disline))) ))
- {
- // If we couldn't open or write to the file, error message & abort.
- informUser(data,dgs(MSG_ERRWRITE),TRUE,NULL);
- outfile = NULL; // Signal to abort.
- }
- else
- {
- // Now output the differences dump for each DiffRange in turn.
- // If outfile goes NULL it will abort the loop.
-
- for(drnew = (data->drbase); (outfile) && (drnew); drnew = drnew->next)
- {
- // Include DR_EXTENT bytes either side of the interval,
- // but make sure we don't try to read outside of the file.
- // (The DiffRange may already point outside the file, but we'll
- // also fix that here anyway.)
-
- if (0 > ((drnew->start) -= DR_EXTEND))
- (drnew->start) = 0;
- if ((data->minsize) <= ((drnew->end)+=DR_EXTEND))
- (drnew->end) = ((data->minsize)-1);
-
- // Set number of bytes done (also start offset in file).
- donebytes = (drnew->start);
-
- // Calculate the interval (block) size and allocate two buffers
- // associated with each file.
-
- dobytes = ( ((drnew->end)-(drnew->start)) + 1 );
-
- if (!( (f1block = (char *)\
- allocMemResNode(&data->rnd,data->file1rn,dobytes)) && \
- (f2block = (char *)\
- allocMemResNode(&data->rnd,data->file2rn,dobytes)) ))
- {
- // Free the file1 memory if it got allocated.
- freeMemResNode(&data->rnd,data->file1rn);
-
- // Close & delete output file to free as much mem as we can.
- deleteResNode(&data->rnd,tempfilern);
- tempfilern = NULL; // Make safe the delete attempt below.
- outfile = NULL; // Signal to abort.
- informUser(data,dgs(MSG_OUTOFMEM),TRUE,NULL);
- }
- else
- {
- Seek(data->file1rn->rn_FHandle,drnew->start,OFFSET_BEGINNING);
- errkeep = IoErr();
- Seek(data->file2rn->rn_FHandle,drnew->start,OFFSET_BEGINNING);
- errkeep = (errkeep | IoErr());
-
- if (errkeep)
- {
- // Free the two buffers to get as much mem as we can.
- freeMemResNode(&data->rnd,data->file1rn);
- freeMemResNode(&data->rnd,data->file2rn);
-
- // Close & delete temp file to free as much mem as we can.
- deleteResNode(&data->rnd,tempfilern);
- tempfilern = NULL; // Make safe the delete attempt below.
- outfile = NULL; // Signal to abort.
- informUser(data,dgs(MSG_READFAIL),TRUE,NULL);
- }
- else
- {
- // Attempt to read the files into the buffers.
- if ((0 >= Read(data->file1rn->rn_FHandle,f1block,dobytes))\
- || (0 >= Read(data->file2rn->rn_FHandle,f2block,dobytes)))
- {
- // Free the two buffers to get as much mem as we can.
- freeMemResNode(&data->rnd,data->file1rn);
- freeMemResNode(&data->rnd,data->file2rn);
-
- // Close & delete temp file to free as much mem as can.
- deleteResNode(&data->rnd,tempfilern);
- tempfilern = NULL; // Make safe the delete below.
- outfile = NULL; // Signal to abort.
- informUser(data,dgs(MSG_READFAIL),TRUE,NULL);
- }
- else
- {
- if (!(outrepline(data,outfile,f1block,f2block,dobytes,
- donebytes,disline,
- hexbuf1rn->rn_Mem,hexbuf2rn->rn_Mem,
- ascbuf1rn->rn_Mem,ascbuf2rn->rn_Mem)))
- {
- // Free the two buffers to get as much mem as can.
- freeMemResNode(&data->rnd,data->file1rn);
- freeMemResNode(&data->rnd,data->file2rn);
-
- // Close & delete temp file to free as much mem.
- deleteResNode(&data->rnd,tempfilern);
- tempfilern = NULL; // Make safe the delete below.
- outfile = NULL; // Signal to abort.
- informUser(data,dgs(MSG_ERRWRITE),TRUE,NULL);
- }
- else
- {
- SetProgressWindowTags(progwin,
- PW_FileNum,donebytes,
- TAG_END);
- }
- }
- }
- // Free the two buffers ready for the next interval (block).
- // Safe to call if mem already freed.
- freeMemResNode(&data->rnd,data->file1rn);
- freeMemResNode(&data->rnd,data->file2rn);
- }
- }
-
- // If the file is still open there wasn't an error.
- if (outfile)
- {
- // Make sure the progress window gets to 100%
- SetProgressWindowTags(progwin,
- PW_FileNum,data->minsize,
- TAG_END);
-
- // Stop the file being deleted automatically when the ResNode is.
- // We're going to run "dopus read delete <filename>"
- // asynchronously and let Opus delete the file when it's finished.
- (tempfilern->rn_TempFile) = FALSE;
-
- // We cannot delete the tempfilern yet as it still contains the
- // filename. We have to close the file, though.
-
- closeFileResNode(&data->rnd,tempfilern);
- outfile = NULL;
-
- // Show it to the user.
- if (combufrn = allocNewResNode(&data->rnd,COMMBUFFSIZE))
- {
- sprintf(combufrn->rn_Mem,"dopus read delete %s",
- tempfilern->rn_Name);
- sendExtCmd_nr(data,combufrn->rn_Mem,&cp);
-
- deleteResNode(&data->rnd,combufrn);
- combufrn = NULL;
- }
- }
- }
- // These ResNode pointers must be valid or NULL -- if they get
- // deleted in some error handling code above the code must also NULL
- // the relevante ResNode pointer as well.
- deleteResNode(&data->rnd,tempfilern);
- deleteResNode(&data->rnd,dislinern);
- deleteResNode(&data->rnd,hexbuf1rn);
- deleteResNode(&data->rnd,hexbuf2rn);
- deleteResNode(&data->rnd,ascbuf1rn);
- deleteResNode(&data->rnd,ascbuf2rn);
- }
- CloseProgressWindow(progwin);
- }
- }
-
- /*= outrepline() =====================================================================-.
- || Writes the actual side-by-side hex dump to the output file of the full display. ||
- || Returns boolean success, Doesn't attempt to clean anything up on failure, that's ||
- || left to the calling routine to keep things simpler. ||
- || Doesn't send error messages, caller should free buffers and report the error. ||
- `-====================================================================================*/
- BOOL outrepline(Compare_Data *data,BPTR outfile,char *f1block,char *f2block,\
- LONG dobytes, LONG donebytes,char *disline,char *hex1,char *hex2,
- char *asc1,char *asc2)
- {
- BOOL orl_return = TRUE;
- char *disp;
- char *h1;
- char *h2;
- char *a1;
- char *a2;
- BOOL indif; // When true, the inverse-ANSI code is 'active'.
- int i,j;
-
- while( (orl_return) && (dobytes > 0) )
- {
-
- /*- Generate the various components in hex1,hex2,asc1,asc2... ------------------------*/
-
- h1 = hex1; // -.
- h2 = hex2; // |_ We use these new pointers while building the string.
- a1 = asc1; // | Need to keep the old pointers to the begginings.
- a2 = asc2; // -'
-
- indif = FALSE;
-
- for (j = 0; j < 2; j++)
- {
- for (i = 0; i < 4; i++)
- {
- if (dobytes > 0)
- {
- if ( (*f1block) == (*f2block) )
- {
- if (indif)
- {
- h1 = stpcpy(h1,"");
- h2 = stpcpy(h2,"");
- a1 = stpcpy(a1,"");
- a2 = stpcpy(a2,"");
- indif = FALSE;
- }
- sprintf(h1,"%02x",(int)(*f1block));
- h2 = stpcpy(h2,h1);
- h1 += 2;
- (*(a1++)) = (*(a2++)) = printchar(*f1block);
- }
- else
- {
- if (!(indif))
- {
- h1 = stpcpy(h1,"");
- h2 = stpcpy(h2,"");
- a1 = stpcpy(a1,"");
- a2 = stpcpy(a2,"");
- indif = TRUE;
- }
- sprintf(h1,"%02x",(int)(*f1block));
- h1 += 2;
- sprintf(h2,"%02x",(int)(*f2block));
- h2 += 2;
- (*(a1++)) = printchar(*f1block);
- (*(a2++)) = printchar(*f2block);
- }
-
- f1block++;
- f2block++;
- dobytes--;
- }
- else
- {
- if (indif)
- {
- h1 = stpcpy(h1,"");
- h2 = stpcpy(h2,"");
- a1 = stpcpy(a1,"");
- a2 = stpcpy(a2,"");
- indif = FALSE;
- }
- (*(h1++)) = (*(h2++)) = (*(a1++)) = (*(a2++)) = ' ';
- (*(h1++)) = (*(h2++)) = ' ';
- }
- }
-
- // Make sure the ANSI codes are reset at the end of the strings.
- if (indif)
- {
- h1 = stpcpy(h1,"");
- h2 = stpcpy(h2,"");
- a1 = stpcpy(a1,"");
- a2 = stpcpy(a2,"");
- indif = FALSE;
- }
-
- // Always add a space after the hex strings.
- // If we've just done the second four (out of eight), NULL terminate all
- // strings.
-
- (*(h1++)) = (*(h2++)) = ' ';
- if (i > 0)
- {
- (*h1) = (*h2) = (*a1) = (*a2) = '\0';
- }
- }
-
- /*- Join the various pieces together into one line... --------------------------------*/
-
- // Offset.
- sprintf(disline,"%08lx | ",donebytes);
- disp = (disline + 11); // Point to just after "89ABCDEF: "
-
- donebytes += 8;
-
- disp = stpcpy(disp,hex1); // Left hex dump.
- disp = stpcpy(disp,asc1); // Left ASCII dump.
- disp = stpcpy(disp," | "); // Divider.
- disp = stpcpy(disp,hex2); // Right hex dump.
- disp = stpcpy(disp,asc2); // Right ASCII dump.
- stpcpy(disp,"\n"); // End of line.
-
- // Write to the output.
- if (0 >= Write(outfile,disline,strlen(disline)))
- {
- orl_return = FALSE; // Flag failure (Abort).
- }
- }
-
- if (orl_return)
- {
- if (0 >= Write(outfile,"\n",strlen("\n")))
- {
- orl_return = FALSE; // Flag failure (Abort).
- }
- }
-
- return(orl_return);
- }
-
- /*= sendExtCmd_nr() ==================================================================-.
- || Function to make calling Opus ARexx commands slightly cleaner. This version for ||
- || when you do not want a result string. ||
- `-====================================================================================*/
- void sendExtCmd_nr(Compare_Data *data,char *cmdstring,struct command_packet *cpp)
- {
- // We do NOT want a result, but dopus5.library seems prone to
- // memory leaks when one isn't requested for certain commands,
- // so we'll ask for one and free it immediately afterwards.
- cpp->flags = COMMANDF_RESULT;
- cpp->command = cmdstring;
- data->func_callback(EXTCMD_SEND_COMMAND,IPCDATA(data->ipc),cpp);
- FreeVec(cpp->result);
- cpp->result = NULL;
- }
-
- /*= copyword() =======================================================================-.
- || Copy the string from source to dest until the first space or NULL in source. ||
- `-====================================================================================*/
- void copyword(char *dest,char *source)
- {
- while((*source) && (*source != ' '))
- *(dest++) = *(source++);
-
- *dest = '\0';
- }
-
- /*= copywordquoted() =================================================================-.
- || Copy the string from source to dest until the first quote or NULL in source. ||
- || If the source string starts with a quote it will be skipped. ||
- `-====================================================================================*/
- void copywordquoted(char *dest,char *source)
- {
- if (*source == '"')
- source++;
-
- while((*source) && (*source != '"'))
- *(dest++) = *(source++);
-
- *dest = '\0';
- }
-
- /*= parseArgs() ======================================================================-.
- || Parses command-line arguments and sets the "BUFFSIZE" number. ||
- || If there is no command-line, or some kind of error/problem occurs during parsing, ||
- || defaults will be used. ||
- `-====================================================================================*/
- FuncArgs *parseArgs(Compare_Data *data,char *args)
- {
- FuncArgs *fa;
-
- // Defaults.
- (data->buffsize) = DEFCOMPAREBUFFSIZE;
- (data->nosizewarn) = FALSE;
-
- if (fa = ParseArgs(CMD_TEMPLATE,args))
- {
- (data->nosizewarn) = (BOOL)((fa->FA_Arguments)[ARG_NOSIZEWARN]);
-
- if ( ((fa->FA_Arguments)[ARG_BUFFSIZE]) &&
- (0 < (*(LONG *)((fa->FA_Arguments)[ARG_BUFFSIZE]))) )
- (data->buffsize) = *(LONG *)((fa->FA_Arguments)[ARG_BUFFSIZE]);
- }
-
- return(fa);
- }
-
- /*= freeArgs() =======================================================================-.
- || Frees the structure returned by parseArgs(), if one returned at all. ||
- || All pointers into the structure will be invalid after this call. ||
- `-====================================================================================*/
- void freeArgs(FuncArgs *fa)
- {
- if (fa)
- DisposeArgs(fa);
- }
-