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:
Dietrich Epp 2023-05-06 19:41:40 -04:00
parent 56a3983129
commit b1bcae531b
7 changed files with 163 additions and 94 deletions

View File

@ -47,7 +47,8 @@ static void SelectCurrentDirectory(struct ChooseDirReply *reply)
ci.dirInfo.ioDrDirID = LMGetCurDirStore();
err = PBGetCatInfoSync(&ci);
if (err != 0) {
ExitErrorOS(kErrUnknown, err);
ShowError(kErrNone, kErrNone, err, NULL);
return;
}
directory = reply->directory;
directory->vRefNum = ci.dirInfo.ioVRefNum;
@ -99,10 +100,10 @@ Boolean ChooseDirectory(FSSpec *directory)
switch (cdreply.status) {
case kCDChild:
BlockMoveData(&sfreply.sfFile, directory, sizeof(FSSpec));
return 1;
return true;
case kCDCurrent:
return 1;
return true;
default:
return 0;
return false;
}
}

View File

@ -3,73 +3,120 @@
// Mozilla Public License, version 2.0. See LICENSE.txt for details.
#include "macos/error.h"
#include "lib/defs.h"
#include "macos/main.h"
#include "macos/pstrbuilder.h"
#include "macos/resources.h"
#include "macos/strutil.h"
#include <Dialogs.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;
GetIndString(message, rSTRS_Errors, errCode);
if (message[0] == 0) {
GetIndString(message, rSTRS_Errors, kErrUnknown);
if (str->data[0] > 0 && sep != 0) {
PStrAppendChar(str, sep);
}
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;
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();
AppendErrorArray(str, sep, strNum, 0, NULL);
}
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,
const unsigned char *message)
const unsigned char *assertion)
{
Str255 dmessage;
struct PStrBuilder str;
GetIndString(dmessage, rSTRS_Errors, kErrInternal);
StrAppendFormat(dmessage, "\p\rError at: %S:%d", file, line);
if (message != NULL) {
StrAppendFormat(dmessage, "\p: %S", message);
PStrInit(&str);
AppendError0(&str, 0, kStrInternal);
if (file != NULL) {
AppendError2(&str, '\r', kStrErrorAt, file, line);
}
ParamText(dmessage, NULL, NULL, NULL);
Alert(rAlrtError, NULL);
if (assertion != NULL) {
AppendError1(&str, '\r', kStrAssertion, assertion);
}
ShowErrorAlert(str.data, kStrQuit);
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);
if (message[0] == 0) {
GetIndString(message, rSTRS_Errors, kErrUnknown);
} else if (p->strParam != NULL) {
StrSubstitute(message, p->strParam);
PStrInit(&str);
if (err1 != kErrNone) {
AppendError1(&str, ' ', kStrMessageBase - 1 + err1, strParam);
}
if (p->osErr != NULL) {
StrAppendFormat(message, "\p\rError code: %d", p->osErr);
if (err2 != kErrNone) {
AppendError1(&str, ' ', kStrMessageBase - 1 + err2, strParam);
}
ParamText(message, NULL, NULL, NULL);
Alert(rAlrtError, NULL);
if (osErr != 0) {
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);
}

View File

@ -6,32 +6,34 @@
// Error codes, corresponding to messages in a STR# resource. This should be
// kept in sync with STR# rSTRS_Errors in resources.r.
typedef enum {
// An unknown error occurred.
kErrUnknown = 1,
// An internal error occurred.
kErrInternal,
typedef enum ErrorCode {
// (no error)
kErrNone,
// Out of memory.
kErrOutOfMemory,
// Could not save project "^1".
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;
// ExitError shows an error dialog with the given error message, then quits the
// program.
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.
// ExitAssert shows an assertion error and quits the program. Either the file or
// the assertion may be NULL.
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
// be NULL.
@ -45,24 +47,13 @@ void ExitAssert(const unsigned char *file, int line,
ExitAssert("\p" __FILE__, __LINE__, "\p" #p); \
} while (0)
// An ErrorParams contains the parameters for displaying en error alert window.
// This structure should be zeroed before use, in case additional fields are
// added.
struct ErrorParams {
// The application error code. If this is zero, it will be treated as
// kErrInternal.
ErrorCode err;
// ShowMemError shows an out of memory error to the user.
void ShowMemError(void);
// The OS error code. This is displayed at the end of the error message,
// unless it is zero.
short osErr;
// If the error messages contain any string substitutions like "^1", those
// 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);
// ShowError shows an error alert window to the user. The error codes are
// displayed if they are not 0. If osErr is not 0, then it is displayed as well.
// Any ^1 parameters in the error messages are replaced with strParam.
void ShowError(ErrorCode err1, ErrorCode err2, short osErr,
const unsigned char *strParam);
#endif

View File

@ -130,11 +130,11 @@ void ProjectNew(void)
project = (ProjectHandle)NewHandle(sizeof(struct Project));
if (project == NULL) {
EXIT_ASSERT(NULL);
EXIT_INTERNAL();
}
window = GetNewCWindow(rWIND_Project, NULL, (WindowPtr)-1);
if (window == NULL) {
EXIT_ASSERT(NULL);
EXIT_INTERNAL();
}
windowWidth = window->portRect.right - window->portRect.left;
SetWRefCon(window, (long)project);
@ -145,7 +145,7 @@ void ProjectNew(void)
for (i = 0; i < 2; i++) {
control = GetNewControl(rCNTL_ChooseFolder, window);
if (control == NULL) {
EXIT_ASSERT(NULL);
EXIT_INTERNAL();
}
controlWidth =
(*control)->contrlRect.right - (*control)->contrlRect.left;
@ -453,7 +453,6 @@ static void ProjectSave(WindowRef window, ProjectHandle project,
Str255 name;
StandardFileReply reply;
OSErr err;
struct ErrorParams errp;
FSSpec spec;
ScriptCode script;
@ -483,11 +482,7 @@ static void ProjectSave(WindowRef window, ProjectHandle project,
return;
error:
memset(&errp, 0, sizeof(errp));
errp.err = kErrCouldNotSaveProject;
errp.osErr = err;
errp.strParam = reply.sfFile.name;
ShowError(&errp);
ShowError(kErrCouldNotSaveProject, kErrNone, err, reply.sfFile.name);
}
void ProjectCommand(WindowRef window, ProjectHandle project, int menuID,
@ -569,7 +564,8 @@ static Boolean VolumeDoesSupportIDs(short vRefNum)
hb.ioParam.ioReqCount = sizeof(parms);
err = PBHGetVolParms(&hb, false);
if (err != noErr) {
ExitErrorOS(kErrUnknown, err);
ShowError(kErrVolumeQuery, kErrNone, err, NULL);
return false;
}
return (parms.vMAttrib & (1ul << bHasFileIDs)) != 0;
}
@ -591,15 +587,21 @@ static void ProjectChooseDir(WindowRef window, ProjectHandle project,
if (VolumeDoesSupportIDs(directory.vRefNum)) {
err = NewAlias(NULL, &directory, &alias);
if (err != noErr) {
ExitErrorOS(kErrUnknown, err);
ShowError(kErrAlias, kErrNone, err, NULL);
return;
}
} else {
alias = NULL;
}
err = GetDirPath(&directory, &pathLength, &path);
if (err != noErr) {
ExitErrorOS(kErrUnknown, err);
ShowError(kErrDirPath, kErrNone, err, NULL);
if (alias != NULL) {
DisposeHandle((Handle)alias);
}
return;
}
projectp = *project;
dirp = &projectp->dirs[whichDir];
if (dirp->path != NULL) {

View File

@ -5,6 +5,12 @@
#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,
int slen)
{
@ -35,6 +41,11 @@ static void PStrAppendMem(struct PStrBuilder *buf, const unsigned char *src,
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)
{
PStrAppendMem(buf, src + 1, src[0]);

View File

@ -29,6 +29,12 @@ struct PStrBuilder {
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.
void PStrAppend(struct PStrBuilder *buf, const unsigned char *src);

View File

@ -119,10 +119,21 @@ resource 'BNDL' (128, purgeable) {
};
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 unknown error occurred.",
"Out of memory.",
"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) {