home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AmigActive 14
/
AACD14.ISO
/
CDTools
/
VersionCopy
/
VersionCopy.c
< prev
next >
Wrap
C/C++ Source or Header
|
2000-08-03
|
26KB
|
625 lines
// ___ ___
// _/ /_______\ \_ ___ ___ __ _ _ __ ___ ___
//__// / _______ \ \\___/ \___
//_/ | ' \__ __/ ` | \_/ © Copyright 1999-2000, Christopher Page \__
// \ | | | |__ | | / \ Released as Free Software under the GNU GPL /
// >| . | _/ . |< >--- --- -- - - -- --- ---<
// / \ \ | | / / \ / This file is part of the VersionCopy source \
// \ \ \_/ \_/ / / \ and it is released under the GNU GPL. Please /
// \ \ / / \ read the "COPYING" file which should have /
// //\ \_________/ /\\ //\ been included in the distribution arc. /
//- --\ _______ /-- - --\ for full details of the license /-----
//-----\_/ \_/---------\ ___________________________________ /------
// \_/ \_/
//
// Description:
//
// VersionCopy version aware copy command
//
// Functions:
//
// BOOL IsDirectory (STRPTR TestName)
// STRPTR FindSubBuffer (UBYTE *Source, UBYTE * Pattern, LONG SourceLen, LONG PatternLen)
// VersionData *FindVersion(BPTR Source, struct FileInfoBlock *SourceData)
// void FreeVersion (struct VersionData *OldData)
// LONG FileCopy (STRPTR SourceName, STRPTR TargetName, ULONG BufferSize)
// void CopyAttributes (struct FileInfoBlock *SourceInfo, STRPTR TargetName)
// LONG VersionCopy (struct FileInfoBlock *SourceFIB, struct FileInfoBlock *TargetFIB, STRPTR SourceName, STRPTR TargetName, ULONG BufferSize)
//
// Detail:
//
// VersionCopy is a cut-down "Copy" command which is aware of the version information
// within the file being copied. Before copying the source file it checks whether the
// destination already exists and, if it does, the version numbers of the source and
// destination are compared. The source is only copied over the destination if the
// source version or revision is greater than that of the destination. Unlike Copy,
// wildcards and directory copy are not supported - indeed these facilities would be
// somewhat meaningless for the applications this command is intended.
//
// Fold Markers:
//
// Start: /*GFS*/
// End: /*GFE*/
#include<exec/exec.h>
#include<dos/dos.h>
#include<clib/exec_protos.h>
#include<clib/dos_protos.h>
#include<ctype.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"VersionCopy_rev.h"
const char Copyright[] = "© copyright 2000 Chris Page";
const char Version[] = VERSTAG;
// Typing saver! :)
#define ShowMessage(TextData) if(!((BOOL)ArgResult[OPT_QUIET])) printf("%s\n", TextData)
// Stuff for ReadArgs()
#define ARGS_TEMPLATE "FROM/A,TO/A,QUIET/S,BUF=BUFFER/K/N,CLONE/S,DATE/S,COM/S,NOPRO/S,IGNORENAME/S"
#define OPT_FROM 0
#define OPT_TO 1
#define OPT_QUIET 2
#define OPT_BUFFER 3
#define OPT_CLONE 4
#define OPT_DATE 5
#define OPT_COM 6
#define OPT_NOPRO 7
#define OPT_IGNORE 8
#define OPT_COUNT 9
// Note this does not consider dates!
struct VersionData
{
STRPTR Name ; // Name given after $VER:, NULL if not found
LONG Version ; // Version number
LONG Revision; // Revision, 0 if no revision given.
};
// Prototype type things..
BOOL IsDirectory (STRPTR TestName);
STRPTR FindSubBuffer (UBYTE *Source, UBYTE * Pattern, LONG SourceLen, LONG PatternLen);
struct VersionData *FindVersion(BPTR Source, struct FileInfoBlock *SourceData);
void FreeVersion (struct VersionData *OldData);
LONG FileCopy (STRPTR SourceName, STRPTR TargetName, ULONG BufferSize);
void CopyAttributes (struct FileInfoBlock *SourceInfo, STRPTR TargetName);
LONG VersionCopy (struct FileInfoBlock *SourceFIB, struct FileInfoBlock *TargetFIB, STRPTR SourceName, STRPTR TargetName, ULONG BufferSize);
// Globals...
char WorkBuffer[80] ; // Yeah, this is nasty but it saves some messing about later
ULONG ArgResult [OPT_COUNT]; // ReadArgs store.
/* BOOL IsDirectory(STRPTR) */
/* -=-=-=-=-=-=-=-=-=-=-=-= */
/* If the specified filename is actually a directory this returns TRUE. */
/* */
/* Parameters: */
/* TestName Name of the object to identify. */
/*GFS*/ BOOL IsDirectory(STRPTR TestName)
{
struct FileInfoBlock *TestFIB ;
BPTR TestLock;
BOOL IsDir = TRUE;
if(TestFIB = AllocDosObject(DOS_FIB, NULL)) {
if(TestLock = Lock(TestName, SHARED_LOCK)) {
Examine(TestLock, TestFIB);
IsDir = (TestFIB -> fib_DirEntryType >= 0);
UnLock(TestLock);
} else {
IsDir = FALSE;
}
FreeDosObject(DOS_FIB, TestFIB);
}
return(IsDir);
}/*GFE*/
/* STRPTR FindSubBuffer(UBYTE *, UBYTE *, LONG, LONG) */
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
/* This is basically a version of strstr which will work for non-string data */
/* (ie: buffers containing arbitrary data, not just NULL-terminated strings) */
/* */
/* Parameters: */
/* Source Buffer to scan */
/* Pattern Pattern to find in Source */
/* SourceLen Length of the source buffer in bytes */
/* PatternLen Length of the pattern in bytes */
/*GFS*/ STRPTR FindSubBuffer(UBYTE *Source, UBYTE * Pattern, LONG SourceLen, LONG PatternLen)
{
LONG SourcePos = 0;
while(SourcePos <= SourceLen) {
if(!memcmp(&Source[SourcePos], Pattern, PatternLen)) {
return(&Source[SourcePos]);
} else {
SourcePos ++;
}
}
return(NULL);
}/*GFE*/
/* VersionData *FindVersion(BPTR, FileInfoBlock *) */
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
/* This routine loads the file specified by the provided file handle into a */
/* memory buffer and searches it for the $VER: string that identifies the */
/* start of an Amiga version string. If it finds this the name, version and */
/* revision are copied into a VersionData structure and returned. Use FreeVec*/
/* to release the name buffer and VersionData structure returned by this */
/* If this returns NULL then IoErr() will contain a code indicating why */
/* */
/* Parameters: */
/* Source File handle for the file to load */
/* SourceData FIB filled in by calling ExamineFH() on the source file */
/*GFS*/ struct VersionData *FindVersion(BPTR Source, struct FileInfoBlock *SourceData)
{
struct VersionData *NewData;
STRPTR Buffer ;
STRPTR Search ;
STRPTR Start ;
BOOL Spaced = FALSE;
BOOL Complete = FALSE;
LONG ErrorCode = 0;
// This is a hack to get around vbcc not putting the Version string near
// the start of the file. If FindBuffer[] = "$VER:" then Version VersionCopy
// gets confused between the $VER: for FindBuffer and the one for the version
char FindBuffer[] = " VER:";
FindBuffer[0] = '$';
if(Buffer = AllocVec(SourceData -> fib_Size, MEMF_ANY)) {
Read(Source, Buffer, SourceData -> fib_Size);
Search = FindSubBuffer(Buffer, FindBuffer, SourceData -> fib_Size - 6, 6);
if(Search) {
// Skip the version header
Search += 6;
// Skip any leading whitespace
while(*Search && (*Search == ' ')) Search++;
// Remember the start location
Start = Search;
// Look for the first digit after a space
do {
if(isdigit(*Search) && Spaced) {
Complete = TRUE;
} else {
Spaced = (*Search == ' ');
Search++;
}
} while(!Complete && *Search);
if(NewData = AllocVec(sizeof(struct VersionData), MEMF_ANY|MEMF_CLEAR)) {
// Allocate space for the name and copy it.
if(NewData -> Name = AllocVec(Search - Start, MEMF_ANY|MEMF_CLEAR)) {
CopyMem(Start, NewData -> Name, Search - Start - 1);
// got a version number?
if(Complete) {
NewData -> Version = atol(Search);
// Try to locate the revision
while(*Search && (*Search != '.')) Search++;
if(*Search == '.') {
Search++;
Start = Search;
// whack a NULL in at the cirst non-digit encountered
while(isdigit(*Search)) Search++;
*Search = '\0';
NewData -> Revision = atol(Start);
}
FreeVec(Buffer);
SetIoErr(0);
return(NewData);
}
} else {
ErrorCode = ERROR_NO_FREE_STORE;
ShowMessage("Unable to allocate version name buffer");
}
FreeVec(NewData);
} else {
ErrorCode = ERROR_NO_FREE_STORE;
ShowMessage("Unable to allocate version data");
}
} else {
ErrorCode = ERROR_OBJECT_WRONG_TYPE;
if(!((BOOL)ArgResult[OPT_QUIET])) printf("%s does not contain version information\n", SourceData -> fib_FileName);
}
FreeVec(Buffer);
} else {
ErrorCode = ERROR_NO_FREE_STORE;
ShowMessage("Unable to allocate search buffer");
}
SetIoErr(ErrorCode);
return(NULL);
}/*GFE*/
/* void FreeVersion(VersionData *) */
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
/* Small routine to free the specified VersionData structure - if the pointer*/
/* is not NULL then the Name field is freed (if required) and the the struct */
/* itself is released. */
/* */
/* Parameters: */
/* OldData The VersionData structure to free. */
/*GFS*/ void FreeVersion(struct VersionData *OldData)
{
if(OldData) {
if(OldData -> Name) FreeVec(OldData -> Name);
FreeVec(OldData);
}
}/*GFE*/
/* LONG FileCopy(STRPTR, STRPTR, ULONG) */
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
/* This does the actual duplication of the source file, copying BufferSize */
/* chunks of the file until the whole file is copied or the user does a */
/* CTRL-C to abort it the copy. Errors in reading and writing are detected. */
/* */
/* Parameters: */
/* SourceName Name of the file to copy */
/* TargetName Name of the destination file */
/* BufferSize Size of the copy buffer in bytes. */
/*GFS*/ LONG FileCopy(STRPTR SourceName, STRPTR TargetName, ULONG BufferSize)
{
BPTR SourceFile, DestFile ;
UBYTE *CopyBuffer ;
LONG CopyLength ;
LONG WriteLength ;
LONG ReturnCode = RETURN_WARN;
if(CopyBuffer = (UBYTE *)AllocVec(BufferSize, MEMF_ANY)) {
if(SourceFile = Open(SourceName, MODE_OLDFILE)) {
if(DestFile = Open(TargetName, MODE_NEWFILE)) {
// Read & write BufferSize chunks of the file, allowing user abort and
// filesystem full handling..
do {
WriteLength = 0;
if(CopyLength = Read(SourceFile, CopyBuffer, BufferSize)) {
// Allow user abort..
if(!(SetSignal(0, 0) & SIGBREAKF_CTRL_C)) {
WriteLength = Write(DestFile , CopyBuffer, CopyLength);
}
}
} while((CopyLength > 0) && (WriteLength == CopyLength));
// should catch the last error
if((CopyLength < 0) || (WriteLength != CopyLength)) ReturnCode = IoErr();
Close(DestFile);
// Failed to write the last read, erase destination..
if((WriteLength != CopyLength) && (CopyLength > 0)) {
DeleteFile(TargetName);
if(ReturnCode && !(BOOL)ArgResult[OPT_QUIET]) {
Fault(ReturnCode, NULL, WorkBuffer, 80);
printf("Unable to write %s: %s\n", TargetName, WorkBuffer);
}
ShowMessage("Removed incomplete file...\n");
}
} else {
ReturnCode = IoErr();
if(!((BOOL)ArgResult[OPT_QUIET])) {
Fault(ReturnCode, NULL, WorkBuffer, 80);
printf("Unable to open %s for writing: %s\n", TargetName, WorkBuffer);
}
}
Close(SourceFile);
// This one shouldn't really happen but anyway....
} else {
ReturnCode = IoErr();
if(!((BOOL)ArgResult[OPT_QUIET])) {
Fault(ReturnCode, NULL, WorkBuffer, 80);
printf("Unable to open %s for reading: %s\n", TargetName, WorkBuffer);
}
}
FreeVec(CopyBuffer);
} else {
ReturnCode = ERROR_NO_FREE_STORE;
ShowMessage("Unable to allocate the copy buffer");
}
return(ReturnCode);
}/*GFE*/
/* void CopyAttributes(FileInfoblock *, STRPTR) */
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
/* If the file has been copied then some attributes of the source may need */
/* to be copied to the target: if the clone has been activated then all the */
/* file attributes of the source need to be copied, otherwise the only attr. */
/* copied by default is the protection bits - this cane be disabled with a */
/* flag and other attributes can be activated with the DATE and COM args.. */
/* */
/* Parameters: */
/* SourceInfo FileInfoBlock containing the source attributes */
/* TargetName Name of the target file (must exist!!) */
/*GFS*/ void CopyAttributes(struct FileInfoBlock *SourceInfo, STRPTR TargetName)
{
if((BOOL)ArgResult[OPT_CLONE]) {
SetComment (TargetName, SourceInfo -> fib_Comment);
SetFileDate (TargetName, &SourceInfo -> fib_Date);
SetProtection(TargetName, SourceInfo -> fib_Protection);
} else {
if((BOOL)ArgResult[OPT_COM] ) SetComment (TargetName, SourceInfo -> fib_Comment);
if((BOOL)ArgResult[OPT_DATE]) SetFileDate (TargetName, &SourceInfo -> fib_Date);
if(!((BOOL)ArgResult[OPT_NOPRO])) SetProtection(TargetName, SourceInfo -> fib_Protection);
}
}/*GFE*/
/* LONG VersionCopy(FileInfoBlock *, FileInfoBlock *, STRPTR, STRPTR, ULONG) */
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
/* This checks the source and target versions and copies the source over the */
/* target if the target version is lower than the source or the target has */
/* no version information. */
/* */
/* Parameters: */
/* SourceFIB FileInfoBlock containing the source attributes */
/* TargetFIB FileInfoBlock containing the target attributes */
/* SourceName Filename of the source file */
/* TargetName Filename of the target */
/* BufferSize Size of the copy buffer in bytes */
/*GFS*/ LONG VersionCopy(struct FileInfoBlock *SourceFIB, struct FileInfoBlock *TargetFIB, STRPTR SourceName, STRPTR TargetName, ULONG BufferSize)
{
struct VersionData *SourceVers;
struct VersionData *TargetVers;
BPTR SourceFile;
BPTR TargetFile;
LONG ErrorCode = RETURN_WARN;
// Open the source and get it's version - these must both succeed..
if(SourceFile = Open(SourceName, MODE_OLDFILE)) {
if(SourceVers = FindVersion(SourceFile, SourceFIB)) {
// Open the target & get version info if possible...
if(TargetFile = Open(TargetName, MODE_OLDFILE)) {
if(TargetVers = FindVersion(TargetFile, TargetFIB)) {
// Don't need these any more...
Close(SourceFile);
Close(TargetFile);
SourceFile = 0L;
// If the names match, or ignore has been set, then pass through..
if((BOOL)ArgResult[OPT_IGNORE] || !strcmp(SourceVers -> Name, TargetVers -> Name)) {
// Check the version and revision
if((SourceVers -> Version > TargetVers -> Version) ||
((SourceVers -> Version == TargetVers -> Version) &&
(SourceVers -> Revision > TargetVers -> Revision))) {
// Source is newer - copy it.
ErrorCode = FileCopy(SourceName, TargetName, BufferSize);
} else {
// Neil says he wants an OK for this case...
ErrorCode = RETURN_OK;
ShowMessage("Target is up to date");
}
} else {
ErrorCode = ERROR_OBJECT_WRONG_TYPE;
ShowMessage("Version names in source and target do not match");
}
FreeVersion(TargetVers);
// No version info in the file.. just write over it.
} else {
// Don't need these now..
Close(TargetFile);
Close(SourceFile);
SourceFile = 0L;
// Target doesn't contain any version, straight copy
ErrorCode = FileCopy((STRPTR)ArgResult[OPT_FROM], TargetName, BufferSize);
// If the copy was successful then copy the file attributes
if(ErrorCode == RETURN_WARN) CopyAttributes(SourceFIB, TargetName);
}
} else {
// Hmm.. opening the target failed. We know it exists (or we wouldn't be here)
// but it won't open so something screwy is going on..
ErrorCode = IoErr();
if(!((BOOL)ArgResult[OPT_QUIET])) {
Fault(ErrorCode, NULL, WorkBuffer, 80);
printf("Unable to open target: %s\n", WorkBuffer);
}
}
FreeVersion(SourceVers);
} else {
ErrorCode = IoErr();
}
if(SourceFile) Close(SourceFile);
} else {
ErrorCode = IoErr();
if(!((BOOL)ArgResult[OPT_QUIET])) {
Fault(ErrorCode, NULL, WorkBuffer, 80);
printf("Unable to open source: %s\n", WorkBuffer);
}
}
return(ErrorCode);
}/*GFE*/
int main(int argc, char **argv)
{
struct RDArgs *ArgsData ;
struct FileInfoBlock *SourceFIB ;
struct FileInfoBlock *TargetFIB ;
BPTR SourceLock;
BPTR TargetLock;
STRPTR TargetName;
STRPTR TempString;
BOOL FreeTarget = FALSE;
ULONG TargetSize = 0L;
ULONG BufferSize = 0L;
LONG ErrorCode = 0L;
// Can't do anything if the user stuffs up the command line...
if(ArgsData = ReadArgs(ARGS_TEMPLATE, ArgResult, NULL)) {
// Work out how big the copy buffer should be
if(ArgResult[OPT_BUFFER]) {
BufferSize = (*(ULONG *)ArgResult[OPT_BUFFER]) * 512;
}
if(BufferSize == 0) BufferSize = 102400;
// Allocate the source fib, lock the source and examine it
if(SourceFIB = AllocDosObject(DOS_FIB, NULL)) {
if(SourceLock = Lock((STRPTR)ArgResult[OPT_FROM], SHARED_LOCK)) {
Examine(SourceLock, SourceFIB);
// Only continue if the source is a file (no directory copy in this version...)
if(SourceFIB -> fib_DirEntryType < 0) {
// Quick hack to make sure we have a valid destination name..
if(strlen((STRPTR)ArgResult[OPT_TO]) == 0) {
TargetName = FilePart((STRPTR)ArgResult[OPT_FROM]);
} else {
// User has specified a destination (ie: not "") but is it a dir?
TargetName = (STRPTR)ArgResult[OPT_TO];
// If the target is a directory we need to add the filename...
if(IsDirectory(TargetName)) {
TempString = FilePart((STRPTR)ArgResult[OPT_FROM]);
TargetSize = strlen(TargetName) + strlen(TempString) + 3;
// Allocate space for the new name
if(TargetName = AllocVec(TargetSize, MEMF_ANY)) {
strcpy(TargetName, (STRPTR)ArgResult[OPT_TO]);
AddPart(TargetName, TempString, TargetSize);
FreeTarget = TRUE;
} else {
ErrorCode = ERROR_NO_FREE_STORE;
ShowMessage("Unable to allocate target name buffer");
}
}
}
if(TargetName) {
// Alloc fib, lock and examine as usual...
if(TargetFIB = AllocDosObject(DOS_FIB, NULL)) {
if(TargetLock = Lock(TargetName, SHARED_LOCK)) {
Examine(TargetLock, TargetFIB);
// Don't need these no more...
UnLock(TargetLock);
UnLock(SourceLock);
SourceLock = 0L;
// Copy only if the version wills it..
ErrorCode = VersionCopy(SourceFIB, TargetFIB, (STRPTR)ArgResult[OPT_FROM], TargetName, BufferSize);
// If the copy was successful then copy the file attributes
if(ErrorCode == RETURN_WARN) CopyAttributes(SourceFIB, TargetName);
} else {
ErrorCode = IoErr();
// Can't lock the target - is it just that it doesn't exist....
if(ErrorCode == ERROR_OBJECT_NOT_FOUND) {
// Target doesn't exist, straight copy
ErrorCode = FileCopy((STRPTR)ArgResult[OPT_FROM], TargetName, BufferSize);
// If the copy was successful then copy the file attributes
if(ErrorCode == RETURN_WARN) CopyAttributes(SourceFIB, TargetName);
} else {
// .... or is it something worse?
if(!((BOOL)ArgResult[OPT_QUIET])) {
Fault(ErrorCode, NULL, WorkBuffer, 80);
printf("Target not locked: (%ld) %s\n", ErrorCode, WorkBuffer);
}
}
}
FreeDosObject(DOS_FIB, TargetFIB);
}
if(FreeTarget) FreeVec(TargetName);
}
} else {
ErrorCode = ERROR_OBJECT_WRONG_TYPE;
ShowMessage("The source is not a file");
}
if(SourceLock) UnLock(SourceLock);
} else {
ErrorCode = IoErr();
if(!((BOOL)ArgResult[OPT_QUIET])) {
Fault(ErrorCode, NULL, WorkBuffer, 80);
printf("Unable to open %s: %s\n", (STRPTR)ArgResult[OPT_FROM], WorkBuffer);
}
}
FreeDosObject(DOS_FIB, SourceFIB);
} else {
ErrorCode = ERROR_NO_FREE_STORE;
ShowMessage("Unable to allocate source FIB\n");
}
FreeArgs(ArgsData);
} else {
printf("%s: required argument missing\n", argv[0]);
ErrorCode = ERROR_REQUIRED_ARG_MISSING;
}
exit(ErrorCode);
}