Various error-handling fixes, mostly in Unix PuTTY but one (failure
[u/mdw/putty] / mac / macstore.c
CommitLineData
3f935d5b 1/* $Id: macstore.c,v 1.19 2003/04/01 18:10:25 simon Exp $ */
d082ac49 2
3/*
4 * macstore.c: Macintosh-specific impementation of the interface
5 * defined in storage.h
6 */
7
8#include <MacTypes.h>
9#include <Folders.h>
10#include <Memory.h>
11#include <Resources.h>
12#include <TextUtils.h>
13
b537dd42 14#include <stdio.h>
d082ac49 15#include <string.h>
16
17#include "putty.h"
18#include "storage.h"
ce283213 19#include "mac.h"
1d4cb74b 20#include "macresid.h"
d082ac49 21
d082ac49 22
d082ac49 23OSErr FSpGetDirID(FSSpec *f, long *idp, Boolean makeit);
24
25/*
26 * We store each session as a file in the "PuTTY" sub-directory of the
27 * preferences folder. Each (key,value) pair is stored as a resource.
28 */
29
0c4b7799 30OSErr get_putty_dir(Boolean makeit, short *pVRefNum, long *pDirID)
31{
d082ac49 32 OSErr error = noErr;
33 short prefVRefNum;
0c4b7799 34 FSSpec puttydir;
35 long prefDirID, puttyDirID;
d082ac49 36
37 error = FindFolder(kOnSystemDisk, kPreferencesFolderType, makeit,
38 &prefVRefNum, &prefDirID);
39 if (error != noErr) goto out;
40
41 error = FSMakeFSSpec(prefVRefNum, prefDirID, "\pPuTTY", &puttydir);
42 if (error != noErr && error != fnfErr) goto out;
43 error = FSpGetDirID(&puttydir, &puttyDirID, makeit);
44 if (error != noErr) goto out;
45
0c4b7799 46 *pVRefNum = prefVRefNum;
47 *pDirID = puttyDirID;
48
49 out:
50 return error;
51}
52
53OSErr get_session_dir(Boolean makeit, short *pVRefNum, long *pDirID) {
54 OSErr error = noErr;
55 short puttyVRefNum;
56 FSSpec sessdir;
57 long puttyDirID, sessDirID;
58
59 error = get_putty_dir(makeit, &puttyVRefNum, &puttyDirID);
60 if (error != noErr) goto out;
61 error = FSMakeFSSpec(puttyVRefNum, puttyDirID, "\pSaved Sessions",
d082ac49 62 &sessdir);
63 if (error != noErr && error != fnfErr) goto out;
64 error = FSpGetDirID(&sessdir, &sessDirID, makeit);
65 if (error != noErr) goto out;
66
0c4b7799 67 *pVRefNum = puttyVRefNum;
d082ac49 68 *pDirID = sessDirID;
69
70 out:
71 return error;
72}
73
74OSErr FSpGetDirID(FSSpec *f, long *idp, Boolean makeit) {
75 CInfoPBRec pb;
76 OSErr error = noErr;
77
78 pb.dirInfo.ioNamePtr = f->name;
79 pb.dirInfo.ioVRefNum = f->vRefNum;
80 pb.dirInfo.ioDrDirID = f->parID;
81 pb.dirInfo.ioFDirIndex = 0;
82 error = PBGetCatInfoSync(&pb);
83 if (error == fnfErr && makeit)
84 return FSpDirCreate(f, smSystemScript, idp);
85 if (error != noErr) goto out;
86 if ((pb.dirInfo.ioFlAttrib & ioDirMask) == 0) {
87 error = dirNFErr;
88 goto out;
89 }
90 *idp = pb.dirInfo.ioDrDirID;
91
92 out:
93 return error;
94}
95
1d4cb74b 96/* Copy a resource into the current resource file */
97static OSErr copy_resource(ResType restype, short resid)
98{
99 Handle h;
100 Str255 resname;
101
1d4cb74b 102 h = GetResource(restype, resid);
103 if (h != NULL) {
104 GetResInfo(h, &resid, &restype, resname);
105 DetachResource(h);
106 AddResource(h, restype, resid, resname);
107 if (ResError() == noErr)
108 WriteResource(h);
109 }
1d4cb74b 110 return ResError();
111}
112
d082ac49 113struct write_settings {
114 int fd;
115 FSSpec tmpfile;
116 FSSpec dstfile;
117};
118
3f935d5b 119void *open_settings_w(char const *sessionname, char **errmsg) {
b537dd42 120 short sessVRefNum;
121 long sessDirID;
d082ac49 122 OSErr error;
123 Str255 psessionname;
b537dd42 124 FSSpec dstfile;
3f935d5b 125
126 *errmsg = NULL;
127
d082ac49 128 error = get_session_dir(kCreateFolder, &sessVRefNum, &sessDirID);
b537dd42 129 if (error != noErr) return NULL;
d082ac49 130
746dbe2c 131 if (!sessionname || !*sessionname)
132 sessionname = "Default Settings";
d082ac49 133 c2pstrcpy(psessionname, sessionname);
b537dd42 134 error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &dstfile);
d082ac49 135 if (error == fnfErr) {
b537dd42 136 FSpCreateResFile(&dstfile, PUTTY_CREATOR, SESS_TYPE, smSystemScript);
137 if ((error = ResError()) != noErr) return NULL;
138 } else if (error != noErr) return NULL;
139
140 return open_settings_w_fsp(&dstfile);
141}
142
143/*
144 * NB: Destination file must exist.
145 */
146void *open_settings_w_fsp(FSSpec *dstfile)
147{
148 short tmpVRefNum;
149 long tmpDirID;
150 struct write_settings *ws;
151 OSErr error;
152 Str255 tmpname;
153
34773273 154 ws = snew(struct write_settings);
b537dd42 155 ws->dstfile = *dstfile;
d082ac49 156
157 /* Create a temporary file to save to first. */
b537dd42 158 error = FindFolder(ws->dstfile.vRefNum, kTemporaryFolderType,
159 kCreateFolder, &tmpVRefNum, &tmpDirID);
d082ac49 160 if (error != noErr) goto out;
b537dd42 161 c2pstrcpy(tmpname, tmpnam(NULL));
162 error = FSMakeFSSpec(tmpVRefNum, tmpDirID, tmpname, &ws->tmpfile);
d082ac49 163 if (error != noErr && error != fnfErr) goto out;
164 if (error == noErr) {
165 error = FSpDelete(&ws->tmpfile);
166 if (error != noErr) goto out;
167 }
168 FSpCreateResFile(&ws->tmpfile, PUTTY_CREATOR, SESS_TYPE, smSystemScript);
169 if ((error = ResError()) != noErr) goto out;
170
171 ws->fd = FSpOpenResFile(&ws->tmpfile, fsWrPerm);
172 if (ws->fd == -1) {error = ResError(); goto out;}
173
1d4cb74b 174 /* Set up standard resources. Doesn't matter if these fail. */
175 copy_resource('STR ', -16396);
176 copy_resource('TMPL', TMPL_Int);
177
d082ac49 178 return ws;
179
180 out:
181 safefree(ws);
182 fatalbox("Failed to open session for write (%d)", error);
183}
184
f5d2d791 185void write_setting_s(void *handle, char const *key, char const *value) {
d082ac49 186 int fd = *(int *)handle;
187 Handle h;
188 int id;
803efcdf 189 OSErr error;
a7459895 190 Str255 pkey;
d082ac49 191
192 UseResFile(fd);
193 if (ResError() != noErr)
194 fatalbox("Failed to open saved session (%d)", ResError());
195
196 error = PtrToHand(value, &h, strlen(value));
197 if (error != noErr)
198 fatalbox("Failed to allocate memory");
199 /* Put the data in a resource. */
200 id = Unique1ID(FOUR_CHAR_CODE('TEXT'));
201 if (ResError() != noErr)
202 fatalbox("Failed to get ID for resource %s (%d)", key, ResError());
a7459895 203 c2pstrcpy(pkey, key);
204 AddResource(h, FOUR_CHAR_CODE('TEXT'), id, pkey);
d082ac49 205 if (ResError() != noErr)
206 fatalbox("Failed to add resource %s (%d)", key, ResError());
207}
208
f5d2d791 209void write_setting_i(void *handle, char const *key, int value) {
d082ac49 210 int fd = *(int *)handle;
211 Handle h;
212 int id;
213 OSErr error;
a7459895 214 Str255 pkey;
d082ac49 215
216 UseResFile(fd);
217 if (ResError() != noErr)
218 fatalbox("Failed to open saved session (%d)", ResError());
219
220 /* XXX assume all systems have the same "int" format */
221 error = PtrToHand(&value, &h, sizeof(int));
222 if (error != noErr)
223 fatalbox("Failed to allocate memory (%d)", error);
224
225 /* Put the data in a resource. */
226 id = Unique1ID(FOUR_CHAR_CODE('Int '));
227 if (ResError() != noErr)
228 fatalbox("Failed to get ID for resource %s (%d)", key, ResError());
a7459895 229 c2pstrcpy(pkey, key);
230 AddResource(h, FOUR_CHAR_CODE('Int '), id, pkey);
d082ac49 231 if (ResError() != noErr)
232 fatalbox("Failed to add resource %s (%d)", key, ResError());
233}
234
235void close_settings_w(void *handle) {
236 struct write_settings *ws = handle;
237 OSErr error;
238
239 CloseResFile(ws->fd);
240 if ((error = ResError()) != noErr)
241 goto out;
242 error = FSpExchangeFiles(&ws->tmpfile, &ws->dstfile);
243 if (error != noErr) goto out;
244 error = FSpDelete(&ws->tmpfile);
245 if (error != noErr) goto out;
246 return;
247
248 out:
249 fatalbox("Close of saved session failed (%d)", error);
250 safefree(handle);
251}
252
f5d2d791 253void *open_settings_r(char const *sessionname)
ce283213 254{
d082ac49 255 short sessVRefNum;
256 long sessDirID;
257 FSSpec sessfile;
258 OSErr error;
259 Str255 psessionname;
d082ac49 260
261 error = get_session_dir(kDontCreateFolder, &sessVRefNum, &sessDirID);
262
746dbe2c 263 if (!sessionname || !*sessionname)
264 sessionname = "Default Settings";
d082ac49 265 c2pstrcpy(psessionname, sessionname);
266 error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &sessfile);
267 if (error != noErr) goto out;
ce283213 268 return open_settings_r_fsp(&sessfile);
269
270 out:
271 return NULL;
272}
273
274void *open_settings_r_fsp(FSSpec *sessfile)
275{
276 OSErr error;
277 int fd;
278 int *handle;
279
280 fd = FSpOpenResFile(sessfile, fsRdPerm);
d082ac49 281 if (fd == 0) {error = ResError(); goto out;}
282
34773273 283 handle = snew(int);
d082ac49 284 *handle = fd;
285 return handle;
286
287 out:
288 return NULL;
289}
290
f5d2d791 291char *read_setting_s(void *handle, char const *key, char *buffer, int buflen) {
d082ac49 292 int fd;
293 Handle h;
086efcde 294 size_t len;
a7459895 295 Str255 pkey;
d082ac49 296
297 if (handle == NULL) goto out;
298 fd = *(int *)handle;
299 UseResFile(fd);
300 if (ResError() != noErr) goto out;
a7459895 301 c2pstrcpy(pkey, key);
302 h = Get1NamedResource(FOUR_CHAR_CODE('TEXT'), pkey);
d082ac49 303 if (h == NULL) goto out;
304
086efcde 305 len = GetHandleSize(h);
306 if (len + 1 > buflen) goto out;
307 memcpy(buffer, *h, len);
308 buffer[len] = '\0';
309
d082ac49 310 ReleaseResource(h);
311 if (ResError() != noErr) goto out;
312 return buffer;
313
314 out:
315 return NULL;
316}
317
f5d2d791 318int read_setting_i(void *handle, char const *key, int defvalue) {
d082ac49 319 int fd;
320 Handle h;
d082ac49 321 int value;
a7459895 322 Str255 pkey;
d082ac49 323
324 if (handle == NULL) goto out;
325 fd = *(int *)handle;
326 UseResFile(fd);
327 if (ResError() != noErr) goto out;
a7459895 328 c2pstrcpy(pkey, key);
329 h = Get1NamedResource(FOUR_CHAR_CODE('Int '), pkey);
d082ac49 330 if (h == NULL) goto out;
331 value = *(int *)*h;
332 ReleaseResource(h);
333 if (ResError() != noErr) goto out;
334 return value;
335
336 out:
337 return defvalue;
338}
339
9a30e26b 340int read_setting_fontspec(void *handle, const char *name, FontSpec *result)
341{
342 char *settingname;
343 FontSpec ret;
e3c5b245 344 char tmp[256];
9a30e26b 345
e3c5b245 346 if (!read_setting_s(handle, name, tmp, sizeof(tmp)))
9a30e26b 347 return 0;
e3c5b245 348 c2pstrcpy(ret.name, tmp);
349 settingname = dupcat(name, "Face", NULL);
350 ret.face = read_setting_i(handle, settingname, 0);
9a30e26b 351 sfree(settingname);
9a30e26b 352 settingname = dupcat(name, "Height", NULL);
e3c5b245 353 ret.size = read_setting_i(handle, settingname, 0);
9a30e26b 354 sfree(settingname);
e3c5b245 355 if (ret.size == 0) return 0;
9a30e26b 356 *result = ret;
357 return 1;
358}
359
360void write_setting_fontspec(void *handle, const char *name, FontSpec font)
361{
362 char *settingname;
e3c5b245 363 char tmp[256];
9a30e26b 364
e3c5b245 365 p2cstrcpy(tmp, font.name);
366 write_setting_s(handle, name, tmp);
367 settingname = dupcat(name, "Face", NULL);
368 write_setting_i(handle, settingname, font.face);
9a30e26b 369 sfree(settingname);
e3c5b245 370 settingname = dupcat(name, "Size", NULL);
371 write_setting_i(handle, settingname, font.size);
9a30e26b 372 sfree(settingname);
373}
374
a7459895 375int read_setting_filename(void *handle, const char *key, Filename *result)
9a30e26b 376{
02cf4001 377 int fd;
378 AliasHandle h;
379 Boolean changed;
380 OSErr err;
a7459895 381 Str255 pkey;
02cf4001 382
383 if (handle == NULL) goto out;
384 fd = *(int *)handle;
385 UseResFile(fd);
386 if (ResError() != noErr) goto out;
a7459895 387 c2pstrcpy(pkey, key);
388 h = (AliasHandle)Get1NamedResource(rAliasType, pkey);
02cf4001 389 if (h == NULL) goto out;
390 if ((*h)->userType == 'pTTY' && (*h)->aliasSize == sizeof(**h))
391 memset(result, 0, sizeof(*result));
392 else {
393 err = ResolveAlias(NULL, h, &result->fss, &changed);
394 if (err != noErr && err != fnfErr) goto out;
395 if ((*h)->userType == 'pTTY') {
396 long dirid;
397 StrFileName fname;
398
399 /* Tail of record is pascal string contaning leafname */
400 if (FSpGetDirID(&result->fss, &dirid, FALSE) != noErr) goto out;
401 memcpy(fname, (char *)*h + (*h)->aliasSize,
402 GetHandleSize((Handle)h) - (*h)->aliasSize);
403 err = FSMakeFSSpec(result->fss.vRefNum, dirid, fname,
404 &result->fss);
405 if (err != noErr && err != fnfErr) goto out;
406 }
407 }
408 ReleaseResource((Handle)h);
409 if (ResError() != noErr) goto out;
410 return 1;
411
412 out:
413 return 0;
9a30e26b 414}
415
a7459895 416void write_setting_filename(void *handle, const char *key, Filename fn)
9a30e26b 417{
02cf4001 418 int fd = *(int *)handle;
419 AliasHandle h;
420 int id;
421 OSErr error;
a7459895 422 Str255 pkey;
02cf4001 423
424 UseResFile(fd);
425 if (ResError() != noErr)
426 fatalbox("Failed to open saved session (%d)", ResError());
427
428 if (filename_is_null(fn)) {
429 /* Generate a special "null" alias */
430 h = (AliasHandle)NewHandle(sizeof(**h));
431 if (h == NULL)
432 fatalbox("Failed to create fake alias");
433 (*h)->userType = 'pTTY';
434 (*h)->aliasSize = sizeof(**h);
435 } else {
436 error = NewAlias(NULL, &fn.fss, &h);
437 if (error == fnfErr) {
438 /*
439 * NewAlias can't create an alias for a nonexistent file.
440 * Create an alias for the directory, and record the
441 * filename as well.
442 */
443 FSSpec tmpfss;
444
445 FSMakeFSSpec(fn.fss.vRefNum, fn.fss.parID, NULL, &tmpfss);
446 error = NewAlias(NULL, &tmpfss, &h);
447 if (error != noErr)
448 fatalbox("Failed to create alias");
449 (*h)->userType = 'pTTY';
450 SetHandleSize((Handle)h, (*h)->aliasSize + fn.fss.name[0] + 1);
451 if (MemError() != noErr)
452 fatalbox("Failed to create alias");
453 memcpy((char *)*h + (*h)->aliasSize, fn.fss.name,
454 fn.fss.name[0] + 1);
455 }
456 if (error != noErr)
457 fatalbox("Failed to create alias");
458 }
459 /* Put the data in a resource. */
460 id = Unique1ID(rAliasType);
461 if (ResError() != noErr)
a7459895 462 fatalbox("Failed to get ID for resource %s (%d)", key, ResError());
463 c2pstrcpy(pkey, key);
464 AddResource((Handle)h, rAliasType, id, pkey);
02cf4001 465 if (ResError() != noErr)
a7459895 466 fatalbox("Failed to add resource %s (%d)", key, ResError());
9a30e26b 467}
468
d082ac49 469void close_settings_r(void *handle) {
470 int fd;
471
472 if (handle == NULL) return;
473 fd = *(int *)handle;
474 CloseResFile(fd);
475 if (ResError() != noErr)
476 fatalbox("Close of saved session failed (%d)", ResError());
a7459895 477 sfree(handle);
d082ac49 478}
479
f5d2d791 480void del_settings(char const *sessionname) {
d082ac49 481 OSErr error;
482 FSSpec sessfile;
483 short sessVRefNum;
484 long sessDirID;
485 Str255 psessionname;
486
487 error = get_session_dir(kDontCreateFolder, &sessVRefNum, &sessDirID);
488
489 c2pstrcpy(psessionname, sessionname);
490 error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &sessfile);
491 if (error != noErr) goto out;
492
493 error = FSpDelete(&sessfile);
494 return;
495 out:
496 fatalbox("Delete session failed (%d)", error);
497}
498
499struct enum_settings_state {
500 short vRefNum;
501 long dirID;
502 int index;
503};
504
505void *enum_settings_start(void) {
506 OSErr error;
507 struct enum_settings_state *state;
508
509 state = safemalloc(sizeof(*state));
510 error = get_session_dir(kDontCreateFolder, &state->vRefNum, &state->dirID);
511 if (error != noErr) {
512 safefree(state);
513 return NULL;
514 }
515 state->index = 1;
516 return state;
517}
518
519char *enum_settings_next(void *handle, char *buffer, int buflen) {
520 struct enum_settings_state *e = handle;
521 CInfoPBRec pb;
522 OSErr error = noErr;
523 Str255 name;
524
525 if (e == NULL) return NULL;
526 do {
527 pb.hFileInfo.ioNamePtr = name;
528 pb.hFileInfo.ioVRefNum = e->vRefNum;
529 pb.hFileInfo.ioDirID = e->dirID;
530 pb.hFileInfo.ioFDirIndex = e->index++;
531 error = PBGetCatInfoSync(&pb);
532 if (error != noErr) return NULL;
533 } while (!((pb.hFileInfo.ioFlAttrib & ioDirMask) == 0 &&
534 pb.hFileInfo.ioFlFndrInfo.fdCreator == PUTTY_CREATOR &&
535 pb.hFileInfo.ioFlFndrInfo.fdType == SESS_TYPE &&
536 name[0] < buflen));
537
538 p2cstrcpy(buffer, name);
539 return buffer;
540}
541
542void enum_settings_finish(void *handle) {
543
544 safefree(handle);
545}
546
0c4b7799 547#define SEED_SIZE 512
548
549void read_random_seed(noise_consumer_t consumer)
550{
551 short puttyVRefNum;
552 long puttyDirID;
553 OSErr error;
554 char buf[SEED_SIZE];
555 short refnum;
556 long count = SEED_SIZE;
557
558 if (get_putty_dir(kDontCreateFolder, &puttyVRefNum, &puttyDirID) != noErr)
559 return;
560 if (HOpenDF(puttyVRefNum, puttyDirID, "\pPuTTY Random Seed", fsRdPerm,
561 &refnum) != noErr)
562 return;
563 error = FSRead(refnum, &count, buf);
564 if (error != noErr && error != eofErr)
565 return;
566 (*consumer)(buf, count);
567 FSClose(refnum);
568}
569
604379f1 570/*
571 * We don't bother with the usual FSpExchangeFiles dance here because
572 * it doesn't really matter if the old random seed gets lost.
573 */
0c4b7799 574void write_random_seed(void *data, int len)
575{
604379f1 576 short puttyVRefNum;
577 long puttyDirID;
0c4b7799 578 OSErr error;
604379f1 579 FSSpec dstfile;
0c4b7799 580 short refnum;
581 long count = len;
582
583 if (get_putty_dir(kCreateFolder, &puttyVRefNum, &puttyDirID) != noErr)
584 return;
585
586 error = FSMakeFSSpec(puttyVRefNum, puttyDirID, "\pPuTTY Random Seed",
587 &dstfile);
1d4cb74b 588 if (error == fnfErr) {
589 /* Set up standard resources */
590 FSpCreateResFile(&dstfile, INTERNAL_CREATOR, SEED_TYPE, smRoman);
591 refnum = FSpOpenResFile(&dstfile, fsWrPerm);
592 if (ResError() == noErr) {
593 copy_resource('STR ', -16397);
594 CloseResFile(refnum);
595 }
596 } else if (error != noErr) return;
0c4b7799 597
604379f1 598 if (FSpOpenDF(&dstfile, fsWrPerm, &refnum) != noErr) return;
604379f1 599 FSWrite(refnum, &count, data);
0c4b7799 600 FSClose(refnum);
1d4cb74b 601
604379f1 602 return;
0c4b7799 603}
d082ac49 604
605/*
606 * Emacs magic:
607 * Local Variables:
608 * c-file-style: "simon"
609 * End:
610 */