AmendHub

Download

jcs

/

detritus

/

settings.c

 

(View History)

jcs   settings: C nits Latest amendment: 69 on 2025-10-30

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 "browser.h"
28 #include "settings.h"
29 #include "util.h"
30
31 enum {
32 SETTING_STRING,
33 SETTING_SHORT,
34 SETTING_USHORT,
35 SETTING_LONG,
36 SETTING_PASSWORD,
37 SETTING_BOOL
38 };
39
40 enum {
41 SETTING_FLAG_ALLOW_BLANK = (1 << 0)
42 };
43
44 struct settings settings = { 0 };
45
46 struct setting {
47 char name[32];
48 short type;
49 long flags;
50 long min;
51 long max;
52 char sdefault[32];
53 short ditl_id;
54 unsigned long offset;
55 unsigned long size;
56 char password_storage[256];
57 };
58
59 void settings_load_defaults(void);
60 void settings_find_prefs_folder(short *vrefnum, long *dirid);
61 bool settings_edit(struct setting *defs, size_t defs_count, short dlog_id,
62 bool use_defaults);
63
64 void
65 settings_load(void)
66 {
67 HParamBlockRec pb;
68 struct settings tsettings;
69 char fn[] = SETTINGS_FILENAME;
70 char *res;
71 short error, vrefnum, prefrefnum;
72 long len, rlen, dirid;
73 Handle h;
74
75 GetSystemSubfolder(kPreferencesFolderType, true, &vrefnum, &dirid);
76
77 memset(&pb, 0, sizeof(pb));
78 pb.ioParam.ioNamePtr = (StringPtr)&fn;
79 pb.ioParam.ioVRefNum = vrefnum;
80 pb.ioParam.ioPermssn = fsRdPerm;
81 pb.fileParam.ioDirID = dirid;
82 error = PBHOpen(&pb, false);
83 if (error) {
84 if (error != fnfErr)
85 panic("Failed reading preferences file %s: %d", PtoCstr(fn),
86 error);
87
88 goto load_defaults;
89 }
90
91 rlen = len = sizeof(struct settings);
92 FSRead(pb.ioParam.ioRefNum, &rlen, &tsettings);
93
94 while (tsettings.version < SETTINGS_VERSION) {
95 switch (tsettings.version) {
96 default:
97 FSClose(pb.ioParam.ioRefNum);
98
99 if (ask("Unknown preferences version (%d), reset?",
100 tsettings.version))
101 goto load_defaults;
102
103 ExitToShell();
104 }
105
106 tsettings.version++;
107 }
108
109 FSClose(pb.ioParam.ioRefNum);
110
111 settings = tsettings;
112 return;
113
114 load_defaults:
115 settings_load_defaults();
116 }
117
118 void
119 settings_load_defaults(void)
120 {
121 short n;
122
123 memset(&settings, 0, sizeof(settings));
124
125 for (n = 0; default_bookmarks[n].name[0] != '\0'; n++) {
126 strlcpy(settings.bookmark_names[n], default_bookmarks[n].name,
127 sizeof(settings.bookmark_names[0]));
128 strlcpy(settings.bookmark_uris[n], default_bookmarks[n].uri,
129 sizeof(settings.bookmark_uris[0]));
130 }
131
132 settings.version = SETTINGS_VERSION;
133 }
134
135 void
136 settings_save(struct settings *tsettings)
137 {
138 HParamBlockRec pb;
139 char fn[] = SETTINGS_FILENAME;
140 short error, vrefnum, prefrefnum;
141 long dirid, len;
142 Handle h;
143
144 GetSystemSubfolder(kPreferencesFolderType, true, &vrefnum, &dirid);
145
146 memset(&pb, 0, sizeof(pb));
147 pb.ioParam.ioNamePtr = (StringPtr)&fn;
148 pb.ioParam.ioVRefNum = vrefnum;
149 pb.fileParam.ioDirID = dirid;
150 error = PBHCreate(&pb, false);
151 if (error && error != dupFNErr)
152 goto create_failed;
153 if (!error) {
154 memset(&pb, 0, sizeof(pb));
155 pb.ioParam.ioNamePtr = (StringPtr)&fn;
156 pb.ioParam.ioVRefNum = vrefnum;
157 pb.fileParam.ioDirID = dirid;
158 if ((error = PBHGetFInfo(&pb, false)))
159 goto create_failed;
160
161 pb.fileParam.ioDirID = dirid; /* required after PBHGetFInfo */
162 pb.fileParam.ioFlFndrInfo.fdType = 'pref';
163 pb.fileParam.ioFlFndrInfo.fdCreator = SETTINGS_FILE_CREATOR;
164 if ((error = PBHSetFInfo(&pb, false)))
165 goto create_failed;
166
167 memset(&pb, 0, sizeof(pb));
168 pb.ioParam.ioVRefNum = vrefnum;
169 PBFlushVol(&pb, false);
170 }
171
172 memset(&pb, 0, sizeof(pb));
173 pb.ioParam.ioNamePtr = (StringPtr)&fn;
174 pb.ioParam.ioVRefNum = vrefnum;
175 pb.ioParam.ioPermssn = fsWrPerm;
176 pb.fileParam.ioDirID = dirid;
177 error = PBHOpen(&pb, false);
178 if (error)
179 goto create_failed;
180
181 len = sizeof(struct settings);
182 tsettings->version = SETTINGS_VERSION;
183 FSWrite(pb.ioParam.ioRefNum, &len, tsettings);
184 FSClose(pb.ioParam.ioRefNum);
185
186 return;
187
188 create_failed:
189 panic("Failed creating preferences file %s: %d", PtoCstr(fn), error);
190 }
191
192 bool
193 bookmarks_dialog(void)
194 {
195 Str255 txt;
196 WindowPtr win;
197 DialogPtr dlg;
198 struct settings tsettings;
199 struct URI *uri;
200 short name_id, uri_id, itype, n, hit, bookmarkn;
201 Handle ihandle;
202 Rect irect, dlgrect;
203
204 GetPort(&win);
205
206 if ((dlg = GetNewDialog(BOOKMARKS_DLOG_ID, NULL,
207 (WindowPtr)-1)) == NULL) {
208 warn("Can't find DLOG %d", BOOKMARKS_DLOG_ID);
209 return false;
210 }
211 center_in_screen(dlg->portRect.right - dlg->portRect.left,
212 dlg->portRect.bottom - dlg->portRect.top, true, &dlgrect);
213 MoveWindow(dlg, dlgrect.left, dlgrect.top, true);
214
215 for (n = 0; n < BOOKMARKS_COUNT; n++) {
216 if (settings.bookmark_uris[n][0] == '\0')
217 continue;
218
219 name_id = BOOKMARKS_NAME_ID_START + (n * 2);
220 GetDItem(dlg, name_id, &itype, &ihandle, &irect);
221 strlcpy((char *)&txt, settings.bookmark_names[n], sizeof(txt));
222 CtoPstr(txt);
223 SetIText(ihandle, txt);
224
225 uri_id = BOOKMARKS_URI_ID_START + (n * 2);
226 GetDItem(dlg, uri_id, &itype, &ihandle, &irect);
227 strlcpy((char *)&txt, settings.bookmark_uris[n], sizeof(txt));
228 CtoPstr(txt);
229 SetIText(ihandle, txt);
230 }
231
232 tsettings = settings;
233
234 ShowWindow(dlg);
235 for (;;) {
236 modal:
237 ModalDialog(ModalDialogFilter, &hit);
238 if (hit == cancel)
239 break;
240 if (hit != ok)
241 continue;
242
243 memset(&tsettings.bookmark_names, 0,
244 sizeof(tsettings.bookmark_names));
245 memset(&tsettings.bookmark_uris, 0,
246 sizeof(tsettings.bookmark_uris));
247
248 for (n = 0, bookmarkn = 0; n < BOOKMARKS_COUNT; n++) {
249 uri_id = BOOKMARKS_URI_ID_START + (n * 2);
250 GetDItem(dlg, uri_id, &itype, &ihandle, &irect);
251 GetIText(ihandle, txt);
252
253 if (txt[0] != '\0') {
254 PtoCstr(txt);
255
256 uri = parse_uri((char *)&txt);
257 if (uri == NULL) {
258 warn("Could not parse URI \"%s\"", txt);
259 goto modal;
260 }
261 xfree(&uri);
262 strlcpy(tsettings.bookmark_uris[bookmarkn], (char *)&txt,
263 sizeof(tsettings.bookmark_uris[bookmarkn]));
264
265 name_id = BOOKMARKS_NAME_ID_START + (n * 2);
266 GetDItem(dlg, name_id, &itype, &ihandle, &irect);
267 GetIText(ihandle, txt);
268 PtoCstr(txt);
269 strlcpy(tsettings.bookmark_names[bookmarkn], (char *)&txt,
270 sizeof(tsettings.bookmark_names[bookmarkn]));
271
272 bookmarkn++;
273 }
274 }
275
276 settings_save(&tsettings);
277 settings = tsettings;
278 menu_load_bookmarks();
279 break;
280 }
281
282 DisposDialog(dlg);
283 SetPort(win);
284 }
285
286 bool
287 settings_edit(struct setting *defs, size_t defs_count, short dlog_id,
288 bool use_defaults)
289 {
290 struct settings tsettings = settings;
291 struct setting *s;
292 DialogTHndl dlgh;
293 DialogPtr dlg;
294 Handle ihandle;
295 Rect irect;
296 size_t size, n, m, slen;
297 long lval;
298 bool bval, save;
299 short hit, itype, ret;
300 char *sdata, *strval;
301 Str255 stmp;
302
303 /* center dialog in screen */
304 dlgh = (DialogTHndl)xGetResource('DLOG', dlog_id);
305 HLock(dlgh);
306 center_in_screen((**dlgh).boundsRect.right - (**dlgh).boundsRect.left,
307 (**dlgh).boundsRect.bottom - (**dlgh).boundsRect.top,
308 true, &(**dlgh).boundsRect);
309 HUnlock(dlgh);
310
311 if ((dlg = GetNewDialog(dlog_id, nil, (WindowPtr)-1)) == NULL)
312 panic("Can't find settings DLOG %d", dlog_id);
313
314 for (n = 0; n < defs_count; n++) {
315 s = &defs[n];
316 sdata = (char *)&tsettings + s->offset;
317
318 GetDItem(dlg, s->ditl_id, &itype, &ihandle, &irect);
319
320 switch (s->type) {
321 case SETTING_PASSWORD:
322 PasswordDialogFieldFilterSetup(s->ditl_id,
323 (char *)s->password_storage, sizeof(s->password_storage));
324
325 strval = (use_defaults ? (char *)s->sdefault : sdata);
326 size = strlen(strval);
327 if (size >= sizeof(s->password_storage) - 1)
328 size = 0;
329
330 /* show a masked value */
331 stmp[0] = size;
332 for (m = 1; m <= size; m++)
333 stmp[m] = '•';
334 SetIText(ihandle, stmp);
335
336 /* but store the actual password */
337 memcpy(s->password_storage, strval, size);
338 s->password_storage[size] = '\0';
339 break;
340 case SETTING_STRING:
341 strval = (use_defaults ? s->sdefault : sdata);
342 memcpy(stmp, strval, sizeof(stmp));
343 stmp[sizeof(stmp) - 1] = '\0';
344 CtoPstr(stmp);
345 SetIText(ihandle, stmp);
346 break;
347 case SETTING_SHORT:
348 case SETTING_USHORT:
349 case SETTING_LONG:
350 if (use_defaults) {
351 memcpy(stmp, s->sdefault, sizeof(stmp));
352 stmp[sizeof(stmp) - 1] = '\0';
353 } else if (s->type == SETTING_SHORT) {
354 snprintf((char *)stmp, sizeof(stmp), "%d",
355 BYTES_TO_SHORT(sdata[0], sdata[1]));
356 } else if (s->type == SETTING_USHORT) {
357 snprintf((char *)stmp, sizeof(stmp), "%u",
358 BYTES_TO_SHORT(sdata[0], sdata[1]));
359 } else if (s->type == SETTING_LONG) {
360 snprintf((char *)stmp, sizeof(stmp), "%ld",
361 BYTES_TO_LONG(sdata[0], sdata[1], sdata[2], sdata[3]));
362 }
363 CtoPstr(stmp);
364 SetIText(ihandle, stmp);
365 break;
366 case SETTING_BOOL:
367 SetCtlValue(ihandle, (use_defaults ? s->sdefault[0] :
368 sdata[0]) == 1);
369 break;
370 default:
371 panic("Unknown setting type %d", s->type);
372 }
373 }
374
375 ret = false;
376 ShowWindow(dlg);
377 SelectWindow(dlg);
378
379 get_input:
380 ModalDialog(PasswordDialogFieldFilter, &hit);
381 switch (hit) {
382 case -1:
383 goto settings_done;
384 case OK:
385 goto verify;
386 default:
387 GetDItem(dlg, hit, &itype, &ihandle, &irect);
388 if (itype == (ctrlItem + chkCtrl))
389 SetCtlValue(ihandle, !GetCtlValue(ihandle));
390 goto get_input;
391 }
392
393 save = false;
394 verify:
395 for (n = 0; n < defs_count; n++) {
396 s = &defs[n];
397 sdata = (char *)&tsettings + s->offset;
398
399 GetDItem(dlg, s->ditl_id, &itype, &ihandle, &irect);
400
401 if (s->type == SETTING_PASSWORD) {
402 memcpy((char *)&stmp, s->password_storage, sizeof(stmp));
403 } else if (s->type == SETTING_BOOL) {
404 snprintf((char *)&stmp, sizeof(stmp), "%d",
405 GetCtlValue(ihandle));
406 } else {
407 GetIText(ihandle, stmp);
408 PtoCstr(stmp);
409 }
410
411 switch (s->type) {
412 case SETTING_STRING:
413 case SETTING_PASSWORD:
414 slen = strlen((char *)stmp);
415
416 if (slen == 0 && (s->flags & SETTING_FLAG_ALLOW_BLANK)) {
417 /* ok */
418 } else {
419 if (s->min && s->max && s->min == s->max &&
420 slen != s->min) {
421 warn("%s must be %ld character%s", s->name, s->min,
422 s->min == 1 ? "" : "s");
423 goto get_input;
424 }
425 if (s->min && slen < s->min) {
426 warn("%s is too short (minimum %ld)", s->name, s->min);
427 goto get_input;
428 }
429 if (s->max && slen > s->max) {
430 warn("%s is too long (maximum %ld)", s->name, s->max);
431 goto get_input;
432 }
433 }
434
435 if (save) {
436 memset(sdata, 0, s->size);
437 strlcpy(sdata, (char *)stmp, s->size);
438 }
439 break;
440 case SETTING_SHORT:
441 case SETTING_USHORT:
442 case SETTING_LONG:
443 lval = atol((char *)stmp);
444 if (lval < s->min) {
445 if (s->type == SETTING_USHORT)
446 warn("%s must be at least %ul", s->name, s->min);
447 else
448 warn("%s must be at least %ld", s->name, s->min);
449 goto get_input;
450 }
451 if (lval > s->max) {
452 if (s->type == SETTING_USHORT)
453 warn("%s must be less than %ul", s->name, s->max);
454 else
455 warn("%s must be less than %ld", s->name, s->max);
456 goto get_input;
457 }
458 if (save) {
459 if (s->type == SETTING_LONG) {
460 sdata[0] = (lval >> 24) & 0xff;
461 sdata[1] = (lval >> 16) & 0xff;
462 sdata[2] = (lval >> 8) & 0xff;
463 sdata[3] = lval & 0xff;
464 } else {
465 sdata[0] = (lval >> 8) & 0xff;
466 sdata[1] = lval & 0xff;
467 }
468 }
469 break;
470 case SETTING_BOOL:
471 if (save)
472 sdata[0] = (stmp[0] == '1');
473 break;
474 }
475 }
476
477 if (!save) {
478 save = true;
479 goto verify;
480 }
481
482 /* all validated ok and fields written */
483 settings_save(&tsettings);
484 settings = tsettings;
485 ret = true;
486
487 settings_done:
488 PasswordDialogFieldFinish();
489 DisposeDialog(dlg);
490 ReleaseResource(dlgh);
491
492 return ret;
493 }