mirror of
https://github.com/depp/syncfiles.git
synced 2025-02-16 17:30:25 +00:00
359 lines
8.2 KiB
C
359 lines
8.2 KiB
C
#include "defs.h"
|
|
|
|
#include <Files.h>
|
|
#include <Script.h>
|
|
|
|
#include <string.h>
|
|
|
|
enum {
|
|
// Maximum file size that we will copy.
|
|
kMaxFileSize = 64 * 1024,
|
|
};
|
|
|
|
// Read the entire data fork of a file. The result must be freed with
|
|
// DisposePtr.
|
|
static int read_file(FSSpec *spec, Ptr *data, long *length) {
|
|
CInfoPBRec ci;
|
|
Ptr ptr;
|
|
long dataLength, pos, count;
|
|
OSErr err;
|
|
short fref;
|
|
|
|
// Get file size.
|
|
memset(&ci, 0, sizeof(ci));
|
|
ci.hFileInfo.ioNamePtr = spec->name;
|
|
ci.hFileInfo.ioVRefNum = spec->vRefNum;
|
|
ci.hFileInfo.ioDirID = spec->parID;
|
|
err = PBGetCatInfoSync(&ci);
|
|
if (err != 0) {
|
|
print_errcode(err, "could not get file metadata");
|
|
return 1;
|
|
}
|
|
if ((ci.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0) {
|
|
print_err("is a directory");
|
|
return 1;
|
|
}
|
|
dataLength = ci.hFileInfo.ioFlLgLen;
|
|
if (dataLength > kMaxFileSize) {
|
|
print_err("file is too large: size=%ld, max=%ld", dataLength,
|
|
kMaxFileSize);
|
|
return 1;
|
|
}
|
|
if (dataLength == 0) {
|
|
*data = NULL;
|
|
*length = 0;
|
|
return 0;
|
|
}
|
|
|
|
// Allocate memory.
|
|
ptr = NewPtr(dataLength);
|
|
if (ptr == NULL) {
|
|
print_memerr(dataLength);
|
|
return 1;
|
|
}
|
|
|
|
// Read file.
|
|
err = FSpOpenDF(spec, fsRdPerm, &fref);
|
|
if (err != 0) {
|
|
DisposePtr(ptr);
|
|
print_errcode(err, "could not open file");
|
|
return 1;
|
|
}
|
|
pos = 0;
|
|
while (pos < dataLength) {
|
|
count = dataLength - pos;
|
|
err = FSRead(fref, &count, ptr + pos);
|
|
if (err != 0) {
|
|
DisposePtr(ptr);
|
|
FSClose(fref);
|
|
print_errcode(err, "could not read file");
|
|
return 1;
|
|
}
|
|
pos += count;
|
|
}
|
|
FSClose(fref);
|
|
*data = ptr;
|
|
*length = dataLength;
|
|
return 0;
|
|
}
|
|
|
|
// Make an FSSpec for a temporary file.
|
|
static int make_temp(FSSpec *temp, short vRefNum, long dirID,
|
|
const unsigned char *name) {
|
|
Str31 tname;
|
|
unsigned pfxlen, maxpfx = 31 - 4;
|
|
OSErr err;
|
|
|
|
pfxlen = name[0];
|
|
if (pfxlen > maxpfx) {
|
|
pfxlen = maxpfx;
|
|
}
|
|
memcpy(tname + 1, name + 1, pfxlen);
|
|
memcpy(tname + 1 + pfxlen, ".tmp", 4);
|
|
tname[0] = pfxlen + 4;
|
|
err = FSMakeFSSpec(vRefNum, dirID, tname, temp);
|
|
if (err != 0 && err != fnfErr) {
|
|
print_errcode(err, "could not create temp file spec");
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Write the entire contents of a file.
|
|
static int write_file(FSSpec *dest, short tempVol, long tempDir, Ptr data,
|
|
long length, long modTime, file_action action) {
|
|
OSType creator = 'MPS ', fileType = 'TEXT';
|
|
FSSpec temp;
|
|
long pos, amt;
|
|
short ref;
|
|
HParamBlockRec pb;
|
|
CMovePBRec cm;
|
|
CInfoPBRec ci;
|
|
Str31 name;
|
|
OSErr err;
|
|
int r;
|
|
bool mustMove, mustRename;
|
|
|
|
// Save the data to a temporary file.
|
|
r = make_temp(&temp, tempVol, tempDir, dest->name);
|
|
if (r != 0) {
|
|
return 1;
|
|
}
|
|
err = FSpCreate(&temp, creator, fileType, smSystemScript);
|
|
if (err == dupFNErr) {
|
|
err = FSpDelete(&temp);
|
|
if (err != 0) {
|
|
print_errcode(err, "could not delete existing temp file");
|
|
return 1;
|
|
}
|
|
err = FSpCreate(&temp, creator, fileType, smSystemScript);
|
|
}
|
|
if (err != 0) {
|
|
print_errcode(err, "could not create file");
|
|
return 1;
|
|
}
|
|
err = FSpOpenDF(&temp, fsRdWrPerm, &ref);
|
|
if (err != 0) {
|
|
print_errcode(err, "could not open temp file");
|
|
goto error;
|
|
}
|
|
pos = 0;
|
|
while (pos < length) {
|
|
amt = length - pos;
|
|
err = FSWrite(ref, &amt, data + pos);
|
|
if (err != 0) {
|
|
FSClose(ref);
|
|
print_errcode(err, "could not write temp file");
|
|
goto error;
|
|
}
|
|
pos += amt;
|
|
}
|
|
err = FSClose(ref);
|
|
if (err != 0) {
|
|
print_errcode(err, "could not close temp file");
|
|
goto error;
|
|
}
|
|
|
|
// Update the modification time.
|
|
memset(&ci, 0, sizeof(ci));
|
|
memcpy(name, temp.name, temp.name[0] + 1);
|
|
ci.hFileInfo.ioNamePtr = name;
|
|
ci.hFileInfo.ioVRefNum = temp.vRefNum;
|
|
ci.hFileInfo.ioDirID = temp.parID;
|
|
err = PBGetCatInfoSync(&ci);
|
|
if (err != 0) {
|
|
print_errcode(err, "could not get temp file info");
|
|
goto error;
|
|
}
|
|
memcpy(name, temp.name, temp.name[0] + 1);
|
|
ci.hFileInfo.ioNamePtr = name;
|
|
ci.hFileInfo.ioVRefNum = temp.vRefNum;
|
|
ci.hFileInfo.ioDirID = temp.parID;
|
|
ci.hFileInfo.ioFlMdDat = modTime;
|
|
err = PBSetCatInfoSync(&ci);
|
|
if (err != 0) {
|
|
print_errcode(err, "could not set temp file info");
|
|
goto error;
|
|
}
|
|
|
|
// First, try to exchange files if destination exists.
|
|
if (action == kActionReplace) {
|
|
err = FSpExchangeFiles(&temp, dest);
|
|
if (gLogLevel >= kLogVerbose) {
|
|
log_call(err, "FSpExchangeFiles");
|
|
}
|
|
if (err == 0) {
|
|
err = FSpDelete(&temp);
|
|
if (err != 0) {
|
|
print_errcode(err, "could not remove temporary file");
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
// paramErr: function not supported by volume.
|
|
if (err != paramErr) {
|
|
print_errcode(err, "could not exchange files");
|
|
goto error;
|
|
}
|
|
// Otherwise, delete destination and move temp file over.
|
|
err = FSpDelete(dest);
|
|
if (err != 0) {
|
|
print_errcode(err, "could not remove destination file");
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
mustMove = dest->parID != temp.parID;
|
|
mustRename = memcmp(dest->name, temp.name, dest->name[0] + 1) != 0;
|
|
|
|
// Next, try MoveRename.
|
|
if (mustMove && mustRename) {
|
|
memset(&pb, 0, sizeof(pb));
|
|
pb.copyParam.ioNamePtr = temp.name;
|
|
pb.copyParam.ioVRefNum = temp.vRefNum;
|
|
pb.copyParam.ioNewName = dest->name;
|
|
pb.copyParam.ioNewDirID = dest->parID;
|
|
pb.copyParam.ioDirID = temp.parID;
|
|
err = PBHMoveRenameSync(&pb);
|
|
if (gLogLevel >= kLogVerbose) {
|
|
log_call(err, "PBHMoveRename");
|
|
}
|
|
if (err == 0) {
|
|
return 0;
|
|
}
|
|
// paramErr: function not supported by volume.
|
|
if (err != paramErr) {
|
|
print_errcode(err, "could not rename temporary file");
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
// Finally, try move and then rename.
|
|
if (mustMove) {
|
|
memset(&cm, 0, sizeof(cm));
|
|
cm.ioNamePtr = temp.name;
|
|
cm.ioVRefNum = temp.vRefNum;
|
|
cm.ioNewDirID = dest->parID;
|
|
cm.ioDirID = temp.parID;
|
|
err = PBCatMoveSync(&cm);
|
|
if (gLogLevel >= kLogVerbose) {
|
|
log_call(err, "PBCatMove");
|
|
}
|
|
if (err != 0) {
|
|
print_errcode(err, "could not move temporary file");
|
|
goto error;
|
|
}
|
|
temp.parID = dest->parID;
|
|
}
|
|
if (mustRename) {
|
|
err = FSpRename(&temp, dest->name);
|
|
if (gLogLevel >= kLogVerbose) {
|
|
log_call(err, "FSpRename");
|
|
}
|
|
if (err != 0) {
|
|
print_errcode(err, "could not rename temporary file");
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
err = FSpDelete(&temp);
|
|
if (err != 0) {
|
|
print_errcode(err, "could not delete temp file");
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int sync_file(struct file_info *file, convert_func func, short srcVol,
|
|
long srcDir, short destVol, long destDir, short tempVol,
|
|
long tempDir) {
|
|
FSSpec src, dest;
|
|
Ptr srcData = NULL, destData = NULL;
|
|
long srcLength, destLength;
|
|
int r, result = 1;
|
|
OSErr err;
|
|
unsigned char *outptr, *outend;
|
|
const unsigned char *inptr, *inend;
|
|
|
|
// Handle actions which don't involve conversion.
|
|
if (file->action == kActionNone) {
|
|
return 0;
|
|
}
|
|
if (file->action == kActionDelete) {
|
|
err = FSMakeFSSpec(destVol, destDir, file->name, &dest);
|
|
if (err != 0) {
|
|
print_errcode(err, "could not create destination spec");
|
|
return 1;
|
|
}
|
|
err = FSpDelete(&dest);
|
|
if (err != 0) {
|
|
print_errcode(err, "could not delete destination file");
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Create file specs.
|
|
err = FSMakeFSSpec(srcVol, srcDir, file->name, &src);
|
|
if (err != 0) {
|
|
print_errcode(err, "could not create source spec");
|
|
return 1;
|
|
}
|
|
err = FSMakeFSSpec(destVol, destDir, file->name, &dest);
|
|
if (err != 0 && err != fnfErr) {
|
|
print_errcode(err, "could not create destination spec");
|
|
return 1;
|
|
}
|
|
|
|
// Read the source file into memory.
|
|
r = read_file(&src, &srcData, &srcLength);
|
|
if (r != 0) {
|
|
return 1;
|
|
}
|
|
|
|
// Convert data.
|
|
if (srcLength > 0) {
|
|
destLength = srcLength + (srcLength >> 2) + 16;
|
|
destData = NewPtr(destLength);
|
|
if (destData == NULL) {
|
|
print_memerr(destLength);
|
|
goto done;
|
|
}
|
|
outptr = (unsigned char *)destData;
|
|
outend = outptr + destLength;
|
|
inptr = (unsigned char *)srcData;
|
|
inend = inptr + srcLength;
|
|
func(&outptr, outend, &inptr, inend);
|
|
if (inptr != inend) {
|
|
print_err("conversion function failed");
|
|
goto done;
|
|
}
|
|
destLength = outptr - (unsigned char *)destData;
|
|
} else {
|
|
destLength = 0;
|
|
destData = NULL;
|
|
}
|
|
|
|
// Write destination file.
|
|
r = write_file(&dest, tempVol, tempDir, destData, destLength,
|
|
file->meta[kSrcDir].modTime, file->action);
|
|
if (r != 0) {
|
|
goto done;
|
|
}
|
|
|
|
// Success.
|
|
result = 0;
|
|
|
|
done:
|
|
// Clean up.
|
|
if (srcData != NULL) {
|
|
DisposePtr(srcData);
|
|
}
|
|
if (destData != NULL) {
|
|
DisposePtr(destData);
|
|
}
|
|
return result;
|
|
}
|