AmendHub

Download

jcs

/

wallops

/

settings.c

 

(View History)

jcs   settings: ModalDialogFilter will outline button for us Latest amendment: 126 on 2024-09-20

1 /*
2 * Copyright (c) 2021-2024 joshua stein <jcs@jcs.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <OSUtils.h>
18 #include <Files.h>
19 #include <Folders.h>
20 #include <GestaltEqu.h>
21 #include <Traps.h>
22 #include <stddef.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <time.h>
26
27 #include "settings.h"
28 #include "util.h"
29
30 enum {
31 SETTING_STRING,
32 SETTING_SHORT,
33 SETTING_USHORT,
34 SETTING_LONG,
35 SETTING_PASSWORD,
36 SETTING_BOOL
37 };
38
39 enum {
40 SETTING_FLAG_ALLOW_BLANK = (1 << 0)
41 };
42
43 struct setting {
44 char name[32];
45 short type;
46 long flags;
47 long min;
48 long max;
49 char sdefault[32];
50 short ditl_id;
51 unsigned long offset;
52 unsigned long size;
53 char password_storage[256];
54 } settings_defs[] = {
55 { "Server Name", SETTING_STRING, 0, 1, 0,
56 DEFAULT_SERVER_NAME, SETTINGS_SERVER_ID,
57 offsetof(struct settings, server),
58 member_size(struct settings, server) },
59 { "Server Port", SETTING_USHORT, 0, 1, 65535,
60 DEFAULT_PORT, SETTINGS_PORT_ID,
61 offsetof(struct settings, port),
62 member_size(struct settings, port) },
63 { "Server Password", SETTING_PASSWORD, 0, 0, 0,
64 "", SETTINGS_PASSWORD_ID,
65 offsetof(struct settings, password),
66 member_size(struct settings, password) },
67 { "Nickname", SETTING_STRING, 0, 1, 0,
68 "", SETTINGS_NICK_ID,
69 offsetof(struct settings, nick),
70 member_size(struct settings, nick) },
71 { "Ident", SETTING_STRING, 0, 1, 0,
72 DEFAULT_IDENT, SETTINGS_IDENT_ID,
73 offsetof(struct settings, ident),
74 member_size(struct settings, ident) },
75 { "Realname", SETTING_STRING, 0, 1, 0,
76 DEFAULT_REALNAME, SETTINGS_REALNAME_ID,
77 offsetof(struct settings, realname),
78 member_size(struct settings, realname) },
79 { "Hide MOTD", SETTING_BOOL, 0, 0, 1,
80 DEFAULT_HIDE_MOTD, SETTINGS_HIDE_MOTD_ID,
81 offsetof(struct settings, hide_motd),
82 member_size(struct settings, hide_motd) },
83 { "Channel", SETTING_STRING, 0, 0, 0,
84 DEFAULT_CHANNEL, SETTINGS_CHANNEL_ID,
85 offsetof(struct settings, channel),
86 member_size(struct settings, channel) },
87 };
88
89 void settings_find_prefs_folder(short *vrefnum, long *dirid);
90
91 bool
92 settings_load(void)
93 {
94 HParamBlockRec pb = { 0 };
95 struct settings tsettings = { 0 };
96 char fn[] = SETTINGS_FILENAME;
97 char *res;
98 short error, vrefnum, prefrefnum;
99 long len, rlen, dirid;
100 Handle h;
101
102 GetSystemSubfolder(kPreferencesFolderType, true, &vrefnum, &dirid);
103
104 pb.ioParam.ioNamePtr = (StringPtr)&fn;
105 pb.ioParam.ioVRefNum = vrefnum;
106 pb.ioParam.ioPermssn = fsRdPerm;
107 pb.fileParam.ioDirID = dirid;
108 error = PBHOpen(&pb, false);
109 if (error) {
110 if (error != fnfErr)
111 panic("Failed reading preferences file %s: %d", PtoCstr(fn),
112 error);
113
114 return false;
115 }
116
117 rlen = len = sizeof(struct settings);
118 FSRead(pb.ioParam.ioRefNum, &rlen, &tsettings);
119
120 while (tsettings.version < SETTINGS_VERSION) {
121 switch (tsettings.version) {
122 default:
123 FSClose(pb.ioParam.ioRefNum);
124
125 if (ask("Unknown preferences version (%d), reset?",
126 tsettings.version)) {
127 return false;
128 }
129
130 ExitToShell();
131 }
132
133 tsettings.version++;
134 }
135
136 FSClose(pb.ioParam.ioRefNum);
137
138 settings = tsettings;
139 return true;
140 }
141
142 void
143 settings_save(struct settings *tsettings)
144 {
145 HParamBlockRec pb = { 0 };
146 char fn[] = SETTINGS_FILENAME;
147 short error, vrefnum, prefrefnum;
148 long dirid, len;
149 Handle h;
150
151 GetSystemSubfolder(kPreferencesFolderType, true, &vrefnum, &dirid);
152
153 pb.ioParam.ioNamePtr = (StringPtr)&fn;
154 pb.ioParam.ioVRefNum = vrefnum;
155 pb.fileParam.ioDirID = dirid;
156 error = PBHCreate(&pb, false);
157 if (error && error != dupFNErr)
158 goto create_failed;
159 if (!error) {
160 memset(&pb, 0, sizeof(pb));
161 pb.ioParam.ioNamePtr = (StringPtr)&fn;
162 pb.ioParam.ioVRefNum = vrefnum;
163 pb.fileParam.ioDirID = dirid;
164 if ((error = PBHGetFInfo(&pb, false)))
165 goto create_failed;
166
167 pb.fileParam.ioDirID = dirid; /* required after PBHGetFInfo */
168 pb.fileParam.ioFlFndrInfo.fdType = 'pref';
169 pb.fileParam.ioFlFndrInfo.fdCreator = SETTINGS_FILE_CREATOR;
170 if ((error = PBHSetFInfo(&pb, false)))
171 goto create_failed;
172
173 memset(&pb, 0, sizeof(pb));
174 pb.ioParam.ioVRefNum = vrefnum;
175 PBFlushVol(&pb, false);
176 }
177
178 memset(&pb, 0, sizeof(pb));
179 pb.ioParam.ioNamePtr = (StringPtr)&fn;
180 pb.ioParam.ioVRefNum = vrefnum;
181 pb.ioParam.ioPermssn = fsWrPerm;
182 pb.fileParam.ioDirID = dirid;
183 error = PBHOpen(&pb, false);
184 if (error)
185 goto create_failed;
186
187 len = sizeof(struct settings);
188 tsettings->version = SETTINGS_VERSION;
189 FSWrite(pb.ioParam.ioRefNum, &len, tsettings);
190 FSClose(pb.ioParam.ioRefNum);
191
192 return;
193
194 create_failed:
195 panic("Failed creating preferences file %s: %d", PtoCstr(fn), error);
196 }
197
198 bool
199 settings_edit(bool use_defaults)
200 {
201 struct settings tsettings = settings;
202 struct setting *s;
203 DialogTHndl dlgh;
204 DialogPtr dlg;
205 Handle ihandle;
206 Rect irect;
207 size_t size, n, m, slen;
208 long lval;
209 bool bval, save;
210 short hit, itype, ret;
211 char *sdata, *strval;
212 Str255 stmp;
213
214 /* center dialog in screen */
215 dlgh = (DialogTHndl)xGetResource('DLOG', SETTINGS_DLOG_ID);
216 HLock(dlgh);
217 center_in_screen((**dlgh).boundsRect.right - (**dlgh).boundsRect.left,
218 (**dlgh).boundsRect.bottom - (**dlgh).boundsRect.top,
219 true, &(**dlgh).boundsRect);
220 HUnlock(dlgh);
221
222 if ((dlg = GetNewDialog(SETTINGS_DLOG_ID, nil, (WindowPtr)-1)) == NULL)
223 panic("Can't find settings DLOG %d", SETTINGS_DLOG_ID);
224
225 for (n = 0; n < nitems(settings_defs); n++) {
226 s = &settings_defs[n];
227 sdata = (char *)&tsettings + s->offset;
228
229 GetDItem(dlg, s->ditl_id, &itype, &ihandle, &irect);
230
231 switch (s->type) {
232 case SETTING_PASSWORD:
233 PasswordDialogFieldFilterSetup(s->ditl_id,
234 (char *)s->password_storage, sizeof(s->password_storage));
235
236 strval = (use_defaults ? (char *)s->sdefault : sdata);
237 size = strlen(strval);
238 if (size >= sizeof(s->password_storage) - 1)
239 size = 0;
240
241 /* show a masked value */
242 stmp[0] = size;
243 for (m = 1; m <= size; m++)
244 stmp[m] = '•';
245 SetIText(ihandle, stmp);
246
247 /* but store the actual password */
248 memcpy(s->password_storage, strval, size);
249 s->password_storage[size] = '\0';
250 break;
251 case SETTING_STRING:
252 strval = (use_defaults ? s->sdefault : sdata);
253 memcpy(stmp, strval, sizeof(stmp));
254 stmp[sizeof(stmp) - 1] = '\0';
255 CtoPstr(stmp);
256 SetIText(ihandle, stmp);
257 break;
258 case SETTING_SHORT:
259 case SETTING_USHORT:
260 case SETTING_LONG:
261 if (use_defaults) {
262 memcpy(stmp, s->sdefault, sizeof(stmp));
263 stmp[sizeof(stmp) - 1] = '\0';
264 } else if (s->type == SETTING_SHORT) {
265 snprintf((char *)stmp, sizeof(stmp), "%d",
266 BYTES_TO_SHORT(sdata[0], sdata[1]));
267 } else if (s->type == SETTING_USHORT) {
268 snprintf((char *)stmp, sizeof(stmp), "%u",
269 BYTES_TO_SHORT(sdata[0], sdata[1]));
270 } else if (s->type == SETTING_LONG) {
271 snprintf((char *)stmp, sizeof(stmp), "%ld",
272 BYTES_TO_LONG(sdata[0], sdata[1], sdata[2], sdata[3]));
273 }
274 CtoPstr(stmp);
275 SetIText(ihandle, stmp);
276 break;
277 case SETTING_BOOL:
278 SetCtlValue(ihandle, (use_defaults ? s->sdefault[0] :
279 sdata[0]) == 1);
280 break;
281 default:
282 panic("Unknown setting type %d", s->type);
283 }
284 }
285
286 ret = false;
287 ShowWindow(dlg);
288 SelectWindow(dlg);
289
290 get_input:
291 ModalDialog(PasswordDialogFieldFilter, &hit);
292 switch (hit) {
293 case -1:
294 goto settings_done;
295 case OK:
296 goto verify;
297 default:
298 GetDItem(dlg, hit, &itype, &ihandle, &irect);
299 if (itype == (ctrlItem + chkCtrl))
300 SetCtlValue(ihandle, !GetCtlValue(ihandle));
301 goto get_input;
302 }
303
304 save = false;
305 verify:
306 for (n = 0; n < nitems(settings_defs); n++) {
307 s = &settings_defs[n];
308 sdata = (char *)&tsettings + s->offset;
309
310 GetDItem(dlg, s->ditl_id, &itype, &ihandle, &irect);
311
312 if (s->type == SETTING_PASSWORD) {
313 memcpy((char *)&stmp, s->password_storage, sizeof(stmp));
314 } else if (s->type == SETTING_BOOL) {
315 snprintf((char *)&stmp, sizeof(stmp), "%d",
316 GetCtlValue(ihandle));
317 } else {
318 GetIText(ihandle, stmp);
319 PtoCstr(stmp);
320 }
321
322 switch (s->type) {
323 case SETTING_STRING:
324 case SETTING_PASSWORD:
325 slen = strlen((char *)stmp);
326
327 if (slen == 0 && (s->flags & SETTING_FLAG_ALLOW_BLANK)) {
328 /* ok */
329 } else {
330 if (s->min && s->max && s->min == s->max &&
331 slen != s->min) {
332 warn("%s must be %ld character%s", s->name, s->min,
333 s->min == 1 ? "" : "s");
334 goto get_input;
335 }
336 if (s->min && slen < s->min) {
337 warn("%s is too short (minimum %ld)", s->name, s->min);
338 goto get_input;
339 }
340 if (s->max && slen > s->max) {
341 warn("%s is too long (maximum %ld)", s->name, s->max);
342 goto get_input;
343 }
344 }
345
346 if (save) {
347 memset(sdata, 0, s->size);
348 strlcpy(sdata, (char *)stmp, s->size);
349 }
350 break;
351 case SETTING_SHORT:
352 case SETTING_USHORT:
353 case SETTING_LONG:
354 lval = atol((char *)stmp);
355 if (lval < s->min) {
356 if (s->type == SETTING_USHORT)
357 warn("%s must be at least %ul", s->name, s->min);
358 else
359 warn("%s must be at least %ld", s->name, s->min);
360 goto get_input;
361 }
362 if (lval > s->max) {
363 if (s->type == SETTING_USHORT)
364 warn("%s must be less than %ul", s->name, s->max);
365 else
366 warn("%s must be less than %ld", s->name, s->max);
367 goto get_input;
368 }
369 if (save) {
370 if (s->type == SETTING_LONG) {
371 sdata[0] = (lval >> 24) & 0xff;
372 sdata[1] = (lval >> 16) & 0xff;
373 sdata[2] = (lval >> 8) & 0xff;
374 sdata[3] = lval & 0xff;
375 } else {
376 sdata[0] = (lval >> 8) & 0xff;
377 sdata[1] = lval & 0xff;
378 }
379 }
380 break;
381 case SETTING_BOOL:
382 if (save)
383 sdata[0] = (stmp[0] == '1');
384 break;
385 }
386 }
387
388 if (!save) {
389 save = true;
390 goto verify;
391 }
392
393 /* all validated ok and fields written */
394 settings_save(&tsettings);
395 settings = tsettings;
396 ret = true;
397
398 settings_done:
399 PasswordDialogFieldFinish();
400 DisposeDialog(dlg);
401 ReleaseResource(dlgh);
402
403 return ret;
404 }