mirror of
https://github.com/depp/syncfiles.git
synced 2024-10-31 12:04:44 +00:00
Rework error handling
Previously, various error handling functions were defined, but not all of them were even used. The new error handling functions handle the most common cases: - Assertions. These can be handled with `ASSERT()`, which gives the error location for debugging. - Errors we don't know how to handle, like GetNewWindow returning NULL. These can be handled with `EXIT_INTERNAL()`, which gives the error location for debugging. - Out of memory conditions. These can be handled with `ShowMemError()`. - System errors in response to user operations. These can be handled with `ShowError()`.
This commit is contained in:
parent
56a3983129
commit
b1bcae531b
@ -47,7 +47,8 @@ static void SelectCurrentDirectory(struct ChooseDirReply *reply)
|
|||||||
ci.dirInfo.ioDrDirID = LMGetCurDirStore();
|
ci.dirInfo.ioDrDirID = LMGetCurDirStore();
|
||||||
err = PBGetCatInfoSync(&ci);
|
err = PBGetCatInfoSync(&ci);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
ExitErrorOS(kErrUnknown, err);
|
ShowError(kErrNone, kErrNone, err, NULL);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
directory = reply->directory;
|
directory = reply->directory;
|
||||||
directory->vRefNum = ci.dirInfo.ioVRefNum;
|
directory->vRefNum = ci.dirInfo.ioVRefNum;
|
||||||
@ -99,10 +100,10 @@ Boolean ChooseDirectory(FSSpec *directory)
|
|||||||
switch (cdreply.status) {
|
switch (cdreply.status) {
|
||||||
case kCDChild:
|
case kCDChild:
|
||||||
BlockMoveData(&sfreply.sfFile, directory, sizeof(FSSpec));
|
BlockMoveData(&sfreply.sfFile, directory, sizeof(FSSpec));
|
||||||
return 1;
|
return true;
|
||||||
case kCDCurrent:
|
case kCDCurrent:
|
||||||
return 1;
|
return true;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
127
macos/error.c
127
macos/error.c
@ -3,73 +3,120 @@
|
|||||||
// Mozilla Public License, version 2.0. See LICENSE.txt for details.
|
// Mozilla Public License, version 2.0. See LICENSE.txt for details.
|
||||||
#include "macos/error.h"
|
#include "macos/error.h"
|
||||||
|
|
||||||
|
#include "lib/defs.h"
|
||||||
#include "macos/main.h"
|
#include "macos/main.h"
|
||||||
|
#include "macos/pstrbuilder.h"
|
||||||
#include "macos/resources.h"
|
#include "macos/resources.h"
|
||||||
#include "macos/strutil.h"
|
|
||||||
|
|
||||||
#include <Dialogs.h>
|
#include <Dialogs.h>
|
||||||
#include <TextUtils.h>
|
#include <TextUtils.h>
|
||||||
|
|
||||||
void ExitError(ErrorCode errCode)
|
enum {
|
||||||
|
// OK
|
||||||
|
kStrOK = 1,
|
||||||
|
// Quit
|
||||||
|
kStrQuit,
|
||||||
|
// An error of type ^2 occurred.
|
||||||
|
kStrErrorCode,
|
||||||
|
// Error at: ^1:^2
|
||||||
|
kStrErrorAt,
|
||||||
|
// Assertion: ^1
|
||||||
|
kStrAssertion,
|
||||||
|
// An internal error occurred.
|
||||||
|
kStrInternal,
|
||||||
|
// An unknown error occurred.
|
||||||
|
kStrUnknown,
|
||||||
|
// (Base value for error messages.)
|
||||||
|
kStrMessageBase
|
||||||
|
};
|
||||||
|
|
||||||
|
static void AppendErrorArray(struct PStrBuilder *str, int sep, int strNum,
|
||||||
|
int paramCount, const unsigned char *const *params)
|
||||||
{
|
{
|
||||||
Str255 message;
|
Str255 message;
|
||||||
|
|
||||||
GetIndString(message, rSTRS_Errors, errCode);
|
if (str->data[0] > 0 && sep != 0) {
|
||||||
if (message[0] == 0) {
|
PStrAppendChar(str, sep);
|
||||||
GetIndString(message, rSTRS_Errors, kErrUnknown);
|
}
|
||||||
|
GetIndString(message, rSTRS_Errors, strNum);
|
||||||
|
if (message[0] == 0) {
|
||||||
|
PStrAppendChar(str, '?');
|
||||||
|
} else {
|
||||||
|
PStrAppendSubstitute(str, message, paramCount, params);
|
||||||
}
|
}
|
||||||
ParamText(message, NULL, NULL, NULL);
|
|
||||||
Alert(rAlrtError, NULL);
|
|
||||||
QuitApp();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExitErrorOS(ErrorCode errCode, short osErr)
|
static void AppendError0(struct PStrBuilder *str, int sep, int strNum)
|
||||||
{
|
{
|
||||||
Str255 message;
|
AppendErrorArray(str, sep, strNum, 0, NULL);
|
||||||
|
|
||||||
GetIndString(message, rSTRS_Errors, errCode);
|
|
||||||
if (message[0] == 0) {
|
|
||||||
GetIndString(message, rSTRS_Errors, kErrUnknown);
|
|
||||||
}
|
|
||||||
StrAppendFormat(message, "\p\rError code: %d", osErr);
|
|
||||||
ParamText(message, NULL, NULL, NULL);
|
|
||||||
Alert(rAlrtError, NULL);
|
|
||||||
QuitApp();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExitMemError(void)
|
static void AppendError1(struct PStrBuilder *str, int sep, int strNum,
|
||||||
|
const unsigned char *strParam)
|
||||||
{
|
{
|
||||||
ExitErrorOS(kErrOutOfMemory, MemError());
|
AppendErrorArray(str, sep, strNum, 1, &strParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AppendError2(struct PStrBuilder *str, int sep, int strNum,
|
||||||
|
const unsigned char *strParam, int intParam)
|
||||||
|
{
|
||||||
|
unsigned char num[16];
|
||||||
|
const unsigned char *params[2];
|
||||||
|
|
||||||
|
params[0] = strParam;
|
||||||
|
NumToString(intParam, num);
|
||||||
|
params[1] = num;
|
||||||
|
AppendErrorArray(str, sep, strNum, 2, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ShowErrorAlert(const unsigned char *message, int button)
|
||||||
|
{
|
||||||
|
Str255 buttonText;
|
||||||
|
|
||||||
|
GetIndString(buttonText, rSTRS_Errors, button);
|
||||||
|
ParamText(message, buttonText, NULL, NULL);
|
||||||
|
Alert(rAlrtError, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExitAssert(const unsigned char *file, int line,
|
void ExitAssert(const unsigned char *file, int line,
|
||||||
const unsigned char *message)
|
const unsigned char *assertion)
|
||||||
{
|
{
|
||||||
Str255 dmessage;
|
struct PStrBuilder str;
|
||||||
|
|
||||||
GetIndString(dmessage, rSTRS_Errors, kErrInternal);
|
PStrInit(&str);
|
||||||
StrAppendFormat(dmessage, "\p\rError at: %S:%d", file, line);
|
AppendError0(&str, 0, kStrInternal);
|
||||||
if (message != NULL) {
|
if (file != NULL) {
|
||||||
StrAppendFormat(dmessage, "\p: %S", message);
|
AppendError2(&str, '\r', kStrErrorAt, file, line);
|
||||||
}
|
}
|
||||||
ParamText(dmessage, NULL, NULL, NULL);
|
if (assertion != NULL) {
|
||||||
Alert(rAlrtError, NULL);
|
AppendError1(&str, '\r', kStrAssertion, assertion);
|
||||||
|
}
|
||||||
|
ShowErrorAlert(str.data, kStrQuit);
|
||||||
QuitApp();
|
QuitApp();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShowError(const struct ErrorParams *p)
|
void ShowError(ErrorCode err1, ErrorCode err2, short osErr,
|
||||||
|
const unsigned char *strParam)
|
||||||
{
|
{
|
||||||
Str255 message;
|
struct PStrBuilder str;
|
||||||
|
|
||||||
GetIndString(message, rSTRS_Errors, p->err);
|
PStrInit(&str);
|
||||||
if (message[0] == 0) {
|
if (err1 != kErrNone) {
|
||||||
GetIndString(message, rSTRS_Errors, kErrUnknown);
|
AppendError1(&str, ' ', kStrMessageBase - 1 + err1, strParam);
|
||||||
} else if (p->strParam != NULL) {
|
|
||||||
StrSubstitute(message, p->strParam);
|
|
||||||
}
|
}
|
||||||
if (p->osErr != NULL) {
|
if (err2 != kErrNone) {
|
||||||
StrAppendFormat(message, "\p\rError code: %d", p->osErr);
|
AppendError1(&str, ' ', kStrMessageBase - 1 + err2, strParam);
|
||||||
}
|
}
|
||||||
ParamText(message, NULL, NULL, NULL);
|
if (osErr != 0) {
|
||||||
Alert(rAlrtError, NULL);
|
AppendError2(&str, ' ', kStrErrorCode, NULL, osErr);
|
||||||
|
}
|
||||||
|
if (str.data[0] == 0) {
|
||||||
|
AppendError0(&str, ' ', kStrUnknown);
|
||||||
|
}
|
||||||
|
ShowErrorAlert(str.data, kStrOK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShowMemError(void)
|
||||||
|
{
|
||||||
|
ShowError(kErrOutOfMemory, kErrNone, MemError(), NULL);
|
||||||
}
|
}
|
||||||
|
@ -6,32 +6,34 @@
|
|||||||
|
|
||||||
// Error codes, corresponding to messages in a STR# resource. This should be
|
// Error codes, corresponding to messages in a STR# resource. This should be
|
||||||
// kept in sync with STR# rSTRS_Errors in resources.r.
|
// kept in sync with STR# rSTRS_Errors in resources.r.
|
||||||
typedef enum {
|
typedef enum ErrorCode {
|
||||||
// An unknown error occurred.
|
// (no error)
|
||||||
kErrUnknown = 1,
|
kErrNone,
|
||||||
// An internal error occurred.
|
|
||||||
kErrInternal,
|
|
||||||
// Out of memory.
|
// Out of memory.
|
||||||
kErrOutOfMemory,
|
kErrOutOfMemory,
|
||||||
// Could not save project "^1".
|
// Could not save project "^1".
|
||||||
kErrCouldNotSaveProject,
|
kErrCouldNotSaveProject,
|
||||||
|
// Could not read project "^1".
|
||||||
|
kErrCouldNotReadProject,
|
||||||
|
// The project file is damaged.
|
||||||
|
kErrProjectDamaged,
|
||||||
|
// The project file is from an unknown version of SyncFiles.
|
||||||
|
kErrProjectUnknownVersion,
|
||||||
|
// Could not query volume parameters.
|
||||||
|
kErrVolumeQuery,
|
||||||
|
// Could not create alias.
|
||||||
|
kErrAlias,
|
||||||
|
// Could not get directory path.
|
||||||
|
kErrDirPath
|
||||||
} ErrorCode;
|
} ErrorCode;
|
||||||
|
|
||||||
// ExitError shows an error dialog with the given error message, then quits the
|
// ExitAssert shows an assertion error and quits the program. Either the file or
|
||||||
// program.
|
// the assertion may be NULL.
|
||||||
void ExitError(ErrorCode errCode);
|
|
||||||
|
|
||||||
// ExitErrorOS shows an error dialog with the given error message and an OS
|
|
||||||
// error code, then quits the program.
|
|
||||||
void ExitErrorOS(ErrorCode errCode, short osErr);
|
|
||||||
|
|
||||||
// ExitMemError shows an out of memory error and quits the program.
|
|
||||||
void ExitMemError(void);
|
|
||||||
|
|
||||||
// ExitAssert shows an assertion error and quits the program. The message may be
|
|
||||||
// NULL.
|
|
||||||
void ExitAssert(const unsigned char *file, int line,
|
void ExitAssert(const unsigned char *file, int line,
|
||||||
const unsigned char *message);
|
const unsigned char *assertion);
|
||||||
|
|
||||||
|
// EXIT_INTERNAL shows an internal error message and quits the program.
|
||||||
|
#define EXIT_INTERNAL() ExitAssert("\p" __FILE__, __LINE__, NULL)
|
||||||
|
|
||||||
// EXIT_ASSERT shows an assertion error and quits the program. The message may
|
// EXIT_ASSERT shows an assertion error and quits the program. The message may
|
||||||
// be NULL.
|
// be NULL.
|
||||||
@ -45,24 +47,13 @@ void ExitAssert(const unsigned char *file, int line,
|
|||||||
ExitAssert("\p" __FILE__, __LINE__, "\p" #p); \
|
ExitAssert("\p" __FILE__, __LINE__, "\p" #p); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
// An ErrorParams contains the parameters for displaying en error alert window.
|
// ShowMemError shows an out of memory error to the user.
|
||||||
// This structure should be zeroed before use, in case additional fields are
|
void ShowMemError(void);
|
||||||
// added.
|
|
||||||
struct ErrorParams {
|
|
||||||
// The application error code. If this is zero, it will be treated as
|
|
||||||
// kErrInternal.
|
|
||||||
ErrorCode err;
|
|
||||||
|
|
||||||
// The OS error code. This is displayed at the end of the error message,
|
// ShowError shows an error alert window to the user. The error codes are
|
||||||
// unless it is zero.
|
// displayed if they are not 0. If osErr is not 0, then it is displayed as well.
|
||||||
short osErr;
|
// Any ^1 parameters in the error messages are replaced with strParam.
|
||||||
|
void ShowError(ErrorCode err1, ErrorCode err2, short osErr,
|
||||||
// If the error messages contain any string substitutions like "^1", those
|
const unsigned char *strParam);
|
||||||
// substitutions are replaced with this string.
|
|
||||||
const unsigned char *strParam;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ShowError shows an error alert window to the user.
|
|
||||||
void ShowError(const struct ErrorParams *p);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -130,11 +130,11 @@ void ProjectNew(void)
|
|||||||
|
|
||||||
project = (ProjectHandle)NewHandle(sizeof(struct Project));
|
project = (ProjectHandle)NewHandle(sizeof(struct Project));
|
||||||
if (project == NULL) {
|
if (project == NULL) {
|
||||||
EXIT_ASSERT(NULL);
|
EXIT_INTERNAL();
|
||||||
}
|
}
|
||||||
window = GetNewCWindow(rWIND_Project, NULL, (WindowPtr)-1);
|
window = GetNewCWindow(rWIND_Project, NULL, (WindowPtr)-1);
|
||||||
if (window == NULL) {
|
if (window == NULL) {
|
||||||
EXIT_ASSERT(NULL);
|
EXIT_INTERNAL();
|
||||||
}
|
}
|
||||||
windowWidth = window->portRect.right - window->portRect.left;
|
windowWidth = window->portRect.right - window->portRect.left;
|
||||||
SetWRefCon(window, (long)project);
|
SetWRefCon(window, (long)project);
|
||||||
@ -145,7 +145,7 @@ void ProjectNew(void)
|
|||||||
for (i = 0; i < 2; i++) {
|
for (i = 0; i < 2; i++) {
|
||||||
control = GetNewControl(rCNTL_ChooseFolder, window);
|
control = GetNewControl(rCNTL_ChooseFolder, window);
|
||||||
if (control == NULL) {
|
if (control == NULL) {
|
||||||
EXIT_ASSERT(NULL);
|
EXIT_INTERNAL();
|
||||||
}
|
}
|
||||||
controlWidth =
|
controlWidth =
|
||||||
(*control)->contrlRect.right - (*control)->contrlRect.left;
|
(*control)->contrlRect.right - (*control)->contrlRect.left;
|
||||||
@ -453,7 +453,6 @@ static void ProjectSave(WindowRef window, ProjectHandle project,
|
|||||||
Str255 name;
|
Str255 name;
|
||||||
StandardFileReply reply;
|
StandardFileReply reply;
|
||||||
OSErr err;
|
OSErr err;
|
||||||
struct ErrorParams errp;
|
|
||||||
FSSpec spec;
|
FSSpec spec;
|
||||||
ScriptCode script;
|
ScriptCode script;
|
||||||
|
|
||||||
@ -483,11 +482,7 @@ static void ProjectSave(WindowRef window, ProjectHandle project,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
memset(&errp, 0, sizeof(errp));
|
ShowError(kErrCouldNotSaveProject, kErrNone, err, reply.sfFile.name);
|
||||||
errp.err = kErrCouldNotSaveProject;
|
|
||||||
errp.osErr = err;
|
|
||||||
errp.strParam = reply.sfFile.name;
|
|
||||||
ShowError(&errp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectCommand(WindowRef window, ProjectHandle project, int menuID,
|
void ProjectCommand(WindowRef window, ProjectHandle project, int menuID,
|
||||||
@ -569,7 +564,8 @@ static Boolean VolumeDoesSupportIDs(short vRefNum)
|
|||||||
hb.ioParam.ioReqCount = sizeof(parms);
|
hb.ioParam.ioReqCount = sizeof(parms);
|
||||||
err = PBHGetVolParms(&hb, false);
|
err = PBHGetVolParms(&hb, false);
|
||||||
if (err != noErr) {
|
if (err != noErr) {
|
||||||
ExitErrorOS(kErrUnknown, err);
|
ShowError(kErrVolumeQuery, kErrNone, err, NULL);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return (parms.vMAttrib & (1ul << bHasFileIDs)) != 0;
|
return (parms.vMAttrib & (1ul << bHasFileIDs)) != 0;
|
||||||
}
|
}
|
||||||
@ -591,15 +587,21 @@ static void ProjectChooseDir(WindowRef window, ProjectHandle project,
|
|||||||
if (VolumeDoesSupportIDs(directory.vRefNum)) {
|
if (VolumeDoesSupportIDs(directory.vRefNum)) {
|
||||||
err = NewAlias(NULL, &directory, &alias);
|
err = NewAlias(NULL, &directory, &alias);
|
||||||
if (err != noErr) {
|
if (err != noErr) {
|
||||||
ExitErrorOS(kErrUnknown, err);
|
ShowError(kErrAlias, kErrNone, err, NULL);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
alias = NULL;
|
alias = NULL;
|
||||||
}
|
}
|
||||||
err = GetDirPath(&directory, &pathLength, &path);
|
err = GetDirPath(&directory, &pathLength, &path);
|
||||||
if (err != noErr) {
|
if (err != noErr) {
|
||||||
ExitErrorOS(kErrUnknown, err);
|
ShowError(kErrDirPath, kErrNone, err, NULL);
|
||||||
|
if (alias != NULL) {
|
||||||
|
DisposeHandle((Handle)alias);
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
projectp = *project;
|
projectp = *project;
|
||||||
dirp = &projectp->dirs[whichDir];
|
dirp = &projectp->dirs[whichDir];
|
||||||
if (dirp->path != NULL) {
|
if (dirp->path != NULL) {
|
||||||
|
@ -5,6 +5,12 @@
|
|||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
void PStrInit(struct PStrBuilder *buf)
|
||||||
|
{
|
||||||
|
buf->data[0] = 0;
|
||||||
|
buf->truncated = 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void PStrAppendMem(struct PStrBuilder *buf, const unsigned char *src,
|
static void PStrAppendMem(struct PStrBuilder *buf, const unsigned char *src,
|
||||||
int slen)
|
int slen)
|
||||||
{
|
{
|
||||||
@ -35,6 +41,11 @@ static void PStrAppendMem(struct PStrBuilder *buf, const unsigned char *src,
|
|||||||
buf->data[0] = dlen + slen;
|
buf->data[0] = dlen + slen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PStrAppendChar(struct PStrBuilder *buf, unsigned char c)
|
||||||
|
{
|
||||||
|
PStrAppendMem(buf, &c, 1);
|
||||||
|
}
|
||||||
|
|
||||||
void PStrAppend(struct PStrBuilder *buf, const unsigned char *src)
|
void PStrAppend(struct PStrBuilder *buf, const unsigned char *src)
|
||||||
{
|
{
|
||||||
PStrAppendMem(buf, src + 1, src[0]);
|
PStrAppendMem(buf, src + 1, src[0]);
|
||||||
|
@ -29,6 +29,12 @@ struct PStrBuilder {
|
|||||||
Boolean truncated;
|
Boolean truncated;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// PStrInit initializes a string buffer.
|
||||||
|
void PStrInit(struct PStrBuilder *buf);
|
||||||
|
|
||||||
|
// PStrAppendChar appends a single character to the buffer.
|
||||||
|
void PStrAppendChar(struct PStrBuilder *buf, unsigned char c);
|
||||||
|
|
||||||
// PStrAppend appends a string to the buffer.
|
// PStrAppend appends a string to the buffer.
|
||||||
void PStrAppend(struct PStrBuilder *buf, const unsigned char *src);
|
void PStrAppend(struct PStrBuilder *buf, const unsigned char *src);
|
||||||
|
|
||||||
|
@ -119,10 +119,21 @@ resource 'BNDL' (128, purgeable) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
resource 'STR#' (rSTRS_Errors) {{
|
resource 'STR#' (rSTRS_Errors) {{
|
||||||
"An unknown error occurred.",
|
"OK",
|
||||||
|
"Quit",
|
||||||
|
"An error of type ^2 occurred.",
|
||||||
|
"Error at: ^1:^2",
|
||||||
|
"Assertion: ^1",
|
||||||
"An internal error occurred.",
|
"An internal error occurred.",
|
||||||
|
"An unknown error occurred.",
|
||||||
"Out of memory.",
|
"Out of memory.",
|
||||||
"Could not save project “^1”.",
|
"Could not save project “^1”.",
|
||||||
|
"Could not read project “^1”.",
|
||||||
|
"The project file is damaged.",
|
||||||
|
"The project file is from an unknown version of SyncFiles.",
|
||||||
|
"Could not query volume parameters.",
|
||||||
|
"Could not create alias.",
|
||||||
|
"Could not get directory path.",
|
||||||
}};
|
}};
|
||||||
|
|
||||||
resource 'WIND' (rWIND_Project, preload, purgeable) {
|
resource 'WIND' (rWIND_Project, preload, purgeable) {
|
||||||
|
Loading…
Reference in New Issue
Block a user