Copy files to destination directory
GitOrigin-RevId: 34f4a68c4aa26def783f1192a26768b4393ed087
This commit is contained in:
parent
1264148f6d
commit
b1a85945d7
16
Makefile
16
Makefile
|
@ -8,14 +8,17 @@ COptions-68K = {COptions} {Sym-68K}
|
|||
### Source Files ###
|
||||
|
||||
SrcFiles = ∂
|
||||
mac_to_unix.c ∂
|
||||
sync.c
|
||||
|
||||
### Object Files ###
|
||||
|
||||
ObjFiles-PPC = ∂
|
||||
mac_to_unix.c.x ∂
|
||||
sync.c.x
|
||||
|
||||
ObjFiles-68K = ∂
|
||||
mac_to_unix.c.o ∂
|
||||
sync.c.o
|
||||
|
||||
### Libraries ###
|
||||
|
@ -69,8 +72,19 @@ SyncFiles ƒƒ {ObjFiles-68K} {LibFiles-68K}
|
|||
|
||||
Dependencies ƒ $OutOfDate
|
||||
MakeDepend ∂
|
||||
-append {MAKEFILE} ∂
|
||||
-append Makefile ∂
|
||||
-ignore "{CIncludes}" ∂
|
||||
-objext .x ∂
|
||||
-objext .o ∂
|
||||
{SrcFiles}
|
||||
|
||||
#*** Dependencies: Cut here ***
|
||||
# These dependencies were produced at 10:09:51 PM on Sat, Mar 6, 2021 by MakeDepend
|
||||
|
||||
:mac_to_unix.c.x :mac_to_unix.c.o ƒ ∂
|
||||
:mac_to_unix.c ∂
|
||||
:defs.h
|
||||
|
||||
:sync.c.x :sync.c.o ƒ ∂
|
||||
:sync.c ∂
|
||||
:defs.h
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
void mac_to_unix(unsigned char **outptr, unsigned char *outend,
|
||||
const unsigned char **inptr, const unsigned char *inend);
|
|
@ -0,0 +1,65 @@
|
|||
#include "defs.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Table that converts Macintosh Roman characters to UTF-8, and CR to LF.
|
||||
static const unsigned short kToUnixTable[256] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
|
||||
12, 10, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
|
||||
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
|
||||
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
|
||||
60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
|
||||
72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
|
||||
84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
|
||||
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
|
||||
108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
|
||||
120, 121, 122, 123, 124, 125, 126, 127, 196, 197, 199, 201,
|
||||
209, 214, 220, 225, 224, 226, 228, 227, 229, 231, 233, 232,
|
||||
234, 235, 237, 236, 238, 239, 241, 243, 242, 244, 246, 245,
|
||||
250, 249, 251, 252, 8224, 176, 162, 163, 167, 8226, 182, 223,
|
||||
174, 169, 8482, 180, 168, 8800, 198, 216, 8734, 177, 8804, 8805,
|
||||
165, 181, 8706, 8721, 8719, 960, 8747, 170, 186, 937, 230, 248,
|
||||
191, 161, 172, 8730, 402, 8776, 8710, 171, 187, 8230, 160, 192,
|
||||
195, 213, 338, 339, 8211, 8212, 8220, 8221, 8216, 8217, 247, 9674,
|
||||
255, 376, 8260, 8364, 8249, 8250, 64257, 64258, 8225, 183, 8218, 8222,
|
||||
8240, 194, 202, 193, 203, 200, 205, 206, 207, 204, 211, 212,
|
||||
63743, 210, 218, 219, 217, 305, 710, 732, 175, 728, 729, 730,
|
||||
184, 733, 731, 711,
|
||||
};
|
||||
|
||||
void mac_to_unix(unsigned char **outptr, unsigned char *outend,
|
||||
const unsigned char **inptr, const unsigned char *inend) {
|
||||
unsigned char *op = *outptr;
|
||||
const unsigned char *ip = *inptr;
|
||||
unsigned cp;
|
||||
|
||||
while (ip < inend) {
|
||||
cp = kToUnixTable[*ip];
|
||||
if (cp < 0x80) {
|
||||
if (outend - op < 1) {
|
||||
break;
|
||||
}
|
||||
op[0] = cp;
|
||||
op += 1;
|
||||
} else if (cp < 0x400) {
|
||||
if (outend - op < 2) {
|
||||
break;
|
||||
}
|
||||
op[0] = (cp >> 6) | 0xc0;
|
||||
op[1] = (cp & 0x3f) | 0x80;
|
||||
op += 2;
|
||||
} else {
|
||||
if (outend - op < 3) {
|
||||
break;
|
||||
}
|
||||
op[0] = (cp >> 12) | 0xe0;
|
||||
op[1] = ((cp >> 6) & 0x3f) | 0x80;
|
||||
op[2] = (cp & 0x3f) | 0x80;
|
||||
op += 3;
|
||||
}
|
||||
ip++;
|
||||
}
|
||||
*outptr = op;
|
||||
*inptr = ip;
|
||||
}
|
515
sync.c
515
sync.c
|
@ -1,4 +1,7 @@
|
|||
#include "defs.h"
|
||||
|
||||
#include <Files.h>
|
||||
#include <Folders.h>
|
||||
#include <MacMemory.h>
|
||||
#include <StringCompare.h>
|
||||
|
||||
|
@ -6,6 +9,15 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
enum {
|
||||
// Maximum file size that we will copy.
|
||||
kMaxFileSize = 64 * 1024,
|
||||
};
|
||||
|
||||
typedef void (*convert_func)(unsigned char **outptr, unsigned char *outend,
|
||||
const unsigned char **inptr,
|
||||
const unsigned char *inend);
|
||||
|
||||
static void print_err(const char *msg, ...) {
|
||||
va_list ap;
|
||||
fputs("## Error: ", stderr);
|
||||
|
@ -15,15 +27,63 @@ static void print_err(const char *msg, ...) {
|
|||
fputc('\n', stderr);
|
||||
}
|
||||
|
||||
// Map from OSErr codes to messages. As a heuristic, this should include error
|
||||
// codes caused by toolbox calls in this program which are readily understood by
|
||||
// the user. It can exclude error codes that are likely to indicate a
|
||||
// programming error.
|
||||
struct error_message {
|
||||
OSErr err;
|
||||
const char *msg;
|
||||
};
|
||||
const struct error_message kErrorMessages[] = {
|
||||
{dirFulErr, "directory full"}, // -33
|
||||
{dskFulErr, "disk full"}, // -34
|
||||
{ioErr, "I/O error"}, // -36
|
||||
{bdNamErr, "bad name"}, // -37
|
||||
{fnfErr, "file not found"}, // -43
|
||||
{wPrErr, "disk is write-protected"}, // -44
|
||||
{fLckdErr, "file is locked"}, // -45
|
||||
{vLckdErr, "volume is locked"}, // -46
|
||||
{dupFNErr, "destination already exists"}, // -48
|
||||
{opWrErr, "file already open for writing"}, // -49
|
||||
{paramErr, "parameter error"}, // -50
|
||||
{permErr, "cannot write to locked file"}, // -54
|
||||
{dirNFErr, "directory not found"}, // -120
|
||||
{wrgVolTypErr, "not an HFS volume"}, // -123
|
||||
{diffVolErr, "files on different volumes"}, // -1303
|
||||
{afpAccessDenied, "user does not have access privileges (AFP)"}, // -5000
|
||||
{afpObjectTypeErr,
|
||||
"file/directory specified where directory/file expected"}, // -5025
|
||||
{afpSameObjectErr, "objects are the same"}, // -5038
|
||||
};
|
||||
|
||||
static const char *mac_strerror(OSErr err) {
|
||||
int i, n = sizeof(kErrorMessages) / sizeof(*kErrorMessages);
|
||||
for (i = 0; i < n; i++) {
|
||||
if (kErrorMessages[i].err == err) {
|
||||
return kErrorMessages[i].msg;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void print_errcode(OSErr err, const char *msg, ...) {
|
||||
va_list ap;
|
||||
const char *emsg;
|
||||
|
||||
fputs("## Error: ", stderr);
|
||||
va_start(ap, msg);
|
||||
vfprintf(stderr, msg, ap);
|
||||
va_end(ap);
|
||||
fprintf(stderr, ": err=%d\n", err);
|
||||
emsg = mac_strerror(err);
|
||||
if (emsg != NULL) {
|
||||
fprintf(stderr, ": %s (%d)\n", emsg, err);
|
||||
} else {
|
||||
fprintf(stderr, ": err=%d\n", err);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert a C to Pascal string.
|
||||
static int c2pstr(Str255 ostr, const char *istr) {
|
||||
size_t n = strlen(istr);
|
||||
if (n > 255) {
|
||||
|
@ -36,6 +96,13 @@ static int c2pstr(Str255 ostr, const char *istr) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Convert a Pascal to C string.
|
||||
static void p2cstr(char *ostr, const unsigned char *istr) {
|
||||
unsigned len = istr[0];
|
||||
memcpy(ostr, istr + 1, len);
|
||||
ostr[len] = '\0';
|
||||
}
|
||||
|
||||
struct file_meta {
|
||||
Boolean exists;
|
||||
long modTime;
|
||||
|
@ -46,15 +113,24 @@ enum {
|
|||
kDestDir,
|
||||
};
|
||||
|
||||
enum {
|
||||
kModeAuto,
|
||||
kModePush,
|
||||
kModePull,
|
||||
};
|
||||
|
||||
struct file_info {
|
||||
Str31 name;
|
||||
struct file_meta meta[2];
|
||||
int mode;
|
||||
};
|
||||
|
||||
// Array of metadata entries for files.
|
||||
static Handle gFiles;
|
||||
static unsigned gFileCount;
|
||||
static unsigned gFileAlloc;
|
||||
|
||||
// Get the metadata entry for a file with the given name.
|
||||
static struct file_info *get_file(const unsigned char *name) {
|
||||
unsigned i, n = gFileCount, len, newAlloc;
|
||||
struct file_info *file, *array;
|
||||
|
@ -103,6 +179,7 @@ static struct file_info *get_file(const unsigned char *name) {
|
|||
return file;
|
||||
}
|
||||
|
||||
// Get the volume and directory ID for a directory from its path.
|
||||
static int dir_from_path(short *vRefNum, long *dirID, const char *dirpath) {
|
||||
Str255 ppath;
|
||||
FSSpec spec;
|
||||
|
@ -139,6 +216,8 @@ static int dir_from_path(short *vRefNum, long *dirID, const char *dirpath) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Return true if a file with the given name should be included. The name is a
|
||||
// Pascal string.
|
||||
static int filter_name(unsigned char *name) {
|
||||
int len, i, stem;
|
||||
unsigned char *ext;
|
||||
|
@ -167,6 +246,8 @@ static int filter_name(unsigned char *name) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
// List files in a directory, filter them, and add the files matching the filter
|
||||
// to the file list. The value of 'which' should be kSrcDir or kDestDir.
|
||||
static int list_dir(short vRefNum, long dirID, int which) {
|
||||
Str255 ppath;
|
||||
CInfoPBRec ci;
|
||||
|
@ -200,14 +281,318 @@ static int list_dir(short vRefNum, long dirID, int which) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int command_main(char *destpath) {
|
||||
short srcVol, destVol;
|
||||
long srcDir, destDir;
|
||||
struct file_info *array, *file;
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Allocate memory.
|
||||
ptr = NewPtr(dataLength);
|
||||
err = MemError();
|
||||
if (err != 0) {
|
||||
print_errcode(err, "out of memory");
|
||||
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) {
|
||||
print_err("temporary file exists");
|
||||
return 1;
|
||||
} else if (err == fnfErr) {
|
||||
return 0;
|
||||
} else {
|
||||
print_errcode(err, "could not create temp file spec");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Write the entire contents of a file.
|
||||
static int write_file(FSSpec *dest, short tempVol, long tempDir, Ptr data,
|
||||
long length, long modTime, Boolean destExists) {
|
||||
FSSpec temp;
|
||||
long pos, amt;
|
||||
short ref;
|
||||
HParamBlockRec pb;
|
||||
CMovePBRec cm;
|
||||
CInfoPBRec ci;
|
||||
Str31 name;
|
||||
OSErr err;
|
||||
int r;
|
||||
|
||||
// Save the data to a temporary file.
|
||||
r = make_temp(&temp, tempVol, tempDir, dest->name);
|
||||
if (r != 0) {
|
||||
return 1;
|
||||
}
|
||||
err = FSpCreate(&temp, 'MPS ', 'TEXT', 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 (destExists) {
|
||||
err = FSpExchangeFiles(&temp, dest);
|
||||
if (err == 0) {
|
||||
err = FSpDelete(&temp);
|
||||
if (err != 0) {
|
||||
print_errcode(err, "could not rename temporary file");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
// paramErr: function not supported by volume.
|
||||
if (err != paramErr) {
|
||||
print_errcode(err, "could not delete temp file");
|
||||
return 1;
|
||||
}
|
||||
// Otherwise, delete destination and move temp file over.
|
||||
err = FSpDelete(dest);
|
||||
if (err != 0) {
|
||||
print_errcode(err, "could not remove destination file");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
// Next, try MoveRename.
|
||||
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 (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 (dest->parID != temp.parID) {
|
||||
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 (err != 0) {
|
||||
print_errcode(err, "could not move temporary file");
|
||||
goto error;
|
||||
}
|
||||
temp.parID = dest->parID;
|
||||
}
|
||||
if (memcmp(dest->name, temp.name, dest->name[0] + 1) != 0) {
|
||||
err = FSpRename(&temp, dest->name);
|
||||
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;
|
||||
}
|
||||
|
||||
// Copy the source file to the destination file. A temporary file is created in
|
||||
// the specified temporary directory.
|
||||
static int sync_file(struct file_info *file, convert_func func, long srcVol,
|
||||
short srcDir, long destVol, short destDir, long tempVol,
|
||||
short tempDir, long modTime) {
|
||||
FSSpec src, dest;
|
||||
Ptr srcData = NULL, destData = NULL;
|
||||
long srcLength, destLength;
|
||||
int r;
|
||||
OSErr err;
|
||||
unsigned char *outptr, *outend;
|
||||
const unsigned char *inptr, *inend;
|
||||
Boolean destExists;
|
||||
|
||||
// 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) {
|
||||
destExists = TRUE;
|
||||
} else if (err == fnfErr) {
|
||||
destExists = FALSE;
|
||||
} else if (err != 0) {
|
||||
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.
|
||||
destLength = srcLength + (srcLength >> 2) + 16;
|
||||
destData = NewPtr(destLength);
|
||||
err = MemError();
|
||||
if (err != 0) {
|
||||
print_errcode(err, "out of memory");
|
||||
goto error;
|
||||
}
|
||||
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 error;
|
||||
}
|
||||
destLength = outptr - (unsigned char *)destData;
|
||||
r = write_file(&dest, tempVol, tempDir, destData, destLength, destExists,
|
||||
modTime);
|
||||
if (r != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Clean up.
|
||||
DisposePtr(srcData);
|
||||
DisposePtr(destData);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (srcData != NULL) {
|
||||
DisposePtr(srcData);
|
||||
}
|
||||
if (destData != NULL) {
|
||||
DisposePtr(destData);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int command_main(char *destpath, int mode) {
|
||||
short srcVol, destVol, tempVol;
|
||||
long srcDir, destDir, tempDir;
|
||||
struct file_info *array, *file, *srcNewer, *destNewer;
|
||||
OSErr err;
|
||||
int r, i, n;
|
||||
char name[32];
|
||||
|
||||
// Get handles to src and dest directories.
|
||||
err = HGetVol(NULL, &srcVol, &srcDir);
|
||||
if (err != 0) {
|
||||
print_errcode(err, "HGetVol");
|
||||
|
@ -217,6 +602,8 @@ static int command_main(char *destpath) {
|
|||
if (r != 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// List files in src and dest directories.
|
||||
r = list_dir(srcVol, srcDir, kSrcDir);
|
||||
if (r != 0) {
|
||||
return 1;
|
||||
|
@ -229,32 +616,124 @@ static int command_main(char *destpath) {
|
|||
print_err("no files");
|
||||
return 1;
|
||||
}
|
||||
|
||||
HLock(gFiles);
|
||||
array = (struct file_info *)*gFiles;
|
||||
n = gFileCount;
|
||||
|
||||
// Figure out the direction for each file.
|
||||
srcNewer = NULL;
|
||||
destNewer = NULL;
|
||||
for (i = 0; i < n; i++) {
|
||||
file = &array[i];
|
||||
memcpy(name, file->name + 1, file->name[0]);
|
||||
name[file->name[0]] = '\0';
|
||||
printf("File: %s\n", name);
|
||||
printf(" exist: %c %c\n", file->meta[0].exists ? 'Y' : '-',
|
||||
file->meta[1].exists ? 'Y' : '-');
|
||||
printf(" modTime: %ld %ld\n", file->meta[0].modTime,
|
||||
file->meta[1].modTime);
|
||||
if (!file->meta[kSrcDir].exists) {
|
||||
file->mode = kModePull;
|
||||
destNewer = file;
|
||||
} else if (!file->meta[kDestDir].exists) {
|
||||
file->mode = kModePush;
|
||||
srcNewer = file;
|
||||
} else if (file->meta[kSrcDir].modTime < file->meta[kDestDir].modTime) {
|
||||
file->mode = kModePull;
|
||||
destNewer = file;
|
||||
} else if (file->meta[kSrcDir].modTime > file->meta[kDestDir].modTime) {
|
||||
file->mode = kModePush;
|
||||
srcNewer = file;
|
||||
}
|
||||
}
|
||||
|
||||
// Figure out the mode: push or pull.
|
||||
if (mode == kModeAuto) {
|
||||
if (srcNewer != NULL) {
|
||||
if (destNewer != NULL) {
|
||||
fputs("## Error: both source and destination have new files\n",
|
||||
stderr);
|
||||
p2cstr(name, srcNewer->name);
|
||||
fprintf(stderr, "## New file in source: %s\n", name);
|
||||
p2cstr(name, destNewer->name);
|
||||
fprintf(stderr, "## New file in destination: %s\n", name);
|
||||
return 1;
|
||||
}
|
||||
mode = kModePush;
|
||||
fputs("## Mode: push\n", stderr);
|
||||
} else if (destNewer != NULL) {
|
||||
mode = kModePull;
|
||||
fputs("## Mode: pull\n", stderr);
|
||||
} else {
|
||||
fputs("## No changes.\n", stderr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Synchronize the files.
|
||||
tempVol = 0;
|
||||
tempDir = 0;
|
||||
for (i = 0; i < n; i++) {
|
||||
file = &array[i];
|
||||
if (file->mode == mode) {
|
||||
if (mode == kModePush) {
|
||||
// When pushing, we use the destination directory as the
|
||||
// temporary folder, to avoid crossing filesystem boundaries on
|
||||
// the host.
|
||||
r = sync_file(file, mac_to_unix, srcVol, srcDir, destVol,
|
||||
destDir, destVol, destDir,
|
||||
file->meta[kSrcDir].modTime);
|
||||
} else {
|
||||
if (tempDir == 0) {
|
||||
err = FindFolder(destVol, kTemporaryFolderType, TRUE,
|
||||
&tempVol, &tempDir);
|
||||
if (err != 0) {
|
||||
print_errcode(err, "could not find temporary folder");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
r = sync_file(file, mac_to_unix, destVol, destDir, srcVol,
|
||||
srcDir, tempVol, tempDir,
|
||||
file->meta[kDestDir].modTime);
|
||||
}
|
||||
if (r) {
|
||||
p2cstr(name, file->name);
|
||||
print_err("failed to copy file: %s", name);
|
||||
return 1;
|
||||
}
|
||||
} else if (file->mode != kModeAuto) {
|
||||
p2cstr(name, file->name);
|
||||
fprintf(stderr, "## Refusing to overwrite '%s', file is newer\n",
|
||||
name);
|
||||
}
|
||||
}
|
||||
HUnlock(gFiles);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int r;
|
||||
char *destDir = NULL, *arg, *opt;
|
||||
int i, r, mode = kModeAuto;
|
||||
|
||||
if (argc != 2) {
|
||||
fputs("## Usage: SyncFiles <directory>\n", stderr);
|
||||
return 1;
|
||||
for (i = 1; i < argc; i++) {
|
||||
arg = argv[i];
|
||||
if (*arg == '-') {
|
||||
opt = arg + 1;
|
||||
if (*opt == '-') {
|
||||
opt++;
|
||||
}
|
||||
if (strcmp(opt, "push") == 0) {
|
||||
mode = kModePush;
|
||||
} else if (strcmp(opt, "pull") == 0) {
|
||||
mode = kModePull;
|
||||
} else {
|
||||
print_err("unknown flag: %s", arg);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (destDir != NULL) {
|
||||
print_err("unexpected argument: %s", arg);
|
||||
return 1;
|
||||
}
|
||||
destDir = arg;
|
||||
}
|
||||
}
|
||||
r = command_main(argv[1]);
|
||||
|
||||
r = command_main(argv[1], mode);
|
||||
if (gFiles != NULL) {
|
||||
DisposeHandle(gFiles);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue