]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - mac/macstore.c
Created new data types `Filename' and `FontSpec', intended to be
[PuTTY.git] / mac / macstore.c
1 /* $Id: macstore.c,v 1.13 2003/02/01 12:54:40 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) {
120     short sessVRefNum;
121     long sessDirID;
122     OSErr error;
123     Str255 psessionname;
124     FSSpec dstfile;
125     
126     error = get_session_dir(kCreateFolder, &sessVRefNum, &sessDirID);
127     if (error != noErr) return NULL;
128
129     c2pstrcpy(psessionname, sessionname);
130     error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &dstfile);
131     if (error == fnfErr) {
132         FSpCreateResFile(&dstfile, PUTTY_CREATOR, SESS_TYPE, smSystemScript);
133         if ((error = ResError()) != noErr) return NULL;
134     } else if (error != noErr) return NULL;
135
136     return open_settings_w_fsp(&dstfile);
137 }
138
139 /*
140  * NB: Destination file must exist.
141  */
142 void *open_settings_w_fsp(FSSpec *dstfile)
143 {
144     short tmpVRefNum;
145     long tmpDirID;
146     struct write_settings *ws;
147     OSErr error;
148     Str255 tmpname;
149
150     ws = smalloc(sizeof *ws);
151     ws->dstfile = *dstfile;
152
153     /* Create a temporary file to save to first. */
154     error = FindFolder(ws->dstfile.vRefNum, kTemporaryFolderType,
155                        kCreateFolder, &tmpVRefNum, &tmpDirID);
156     if (error != noErr) goto out;
157     c2pstrcpy(tmpname, tmpnam(NULL));
158     error = FSMakeFSSpec(tmpVRefNum, tmpDirID, tmpname, &ws->tmpfile);
159     if (error != noErr && error != fnfErr) goto out;
160     if (error == noErr) {
161         error = FSpDelete(&ws->tmpfile);
162         if (error != noErr) goto out;
163     }
164     FSpCreateResFile(&ws->tmpfile, PUTTY_CREATOR, SESS_TYPE, smSystemScript);
165     if ((error = ResError()) != noErr) goto out;
166
167     ws->fd = FSpOpenResFile(&ws->tmpfile, fsWrPerm);
168     if (ws->fd == -1) {error = ResError(); goto out;}
169
170     /* Set up standard resources.  Doesn't matter if these fail. */
171     copy_resource('STR ', -16396);
172     copy_resource('TMPL', TMPL_Int);
173
174     return ws;
175
176   out:
177     safefree(ws);
178     fatalbox("Failed to open session for write (%d)", error);
179 }
180
181 void write_setting_s(void *handle, char const *key, char const *value) {
182     int fd = *(int *)handle;
183     Handle h;
184     int id;
185     OSErr error;
186
187     UseResFile(fd);
188     if (ResError() != noErr)
189         fatalbox("Failed to open saved session (%d)", ResError());
190
191     error = PtrToHand(value, &h, strlen(value));
192     if (error != noErr)
193         fatalbox("Failed to allocate memory");
194     /* Put the data in a resource. */
195     id = Unique1ID(FOUR_CHAR_CODE('TEXT'));
196     if (ResError() != noErr)
197         fatalbox("Failed to get ID for resource %s (%d)", key, ResError());
198     addresource(h, FOUR_CHAR_CODE('TEXT'), id, key);
199     if (ResError() != noErr)
200         fatalbox("Failed to add resource %s (%d)", key, ResError());
201 }
202
203 void write_setting_i(void *handle, char const *key, int value) {
204     int fd = *(int *)handle;
205     Handle h;
206     int id;
207     OSErr error;
208
209     UseResFile(fd);
210     if (ResError() != noErr)
211         fatalbox("Failed to open saved session (%d)", ResError());
212
213     /* XXX assume all systems have the same "int" format */
214     error = PtrToHand(&value, &h, sizeof(int));
215     if (error != noErr)
216         fatalbox("Failed to allocate memory (%d)", error);
217
218     /* Put the data in a resource. */
219     id = Unique1ID(FOUR_CHAR_CODE('Int '));
220     if (ResError() != noErr)
221         fatalbox("Failed to get ID for resource %s (%d)", key, ResError());
222     addresource(h, FOUR_CHAR_CODE('Int '), id, key);
223     if (ResError() != noErr)
224         fatalbox("Failed to add resource %s (%d)", key, ResError());
225 }
226
227 void close_settings_w(void *handle) {
228     struct write_settings *ws = handle;
229     OSErr error;
230
231     CloseResFile(ws->fd);
232     if ((error = ResError()) != noErr)
233         goto out;
234     error = FSpExchangeFiles(&ws->tmpfile, &ws->dstfile);
235     if (error != noErr) goto out;
236     error = FSpDelete(&ws->tmpfile);
237     if (error != noErr) goto out;
238     return;
239
240   out:
241     fatalbox("Close of saved session failed (%d)", error);
242     safefree(handle);
243 }
244
245 void *open_settings_r(char const *sessionname)
246 {
247     short sessVRefNum;
248     long sessDirID;
249     FSSpec sessfile;
250     OSErr error;
251     Str255 psessionname;
252
253     error = get_session_dir(kDontCreateFolder, &sessVRefNum, &sessDirID);
254
255     c2pstrcpy(psessionname, sessionname);
256     error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &sessfile);
257     if (error != noErr) goto out;
258     return open_settings_r_fsp(&sessfile);
259
260   out:
261     return NULL;
262 }
263
264 void *open_settings_r_fsp(FSSpec *sessfile)
265 {
266     OSErr error;
267     int fd;
268     int *handle;
269
270     fd = FSpOpenResFile(sessfile, fsRdPerm);
271     if (fd == 0) {error = ResError(); goto out;}
272
273     handle = safemalloc(sizeof *handle);
274     *handle = fd;
275     return handle;
276
277   out:
278     return NULL;
279 }
280
281 char *read_setting_s(void *handle, char const *key, char *buffer, int buflen) {
282     int fd;
283     Handle h;
284     size_t len;
285
286     if (handle == NULL) goto out;
287     fd = *(int *)handle;
288     UseResFile(fd);
289     if (ResError() != noErr) goto out;
290     h = get1namedresource(FOUR_CHAR_CODE('TEXT'), key);
291     if (h == NULL) goto out;
292
293     len = GetHandleSize(h);
294     if (len + 1 > buflen) goto out;
295     memcpy(buffer, *h, len);
296     buffer[len] = '\0';
297
298     ReleaseResource(h);
299     if (ResError() != noErr) goto out;
300     return buffer;
301
302   out:
303     return NULL;
304 }
305
306 int read_setting_i(void *handle, char const *key, int defvalue) {
307     int fd;
308     Handle h;
309     int value;
310
311     if (handle == NULL) goto out;
312     fd = *(int *)handle;
313     UseResFile(fd);
314     if (ResError() != noErr) goto out;
315     h = get1namedresource(FOUR_CHAR_CODE('Int '), key);
316     if (h == NULL) goto out;
317     value = *(int *)*h;
318     ReleaseResource(h);
319     if (ResError() != noErr) goto out;
320     return value;
321
322   out:
323     return defvalue;
324 }
325
326 int read_setting_fontspec(void *handle, const char *name, FontSpec *result)
327 {
328     char *settingname;
329     FontSpec ret;
330
331     if (!read_setting_s(handle, name, ret.name, sizeof(ret.name)))
332         return 0;
333     settingname = dupcat(name, "IsBold", NULL);
334     ret.isbold = read_setting_i(handle, settingname, -1);
335     sfree(settingname);
336     if (ret.isbold == -1) return 0;
337     if (ret.charset == -1) return 0;
338     settingname = dupcat(name, "Height", NULL);
339     ret.height = read_setting_i(handle, settingname, INT_MIN);
340     sfree(settingname);
341     if (ret.height == INT_MIN) return 0;
342     *result = ret;
343     return 1;
344 }
345
346 void write_setting_fontspec(void *handle, const char *name, FontSpec font)
347 {
348     char *settingname;
349
350     write_setting_s(handle, name, font.name);
351     settingname = dupcat(name, "IsBold", NULL);
352     write_setting_i(handle, settingname, font.isbold);
353     sfree(settingname);
354     settingname = dupcat(name, "Height", NULL);
355     write_setting_i(handle, settingname, font.height);
356     sfree(settingname);
357 }
358
359 int read_setting_filename(void *handle, const char *name, Filename *result)
360 {
361     return !!read_setting_s(handle, name, result->path, sizeof(result->path));
362 }
363
364 void write_setting_filename(void *handle, const char *name, Filename result)
365 {
366     write_setting_s(handle, name, result.path);
367 }
368
369 void close_settings_r(void *handle) {
370     int fd;
371
372     if (handle == NULL) return;
373     fd = *(int *)handle;
374     CloseResFile(fd);
375     if (ResError() != noErr)
376         fatalbox("Close of saved session failed (%d)", ResError());
377     safefree(handle);
378 }
379
380 void del_settings(char const *sessionname) {
381     OSErr error;
382     FSSpec sessfile;
383     short sessVRefNum;
384     long sessDirID;
385     Str255 psessionname;
386
387     error = get_session_dir(kDontCreateFolder, &sessVRefNum, &sessDirID);
388
389     c2pstrcpy(psessionname, sessionname);
390     error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &sessfile);
391     if (error != noErr) goto out;
392
393     error = FSpDelete(&sessfile);
394     return;
395   out:
396     fatalbox("Delete session failed (%d)", error);
397 }
398
399 struct enum_settings_state {
400     short vRefNum;
401     long dirID;
402     int index;
403 };
404
405 void *enum_settings_start(void) {
406     OSErr error;
407     struct enum_settings_state *state;
408
409     state = safemalloc(sizeof(*state));
410     error = get_session_dir(kDontCreateFolder, &state->vRefNum, &state->dirID);
411     if (error != noErr) {
412         safefree(state);
413         return NULL;
414     }
415     state->index = 1;
416     return state;
417 }
418
419 char *enum_settings_next(void *handle, char *buffer, int buflen) {
420     struct enum_settings_state *e = handle;
421     CInfoPBRec pb;
422     OSErr error = noErr;
423     Str255 name;
424
425     if (e == NULL) return NULL;
426     do {
427         pb.hFileInfo.ioNamePtr = name;
428         pb.hFileInfo.ioVRefNum = e->vRefNum;
429         pb.hFileInfo.ioDirID = e->dirID;
430         pb.hFileInfo.ioFDirIndex = e->index++;
431         error = PBGetCatInfoSync(&pb);
432         if (error != noErr) return NULL;
433     } while (!((pb.hFileInfo.ioFlAttrib & ioDirMask) == 0 &&
434                pb.hFileInfo.ioFlFndrInfo.fdCreator == PUTTY_CREATOR &&
435                pb.hFileInfo.ioFlFndrInfo.fdType == SESS_TYPE &&
436                name[0] < buflen));
437
438     p2cstrcpy(buffer, name);
439     return buffer;
440 }
441
442 void enum_settings_finish(void *handle) {
443
444     safefree(handle);
445 }
446
447 #define SEED_SIZE 512
448
449 void read_random_seed(noise_consumer_t consumer)
450 {
451     short puttyVRefNum;
452     long puttyDirID;
453     OSErr error;
454     char buf[SEED_SIZE];
455     short refnum;
456     long count = SEED_SIZE;
457
458     if (get_putty_dir(kDontCreateFolder, &puttyVRefNum, &puttyDirID) != noErr)
459         return;
460     if (HOpenDF(puttyVRefNum, puttyDirID, "\pPuTTY Random Seed", fsRdPerm,
461                 &refnum) != noErr)
462         return;
463     error = FSRead(refnum, &count, buf);
464     if (error != noErr && error != eofErr)
465         return;
466     (*consumer)(buf, count);
467     FSClose(refnum);
468 }
469
470 /*
471  * We don't bother with the usual FSpExchangeFiles dance here because
472  * it doesn't really matter if the old random seed gets lost.
473  */
474 void write_random_seed(void *data, int len)
475 {
476     short puttyVRefNum;
477     long puttyDirID;
478     OSErr error;
479     FSSpec dstfile;
480     short refnum;
481     long count = len;
482
483     if (get_putty_dir(kCreateFolder, &puttyVRefNum, &puttyDirID) != noErr)
484         return;
485
486     error = FSMakeFSSpec(puttyVRefNum, puttyDirID, "\pPuTTY Random Seed",
487                          &dstfile);
488     if (error == fnfErr) {
489         /* Set up standard resources */
490         FSpCreateResFile(&dstfile, INTERNAL_CREATOR, SEED_TYPE, smRoman);
491         refnum = FSpOpenResFile(&dstfile, fsWrPerm);
492         if (ResError() == noErr) {
493             copy_resource('STR ', -16397);
494             CloseResFile(refnum);
495         }
496     } else if (error != noErr) return;
497
498     if (FSpOpenDF(&dstfile, fsWrPerm, &refnum) != noErr) return;
499     FSWrite(refnum, &count, data);
500     FSClose(refnum);
501
502     return;
503 }
504
505 /*
506  * Emacs magic:
507  * Local Variables:
508  * c-file-style: "simon"
509  * End:
510  */