Various error-handling fixes, mostly in Unix PuTTY but one (failure
[u/mdw/putty] / mac / macstore.c
1 /* $Id: macstore.c,v 1.19 2003/04/01 18:10:25 simon Exp $ */
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
14 #include <stdio.h>
15 #include <string.h>
16
17 #include "putty.h"
18 #include "storage.h"
19 #include "mac.h"
20 #include "macresid.h"
21
22
23 OSErr 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
30 OSErr get_putty_dir(Boolean makeit, short *pVRefNum, long *pDirID)
31 {
32 OSErr error = noErr;
33 short prefVRefNum;
34 FSSpec puttydir;
35 long prefDirID, puttyDirID;
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
46 *pVRefNum = prefVRefNum;
47 *pDirID = puttyDirID;
48
49 out:
50 return error;
51 }
52
53 OSErr 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",
62 &sessdir);
63 if (error != noErr && error != fnfErr) goto out;
64 error = FSpGetDirID(&sessdir, &sessDirID, makeit);
65 if (error != noErr) goto out;
66
67 *pVRefNum = puttyVRefNum;
68 *pDirID = sessDirID;
69
70 out:
71 return error;
72 }
73
74 OSErr 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
96 /* Copy a resource into the current resource file */
97 static OSErr copy_resource(ResType restype, short resid)
98 {
99 Handle h;
100 Str255 resname;
101
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 }
110 return ResError();
111 }
112
113 struct write_settings {
114 int fd;
115 FSSpec tmpfile;
116 FSSpec dstfile;
117 };
118
119 void *open_settings_w(char const *sessionname, char **errmsg) {
120 short sessVRefNum;
121 long sessDirID;
122 OSErr error;
123 Str255 psessionname;
124 FSSpec dstfile;
125
126 *errmsg = NULL;
127
128 error = get_session_dir(kCreateFolder, &sessVRefNum, &sessDirID);
129 if (error != noErr) return NULL;
130
131 if (!sessionname || !*sessionname)
132 sessionname = "Default Settings";
133 c2pstrcpy(psessionname, sessionname);
134 error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &dstfile);
135 if (error == fnfErr) {
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 */
146 void *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
154 ws = snew(struct write_settings);
155 ws->dstfile = *dstfile;
156
157 /* Create a temporary file to save to first. */
158 error = FindFolder(ws->dstfile.vRefNum, kTemporaryFolderType,
159 kCreateFolder, &tmpVRefNum, &tmpDirID);
160 if (error != noErr) goto out;
161 c2pstrcpy(tmpname, tmpnam(NULL));
162 error = FSMakeFSSpec(tmpVRefNum, tmpDirID, tmpname, &ws->tmpfile);
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
174 /* Set up standard resources. Doesn't matter if these fail. */
175 copy_resource('STR ', -16396);
176 copy_resource('TMPL', TMPL_Int);
177
178 return ws;
179
180 out:
181 safefree(ws);
182 fatalbox("Failed to open session for write (%d)", error);
183 }
184
185 void write_setting_s(void *handle, char const *key, char const *value) {
186 int fd = *(int *)handle;
187 Handle h;
188 int id;
189 OSErr error;
190 Str255 pkey;
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());
203 c2pstrcpy(pkey, key);
204 AddResource(h, FOUR_CHAR_CODE('TEXT'), id, pkey);
205 if (ResError() != noErr)
206 fatalbox("Failed to add resource %s (%d)", key, ResError());
207 }
208
209 void write_setting_i(void *handle, char const *key, int value) {
210 int fd = *(int *)handle;
211 Handle h;
212 int id;
213 OSErr error;
214 Str255 pkey;
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());
229 c2pstrcpy(pkey, key);
230 AddResource(h, FOUR_CHAR_CODE('Int '), id, pkey);
231 if (ResError() != noErr)
232 fatalbox("Failed to add resource %s (%d)", key, ResError());
233 }
234
235 void 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
253 void *open_settings_r(char const *sessionname)
254 {
255 short sessVRefNum;
256 long sessDirID;
257 FSSpec sessfile;
258 OSErr error;
259 Str255 psessionname;
260
261 error = get_session_dir(kDontCreateFolder, &sessVRefNum, &sessDirID);
262
263 if (!sessionname || !*sessionname)
264 sessionname = "Default Settings";
265 c2pstrcpy(psessionname, sessionname);
266 error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &sessfile);
267 if (error != noErr) goto out;
268 return open_settings_r_fsp(&sessfile);
269
270 out:
271 return NULL;
272 }
273
274 void *open_settings_r_fsp(FSSpec *sessfile)
275 {
276 OSErr error;
277 int fd;
278 int *handle;
279
280 fd = FSpOpenResFile(sessfile, fsRdPerm);
281 if (fd == 0) {error = ResError(); goto out;}
282
283 handle = snew(int);
284 *handle = fd;
285 return handle;
286
287 out:
288 return NULL;
289 }
290
291 char *read_setting_s(void *handle, char const *key, char *buffer, int buflen) {
292 int fd;
293 Handle h;
294 size_t len;
295 Str255 pkey;
296
297 if (handle == NULL) goto out;
298 fd = *(int *)handle;
299 UseResFile(fd);
300 if (ResError() != noErr) goto out;
301 c2pstrcpy(pkey, key);
302 h = Get1NamedResource(FOUR_CHAR_CODE('TEXT'), pkey);
303 if (h == NULL) goto out;
304
305 len = GetHandleSize(h);
306 if (len + 1 > buflen) goto out;
307 memcpy(buffer, *h, len);
308 buffer[len] = '\0';
309
310 ReleaseResource(h);
311 if (ResError() != noErr) goto out;
312 return buffer;
313
314 out:
315 return NULL;
316 }
317
318 int read_setting_i(void *handle, char const *key, int defvalue) {
319 int fd;
320 Handle h;
321 int value;
322 Str255 pkey;
323
324 if (handle == NULL) goto out;
325 fd = *(int *)handle;
326 UseResFile(fd);
327 if (ResError() != noErr) goto out;
328 c2pstrcpy(pkey, key);
329 h = Get1NamedResource(FOUR_CHAR_CODE('Int '), pkey);
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
340 int read_setting_fontspec(void *handle, const char *name, FontSpec *result)
341 {
342 char *settingname;
343 FontSpec ret;
344 char tmp[256];
345
346 if (!read_setting_s(handle, name, tmp, sizeof(tmp)))
347 return 0;
348 c2pstrcpy(ret.name, tmp);
349 settingname = dupcat(name, "Face", NULL);
350 ret.face = read_setting_i(handle, settingname, 0);
351 sfree(settingname);
352 settingname = dupcat(name, "Height", NULL);
353 ret.size = read_setting_i(handle, settingname, 0);
354 sfree(settingname);
355 if (ret.size == 0) return 0;
356 *result = ret;
357 return 1;
358 }
359
360 void write_setting_fontspec(void *handle, const char *name, FontSpec font)
361 {
362 char *settingname;
363 char tmp[256];
364
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);
369 sfree(settingname);
370 settingname = dupcat(name, "Size", NULL);
371 write_setting_i(handle, settingname, font.size);
372 sfree(settingname);
373 }
374
375 int read_setting_filename(void *handle, const char *key, Filename *result)
376 {
377 int fd;
378 AliasHandle h;
379 Boolean changed;
380 OSErr err;
381 Str255 pkey;
382
383 if (handle == NULL) goto out;
384 fd = *(int *)handle;
385 UseResFile(fd);
386 if (ResError() != noErr) goto out;
387 c2pstrcpy(pkey, key);
388 h = (AliasHandle)Get1NamedResource(rAliasType, pkey);
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;
414 }
415
416 void write_setting_filename(void *handle, const char *key, Filename fn)
417 {
418 int fd = *(int *)handle;
419 AliasHandle h;
420 int id;
421 OSErr error;
422 Str255 pkey;
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)
462 fatalbox("Failed to get ID for resource %s (%d)", key, ResError());
463 c2pstrcpy(pkey, key);
464 AddResource((Handle)h, rAliasType, id, pkey);
465 if (ResError() != noErr)
466 fatalbox("Failed to add resource %s (%d)", key, ResError());
467 }
468
469 void 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());
477 sfree(handle);
478 }
479
480 void del_settings(char const *sessionname) {
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
499 struct enum_settings_state {
500 short vRefNum;
501 long dirID;
502 int index;
503 };
504
505 void *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
519 char *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
542 void enum_settings_finish(void *handle) {
543
544 safefree(handle);
545 }
546
547 #define SEED_SIZE 512
548
549 void 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
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 */
574 void write_random_seed(void *data, int len)
575 {
576 short puttyVRefNum;
577 long puttyDirID;
578 OSErr error;
579 FSSpec dstfile;
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);
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;
597
598 if (FSpOpenDF(&dstfile, fsWrPerm, &refnum) != noErr) return;
599 FSWrite(refnum, &count, data);
600 FSClose(refnum);
601
602 return;
603 }
604
605 /*
606 * Emacs magic:
607 * Local Variables:
608 * c-file-style: "simon"
609 * End:
610 */