Remove old MPW tool version

This commit is contained in:
Dietrich Epp 2023-05-06 13:59:50 -04:00
parent 873192318f
commit 9224a9b789
12 changed files with 0 additions and 1703 deletions

View File

@ -1,59 +0,0 @@
// convert.c - Conversion helper functions.
#include "convert.h"
#include "defs.h"
#include "mac_from_unix_data.h"
#include <CursorCtl.h>
#include <Files.h>
#include <MacErrors.h>
#include <Quickdraw.h>
int convert_read(short ref, long *count, void *data) {
OSErr err;
SpinCursor(1);
err = FSRead(ref, count, data);
switch (err) {
case noErr:
return kConvertOK;
case eofErr:
return kConvertEOF;
default:
print_errcode(err, "could not read source file");
return kConvertError;
}
}
int convert_write(short ref, long count, const void *data) {
OSErr err;
SpinCursor(1);
err = FSWrite(ref, &count, data);
if (err == noErr) {
return kConvertOK;
}
print_errcode(err, "could not write temp file");
return kConvertError;
}
static unsigned short *gFromUnixData;
// Get the table for converting from Unix to Macintosh.
unsigned short *mac_from_unix_data(void) {
Ptr ptr, src, dest;
if (gFromUnixData != NULL) {
return gFromUnixData;
}
ptr = NewPtr(FROM_UNIX_DATALEN);
if (ptr == NULL) {
print_memerr(FROM_UNIX_DATALEN);
return NULL;
}
src = (void *)kFromUnixData;
dest = ptr;
UnpackBits(&src, &dest, FROM_UNIX_DATALEN);
gFromUnixData = (void *)ptr;
return gFromUnixData;
}

View File

@ -1,53 +0,0 @@
// These helper functions are written so the conversion functions can be written
// for a standard C environment without using Macintosh Toolbox functions.
enum {
// Base size of temporary buffer for converting files, not counting the
// "extra".
kBufferBaseSize = 16 * 1024,
// Extra space past the end of the buffer for converting files.
kBufferExtraSize = 16,
// Total size of a buffer.
kBufferTotalSize = kBufferBaseSize + kBufferExtraSize,
};
// =============================================================================
// Helper functions
// =============================================================================
// Result codes for convert_read and convert_write.
enum {
kConvertOK,
kConvertError,
kConvertEOF,
};
// Read data from a file.
int convert_read(short ref, long *count, void *data);
// Write data to a file.
int convert_write(short ref, long count, const void *data);
// Get the table for converting from Unix to Macintosh.
unsigned short *mac_from_unix_data(void);
// =============================================================================
// Conversion functions
// =============================================================================
// Convert Macintosh encoding with CR line endings to UTF-8 with LF. The source
// and destinations are file handles. The buffers have size buf
int mac_to_unix(short srcRef, short destRef, void *srcBuf, void *destBuf);
// Convert UTF-8 with LF line endings to Macintosh encoding with CR. The source
// and destinations are file handles. The buffers have size kBufferTotalSize.
int mac_from_unix(short srcRef, short destRef, void *srcBuf, void *destBuf);
// Raw data copy.
int copy_data(short srcRef, short destRef, void *buf);
// Convert line endings but don't change encoding.
int convert_line_endings(short srcRef, short destRef, void *buf,
unsigned char srcEnding, unsigned char destEnding);

View File

@ -1,26 +0,0 @@
#include "convert.h"
int convert_line_endings(short srcRef, short destRef, void *buf,
unsigned char srcEnding, unsigned char destEnding) {
unsigned char *ptr, *end;
long count;
int r, r2;
do {
count = kBufferBaseSize;
r = convert_read(srcRef, &count, buf);
if (r == kConvertError) {
return 1;
}
for (ptr = buf, end = ptr + count; ptr != end; ptr++) {
if (*ptr == srcEnding) {
*ptr = destEnding;
}
}
r2 = convert_write(destRef, count, buf);
if (r2 != kConvertOK) {
return 1;
}
} while (r != kConvertEOF);
return 0;
}

View File

@ -1,235 +0,0 @@
#include "convert.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdnoreturn.h>
#include <string.h>
#include "mac_from_unix_data.h"
static noreturn void malloc_fail(size_t sz) {
fprintf(stderr, "Error: malloc(%zu) failed\n", sz);
exit(1);
}
static void *xmalloc(size_t sz) {
void *ptr = malloc(sz);
if (ptr == NULL) {
malloc_fail(sz);
}
return ptr;
}
struct buf {
char *data;
size_t size;
size_t alloc;
};
static void buf_put(struct buf *buf, const void *data, size_t length) {
if (length > buf->alloc - buf->size) {
size_t nalloc = buf->alloc;
if (nalloc == 0) {
nalloc = 1024;
}
while (length > nalloc - buf->size) {
nalloc <<= 1;
}
void *narr = realloc(buf->data, nalloc);
if (narr == NULL) {
malloc_fail(nalloc);
}
buf->data = narr;
buf->alloc = nalloc;
}
memcpy(buf->data + buf->size, data, length);
buf->size += length;
}
// =============================================================================
static unsigned short *gMacFromUnixData;
static noreturn void bad_unpackbits(void) {
fputs("Error: invalid unpackbits data\n", stderr);
exit(1);
}
static void unpackbits(void *dest, size_t destsz, const void *src,
size_t srcsz) {
const unsigned char *ip = src, *ie = ip + srcsz;
unsigned char *op = dest, *oe = op + destsz;
while (op < oe) {
if (ip >= ie) {
bad_unpackbits();
}
int c = (signed char)*ip++;
if (c >= 0) {
int len = c + 1;
if (len > ie - ip || len > oe - op) {
bad_unpackbits();
}
memcpy(op, ip, len);
op += len;
ip += len;
} else {
int len = -c + 1;
if (ip >= ie || len > oe - op) {
bad_unpackbits();
}
memset(op, *ip, len);
op += len;
ip += 1;
}
}
if (ip != ie) {
bad_unpackbits();
}
}
unsigned short *mac_from_unix_data(void) {
unsigned short *ptr = gMacFromUnixData;
if (ptr == NULL) {
unsigned char *bytes = xmalloc(FROM_UNIX_DATALEN);
unpackbits(bytes, FROM_UNIX_DATALEN, kFromUnixData,
sizeof(kFromUnixData));
ptr = xmalloc(FROM_UNIX_DATALEN);
for (int i = 0; i < FROM_UNIX_DATALEN / 2; i++) {
ptr[i] = (bytes[i * 2] << 8) | bytes[i * 2 + 1];
}
free(bytes);
gMacFromUnixData = ptr;
}
return ptr;
}
// =============================================================================
enum {
kSrcRef = 1234,
kDestRef = 5678,
};
static const char *gReadBuf;
static size_t gReadSize;
static size_t gReadPos;
static size_t gReadChunk;
static struct buf gWriteBuf;
int convert_read(short ref, long *count, void *data) {
if (ref != kSrcRef) {
fputs("Wrong ref\n", stderr);
exit(1);
}
size_t amt = *count;
size_t rem = gReadSize - gReadPos;
if (amt > rem) {
amt = rem;
}
if (gReadChunk != 0 && amt > gReadChunk) {
amt = gReadChunk;
}
*count = amt;
memcpy(data, gReadBuf + gReadPos, amt);
gReadPos += amt;
if (gReadPos == gReadSize) {
return kConvertEOF;
}
return kConvertOK;
}
int convert_write(short ref, long count, const void *data) {
if (ref != kDestRef) {
fputs("Wrong ref\n", stderr);
exit(1);
}
buf_put(&gWriteBuf, data, count);
return kConvertOK;
}
// =============================================================================
enum {
kInputSize = 64 * 1024 - 2,
};
static char *gen_input(void) {
char *ptr = xmalloc(kInputSize);
unsigned state = 0x12345678;
for (int i = 0; i < kInputSize; i++) {
// Relatively common LCG.
state = (state * 1103515245 + 12345) & 0x7fffffff;
ptr[i] = state >> 23;
}
return ptr;
}
int main(int argc, char **argv) {
(void)argc;
(void)argv;
int r;
void *sbuf = xmalloc(kBufferTotalSize);
void *dbuf = xmalloc(kBufferTotalSize);
// Generate input.
char *input = gen_input();
// Convert Macintosh -> UTF-8.
gReadBuf = input;
gReadSize = kInputSize;
gReadPos = 0;
r = mac_to_unix(kSrcRef, kDestRef, sbuf, dbuf);
if (r != 0) {
fputs("mac_to_unix failed\n", stderr);
return 1;
}
// Check that we have no CR.
{
const char *data = gWriteBuf.data;
size_t size = gWriteBuf.size;
for (size_t i = 0; i < size; i++) {
if (data[i] == 0x0d) {
fprintf(stderr, "Error: CR at offset %zu\n", i);
return 1;
}
}
}
// Convert back.
gReadBuf = gWriteBuf.data;
gReadSize = gWriteBuf.size;
gReadPos = 0;
gWriteBuf = (struct buf){NULL, 0, 0};
r = mac_from_unix(kSrcRef, kDestRef, sbuf, dbuf);
if (r != 0) {
fputs("mac_from_unix failed\n", stderr);
return 1;
}
// Check that this is equal to original, except with LF changed to CR.
{
const char *data = gWriteBuf.data;
size_t size = gWriteBuf.size;
if (kInputSize != size) {
fprintf(stderr, "Error: size = %zu, expect %d\n", size, kInputSize);
return 1;
}
for (size_t i = 0; i < kInputSize; i++) {
unsigned char x = input[i];
if (x == 0x0a) {
x = 0x0d;
}
unsigned char y = data[i];
if (x != y) {
fprintf(stderr, "Error: data[%zu] = 0x%02x, expect 0x%02x\n", i,
y, x);
return 1;
}
}
}
return 0;
}

19
copy.c
View File

@ -1,19 +0,0 @@
#include "convert.h"
int copy_data(short srcRef, short destRef, void *buf) {
long count;
int r, r2;
do {
count = kBufferBaseSize;
r = convert_read(srcRef, &count, buf);
if (r == kConvertError) {
return 1;
}
r2 = convert_write(destRef, count, buf);
if (r2 != kConvertOK) {
return 1;
}
} while (r != kConvertEOF);
return 0;
}

106
defs.h
View File

@ -1,106 +0,0 @@
#include <MacTypes.h>
typedef unsigned char bool;
#define ARRAY_COUNT(x) (sizeof(x) / sizeof(*x))
// =============================================================================
// util.c
// =============================================================================
// Message log level.
typedef enum {
kLogWarn,
kLogInfo,
kLogVerbose,
} log_level;
// Global log verbosity.
extern log_level gLogLevel;
// -f / -force flag: if true, all destination files are replaced, regardless of
// timestamp.
extern bool gFlagForce;
// -n / -dry-run flag: if true, no actions are taken, but the actions are
// printed out.
extern bool gFlagDryRun;
// -d / -delete flag: if true, destination files are deleted if there is no
// corresponding source file.
extern bool gFlagDelete;
// Print an error message.
void print_err(const char *msg, ...);
// Print an error message with a Macintosh toolbox error code.
void print_errcode(OSErr err, const char *msg, ...);
// Print an out-of-memory error.
void print_memerr(unsigned long size);
// Log the error result of a function call.
void log_call(OSErr err, const char *function);
// Convert a C string to Pascal string. Returns nonzero on failure.
int c2pstr(unsigned char *ostr, const char *istr);
// Convert a Pascall string (maximum 31 characters) to a C string.
void p2cstr(char *ostr, const unsigned char *istr);
// Global operation mode
typedef enum {
kModeUnknown,
kModePush,
kModePull,
} operation_mode;
// =============================================================================
// file.c
// =============================================================================
enum {
kSrcDir,
kDestDir,
};
// Metadata for a file in the source or destination.
struct file_meta {
Boolean exists;
long modTime;
};
// An action to take for a particular file.
typedef enum {
kActionNone, // Leave file alone.
kActionNew, // Copy src to dest, dest does not exist.
kActionReplace, // Replace existing file in dest.
kActionDelete, // Delete dest file.
} file_action;
// A general type of file. Affects the type code and conversions applied.
typedef enum {
kTypeUnknown,
kTypeText, // Text file: convert CR/LF and encoding.
kTypeTextUTF8, // Text file: convert CR/LF only.
kTypeResource, // Resource file: copy resource fork to data fork.
} file_type;
// Information about a file present in the source or destination directory (or
// both).
struct file_info {
// Filename, Pascal string.
Str31 name;
// Metadata indexed by kSrcDir or kDestDir.
struct file_meta meta[2];
// The action to apply to this file.
file_action action;
// The type of file. Used to select type codes and converters.
file_type type;
};
// Synchronize a file according to the action in the action field. The temporary
// directory must be a valid directory on the destination volume.
int sync_file(struct file_info *file, operation_mode mode, short srcVol,
long srcDir, short destVol, long destDir, short tempVol,
long tempDir);

345
file.c
View File

@ -1,345 +0,0 @@
#include "defs.h"
#include "convert.h"
#include <Files.h>
#include <Script.h>
#include <string.h>
// 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;
}
// Set the modification time for a file.
static int set_modtime(FSSpec *spec, long modTime) {
CInfoPBRec ci;
Str31 name;
OSErr err;
memset(&ci, 0, sizeof(ci));
memcpy(name, spec->name, spec->name[0] + 1);
ci.hFileInfo.ioNamePtr = name;
ci.hFileInfo.ioVRefNum = spec->vRefNum;
ci.hFileInfo.ioDirID = spec->parID;
err = PBGetCatInfoSync(&ci);
if (err != 0) {
print_errcode(err, "could not get temp file info");
return 1;
}
memcpy(name, spec->name, spec->name[0] + 1);
ci.hFileInfo.ioNamePtr = name;
ci.hFileInfo.ioVRefNum = spec->vRefNum;
ci.hFileInfo.ioDirID = spec->parID;
ci.hFileInfo.ioFlMdDat = modTime;
err = PBSetCatInfoSync(&ci);
if (err != 0) {
print_errcode(err, "could not set temp file info");
return 1;
}
return 0;
}
// Move a temp file over a destination file. This may modify the temp file spec
// if it moves in multiple stages.
static int replace_file(FSSpec *temp, FSSpec *dest, file_action action) {
HParamBlockRec pb;
CMovePBRec cm;
OSErr err;
bool mustMove, mustRename;
// 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");
return 1;
}
// Otherwise, delete destination and move temp file over.
err = FSpDelete(dest);
if (err != 0) {
print_errcode(err, "could not remove destination file");
return 1;
}
}
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");
return 1;
}
}
// 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");
return 1;
}
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");
return 1;
}
}
return 0;
}
static Ptr gSrcBuffer;
static Ptr gDestBuffer;
int sync_file(struct file_info *file, operation_mode mode, short srcVol,
long srcDir, short destVol, long destDir, short tempVol,
long tempDir) {
OSType creator, fileType;
FSSpec src, dest, temp;
short srcRef = 0, destRef = 0;
bool has_temp = false;
int r;
OSErr err;
// 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;
}
r = make_temp(&temp, tempVol, tempDir, dest.name);
if (r != 0) {
return 1;
}
// Create the temporary file.
switch (file->type) {
case kTypeText:
case kTypeTextUTF8:
creator = 'MPS ';
fileType = 'TEXT';
break;
case kTypeResource:
creator = 'RSED';
fileType = 'rsrc';
break;
default:
print_err("invalid type");
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");
goto error;
}
err = FSpCreate(&temp, creator, fileType, smSystemScript);
}
if (err != 0) {
print_errcode(err, "could not create file");
goto error;
}
has_temp = true;
// Get buffers for conversion.
if (gSrcBuffer == NULL) {
gSrcBuffer = NewPtr(kBufferTotalSize);
if (gSrcBuffer == NULL) {
print_memerr(kBufferTotalSize);
goto error;
}
}
if (gDestBuffer == NULL) {
gDestBuffer = NewPtr(kBufferTotalSize);
if (gDestBuffer == NULL) {
print_memerr(kBufferTotalSize);
goto error;
}
}
// Open the source file for reading.
if (file->type == kTypeResource && mode == kModePush) {
err = FSpOpenRF(&src, fsRdPerm, &srcRef);
} else {
err = FSpOpenDF(&src, fsRdPerm, &srcRef);
}
if (err != 0) {
print_errcode(err, "could not open file");
goto error;
}
if (file->type == kTypeResource && mode == kModePull) {
err = FSpOpenRF(&temp, fsRdWrPerm, &destRef);
} else {
err = FSpOpenDF(&temp, fsRdWrPerm, &destRef);
}
if (err != 0) {
print_errcode(err, "could not open temp file");
goto error;
}
// Convert data.
switch (file->type) {
case kTypeText:
if (mode == kModePush) {
r = mac_to_unix(srcRef, destRef, gSrcBuffer, gDestBuffer);
} else {
r = mac_from_unix(srcRef, destRef, gSrcBuffer, gDestBuffer);
}
break;
case kTypeTextUTF8: {
unsigned char srcEnding, destEnding;
if (mode == kModePush) {
srcEnding = 0x0d;
destEnding = 0x0a;
} else {
srcEnding = 0x0a;
destEnding = 0x0d;
}
r = convert_line_endings(srcRef, destRef, gSrcBuffer, srcEnding,
destEnding);
} break;
case kTypeResource:
r = copy_data(srcRef, destRef, gSrcBuffer);
break;
default:
print_err("invalid type");
goto error;
}
if (r != 0) {
goto error;
}
// Close files.
err = FSClose(srcRef);
srcRef = 0;
if (err != 0) {
print_errcode(err, "could not close source file");
goto error;
}
err = FSClose(destRef);
destRef = 0;
if (err != 0) {
print_errcode(err, "could not close temp file");
goto error;
}
// Set modification time.
r = set_modtime(&temp, file->meta[kSrcDir].modTime);
if (r != 0) {
goto error;
}
// Overwrite destination.
r = replace_file(&temp, &dest, file->action);
if (r != 0) {
goto error;
}
return 0;
error:
// Clean up.
if (srcRef != 0) {
err = FSClose(srcRef);
if (err != 0) {
print_errcode(err, "could not close source file");
}
}
if (destRef != 0) {
err = FSClose(destRef);
if (err != 0) {
print_errcode(err, "could not close destination file");
}
}
if (has_temp) {
err = FSpDelete(&temp);
if (err != 0) {
print_errcode(err, "could not delete temp file");
}
}
return 1;
}

View File

@ -1,150 +0,0 @@
#include "convert.h"
#include <stdio.h>
int mac_from_unix(short srcRef, short destRef, void *srcBuf, void *destBuf) {
unsigned char *op, *oe; // Output ptr, end.
unsigned char *ip, *ie; // Input ptr, end.
unsigned char *tmp, *curpos;
const unsigned short *table; // Conversion table.
unsigned entry, value, state, c, last, curvalue;
long count, i;
int has_eof = 0, need_input = 1, do_unput;
int lineno = 1, r;
table = mac_from_unix_data();
if (table == NULL) {
return 1;
}
// Initialize buffer pointers.
ip = srcBuf;
ie = ip;
need_input = 1;
op = destBuf;
// The destination buffer has an extra byte which may be combined with a
// diacritic.
oe = op + kBufferBaseSize + 1;
for (;;) {
if (need_input || ip >= ie) {
if (has_eof && ip == ie) {
break;
}
// Save unprocessed input, move to beginning of buffer.
count = ie - ip;
if (count > kBufferExtraSize) {
fputs("## Internal error\n", stderr);
return 1;
}
tmp = ip;
ip = (unsigned char *)srcBuf + kBufferExtraSize - count;
for (i = 0; i < count; i++) {
ip[i] = tmp[i];
}
// Try to fill remainder of buffer.
count = kBufferBaseSize;
r = convert_read(srcRef, &count, (char *)srcBuf + kBufferExtraSize);
if (r != kConvertOK) {
if (r == kConvertEOF) {
has_eof = 1;
if (count == 0) {
break;
}
} else {
return 1;
}
}
ie = (unsigned char *)srcBuf + kBufferExtraSize + count;
need_input = 0;
}
// If output buffer has a full chunk and an extra byte, write out the
// chunk and keep the extra byte.
if (op >= oe) {
count = kBufferBaseSize;
r = convert_write(destRef, count, destBuf);
if (r != 0) {
return 1;
}
tmp = destBuf;
tmp[0] = tmp[kBufferBaseSize];
op -= kBufferBaseSize;
}
while (ip < ie && op < oe) {
c = *ip;
if (c < 128) {
ip++;
// Note: \r = 0x0a, \n = 0x0d on old Mac compilers.
if (c == 0x0a || c == 0x0d) {
c = 0x0d;
lineno++;
}
*op++ = c;
last = c;
} else {
// Find the longest matching Unicode character.
// curpos: ip after longest match.
// curvalue: output character after longest match.
state = table[last] & 0xff00;
if (state != 0) {
// Continue from previous character.
do_unput = 1;
curpos = ip;
curvalue = last;
} else {
// Continue with new character.
do_unput = 0;
curpos = NULL;
curvalue = 0;
}
tmp = ip;
do {
entry = table[state | *tmp++];
state = entry & 0xff00;
value = entry & 0xff;
if (value != 0) {
curpos = tmp;
curvalue = value;
}
} while (state != 0 && tmp < ie);
if (state == 0 || has_eof) {
// We cannot consume more bytes. When state == 0, the state
// machine will not consume any more characters. When ip ==
// ie && has_eof, there are no more bytes available.
if (curvalue == 0) {
fprintf(stderr,
"## Error: line %d: invalid character\n",
lineno);
return 1;
}
ip = curpos;
if (do_unput) {
op--;
}
*op++ = curvalue;
last = 0;
} else {
// We can consume more bytes. Get more, and come back.
need_input = 1;
break;
}
}
}
}
// Write remainder of output buffer.
if (op != destBuf) {
count = op - (unsigned char *)destBuf;
r = convert_write(destRef, count, destBuf);
if (r != 0) {
return 1;
}
}
return 0;
}

View File

@ -1,52 +0,0 @@
/* This file is automatically generated. */
// clang-format off
#define FROM_UNIX_DATALEN 28160
static const unsigned char kFromUnixData[1268] = {
254,0,127,1,0,2,0,3,0,4,0,5,0,6,0,7,0,8,0,9,0,13,0,11,0,12,1,13,0,14,0,15,0,16,
0,17,0,18,0,19,0,20,0,21,0,22,0,23,0,24,0,25,0,26,0,27,0,28,0,29,0,30,0,31,0,32,
0,33,0,34,0,35,0,36,0,37,0,38,0,39,0,40,0,41,0,42,0,43,0,44,0,45,0,46,0,47,0,48,
0,49,0,50,0,51,0,52,0,53,0,54,0,55,0,56,0,57,0,58,0,59,0,60,2,61,0,62,0,63,0,64,
4,124,65,0,66,6,67,0,68,8,69,0,70,0,71,0,72,10,73,0,74,0,75,0,76,0,77,12,78,14,
79,0,80,0,81,0,82,0,83,0,84,16,85,0,86,0,87,0,88,18,89,0,90,0,91,0,92,0,93,0,94,
0,95,0,96,20,97,0,98,22,99,0,100,24,101,0,102,0,103,0,104,26,105,0,106,0,107,0,
108,0,109,28,110,30,111,0,112,0,113,0,114,0,115,0,116,32,117,0,118,0,119,0,120,
34,121,0,122,0,123,0,124,0,125,0,126,0,127,129,0,253,0,8,36,0,37,0,38,0,39,0,40,
248,0,0,41,252,0,2,42,0,43,220,0,0,44,232,0,0,52,203,0,0,13,129,0,129,0,129,0,
129,0,129,0,129,0,129,0,255,0,0,3,129,0,129,0,129,0,169,0,0,173,129,0,129,0,129,
0,129,0,219,0,0,5,129,0,129,0,153,0,6,203,0,231,0,229,0,204,248,0,0,128,254,0,0,
129,129,0,129,0,129,0,129,0,129,0,255,0,0,7,129,0,129,0,129,0,203,0,0,130,129,0,
129,0,129,0,129,0,185,0,0,9,129,0,129,0,153,0,4,233,0,131,0,230,246,0,0,232,129,
0,129,0,129,0,129,0,129,0,251,0,0,11,129,0,129,0,153,0,4,237,0,234,0,235,246,0,
0,236,129,0,129,0,129,0,129,0,129,0,251,0,0,13,129,0,129,0,147,0,0,132,129,0,
129,0,129,0,129,0,129,0,241,0,0,15,129,0,129,0,153,0,6,241,0,238,0,239,0,205,
248,0,0,133,129,0,129,0,129,0,129,0,129,0,251,0,0,17,129,0,129,0,153,0,4,244,0,
242,0,243,246,0,0,134,129,0,129,0,129,0,129,0,129,0,251,0,0,19,129,0,129,0,137,
0,0,217,129,0,129,0,129,0,129,0,129,0,251,0,0,21,129,0,129,0,153,0,6,136,0,135,
0,137,0,139,248,0,0,138,254,0,0,140,129,0,129,0,129,0,129,0,129,0,255,0,0,23,
129,0,129,0,129,0,203,0,0,141,129,0,129,0,129,0,129,0,185,0,0,25,129,0,129,0,
153,0,4,143,0,142,0,144,246,0,0,145,129,0,129,0,129,0,129,0,129,0,251,0,0,27,
129,0,129,0,153,0,4,147,0,146,0,148,246,0,0,149,129,0,129,0,129,0,129,0,129,0,
251,0,0,29,129,0,129,0,147,0,0,150,129,0,129,0,129,0,129,0,129,0,241,0,0,31,129,
0,129,0,153,0,6,152,0,151,0,153,0,155,248,0,0,154,129,0,129,0,129,0,129,0,129,0,
251,0,0,33,129,0,129,0,153,0,4,157,0,156,0,158,246,0,0,159,129,0,129,0,129,0,
129,0,129,0,251,0,0,35,129,0,129,0,137,0,0,216,129,0,129,0,129,0,129,0,210,0,6,
202,0,193,0,162,0,163,254,0,0,180,254,0,10,164,0,172,0,169,0,187,0,199,0,194,
254,0,6,168,0,248,0,161,0,177,252,0,8,171,0,181,0,166,0,225,0,252,254,0,2,188,0,
200,250,0,1,192,0,129,0,129,0,129,0,30,203,0,231,0,229,0,204,0,128,0,129,0,174,
0,130,0,233,0,131,0,230,0,232,0,237,0,234,0,235,0,236,254,0,10,132,0,241,0,238,
0,239,0,205,0,133,254,0,8,175,0,244,0,242,0,243,0,134,252,0,32,167,0,136,0,135,
0,137,0,139,0,138,0,140,0,190,0,141,0,143,0,142,0,144,0,145,0,147,0,146,0,148,0,
149,254,0,22,150,0,152,0,151,0,153,0,155,0,154,0,214,0,191,0,157,0,156,0,158,0,
159,252,0,0,216,129,0,129,0,129,0,158,0,0,245,129,0,129,0,129,0,192,0,2,206,0,
207,184,0,0,217,129,0,129,0,129,0,206,0,0,196,129,0,129,0,129,0,154,0,2,246,0,
255,224,0,10,249,0,250,0,251,0,254,0,247,0,253,129,0,129,0,129,0,129,0,234,0,0,
189,129,0,129,0,129,0,212,0,0,185,129,0,129,0,129,0,131,0,4,45,0,46,0,47,254,0,
0,48,250,0,2,49,0,50,230,0,0,51,129,0,129,0,129,0,137,0,2,208,0,209,250,0,4,212,
0,213,0,226,254,0,4,210,0,211,0,227,254,0,4,160,0,224,0,165,250,0,0,201,238,0,0,
228,240,0,2,220,0,221,129,0,129,0,129,0,238,0,0,218,129,0,129,0,129,0,129,0,178,
0,0,219,129,0,129,0,129,0,150,0,0,170,129,0,129,0,129,0,194,0,0,182,250,0,0,198,
240,0,0,184,254,0,0,183,240,0,0,195,250,0,0,176,232,0,0,186,129,0,129,0,129,0,
200,0,0,197,210,0,0,173,250,0,2,178,0,179,129,0,129,0,129,0,184,0,0,215,129,0,
129,0,129,0,129,0,209,0,0,53,240,0,0,54,129,0,129,0,129,0,129,0,219,0,0,240,129,
0,129,0,129,0,254,0,2,222,0,223,129,0,135,0,
};

View File

@ -1,108 +0,0 @@
#include "convert.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,
};
int mac_to_unix(short srcRef, short destRef, void *srcBuf, void *destBuf) {
unsigned char *op, *oe, *tmp; // Output ptr, end.
const unsigned char *ip, *ie; // Input ptr, end.
unsigned cp; // Code point.
int r;
long count;
int has_eof = 0;
// Initialize buffer pointers.
ip = srcBuf;
ie = ip;
op = destBuf;
oe = op + kBufferBaseSize;
for (;;) {
// If input buffer is consumed, read more.
if (ip >= ie) {
if (has_eof) {
break;
}
count = kBufferBaseSize;
r = convert_read(srcRef, &count, srcBuf);
if (r != kConvertOK) {
if (r == kConvertEOF) {
has_eof = 1;
if (count == 0) {
break;
}
} else {
return 1;
}
}
ip = srcBuf;
ie = ip + count;
}
// If output buffer has a full chunk, write it out.
if (op >= oe) {
count = kBufferBaseSize;
r = convert_write(destRef, count, destBuf);
if (r != 0) {
return 1;
}
tmp = destBuf;
tmp[0] = tmp[kBufferBaseSize];
tmp[1] = tmp[kBufferBaseSize + 1];
op -= kBufferBaseSize;
}
// Convert as much as possible. Note that the "extra" past the end of
// the destination buffer may be used, just to simplify bounds checking.
while (ip < ie && op < oe) {
cp = kToUnixTable[*ip++];
if (cp < 0x80) {
op[0] = cp;
op += 1;
} else if (cp < 0x400) {
op[0] = (cp >> 6) | 0xc0;
op[1] = (cp & 0x3f) | 0x80;
op += 2;
} else {
op[0] = (cp >> 12) | 0xe0;
op[1] = ((cp >> 6) & 0x3f) | 0x80;
op[2] = (cp & 0x3f) | 0x80;
op += 3;
}
}
}
// Write remainder of output buffer.
if (op != destBuf) {
count = op - (unsigned char *)destBuf;
r = convert_write(destRef, count, destBuf);
if (r != 0) {
return 1;
}
}
return 0;
}

429
sync.c
View File

@ -1,429 +0,0 @@
#include "defs.h"
#include <CursorCtl.h>
#include <Files.h>
#include <Folders.h>
#include <MacMemory.h>
#include <StringCompare.h>
#include <stdio.h>
#include <string.h>
log_level gLogLevel;
bool gFlagForce;
bool gFlagDryRun;
bool gFlagDelete;
static const char *kActionName[] = {
"no action",
"new",
"replace",
"delete",
};
// 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;
Handle newFiles;
OSErr err;
len = name[0];
if (len > 31) {
fputs("## Error: name too long\n", stderr);
return NULL;
}
if (gFiles == NULL) {
newAlloc = 8;
newFiles = NewHandle(newAlloc * sizeof(struct file_info));
err = MemError();
if (err != noErr) {
fputs("## Error: out of memory\n", stderr);
return NULL;
}
gFiles = newFiles;
gFileAlloc = newAlloc;
} else {
array = (struct file_info *)*gFiles;
for (i = 0; i < n; i++) {
file = &array[i];
if (memcmp(file->name, name, len + 1) == 0) {
return file;
}
}
if (n >= gFileAlloc) {
newAlloc = n * 2;
SetHandleSize(gFiles, newAlloc * sizeof(struct file_info));
err = MemError();
if (err != noErr) {
fputs("## Error: out of memory\n", stderr);
return NULL;
}
gFileAlloc = newAlloc;
}
}
array = (struct file_info *)*gFiles;
file = &array[n];
gFileCount = n + 1;
memset(file, 0, sizeof(*file));
memcpy(file->name, name, len + 1);
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;
CInfoPBRec ci;
OSErr err;
if (c2pstr(ppath, dirpath)) {
return 1;
}
err = FSMakeFSSpec(0, 0, ppath, &spec);
if (err != 0) {
if (err == fnfErr) {
print_err("does not exist: %s", dirpath);
} else {
print_errcode(err, "FSMakeFSSpec");
}
return 1;
}
memset(&ci, 0, sizeof(ci));
ci.dirInfo.ioNamePtr = spec.name;
ci.dirInfo.ioVRefNum = spec.vRefNum;
ci.dirInfo.ioDrDirID = spec.parID;
err = PBGetCatInfoSync(&ci);
if (err != 0) {
print_errcode(err, "PBGetCatInfoSync");
return 1;
}
if ((ci.dirInfo.ioFlAttrib & kioFlAttribDirMask) == 0) {
print_err("not a directory: %s", dirpath);
return 1;
}
*vRefNum = ci.dirInfo.ioVRefNum;
*dirID = ci.dirInfo.ioDrDirID;
return 0;
}
struct extension_info {
unsigned long extension;
file_type type;
};
// clang-format off
static const struct extension_info kExtensions1[] = {
{'c', kTypeText},
{'h', kTypeText},
{'r', kTypeText},
};
static const struct extension_info kExtensions2[] = {
{'cc', kTypeText},
{'cp', kTypeText},
{'hh', kTypeText},
};
static const struct extension_info kExtensions3[] = {
{'cpp', kTypeText},
{'cxx', kTypeText},
{'hpp', kTypeText},
{'hxx', kTypeText},
{'txt', kTypeText},
{'xml', kTypeTextUTF8},
};
static const struct extension_info kExtensions4[] = {
{'rsrc', kTypeResource},
};
// clang-format on
static file_type file_type_from_extension(unsigned long extension,
const struct extension_info *info,
int info_count) {
int i;
for (i = 0; i < info_count; i++) {
if (extension == info[i].extension) {
return info[i].type;
}
}
return kTypeUnknown;
}
// Get the file type for a file with the given name.
static file_type file_type_from_name(const unsigned char *name) {
int len, i, stem;
const unsigned char *ext;
stem = 0;
len = name[0];
for (i = 1; i <= len; i++) {
if (name[i] == '.') {
stem = i;
}
}
if (stem == 0) {
if (EqualString(name, "\pmakefile", false, true)) {
return kTypeText;
}
}
ext = name + stem + 1;
switch (len - stem) {
case 1:
return file_type_from_extension(ext[0], kExtensions1,
ARRAY_COUNT(kExtensions1));
case 2:
return file_type_from_extension((ext[0] << 8) | ext[1], kExtensions2,
ARRAY_COUNT(kExtensions2));
case 3:
return file_type_from_extension(
((unsigned long)ext[0] << 16) | (ext[1] << 8) | ext[2],
kExtensions3, ARRAY_COUNT(kExtensions3));
case 4:
return file_type_from_extension(
((unsigned long)ext[0] << 24) | ((unsigned long)ext[1] << 16) |
(ext[2] << 8) | ext[3],
kExtensions4, ARRAY_COUNT(kExtensions4));
}
return kTypeUnknown;
}
// 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;
struct file_info *file;
OSErr err;
int i;
file_type type;
for (i = 1; i < 100; i++) {
memset(&ci, 0, sizeof(ci));
ci.dirInfo.ioNamePtr = ppath;
ci.dirInfo.ioVRefNum = vRefNum;
ci.dirInfo.ioDrDirID = dirID;
ci.dirInfo.ioFDirIndex = i;
err = PBGetCatInfoSync(&ci);
if (err != 0) {
if (err == fnfErr) {
break;
}
print_errcode(err, "could not list directory");
continue;
}
if ((ci.hFileInfo.ioFlAttrib & kioFlAttribDirMask) == 0) {
type = file_type_from_name(ppath);
if (type != kTypeUnknown) {
ppath[ppath[0] + 1] = '\0';
file = get_file(ppath);
file->meta[which].exists = true;
file->meta[which].modTime = ci.hFileInfo.ioFlMdDat;
file->type = type;
}
}
}
return 0;
}
static int command_main(char *localPath, char *remotePath,
operation_mode mode) {
short localVol, remoteVol, srcVol, destVol, tempVol;
long localDir, remoteDir, srcDir, destDir, tempDir;
struct file_info *array, *file;
OSErr err;
int r, i, n;
char name[32];
bool hasAction;
// Get handles to local and remote directories.
if (localPath == NULL) {
err = HGetVol(NULL, &localVol, &localDir);
if (err != 0) {
print_errcode(err, "HGetVol");
return 1;
}
} else {
r = dir_from_path(&localVol, &localDir, localPath);
if (r != 0) {
return 1;
}
}
r = dir_from_path(&remoteVol, &remoteDir, remotePath);
if (r != 0) {
return 1;
}
if (localVol == remoteVol && localDir == remoteDir) {
print_err("local and remote are the same directory");
return 1;
}
// Get source and destination directories.
if (mode == kModePull) {
srcVol = remoteVol;
srcDir = remoteDir;
destVol = localVol;
destDir = localDir;
} else {
srcVol = localVol;
srcDir = localDir;
destVol = remoteVol;
destDir = remoteDir;
}
// List files in src and dest directories.
r = list_dir(srcVol, srcDir, kSrcDir);
if (r != 0) {
return 1;
}
if (gFileCount == 0) {
print_err("no files in source directory");
return 1;
}
r = list_dir(destVol, destDir, kDestDir);
if (r != 0) {
return 1;
}
HLock(gFiles);
array = (struct file_info *)*gFiles;
n = gFileCount;
// Assign actions to each file.
hasAction = false;
for (i = 0; i < n; i++) {
file = &array[i];
file->action = kActionNone;
if (!file->meta[kSrcDir].exists) {
if (gFlagDelete) {
file->action = kActionDelete;
hasAction = true;
}
} else if (!file->meta[kDestDir].exists) {
file->action = kActionNew;
hasAction = true;
} else if (gFlagForce ||
file->meta[kSrcDir].modTime > file->meta[kDestDir].modTime) {
file->action = kActionReplace;
hasAction = true;
} else if (file->meta[kDestDir].modTime <
file->meta[kDestDir].modTime) {
p2cstr(name, file->name);
fprintf(stderr, "## Warning: destination file is newer: %s\n",
name);
}
}
// Early exit if there are no actions.
if (!hasAction) {
fputs("## No actions\n", stderr);
return 0;
}
// Print actions for a dry run.
if (gFlagDryRun) {
for (i = 0; i < n; i++) {
file = &array[i];
p2cstr(name, file->name);
fprintf(stderr, "## %s: %s\n", kActionName[file->action], name);
}
return 0;
}
// Synchronize the files.
InitCursorCtl(NULL);
if (mode == kModePull) {
err =
FindFolder(destVol, kTemporaryFolderType, true, &tempVol, &tempDir);
if (err != 0) {
print_errcode(err, "could not find temporary folder");
return 1;
}
} else {
// When pushing, we use the destination directory as the temporary
// folder, to avoid crossing filesystem boundaries on the host.
tempVol = destVol;
tempDir = destDir;
}
for (i = 0; i < n; i++) {
file = &array[i];
p2cstr(name, file->name);
if (gLogLevel >= kLogInfo) {
fprintf(stderr, "## %s: %s\n", kActionName[file->action], name);
}
SpinCursor(32);
r = sync_file(&array[i], mode, srcVol, srcDir, destVol, destDir,
tempVol, tempDir);
if (r != 0) {
print_err("failed to synchronize file: %s", name);
return 1;
}
}
return 0;
}
int main(int argc, char **argv) {
char *remoteDir = NULL, *localDir = NULL, *arg, *opt;
int i, r, mode = kModeUnknown;
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 if (strcmp(opt, "verbose") == 0 || strcmp(opt, "v") == 0) {
gLogLevel = kLogVerbose;
} else if (strcmp(opt, "quiet") == 0 || strcmp(opt, "q") == 0) {
gLogLevel = kLogWarn;
} else if (strcmp(opt, "force") == 0 || strcmp(opt, "f") == 0) {
gFlagForce = true;
} else if (strcmp(opt, "dir") == 0) {
if (i + 1 >= argc) {
print_err("expected argument for -dir");
return 1;
}
localDir = argv[++i];
} else if (strcmp(opt, "dry-run") == 0 || strcmp(opt, "n") == 0) {
gFlagDryRun = true;
} else if (strcmp(opt, "delete") == 0 || strcmp(opt, "d") == 0) {
gFlagDelete = true;
} else {
print_err("unknown flag: %s", arg);
return 1;
}
} else {
if (remoteDir != NULL) {
print_err("unexpected argument: %s", arg);
return 1;
}
remoteDir = arg;
}
}
if (mode == kModeUnknown) {
print_err("either -push or -pull is required");
return 1;
}
r = command_main(localDir, remoteDir, mode);
if (gFiles != NULL) {
DisposeHandle(gFiles);
}
if (gLogLevel >= kLogVerbose) {
fputs("## Done\n", stderr);
}
return r;
}

121
util.c
View File

@ -1,121 +0,0 @@
#include "defs.h"
#include <MacErrors.h>
#include <MacMemory.h>
#include <MacTypes.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
void print_err(const char *msg, ...) {
va_list ap;
fputs("## Error: ", stderr);
va_start(ap, msg);
vfprintf(stderr, msg, ap);
va_end(ap);
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;
};
#define E(e, m) \
{ e, m "\0" #e }
static const struct error_message kErrorMessages[] = {
E(dirFulErr, "directory full"), // -33
E(dskFulErr, "disk full"), // -34
E(ioErr, "I/O error"), // -36
E(bdNamErr, "bad name"), // -37
E(fnfErr, "file not found"), // -43
E(wPrErr, "disk is write-protected"), // -44
E(fLckdErr, "file is locked"), // -45
E(vLckdErr, "volume is locked"), // -46
E(fBsyErr, "file is busy"), // -47
E(dupFNErr, "destination already exists"), // -48
E(opWrErr, "file already open for writing"), // -49
E(paramErr, "parameter error"), // -50
E(permErr, "cannot write to locked file"), // -54
E(dirNFErr, "directory not found"), // -120
E(wrgVolTypErr, "not an HFS volume"), // -123
E(diffVolErr, "files on different volumes"), // -1303
E(afpAccessDenied, "user does not have access privileges (AFP)"), // -5000
E(afpObjectTypeErr,
"file/directory specified where directory/file expected"), // -5025
E(afpSameObjectErr, "objects are the same"), // -5038
};
#undef E
// Return the error message for a Macintosh toolbox error code. Returns NULL if
// the error is not known.
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;
}
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);
emsg = mac_strerror(err);
if (emsg != NULL) {
fprintf(stderr, ": %s (%d)\n", emsg, err);
} else {
fprintf(stderr, ": err=%d\n", err);
}
}
void print_memerr(unsigned long size) {
OSErr err;
err = MemError();
print_errcode(err, "out of memory; size=%lu", size);
}
void log_call(OSErr err, const char *function) {
const char *emsg;
if (err == 0) {
fprintf(stderr, "## %s: noErr\n", function);
return;
}
emsg = mac_strerror(err);
if (emsg != NULL) {
emsg += strlen(emsg) + 1;
fprintf(stderr, "## %s: %s (%d)\n", function, emsg, err);
} else {
fprintf(stderr, "## %s: %d\n", function, err);
}
}
int c2pstr(Str255 ostr, const char *istr) {
size_t n = strlen(istr);
if (n > 255) {
print_err("path too long: %s", istr);
return 1;
}
ostr[0] = n;
memcpy(ostr + 1, istr, n);
memset(ostr + 1 + n, 0, 255 - n);
return 0;
}
void p2cstr(char *ostr, const unsigned char *istr) {
unsigned len = istr[0];
memcpy(ostr, istr + 1, len);
ostr[len] = '\0';
}