From b1bcae531b45f6c6a5129851bf086322d970a226 Mon Sep 17 00:00:00 2001 From: Dietrich Epp Date: Sat, 6 May 2023 19:41:40 -0400 Subject: [PATCH] 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()`. --- macos/choose_directory.c | 9 +-- macos/error.c | 127 +++++++++++++++++++++++++++------------ macos/error.h | 65 +++++++++----------- macos/project.c | 26 ++++---- macos/pstrbuilder.c | 11 ++++ macos/pstrbuilder.h | 6 ++ macos/resources.r | 13 +++- 7 files changed, 163 insertions(+), 94 deletions(-) diff --git a/macos/choose_directory.c b/macos/choose_directory.c index 45591d1..0fae2cb 100644 --- a/macos/choose_directory.c +++ b/macos/choose_directory.c @@ -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; } } diff --git a/macos/error.c b/macos/error.c index 8239062..35b4a16 100644 --- a/macos/error.c +++ b/macos/error.c @@ -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 #include -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); } diff --git a/macos/error.h b/macos/error.h index 61247be..ea61257 100644 --- a/macos/error.h +++ b/macos/error.h @@ -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 diff --git a/macos/project.c b/macos/project.c index 666a379..9177110 100644 --- a/macos/project.c +++ b/macos/project.c @@ -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) { diff --git a/macos/pstrbuilder.c b/macos/pstrbuilder.c index 70f5d74..9919e09 100644 --- a/macos/pstrbuilder.c +++ b/macos/pstrbuilder.c @@ -5,6 +5,12 @@ #include +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]); diff --git a/macos/pstrbuilder.h b/macos/pstrbuilder.h index 7fb4cb0..598169b 100644 --- a/macos/pstrbuilder.h +++ b/macos/pstrbuilder.h @@ -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); diff --git a/macos/resources.r b/macos/resources.r index e96623d..7bffd07 100644 --- a/macos/resources.r +++ b/macos/resources.r @@ -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) {