AmendHub

Download:

jcs

/

amend

/

amendments

/

146

settings: Move storage from resources to a separate preferences file

Keeping settings bundled with the app was cleaner but it meant they
would get reset every time the app was updated.
 
On System 7+ these will go in System Folder:Preferences, on System 6
it will go in System Folder.

jcs made amendment 146 over 2 years ago
--- settings.c Thu Jan 11 15:19:35 2024 +++ settings.c Thu Mar 21 08:48:51 2024 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 joshua stein <jcs@jcs.org> + * Copyright (c) 2021-2024 joshua stein <jcs@jcs.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,172 +14,359 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <OSUtils.h> +#include <Files.h> +#include <Folders.h> +#include <GestaltEqu.h> +#include <Traps.h> +#include <stddef.h> #include <stdio.h> #include <string.h> +#include <time.h> #include "amend.h" #include "settings.h" #include "tetab.h" #include "util.h" -struct settings settings = { 0 }; +/* used in pre-2.8 */ +#define STR_AUTHOR_ID 128 +#define STR_TABWIDTH_ID 129 +#define STR_MAX_AMENDMENTS_ID 130 +enum { + SETTING_STRING, + SETTING_SHORT, + SETTING_USHORT, + SETTING_LONG, + SETTING_PASSWORD, + SETTING_BOOL +}; + +struct setting { + char name[32]; + short type; + long min; + long max; + char sdefault[32]; + short ditl_id; + unsigned long offset; + unsigned long size; + char password_storage[256]; +} settings_defs[] = { + { "Author", SETTING_STRING, 1, 0, "unknown", + SETTINGS_AUTHOR_ID, offsetof(struct settings, author), + member_size(struct settings, author) }, + { "Tab Width", SETTING_USHORT, 1, 32, "4", + SETTINGS_TABWIDTH_ID, offsetof(struct settings, tabwidth), + member_size(struct settings, tabwidth) }, + { "Max Amendments to Load", SETTING_USHORT, 1, 65535, "100", + SETTINGS_MAX_AMENDMENTS_ID, offsetof(struct settings, max_amendments), + member_size(struct settings, max_amendments) }, +}; + +void settings_find_prefs_folder(short *vrefnum, long *dirid); +void settings_load(void); + void settings_load(void) { - char *author; + HParamBlockRec pb = { 0 }; + struct settings tsettings = { 0 }; + char fn[] = SETTINGS_FILENAME; + char *res; + short error, vrefnum, prefrefnum; + long len, dirid; + Handle h; + bool use_defaults = true; - author = xGetStringAsChar(STR_AUTHOR_ID); - strlcpy(settings.author, author, sizeof(settings.author)); - xfree(&author); - if (settings.author[0] == '\0') - snprintf(settings.author, sizeof(settings.author), "unknown"); + GetSystemSubfolder(kPreferencesFolderType, true, &vrefnum, &dirid); + + pb.ioParam.ioNamePtr = (StringPtr)&fn; + pb.ioParam.ioVRefNum = vrefnum; + pb.ioParam.ioPermssn = fsRdPerm; + pb.fileParam.ioDirID = dirid; + error = PBHOpen(&pb, false); + if (error) { + if (error != fnfErr) + panic("Failed reading preferences file: %d", error); + + settings_edit(use_defaults); + return; + } - settings.tabwidth = (short)xGetStringAsLong(STR_TABWIDTH_ID); - if (settings.tabwidth < 1 || settings.tabwidth > 20) - settings.tabwidth = 4; + len = sizeof(short); + FSRead(pb.ioParam.ioRefNum, &len, &(tsettings.version)); + SetFPos(pb.ioParam.ioRefNum, fsFromStart, 0); - settings.max_amendments = (short)xGetStringAsLong(STR_MAX_AMENDMENTS_ID); - if (settings.max_amendments < 0) - settings.max_amendments = 0; + switch (tsettings.version) { + case 1: + len = sizeof(struct settings); + FSRead(pb.ioParam.ioRefNum, &len, &tsettings); + if (len != sizeof(struct settings)) + warn("Short read of preferences file, may be corrupt"); + break; + default: + FSClose(pb.ioParam.ioRefNum); + + if (ask("Unknown preferences version (%d), reset?")) { + settings_edit(true); + return; + } else + ExitToShell(); + } + FSClose(pb.ioParam.ioRefNum); + + settings = tsettings; TETabWidth = settings.tabwidth; } void -settings_save(void) +settings_save(struct settings *tsettings) { - Handle res; - size_t l; - char tmp[8]; + HParamBlockRec pb = { 0 }; + char fn[] = SETTINGS_FILENAME; + short error, vrefnum, prefrefnum; + long dirid, len; + Handle h; - res = xGetResource('STR ', STR_AUTHOR_ID); - HLock(res); - l = strlen(settings.author); - xSetHandleSize(res, l + 1); - memcpy(*res, settings.author, l + 1); - CtoPstr(*res); - ChangedResource(res); - ReleaseResource(res); + GetSystemSubfolder(kPreferencesFolderType, true, &vrefnum, &dirid); - res = xGetResource('STR ', STR_TABWIDTH_ID); - HLock(res); - snprintf(tmp, sizeof(tmp), "%d", settings.tabwidth); - l = strlen(tmp); - xSetHandleSize(res, l + 1); - memcpy(*res, tmp, l + 1); - CtoPstr(*res); - ChangedResource(res); - ReleaseResource(res); + pb.ioParam.ioNamePtr = (StringPtr)&fn; + pb.ioParam.ioVRefNum = vrefnum; + pb.fileParam.ioDirID = dirid; + error = PBHCreate(&pb, false); + if (error && error != dupFNErr) + goto create_failed; + if (!error) { + memset(&pb, 0, sizeof(pb)); + pb.ioParam.ioNamePtr = (StringPtr)&fn; + pb.ioParam.ioVRefNum = vrefnum; + pb.fileParam.ioDirID = dirid; + if ((error = PBHGetFInfo(&pb, false))) + goto create_failed; + + pb.fileParam.ioDirID = dirid; /* required after PBHGetFInfo */ + pb.fileParam.ioFlFndrInfo.fdType = 'pref'; + pb.fileParam.ioFlFndrInfo.fdCreator = AMEND_CREATOR; + if ((error = PBHSetFInfo(&pb, false))) + goto create_failed; + + memset(&pb, 0, sizeof(pb)); + pb.ioParam.ioVRefNum = vrefnum; + PBFlushVol(&pb, false); + } - res = xGetResource('STR ', STR_MAX_AMENDMENTS_ID); - HLock(res); - snprintf(tmp, sizeof(tmp), "%d", settings.max_amendments); - l = strlen(tmp); - xSetHandleSize(res, l + 1); - memcpy(*res, tmp, l + 1); - CtoPstr(*res); - ChangedResource(res); - ReleaseResource(res); + memset(&pb, 0, sizeof(pb)); + pb.ioParam.ioNamePtr = (StringPtr)&fn; + pb.ioParam.ioVRefNum = vrefnum; + pb.ioParam.ioPermssn = fsWrPerm; + pb.fileParam.ioDirID = dirid; + error = PBHOpen(&pb, false); + if (error) + goto create_failed; + + len = sizeof(struct settings); + tsettings->version = SETTINGS_VERSION; + FSWrite(pb.ioParam.ioRefNum, &len, tsettings); + FSClose(pb.ioParam.ioRefNum); - UpdateResFile(CurResFile()); - if (ResError()) - warn("UpdateResFile failed! %d", ResError()); + TETabWidth = tsettings->tabwidth; - TETabWidth = settings.tabwidth; + return; + +create_failed: + panic("Failed creating preferences file: %d", error); } void -settings_edit(void) +settings_edit(bool use_defaults) { - Str255 txt; - Handle ihandle; - size_t len; - short itype, hit; - bool done; + struct settings tsettings = settings; + struct setting *s; + DialogTHndl dlgh; DialogPtr dlg; - DialogTHndl dhand; - DialogTPtr dp; + Handle ihandle; Rect irect; - - dhand = (DialogTHndl)GetResource('DLOG', SETTINGS_DLOG_ID); - if (dhand == NULL) - panic("No settings dialog"); - LoadResource((Handle)dhand); + size_t size, n, m; + long lval; + bool bval, save; + short hit, itype, ret; + char *sdata, *strval; + Str255 stmp; - HLock((Handle)dhand); - dp = (DialogTPtr)(*dhand); - center_in_screen(dp->boundsRect.right - dp->boundsRect.left, - dp->boundsRect.bottom - dp->boundsRect.top, false, &dp->boundsRect); - HUnlock((Handle)dhand); + /* center dialog in screen */ + dlgh = (DialogTHndl)xGetResource('DLOG', SETTINGS_DLOG_ID); + HLock(dlgh); + center_in_screen((**dlgh).boundsRect.right - (**dlgh).boundsRect.left, + (**dlgh).boundsRect.bottom - (**dlgh).boundsRect.top, + true, &(**dlgh).boundsRect); + HUnlock(dlgh); + + if ((dlg = GetNewDialog(SETTINGS_DLOG_ID, nil, (WindowPtr)-1)) == NULL) + panic("Can't find settings DLOG %d", SETTINGS_DLOG_ID); + + for (n = 0; n < nitems(settings_defs); n++) { + s = &settings_defs[n]; + sdata = (char *)&tsettings + s->offset; - dlg = GetNewDialog(SETTINGS_DLOG_ID, nil, (WindowPtr)-1); - if (dlg == NULL) - err(1, "Can't find settings DLOG %d", SETTINGS_DLOG_ID); + GetDItem(dlg, s->ditl_id, &itype, &ihandle, &irect); - GetDItem(dlg, SETTINGS_AUTHOR_ID, &itype, &ihandle, &irect); - strlcpy((char *)txt, settings.author, sizeof(txt)); - CtoPstr(txt); - SetIText(ihandle, txt); + switch (s->type) { + case SETTING_PASSWORD: + PasswordDialogFieldFilterSetup(s->ditl_id, + (char *)s->password_storage, sizeof(s->password_storage)); - GetDItem(dlg, SETTINGS_TABWIDTH_ID, &itype, &ihandle, &irect); - snprintf((char *)txt, sizeof(txt), "%d", settings.tabwidth); - CtoPstr(txt); - SetIText(ihandle, txt); + strval = (use_defaults ? (char *)s->sdefault : sdata); + size = strlen(strval); + if (size >= sizeof(s->password_storage) - 1) + size = 0; + + /* show a masked value */ + stmp[0] = size; + for (m = 1; m <= size; m++) + stmp[m] = '•'; + SetIText(ihandle, stmp); + + /* but store the actual password */ + memcpy(s->password_storage, strval, size); + s->password_storage[size] = '\0'; + break; + case SETTING_STRING: + strval = (use_defaults ? s->sdefault : sdata); + memcpy(stmp, strval, sizeof(stmp)); + stmp[sizeof(stmp) - 1] = '\0'; + CtoPstr(stmp); + SetIText(ihandle, stmp); + break; + case SETTING_SHORT: + case SETTING_USHORT: + case SETTING_LONG: + if (use_defaults) { + memcpy(stmp, s->sdefault, sizeof(stmp)); + stmp[sizeof(stmp) - 1] = '\0'; + } else if (s->type == SETTING_SHORT) { + snprintf((char *)stmp, sizeof(stmp), "%d", + BYTES_TO_SHORT(sdata[0], sdata[1])); + } else if (s->type == SETTING_USHORT) { + snprintf((char *)stmp, sizeof(stmp), "%u", + BYTES_TO_SHORT(sdata[0], sdata[1])); + } else if (s->type == SETTING_LONG) { + snprintf((char *)stmp, sizeof(stmp), "%ld", + BYTES_TO_LONG(sdata[0], sdata[1], sdata[2], sdata[3])); + } + CtoPstr(stmp); + SetIText(ihandle, stmp); + break; + case SETTING_BOOL: + SetCtlValue(ihandle, (use_defaults ? s->sdefault[0] : + sdata[0]) == 1); + break; + default: + panic("unknown setting type %d", s->type); + } + } - GetDItem(dlg, SETTINGS_MAX_AMENDMENTS_ID, &itype, &ihandle, &irect); - snprintf((char *)txt, sizeof(txt), "%d", settings.max_amendments); - CtoPstr(txt); - SetIText(ihandle, txt); - ShowWindow(dlg); + GetDItem(dlg, ok, &itype, &ihandle, &irect); -dialog_wait: - done = false; - while (!done) { - ModalDialog(nil, &hit); - switch (hit) { - case SETTINGS_SAVE_ID: - done = true; +get_input: + outline_button((ControlHandle)ihandle); + ModalDialog(PasswordDialogFieldFilter, &hit); + switch (hit) { + case -1: + case cancel: + goto settings_done; + case OK: + goto verify; + default: + goto get_input; + } + + save = false; +verify: + for (n = 0; n < nitems(settings_defs); n++) { + s = &settings_defs[n]; + sdata = (char *)&tsettings + s->offset; + + GetDItem(dlg, s->ditl_id, &itype, &ihandle, &irect); + + if (s->type == SETTING_PASSWORD) { + memcpy((char *)&stmp, s->password_storage, sizeof(stmp)); + } else if (s->type == SETTING_BOOL) { + snprintf((char *)&stmp, sizeof(stmp), "%d", + GetCtlValue(ihandle)); + } else { + GetIText(ihandle, stmp); + PtoCstr(stmp); + } + + switch (s->type) { + case SETTING_STRING: + case SETTING_PASSWORD: + if (s->min && strlen((char *)stmp) < s->min) { + warn("%s is too short (minimum %ld)", s->name, s->min); + goto get_input; + } + if (s->max && strlen((char *)stmp) > s->max) { + warn("%s is too long (maximum %ld)", s->name, s->max); + goto get_input; + } + + if (save) { + memset(sdata, 0, s->size); + strlcpy(sdata, (char *)stmp, s->size); + } break; - case SETTINGS_CANCEL_ID: - goto dialog_close; + case SETTING_SHORT: + case SETTING_USHORT: + case SETTING_LONG: + lval = atol((char *)stmp); + if (lval < s->min) { + if (s->type == SETTING_USHORT) + warn("%s must be at least %ul", s->name, s->min); + else + warn("%s must be at least %ld", s->name, s->min); + goto get_input; + } + if (lval > s->max) { + if (s->type == SETTING_USHORT) + warn("%s must be less than %ul", s->name, s->max); + else + warn("%s must be less than %ld", s->name, s->max); + goto get_input; + } + if (save) { + if (s->type == SETTING_LONG) { + sdata[0] = (lval >> 24) & 0xff; + sdata[1] = (lval >> 16) & 0xff; + sdata[2] = (lval >> 8) & 0xff; + sdata[3] = lval & 0xff; + } else { + sdata[0] = (lval >> 8) & 0xff; + sdata[1] = lval & 0xff; + } + } + break; + case SETTING_BOOL: + if (save) + sdata[0] = (stmp[0] == '1'); + break; } } - GetDItem(dlg, SETTINGS_AUTHOR_ID, &itype, &ihandle, &irect); - GetIText(ihandle, txt); - PtoCstr(txt); - len = strlen((char *)txt); - if (len >= sizeof(settings.author)) { - warn("Author setting is too long (%ld max)", - sizeof(settings.author) - 1); - goto dialog_wait; - } else if (len == 0) { - warn("Author cannot be blank"); - goto dialog_wait; + if (!save) { + save = true; + goto verify; } - strlcpy(settings.author, (char *)txt, sizeof(settings.author)); - - GetDItem(dlg, SETTINGS_TABWIDTH_ID, &itype, &ihandle, &irect); - GetIText(ihandle, txt); - PtoCstr(txt); - settings.tabwidth = atoi((char *)&txt); - if (settings.tabwidth < 1) - settings.tabwidth = 1; - HUnlock(ihandle); - - GetDItem(dlg, SETTINGS_MAX_AMENDMENTS_ID, &itype, &ihandle, &irect); - GetIText(ihandle, txt); - PtoCstr(txt); - settings.max_amendments = atoi((char *)&txt); - if (settings.max_amendments < 0) - settings.max_amendments = 0; - HUnlock(ihandle); + /* all validated ok and fields written */ + settings_save(&tsettings); + settings = tsettings; - settings_save(); - -dialog_close: +settings_done: + PasswordDialogFieldFinish(); DisposeDialog(dlg); -} \ No newline at end of file + ReleaseResource(dlgh); +} --- settings.h Thu May 4 17:18:28 2023 +++ settings.h Wed Mar 20 15:26:31 2024 @@ -17,7 +17,13 @@ #ifndef __SETTINGS_H__ #define __SETTINGS_H__ +#include "stdint.h" + +#define SETTINGS_FILENAME "\pAmend Preferences" + struct settings { + short version; +#define SETTINGS_VERSION 1 char author[32]; short tabwidth; short max_amendments; @@ -26,7 +32,7 @@ struct settings { extern struct settings settings; void settings_load(void); -void settings_save(void); -void settings_edit(void); +void settings_save(struct settings *tsettings); +void settings_edit(bool use_defaults); #endif \ No newline at end of file