Sebastian Kuschel reports that pfd_closing can be called for a socket
[u/mdw/putty] / conf.c
1 /*
2 * conf.c: implementation of the internal storage format used for
3 * the configuration of a PuTTY session.
4 */
5
6 #include <stdio.h>
7 #include <stddef.h>
8 #include <assert.h>
9
10 #include "tree234.h"
11 #include "putty.h"
12
13 /*
14 * Enumeration of types used in keys and values.
15 */
16 typedef enum { TYPE_NONE, TYPE_INT, TYPE_STR, TYPE_FILENAME, TYPE_FONT } Type;
17
18 /*
19 * Arrays which allow us to look up the subkey and value types for a
20 * given primary key id.
21 */
22 #define CONF_SUBKEYTYPE_DEF(valtype, keytype, keyword) TYPE_ ## keytype,
23 static int subkeytypes[] = { CONFIG_OPTIONS(CONF_SUBKEYTYPE_DEF) };
24 #define CONF_VALUETYPE_DEF(valtype, keytype, keyword) TYPE_ ## valtype,
25 static int valuetypes[] = { CONFIG_OPTIONS(CONF_VALUETYPE_DEF) };
26
27 /*
28 * Configuration keys are primarily integers (big enum of all the
29 * different configurable options); some keys have string-designated
30 * subkeys, such as the list of environment variables (subkeys
31 * defined by the variable names); some have integer-designated
32 * subkeys (wordness, colours, preference lists).
33 */
34 struct key {
35 int primary;
36 union {
37 int i;
38 char *s;
39 } secondary;
40 };
41
42 struct value {
43 union {
44 int intval;
45 char *stringval;
46 Filename *fileval;
47 FontSpec *fontval;
48 } u;
49 };
50
51 struct conf_entry {
52 struct key key;
53 struct value value;
54 };
55
56 struct conf_tag {
57 tree234 *tree;
58 };
59
60 /*
61 * Because 'struct key' is the first element in 'struct conf_entry',
62 * it's safe (guaranteed by the C standard) to cast arbitrarily back
63 * and forth between the two types. Therefore, we only need one
64 * comparison function, which can double as a main sort function for
65 * the tree (comparing two conf_entry structures with each other)
66 * and a search function (looking up an externally supplied key).
67 */
68 static int conf_cmp(void *av, void *bv)
69 {
70 struct key *a = (struct key *)av;
71 struct key *b = (struct key *)bv;
72
73 if (a->primary < b->primary)
74 return -1;
75 else if (a->primary > b->primary)
76 return +1;
77 switch (subkeytypes[a->primary]) {
78 case TYPE_INT:
79 if (a->secondary.i < b->secondary.i)
80 return -1;
81 else if (a->secondary.i > b->secondary.i)
82 return +1;
83 return 0;
84 case TYPE_STR:
85 return strcmp(a->secondary.s, b->secondary.s);
86 default:
87 return 0;
88 }
89 }
90
91 /*
92 * Free any dynamic data items pointed to by a 'struct key'. We
93 * don't free the structure itself, since it's probably part of a
94 * larger allocated block.
95 */
96 static void free_key(struct key *key)
97 {
98 if (subkeytypes[key->primary] == TYPE_STR)
99 sfree(key->secondary.s);
100 }
101
102 /*
103 * Copy a 'struct key' into another one, copying its dynamic data
104 * if necessary.
105 */
106 static void copy_key(struct key *to, struct key *from)
107 {
108 to->primary = from->primary;
109 switch (subkeytypes[to->primary]) {
110 case TYPE_INT:
111 to->secondary.i = from->secondary.i;
112 break;
113 case TYPE_STR:
114 to->secondary.s = dupstr(from->secondary.s);
115 break;
116 }
117 }
118
119 /*
120 * Free any dynamic data items pointed to by a 'struct value'. We
121 * don't free the value itself, since it's probably part of a larger
122 * allocated block.
123 */
124 static void free_value(struct value *val, int type)
125 {
126 if (type == TYPE_STR)
127 sfree(val->u.stringval);
128 else if (type == TYPE_FILENAME)
129 filename_free(val->u.fileval);
130 else if (type == TYPE_FONT)
131 fontspec_free(val->u.fontval);
132 }
133
134 /*
135 * Copy a 'struct value' into another one, copying its dynamic data
136 * if necessary.
137 */
138 static void copy_value(struct value *to, struct value *from, int type)
139 {
140 switch (type) {
141 case TYPE_INT:
142 to->u.intval = from->u.intval;
143 break;
144 case TYPE_STR:
145 to->u.stringval = dupstr(from->u.stringval);
146 break;
147 case TYPE_FILENAME:
148 to->u.fileval = filename_copy(from->u.fileval);
149 break;
150 case TYPE_FONT:
151 to->u.fontval = fontspec_copy(from->u.fontval);
152 break;
153 }
154 }
155
156 /*
157 * Free an entire 'struct conf_entry' and its dynamic data.
158 */
159 static void free_entry(struct conf_entry *entry)
160 {
161 free_key(&entry->key);
162 free_value(&entry->value, valuetypes[entry->key.primary]);
163 sfree(entry);
164 }
165
166 Conf *conf_new(void)
167 {
168 Conf *conf = snew(struct conf_tag);
169
170 conf->tree = newtree234(conf_cmp);
171
172 return conf;
173 }
174
175 static void conf_clear(Conf *conf)
176 {
177 struct conf_entry *entry;
178
179 while ((entry = delpos234(conf->tree, 0)) != NULL)
180 free_entry(entry);
181 }
182
183 void conf_free(Conf *conf)
184 {
185 conf_clear(conf);
186 freetree234(conf->tree);
187 sfree(conf);
188 }
189
190 static void conf_insert(Conf *conf, struct conf_entry *entry)
191 {
192 struct conf_entry *oldentry = add234(conf->tree, entry);
193 if (oldentry && oldentry != entry) {
194 del234(conf->tree, oldentry);
195 free_entry(oldentry);
196 oldentry = add234(conf->tree, entry);
197 assert(oldentry == entry);
198 }
199 }
200
201 void conf_copy_into(Conf *newconf, Conf *oldconf)
202 {
203 struct conf_entry *entry, *entry2;
204 int i;
205
206 conf_clear(newconf);
207
208 for (i = 0; (entry = index234(oldconf->tree, i)) != NULL; i++) {
209 entry2 = snew(struct conf_entry);
210 copy_key(&entry2->key, &entry->key);
211 copy_value(&entry2->value, &entry->value,
212 valuetypes[entry->key.primary]);
213 add234(newconf->tree, entry2);
214 }
215 }
216
217 Conf *conf_copy(Conf *oldconf)
218 {
219 Conf *newconf = conf_new();
220
221 conf_copy_into(newconf, oldconf);
222
223 return newconf;
224 }
225
226 int conf_get_int(Conf *conf, int primary)
227 {
228 struct key key;
229 struct conf_entry *entry;
230
231 assert(subkeytypes[primary] == TYPE_NONE);
232 assert(valuetypes[primary] == TYPE_INT);
233 key.primary = primary;
234 entry = find234(conf->tree, &key, NULL);
235 assert(entry);
236 return entry->value.u.intval;
237 }
238
239 int conf_get_int_int(Conf *conf, int primary, int secondary)
240 {
241 struct key key;
242 struct conf_entry *entry;
243
244 assert(subkeytypes[primary] == TYPE_INT);
245 assert(valuetypes[primary] == TYPE_INT);
246 key.primary = primary;
247 key.secondary.i = secondary;
248 entry = find234(conf->tree, &key, NULL);
249 assert(entry);
250 return entry->value.u.intval;
251 }
252
253 char *conf_get_str(Conf *conf, int primary)
254 {
255 struct key key;
256 struct conf_entry *entry;
257
258 assert(subkeytypes[primary] == TYPE_NONE);
259 assert(valuetypes[primary] == TYPE_STR);
260 key.primary = primary;
261 entry = find234(conf->tree, &key, NULL);
262 assert(entry);
263 return entry->value.u.stringval;
264 }
265
266 char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary)
267 {
268 struct key key;
269 struct conf_entry *entry;
270
271 assert(subkeytypes[primary] == TYPE_STR);
272 assert(valuetypes[primary] == TYPE_STR);
273 key.primary = primary;
274 key.secondary.s = (char *)secondary;
275 entry = find234(conf->tree, &key, NULL);
276 return entry ? entry->value.u.stringval : NULL;
277 }
278
279 char *conf_get_str_str(Conf *conf, int primary, const char *secondary)
280 {
281 char *ret = conf_get_str_str_opt(conf, primary, secondary);
282 assert(ret);
283 return ret;
284 }
285
286 char *conf_get_str_strs(Conf *conf, int primary,
287 char *subkeyin, char **subkeyout)
288 {
289 struct key key;
290 struct conf_entry *entry;
291
292 assert(subkeytypes[primary] == TYPE_STR);
293 assert(valuetypes[primary] == TYPE_STR);
294 key.primary = primary;
295 if (subkeyin) {
296 key.secondary.s = subkeyin;
297 entry = findrel234(conf->tree, &key, NULL, REL234_GT);
298 } else {
299 key.secondary.s = "";
300 entry = findrel234(conf->tree, &key, NULL, REL234_GE);
301 }
302 if (!entry || entry->key.primary != primary)
303 return NULL;
304 *subkeyout = entry->key.secondary.s;
305 return entry->value.u.stringval;
306 }
307
308 char *conf_get_str_nthstrkey(Conf *conf, int primary, int n)
309 {
310 struct key key;
311 struct conf_entry *entry;
312 int index;
313
314 assert(subkeytypes[primary] == TYPE_STR);
315 assert(valuetypes[primary] == TYPE_STR);
316 key.primary = primary;
317 key.secondary.s = "";
318 entry = findrelpos234(conf->tree, &key, NULL, REL234_GE, &index);
319 if (!entry || entry->key.primary != primary)
320 return NULL;
321 entry = index234(conf->tree, index + n);
322 if (!entry || entry->key.primary != primary)
323 return NULL;
324 return entry->key.secondary.s;
325 }
326
327 Filename *conf_get_filename(Conf *conf, int primary)
328 {
329 struct key key;
330 struct conf_entry *entry;
331
332 assert(subkeytypes[primary] == TYPE_NONE);
333 assert(valuetypes[primary] == TYPE_FILENAME);
334 key.primary = primary;
335 entry = find234(conf->tree, &key, NULL);
336 assert(entry);
337 return entry->value.u.fileval;
338 }
339
340 FontSpec *conf_get_fontspec(Conf *conf, int primary)
341 {
342 struct key key;
343 struct conf_entry *entry;
344
345 assert(subkeytypes[primary] == TYPE_NONE);
346 assert(valuetypes[primary] == TYPE_FONT);
347 key.primary = primary;
348 entry = find234(conf->tree, &key, NULL);
349 assert(entry);
350 return entry->value.u.fontval;
351 }
352
353 void conf_set_int(Conf *conf, int primary, int value)
354 {
355 struct conf_entry *entry = snew(struct conf_entry);
356
357 assert(subkeytypes[primary] == TYPE_NONE);
358 assert(valuetypes[primary] == TYPE_INT);
359 entry->key.primary = primary;
360 entry->value.u.intval = value;
361 conf_insert(conf, entry);
362 }
363
364 void conf_set_int_int(Conf *conf, int primary, int secondary, int value)
365 {
366 struct conf_entry *entry = snew(struct conf_entry);
367
368 assert(subkeytypes[primary] == TYPE_INT);
369 assert(valuetypes[primary] == TYPE_INT);
370 entry->key.primary = primary;
371 entry->key.secondary.i = secondary;
372 entry->value.u.intval = value;
373 conf_insert(conf, entry);
374 }
375
376 void conf_set_str(Conf *conf, int primary, const char *value)
377 {
378 struct conf_entry *entry = snew(struct conf_entry);
379
380 assert(subkeytypes[primary] == TYPE_NONE);
381 assert(valuetypes[primary] == TYPE_STR);
382 entry->key.primary = primary;
383 entry->value.u.stringval = dupstr(value);
384 conf_insert(conf, entry);
385 }
386
387 void conf_set_str_str(Conf *conf, int primary, const char *secondary,
388 const char *value)
389 {
390 struct conf_entry *entry = snew(struct conf_entry);
391
392 assert(subkeytypes[primary] == TYPE_STR);
393 assert(valuetypes[primary] == TYPE_STR);
394 entry->key.primary = primary;
395 entry->key.secondary.s = dupstr(secondary);
396 entry->value.u.stringval = dupstr(value);
397 conf_insert(conf, entry);
398 }
399
400 void conf_del_str_str(Conf *conf, int primary, const char *secondary)
401 {
402 struct key key;
403 struct conf_entry *entry;
404
405 assert(subkeytypes[primary] == TYPE_STR);
406 assert(valuetypes[primary] == TYPE_STR);
407 key.primary = primary;
408 key.secondary.s = (char *)secondary;
409 entry = find234(conf->tree, &key, NULL);
410 if (entry) {
411 del234(conf->tree, entry);
412 free_entry(entry);
413 }
414 }
415
416 void conf_set_filename(Conf *conf, int primary, const Filename *value)
417 {
418 struct conf_entry *entry = snew(struct conf_entry);
419
420 assert(subkeytypes[primary] == TYPE_NONE);
421 assert(valuetypes[primary] == TYPE_FILENAME);
422 entry->key.primary = primary;
423 entry->value.u.fileval = filename_copy(value);
424 conf_insert(conf, entry);
425 }
426
427 void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value)
428 {
429 struct conf_entry *entry = snew(struct conf_entry);
430
431 assert(subkeytypes[primary] == TYPE_NONE);
432 assert(valuetypes[primary] == TYPE_FONT);
433 entry->key.primary = primary;
434 entry->value.u.fontval = fontspec_copy(value);
435 conf_insert(conf, entry);
436 }
437
438 int conf_serialised_size(Conf *conf)
439 {
440 int i;
441 struct conf_entry *entry;
442 int size = 0;
443
444 for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
445 size += 4; /* primary key */
446 switch (subkeytypes[entry->key.primary]) {
447 case TYPE_INT:
448 size += 4;
449 break;
450 case TYPE_STR:
451 size += 1 + strlen(entry->key.secondary.s);
452 break;
453 }
454 switch (valuetypes[entry->key.primary]) {
455 case TYPE_INT:
456 size += 4;
457 break;
458 case TYPE_STR:
459 size += 1 + strlen(entry->value.u.stringval);
460 break;
461 case TYPE_FILENAME:
462 size += filename_serialise(entry->value.u.fileval, NULL);
463 break;
464 case TYPE_FONT:
465 size += fontspec_serialise(entry->value.u.fontval, NULL);
466 break;
467 }
468 }
469
470 size += 4; /* terminator value */
471
472 return size;
473 }
474
475 void conf_serialise(Conf *conf, void *vdata)
476 {
477 unsigned char *data = (unsigned char *)vdata;
478 int i, len;
479 struct conf_entry *entry;
480
481 for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
482 PUT_32BIT_MSB_FIRST(data, entry->key.primary);
483 data += 4;
484
485 switch (subkeytypes[entry->key.primary]) {
486 case TYPE_INT:
487 PUT_32BIT_MSB_FIRST(data, entry->key.secondary.i);
488 data += 4;
489 break;
490 case TYPE_STR:
491 len = strlen(entry->key.secondary.s);
492 memcpy(data, entry->key.secondary.s, len);
493 data += len;
494 *data++ = 0;
495 break;
496 }
497 switch (valuetypes[entry->key.primary]) {
498 case TYPE_INT:
499 PUT_32BIT_MSB_FIRST(data, entry->value.u.intval);
500 data += 4;
501 break;
502 case TYPE_STR:
503 len = strlen(entry->value.u.stringval);
504 memcpy(data, entry->value.u.stringval, len);
505 data += len;
506 *data++ = 0;
507 break;
508 case TYPE_FILENAME:
509 data += filename_serialise(entry->value.u.fileval, data);
510 break;
511 case TYPE_FONT:
512 data += fontspec_serialise(entry->value.u.fontval, data);
513 break;
514 }
515 }
516
517 PUT_32BIT_MSB_FIRST(data, 0xFFFFFFFFU);
518 }
519
520 int conf_deserialise(Conf *conf, void *vdata, int maxsize)
521 {
522 unsigned char *data = (unsigned char *)vdata;
523 unsigned char *start = data;
524 struct conf_entry *entry;
525 unsigned primary;
526 int used;
527 unsigned char *zero;
528
529 while (maxsize >= 4) {
530 primary = GET_32BIT_MSB_FIRST(data);
531 data += 4, maxsize -= 4;
532
533 if (primary >= N_CONFIG_OPTIONS)
534 break;
535
536 entry = snew(struct conf_entry);
537 entry->key.primary = primary;
538
539 switch (subkeytypes[entry->key.primary]) {
540 case TYPE_INT:
541 if (maxsize < 4) {
542 sfree(entry);
543 goto done;
544 }
545 entry->key.secondary.i = toint(GET_32BIT_MSB_FIRST(data));
546 data += 4, maxsize -= 4;
547 break;
548 case TYPE_STR:
549 zero = memchr(data, 0, maxsize);
550 if (!zero) {
551 sfree(entry);
552 goto done;
553 }
554 entry->key.secondary.s = dupstr((char *)data);
555 maxsize -= (zero + 1 - data);
556 data = zero + 1;
557 break;
558 }
559
560 switch (valuetypes[entry->key.primary]) {
561 case TYPE_INT:
562 if (maxsize < 4) {
563 if (subkeytypes[entry->key.primary] == TYPE_STR)
564 sfree(entry->key.secondary.s);
565 sfree(entry);
566 goto done;
567 }
568 entry->value.u.intval = toint(GET_32BIT_MSB_FIRST(data));
569 data += 4, maxsize -= 4;
570 break;
571 case TYPE_STR:
572 zero = memchr(data, 0, maxsize);
573 if (!zero) {
574 if (subkeytypes[entry->key.primary] == TYPE_STR)
575 sfree(entry->key.secondary.s);
576 sfree(entry);
577 goto done;
578 }
579 entry->value.u.stringval = dupstr((char *)data);
580 maxsize -= (zero + 1 - data);
581 data = zero + 1;
582 break;
583 case TYPE_FILENAME:
584 entry->value.u.fileval =
585 filename_deserialise(data, maxsize, &used);
586 if (!entry->value.u.fileval) {
587 if (subkeytypes[entry->key.primary] == TYPE_STR)
588 sfree(entry->key.secondary.s);
589 sfree(entry);
590 goto done;
591 }
592 data += used;
593 maxsize -= used;
594 break;
595 case TYPE_FONT:
596 entry->value.u.fontval =
597 fontspec_deserialise(data, maxsize, &used);
598 if (!entry->value.u.fontval) {
599 if (subkeytypes[entry->key.primary] == TYPE_STR)
600 sfree(entry->key.secondary.s);
601 sfree(entry);
602 goto done;
603 }
604 data += used;
605 maxsize -= used;
606 break;
607 }
608 conf_insert(conf, entry);
609 }
610
611 done:
612 return (int)(data - start);
613 }