jcs
/wallops
/amendments
/57
settings: Move storage to preferences file
Storing in resources in the application was cleaner but each app
update would wipe them out. Since there is possibly a server
password being stored, a user might not clear the settings out
before sharing the binary.
Save the preferences file in the System folder on System 6, or
System:Preferences on System 7+.
jcs made amendment 57 3 months ago
--- chatter.c Fri Aug 30 16:38:16 2024
+++ chatter.c Fri Aug 30 20:08:34 2024
@@ -61,7 +61,7 @@ chatter_init(const char *server, const unsigned short
struct chatter_tab *tab;
char title[64];
Rect bounds = { 0 };
- short padding = 20;
+ short padding = 20, width, height;
GetIndPattern(&tab_bar_pattern, sysPatListID, 23);
@@ -76,12 +76,12 @@ chatter_init(const char *server, const unsigned short
return NULL;
}
- bounds.left = padding;
- bounds.top = screenBits.bounds.top + padding +
- (GetMBarHeight() * 2) - 1;
- bounds.right = screenBits.bounds.right - padding - 1;
-// bounds.bottom = screenBits.bounds.bottom - padding - 1;
- bounds.bottom = (screenBits.bounds.bottom / 2) - padding - 1;
+ width = screenBits.bounds.right - screenBits.bounds.left - padding;
+ width = MIN(width, 640);
+ height = screenBits.bounds.bottom - screenBits.bounds.top -
+ padding - (GetMBarHeight() * 2);
+ height = MIN(height, 350);
+ center_in_screen(width, height, true, &bounds);
snprintf(title, sizeof(title), "%s: Disconnected", PROGRAM_NAME);
chatter->win = NewWindow(0L, &bounds, CtoPstr(title), false,
--- chatter.h Fri Sep 29 21:12:39 2023
+++ chatter.h Fri Aug 30 17:22:56 2024
@@ -44,32 +44,6 @@
#define EDIT_MENU_COPY_ID 2
#define EDIT_MENU_PASTE_ID 3
-#define CONNECT_DLOG_ID 129
-#define CONNECT_SERVER_ID 2
-#define CONNECT_PORT_ID 3
-#define CONNECT_PASSWORD_ID 4
-#define CONNECT_NICK_ID 5
-#define CONNECT_IDENT_ID 6
-#define CONNECT_REALNAME_ID 7
-#define CONNECT_HIDE_MOTD_ID 8
-#define CONNECT_CHANNEL_ID 9
-
-#define STR_SERVER_ID 1000
-#define STR_PORT_ID 1001
-#define STR_NICK_ID 1002
-#define STR_IDENT_ID 1003
-#define STR_REALNAME_ID 1004
-#define STR_CHANNEL_ID 1005
-#define STR_PASSWORD_ID 1006
-#define STR_HIDE_MOTD_ID 1007
-
-#define DEFAULT_SERVER_NAME "irc.libera.chat"
-#define DEFAULT_PORT "6667"
-#define DEFAULT_IDENT "wallops"
-#define DEFAULT_REALNAME "A Macintosh User"
-#define DEFAULT_CHANNEL "#cyberpals"
-#define DEFAULT_HIDE_MOTD "0"
-
#define WAIT_TYPE_NONE (1 << 0)
#define WAIT_TYPE_BACKGROUND (1 << 1)
#define WAIT_TYPE_FOREGROUND (1 << 2)
--- main.c Mon Jan 1 10:41:33 2024
+++ main.c Fri Aug 30 19:21:35 2024
@@ -17,53 +17,20 @@
#include <string.h>
#include "chatter.h"
#include "irc.h"
+#include "settings.h"
#include "tcp.h"
#include "util.h"
MenuHandle apple_menu, file_menu;
NMRec notification = { 0 };
+struct settings settings;
-enum {
- CONFIG_TYPE_STRING,
- CONFIG_TYPE_SHORT,
- CONFIG_TYPE_PASSWORD,
- CONFIG_TYPE_BOOL
-};
-
-struct config_field {
- char name[32];
- short type;
- unsigned short min;
- unsigned short max;
- short ditl_id;
- short res_id;
- char sdefault[32];
- char password_storage[256];
-} config_fields[] = {
- { "Server Name", CONFIG_TYPE_STRING, 1, 0,
- CONNECT_SERVER_ID, STR_SERVER_ID, DEFAULT_SERVER_NAME },
- { "Server Port", CONFIG_TYPE_SHORT, 1, 65535,
- CONNECT_PORT_ID, STR_PORT_ID, DEFAULT_PORT },
- { "Server Password", CONFIG_TYPE_PASSWORD, 0, 0,
- CONNECT_PASSWORD_ID, STR_PASSWORD_ID, "" },
- { "Nickname", CONFIG_TYPE_STRING, 1, 0,
- CONNECT_NICK_ID, STR_NICK_ID, "" },
- { "Ident", CONFIG_TYPE_STRING, 1, 0,
- CONNECT_IDENT_ID, STR_IDENT_ID, DEFAULT_IDENT },
- { "Realname", CONFIG_TYPE_STRING, 1, 0,
- CONNECT_REALNAME_ID, STR_REALNAME_ID, DEFAULT_REALNAME },
- { "Hide MOTD", CONFIG_TYPE_BOOL, 0, 1,
- CONNECT_HIDE_MOTD_ID, STR_HIDE_MOTD_ID, DEFAULT_HIDE_MOTD },
- { "Channel", CONFIG_TYPE_STRING, 0, 0,
- CONNECT_CHANNEL_ID, STR_CHANNEL_ID, DEFAULT_CHANNEL },
-};
-
void update_menu(void);
void handle_exit(void);
bool handle_menu(long menu_id);
-short show_connect_dialog(void);
+void show_connect_dialog(void);
-int
+short
main(void)
{
Handle mbar;
@@ -225,184 +192,17 @@ main(void)
return 0;
}
-short
+void
show_connect_dialog(void)
{
- Str255 txt, server, ports, nick, ident, realname, channel, password;
- struct chatter *chatter;
- StringHandle h;
- DialogTHndl dlgh;
- DialogPtr dlg;
- Handle ihandle;
- Rect irect;
- size_t size, n, m;
- long port;
- short hit, itype, ret, hide_motd;
+ bool valid;
- /* center dialog in screen */
- dlgh = (DialogTHndl)xGetResource('DLOG', CONNECT_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(CONNECT_DLOG_ID, nil, (WindowPtr)-1)) == NULL)
- panic("Can't find connection DLOG %d", CONNECT_DLOG_ID);
-
- for (n = 0; n < nitems(config_fields); n++) {
- struct config_field *cf = &config_fields[n];
- short sval;
-
- GetDItem(dlg, cf->ditl_id, &itype, &ihandle, &irect);
-
- if (cf->type == CONFIG_TYPE_PASSWORD) {
- PasswordDialogFieldFilterSetup(cf->ditl_id,
- cf->password_storage, sizeof(cf->password_storage));
- }
-
- if (cf->res_id && (h = GetString(cf->res_id))) {
- HLock(h);
- if (cf->type == CONFIG_TYPE_PASSWORD) {
- size = (*h)[0];
- if (size >= sizeof(cf->password_storage) - 1)
- size = 0;
- for (m = 0; m < size; m++)
- cf->password_storage[m] = '•';
- cf->password_storage[m] = '\0';
- CtoPstr(cf->password_storage);
- SetIText(ihandle, cf->password_storage);
- memcpy(cf->password_storage, *h, size);
- cf->password_storage[size] = '\0';
- PtoCstr(cf->password_storage);
- } else if (cf->type == CONFIG_TYPE_BOOL) {
- SetCtlValue(ihandle, ((*h)[1] == '1'));
- } else {
- SetIText(ihandle, *h);
- }
- HUnlock(h);
- ReleaseResource(h);
- } else if (cf->type == CONFIG_TYPE_BOOL) {
- SetCtlValue(ihandle, (cf->sdefault[0] == '1'));
- } else if (cf->sdefault[0] != '\0') {
- strlcpy((char *)&txt, cf->sdefault, sizeof(txt));
- CtoPstr(txt);
- SetIText(ihandle, txt);
- }
- }
-
- ShowWindow(dlg);
-
-get_input:
- ModalDialog(PasswordDialogFieldFilter, &hit);
- switch (hit) {
- case -1:
- DisposeDialog(dlg);
- ReleaseResource(dlgh);
+ if (!settings_edit(!settings_load()))
return;
- case OK:
- goto verify;
- case CONNECT_HIDE_MOTD_ID:
- GetDItem(dlg, hit, &itype, &ihandle, &irect);
- SetCtlValue(ihandle, 1 - GetCtlValue(ihandle));
- /* FALLTHROUGH */
- default:
- goto get_input;
- }
-
-verify:
- for (n = 0; n < nitems(config_fields); n++) {
- struct config_field *cf = &config_fields[n];
- long lval;
- short sval;
-
- GetDItem(dlg, cf->ditl_id, &itype, &ihandle, &irect);
- if (cf->type == CONFIG_TYPE_PASSWORD) {
- memcpy((char *)&txt, cf->password_storage, sizeof(txt));
- } else if (cf->type == CONFIG_TYPE_BOOL) {
- snprintf((char *)&txt, sizeof(txt), "%d",
- GetCtlValue(ihandle));
- } else {
- GetIText(ihandle, txt);
- PtoCstr(txt);
- }
-
- switch (cf->type) {
- case CONFIG_TYPE_STRING:
- case CONFIG_TYPE_PASSWORD:
- if (cf->min && strlen((char *)txt) < cf->min) {
- warn("%s is too short (minimum %d)", cf->name, cf->min);
- goto get_input;
- }
- if (cf->max && strlen((char *)txt) > cf->max) {
- warn("%s is too long (maximum %d)", cf->name, cf->max);
- goto get_input;
- }
- break;
- case CONFIG_TYPE_SHORT:
- lval = atol((char *)txt);
- if (lval < cf->min) {
- warn("%s must be at least %d", cf->name, cf->min);
- goto get_input;
- }
- if (lval > cf->max) {
- warn("%s must be less than %d", cf->name, cf->max);
- goto get_input;
- }
- break;
- case CONFIG_TYPE_BOOL:
- break;
- }
-
- switch (cf->ditl_id) {
- case CONNECT_SERVER_ID:
- strlcpy((char *)&server, (char *)txt, sizeof(server));
- break;
- case CONNECT_PORT_ID:
- port = atol((char *)txt);
- break;
- case CONNECT_PASSWORD_ID:
- strlcpy((char *)&password, (char *)txt, sizeof(password));
- break;
- case CONNECT_NICK_ID:
- strlcpy((char *)&nick, (char *)txt, sizeof(nick));
- break;
- case CONNECT_IDENT_ID:
- strlcpy((char *)&ident, (char *)txt, sizeof(ident));
- break;
- case CONNECT_REALNAME_ID:
- strlcpy((char *)&realname, (char *)txt, sizeof(realname));
- break;
- case CONNECT_HIDE_MOTD_ID:
- hide_motd = (txt[0] == '1');
- break;
- case CONNECT_CHANNEL_ID:
- strlcpy((char *)&channel, (char *)txt, sizeof(channel));
- break;
- }
-
- if ((h = GetString(cf->res_id)) != NULL) {
- RmveResource(h);
- ReleaseResource(h);
- }
- size = strlen((char *)txt) + 1;
- h = (StringHandle)xNewHandle(size);
- strlcpy((char *)*h, (char *)txt, size);
- CtoPstr(*h);
- strlcpy((char *)txt, cf->name, sizeof(txt));
- CtoPstr(txt);
- AddResource(h, 'STR ', cf->res_id, txt);
- ReleaseResource(h);
- }
-
- PasswordDialogFieldFinish();
- DisposeDialog(dlg);
- ReleaseResource(dlgh);
-
- chatter_init((char *)&server, port, (char *)&password,
- (char *)&nick, (char *)&ident, (char *)&realname,
- hide_motd, (char *)&channel);
+ chatter_init(settings.server, settings.port, settings.password,
+ settings.nick, settings.ident, settings.realname,
+ settings.hide_motd, settings.channel);
}
bool
--- settings.c Fri Aug 30 19:15:47 2024
+++ settings.c Fri Aug 30 19:15:47 2024
@@ -0,0 +1,405 @@
+/*
+ * 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
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * 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 "settings.h"
+#include "util.h"
+
+enum {
+ SETTING_STRING,
+ SETTING_SHORT,
+ SETTING_USHORT,
+ SETTING_LONG,
+ SETTING_PASSWORD,
+ SETTING_BOOL
+};
+
+enum {
+ SETTING_FLAG_ALLOW_BLANK = (1 << 0)
+};
+
+struct setting {
+ char name[32];
+ short type;
+ long flags;
+ long min;
+ long max;
+ char sdefault[32];
+ short ditl_id;
+ unsigned long offset;
+ unsigned long size;
+ char password_storage[256];
+} settings_defs[] = {
+ { "Server Name", SETTING_STRING, 0, 1, 0,
+ DEFAULT_SERVER_NAME, SETTINGS_SERVER_ID,
+ offsetof(struct settings, server),
+ member_size(struct settings, server) },
+ { "Server Port", SETTING_USHORT, 0, 1, 65535,
+ DEFAULT_PORT, SETTINGS_PORT_ID,
+ offsetof(struct settings, port),
+ member_size(struct settings, port) },
+ { "Server Password", SETTING_PASSWORD, 0, 0, 0,
+ "", SETTINGS_PASSWORD_ID,
+ offsetof(struct settings, password),
+ member_size(struct settings, password) },
+ { "Nickname", SETTING_STRING, 0, 1, 0,
+ "", SETTINGS_NICK_ID,
+ offsetof(struct settings, nick),
+ member_size(struct settings, nick) },
+ { "Ident", SETTING_STRING, 0, 1, 0,
+ DEFAULT_IDENT, SETTINGS_IDENT_ID,
+ offsetof(struct settings, ident),
+ member_size(struct settings, ident) },
+ { "Realname", SETTING_STRING, 0, 1, 0,
+ DEFAULT_REALNAME, SETTINGS_REALNAME_ID,
+ offsetof(struct settings, realname),
+ member_size(struct settings, realname) },
+ { "Hide MOTD", SETTING_BOOL, 0, 0, 1,
+ DEFAULT_HIDE_MOTD, SETTINGS_HIDE_MOTD_ID,
+ offsetof(struct settings, hide_motd),
+ member_size(struct settings, hide_motd) },
+ { "Channel", SETTING_STRING, 0, 0, 0,
+ DEFAULT_CHANNEL, SETTINGS_CHANNEL_ID,
+ offsetof(struct settings, channel),
+ member_size(struct settings, channel) },
+};
+
+void settings_find_prefs_folder(short *vrefnum, long *dirid);
+
+bool
+settings_load(void)
+{
+ HParamBlockRec pb = { 0 };
+ struct settings tsettings = { 0 };
+ char fn[] = SETTINGS_FILENAME;
+ char *res;
+ short error, vrefnum, prefrefnum;
+ long len, rlen, dirid;
+ Handle h;
+
+ 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);
+
+ return false;
+ }
+
+ rlen = len = sizeof(struct settings);
+ FSRead(pb.ioParam.ioRefNum, &rlen, &tsettings);
+
+ while (tsettings.version < SETTINGS_VERSION) {
+ switch (tsettings.version) {
+ default:
+ FSClose(pb.ioParam.ioRefNum);
+
+ if (ask("Unknown preferences version (%d), reset?",
+ tsettings.version)) {
+ return false;
+ }
+
+ ExitToShell();
+ }
+
+ tsettings.version++;
+ }
+
+ FSClose(pb.ioParam.ioRefNum);
+
+ settings = tsettings;
+ return true;
+}
+
+void
+settings_save(struct settings *tsettings)
+{
+ HParamBlockRec pb = { 0 };
+ char fn[] = SETTINGS_FILENAME;
+ short error, vrefnum, prefrefnum;
+ long dirid, len;
+ Handle h;
+
+ GetSystemSubfolder(kPreferencesFolderType, true, &vrefnum, &dirid);
+
+ 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 = SETTINGS_FILE_CREATOR;
+ if ((error = PBHSetFInfo(&pb, false)))
+ goto create_failed;
+
+ memset(&pb, 0, sizeof(pb));
+ pb.ioParam.ioVRefNum = vrefnum;
+ PBFlushVol(&pb, false);
+ }
+
+ 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);
+
+ return;
+
+create_failed:
+ panic("Failed creating preferences file: %d", error);
+}
+
+bool
+settings_edit(bool use_defaults)
+{
+ struct settings tsettings = settings;
+ struct setting *s;
+ DialogTHndl dlgh;
+ DialogPtr dlg;
+ Handle ihandle;
+ Rect irect;
+ size_t size, n, m, slen;
+ long lval;
+ bool bval, save;
+ short hit, itype, ret;
+ char *sdata, *strval;
+ Str255 stmp;
+
+ /* 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;
+
+ GetDItem(dlg, s->ditl_id, &itype, &ihandle, &irect);
+
+ switch (s->type) {
+ case SETTING_PASSWORD:
+ PasswordDialogFieldFilterSetup(s->ditl_id,
+ (char *)s->password_storage, sizeof(s->password_storage));
+
+ 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);
+ }
+ }
+
+ ret = false;
+ ShowWindow(dlg);
+
+get_input:
+ GetDItem(dlg, ok, &itype, &ihandle, &irect);
+ outline_button((ControlHandle)ihandle);
+ ModalDialog(PasswordDialogFieldFilter, &hit);
+ switch (hit) {
+ case -1:
+ case cancel:
+ goto settings_done;
+ case OK:
+ goto verify;
+ default:
+ GetDItem(dlg, hit, &itype, &ihandle, &irect);
+ if (itype == (ctrlItem + chkCtrl))
+ SetCtlValue(ihandle, !GetCtlValue(ihandle));
+ 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:
+ slen = strlen((char *)stmp);
+
+ if (slen == 0 && (s->flags & SETTING_FLAG_ALLOW_BLANK)) {
+ /* ok */
+ } else {
+ if (s->min && s->max && s->min == s->max &&
+ slen != s->min) {
+ warn("%s must be %ld character%s", s->name, s->min,
+ s->min == 1 ? "" : "s");
+ goto get_input;
+ }
+ if (s->min && slen < s->min) {
+ warn("%s is too short (minimum %ld)", s->name, s->min);
+ goto get_input;
+ }
+ if (s->max && slen > 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 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;
+ }
+ }
+
+ if (!save) {
+ save = true;
+ goto verify;
+ }
+
+ /* all validated ok and fields written */
+ settings_save(&tsettings);
+ settings = tsettings;
+ ret = true;
+
+settings_done:
+ PasswordDialogFieldFinish();
+ DisposeDialog(dlg);
+ ReleaseResource(dlgh);
+
+ return ret;
+}
--- settings.h Fri Aug 30 19:19:30 2024
+++ settings.h Fri Aug 30 19:19:30 2024
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2021 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
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __SETTINGS_H__
+#define __SETTINGS_H__
+
+#include "stdint.h"
+
+#define SETTINGS_FILENAME "\pWallops Preferences"
+#define SETTINGS_FILE_CREATOR 'WLOP'
+
+#define SETTINGS_DLOG_ID 129
+#define SETTINGS_SERVER_ID 2
+#define SETTINGS_PORT_ID 3
+#define SETTINGS_PASSWORD_ID 4
+#define SETTINGS_NICK_ID 5
+#define SETTINGS_IDENT_ID 6
+#define SETTINGS_REALNAME_ID 7
+#define SETTINGS_HIDE_MOTD_ID 8
+#define SETTINGS_CHANNEL_ID 9
+
+#define DEFAULT_SERVER_NAME "irc.libera.chat"
+#define DEFAULT_PORT "6667"
+#define DEFAULT_IDENT "wallops"
+#define DEFAULT_REALNAME "A Macintosh User"
+#define DEFAULT_CHANNEL "#cyberpals"
+#define DEFAULT_HIDE_MOTD "0"
+
+struct settings {
+ short version;
+#define SETTINGS_VERSION 1
+ char server[64];
+ unsigned short port;
+ char password[32];
+ char nick[32];
+ char ident[32];
+ char realname[32];
+ char channel[32];
+ unsigned char hide_motd;
+};
+
+extern struct settings settings;
+
+bool settings_load(void);
+void settings_save(struct settings *tsettings);
+bool settings_edit(bool use_defaults);
+
+#endif
\ No newline at end of file