Change the semantics of 'FontSpec' so that it's a dynamically
[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_FONT)
129 fontspec_free(val->u.fontval);
130 }
131
132 /*
133 * Copy a 'struct value' into another one, copying its dynamic data
134 * if necessary.
135 */
136 static void copy_value(struct value *to, struct value *from, int type)
137 {
138 switch (type) {
139 case TYPE_INT:
140 to->u.intval = from->u.intval;
141 break;
142 case TYPE_STR:
143 to->u.stringval = dupstr(from->u.stringval);
144 break;
145 case TYPE_FILENAME:
146 to->u.fileval = from->u.fileval;
147 break;
148 case TYPE_FONT:
149 to->u.fontval = fontspec_copy(from->u.fontval);
150 break;
151 }
152 }
153
154 /*
155 * Free an entire 'struct conf_entry' and its dynamic data.
156 */
157 static void free_entry(struct conf_entry *entry)
158 {
159 free_key(&entry->key);
160 free_value(&entry->value, valuetypes[entry->key.primary]);
161 sfree(entry);
162 }
163
164 Conf *conf_new(void)
165 {
166 Conf *conf = snew(struct conf_tag);
167
168 conf->tree = newtree234(conf_cmp);
169
170 return conf;
171 }
172
173 static void conf_clear(Conf *conf)
174 {
175 struct conf_entry *entry;
176
177 while ((entry = delpos234(conf->tree, 0)) != NULL)
178 free_entry(entry);
179 }
180
181 void conf_free(Conf *conf)
182 {
183 conf_clear(conf);
184 freetree234(conf->tree);
185 sfree(conf);
186 }
187
188 static void conf_insert(Conf *conf, struct conf_entry *entry)
189 {
190 struct conf_entry *oldentry = add234(conf->tree, entry);
191 if (oldentry && oldentry != entry) {
192 del234(conf->tree, oldentry);
193 free_entry(oldentry);
194 oldentry = add234(conf->tree, entry);
195 assert(oldentry == entry);
196 }
197 }
198
199 void conf_copy_into(Conf *newconf, Conf *oldconf)
200 {
201 struct conf_entry *entry, *entry2;
202 int i;
203
204 conf_clear(newconf);
205
206 for (i = 0; (entry = index234(oldconf->tree, i)) != NULL; i++) {
207 entry2 = snew(struct conf_entry);
208 copy_key(&entry2->key, &entry->key);
209 copy_value(&entry2->value, &entry->value,
210 valuetypes[entry->key.primary]);
211 add234(newconf->tree, entry2);
212 }
213 }
214
215 Conf *conf_copy(Conf *oldconf)
216 {
217 Conf *newconf = conf_new();
218
219 conf_copy_into(newconf, oldconf);
220
221 return newconf;
222 }
223
224 int conf_get_int(Conf *conf, int primary)
225 {
226 struct key key;
227 struct conf_entry *entry;
228
229 assert(subkeytypes[primary] == TYPE_NONE);
230 assert(valuetypes[primary] == TYPE_INT);
231 key.primary = primary;
232 entry = find234(conf->tree, &key, NULL);
233 assert(entry);
234 return entry->value.u.intval;
235 }
236
237 int conf_get_int_int(Conf *conf, int primary, int secondary)
238 {
239 struct key key;
240 struct conf_entry *entry;
241
242 assert(subkeytypes[primary] == TYPE_INT);
243 assert(valuetypes[primary] == TYPE_INT);
244 key.primary = primary;
245 key.secondary.i = secondary;
246 entry = find234(conf->tree, &key, NULL);
247 assert(entry);
248 return entry->value.u.intval;
249 }
250
251 char *conf_get_str(Conf *conf, int primary)
252 {
253 struct key key;
254 struct conf_entry *entry;
255
256 assert(subkeytypes[primary] == TYPE_NONE);
257 assert(valuetypes[primary] == TYPE_STR);
258 key.primary = primary;
259 entry = find234(conf->tree, &key, NULL);
260 assert(entry);
261 return entry->value.u.stringval;
262 }
263
264 char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary)
265 {
266 struct key key;
267 struct conf_entry *entry;
268
269 assert(subkeytypes[primary] == TYPE_STR);
270 assert(valuetypes[primary] == TYPE_STR);
271 key.primary = primary;
272 key.secondary.s = (char *)secondary;
273 entry = find234(conf->tree, &key, NULL);
274 return entry ? entry->value.u.stringval : NULL;
275 }
276
277 char *conf_get_str_str(Conf *conf, int primary, const char *secondary)
278 {
279 char *ret = conf_get_str_str_opt(conf, primary, secondary);
280 assert(ret);
281 return ret;
282 }
283
284 char *conf_get_str_strs(Conf *conf, int primary,
285 char *subkeyin, char **subkeyout)
286 {
287 struct key key;
288 struct conf_entry *entry;
289
290 assert(subkeytypes[primary] == TYPE_STR);
291 assert(valuetypes[primary] == TYPE_STR);
292 key.primary = primary;
293 if (subkeyin) {
294 key.secondary.s = subkeyin;
295 entry = findrel234(conf->tree, &key, NULL, REL234_GT);
296 } else {
297 key.secondary.s = "";
298 entry = findrel234(conf->tree, &key, NULL, REL234_GE);
299 }
300 if (!entry || entry->key.primary != primary)
301 return NULL;
302 *subkeyout = entry->key.secondary.s;
303 return entry->value.u.stringval;
304 }
305
306 char *conf_get_str_nthstrkey(Conf *conf, int primary, int n)
307 {
308 struct key key;
309 struct conf_entry *entry;
310 int index;
311
312 assert(subkeytypes[primary] == TYPE_STR);
313 assert(valuetypes[primary] == TYPE_STR);
314 key.primary = primary;
315 key.secondary.s = "";
316 entry = findrelpos234(conf->tree, &key, NULL, REL234_GE, &index);
317 if (!entry || entry->key.primary != primary)
318 return NULL;
319 entry = index234(conf->tree, index + n);
320 if (!entry || entry->key.primary != primary)
321 return NULL;
322 return entry->key.secondary.s;
323 }
324
325 Filename *conf_get_filename(Conf *conf, int primary)
326 {
327 struct key key;
328 struct conf_entry *entry;
329
330 assert(subkeytypes[primary] == TYPE_NONE);
331 assert(valuetypes[primary] == TYPE_FILENAME);
332 key.primary = primary;
333 entry = find234(conf->tree, &key, NULL);
334 assert(entry);
335 return &entry->value.u.fileval;
336 }
337
338 FontSpec *conf_get_fontspec(Conf *conf, int primary)
339 {
340 struct key key;
341 struct conf_entry *entry;
342
343 assert(subkeytypes[primary] == TYPE_NONE);
344 assert(valuetypes[primary] == TYPE_FONT);
345 key.primary = primary;
346 entry = find234(conf->tree, &key, NULL);
347 assert(entry);
348 return entry->value.u.fontval;
349 }
350
351 void conf_set_int(Conf *conf, int primary, int value)
352 {
353 struct conf_entry *entry = snew(struct conf_entry);
354
355 assert(subkeytypes[primary] == TYPE_NONE);
356 assert(valuetypes[primary] == TYPE_INT);
357 entry->key.primary = primary;
358 entry->value.u.intval = value;
359 conf_insert(conf, entry);
360 }
361
362 void conf_set_int_int(Conf *conf, int primary, int secondary, int value)
363 {
364 struct conf_entry *entry = snew(struct conf_entry);
365
366 assert(subkeytypes[primary] == TYPE_INT);
367 assert(valuetypes[primary] == TYPE_INT);
368 entry->key.primary = primary;
369 entry->key.secondary.i = secondary;
370 entry->value.u.intval = value;
371 conf_insert(conf, entry);
372 }
373
374 void conf_set_str(Conf *conf, int primary, const char *value)
375 {
376 struct conf_entry *entry = snew(struct conf_entry);
377
378 assert(subkeytypes[primary] == TYPE_NONE);
379 assert(valuetypes[primary] == TYPE_STR);
380 entry->key.primary = primary;
381 entry->value.u.stringval = dupstr(value);
382 conf_insert(conf, entry);
383 }
384
385 void conf_set_str_str(Conf *conf, int primary, const char *secondary,
386 const char *value)
387 {
388 struct conf_entry *entry = snew(struct conf_entry);
389
390 assert(subkeytypes[primary] == TYPE_STR);
391 assert(valuetypes[primary] == TYPE_STR);
392 entry->key.primary = primary;
393 entry->key.secondary.s = dupstr(secondary);
394 entry->value.u.stringval = dupstr(value);
395 conf_insert(conf, entry);
396 }
397
398 void conf_del_str_str(Conf *conf, int primary, const char *secondary)
399 {
400 struct key key;
401 struct conf_entry *entry;
402
403 assert(subkeytypes[primary] == TYPE_STR);
404 assert(valuetypes[primary] == TYPE_STR);
405 key.primary = primary;
406 key.secondary.s = (char *)secondary;
407 entry = find234(conf->tree, &key, NULL);
408 if (entry) {
409 del234(conf->tree, entry);
410 free_entry(entry);
411 }
412 }
413
414 void conf_set_filename(Conf *conf, int primary, const Filename *value)
415 {
416 struct conf_entry *entry = snew(struct conf_entry);
417
418 assert(subkeytypes[primary] == TYPE_NONE);
419 assert(valuetypes[primary] == TYPE_FILENAME);
420 entry->key.primary = primary;
421 entry->value.u.fileval = *value; /* structure copy */
422 conf_insert(conf, entry);
423 }
424
425 void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value)
426 {
427 struct conf_entry *entry = snew(struct conf_entry);
428
429 assert(subkeytypes[primary] == TYPE_NONE);
430 assert(valuetypes[primary] == TYPE_FONT);
431 entry->key.primary = primary;
432 entry->value.u.fontval = fontspec_copy(value);
433 conf_insert(conf, entry);
434 }
435
436 int conf_serialised_size(Conf *conf)
437 {
438 int i;
439 struct conf_entry *entry;
440 int size = 0;
441
442 for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
443 size += 4; /* primary key */
444 switch (subkeytypes[entry->key.primary]) {
445 case TYPE_INT:
446 size += 4;
447 break;
448 case TYPE_STR:
449 size += 1 + strlen(entry->key.secondary.s);
450 break;
451 }
452 switch (valuetypes[entry->key.primary]) {
453 case TYPE_INT:
454 size += 4;
455 break;
456 case TYPE_STR:
457 size += 1 + strlen(entry->value.u.stringval);
458 break;
459 case TYPE_FILENAME:
460 size += sizeof(entry->value.u.fileval);
461 break;
462 case TYPE_FONT:
463 size += fontspec_serialise(entry->value.u.fontval, NULL);
464 break;
465 }
466 }
467
468 size += 4; /* terminator value */
469
470 return size;
471 }
472
473 void conf_serialise(Conf *conf, void *vdata)
474 {
475 unsigned char *data = (unsigned char *)vdata;
476 int i, len;
477 struct conf_entry *entry;
478
479 for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
480 PUT_32BIT_MSB_FIRST(data, entry->key.primary);
481 data += 4;
482
483 switch (subkeytypes[entry->key.primary]) {
484 case TYPE_INT:
485 PUT_32BIT_MSB_FIRST(data, entry->key.secondary.i);
486 data += 4;
487 break;
488 case TYPE_STR:
489 len = strlen(entry->key.secondary.s);
490 memcpy(data, entry->key.secondary.s, len);
491 data += len;
492 *data++ = 0;
493 break;
494 }
495 switch (valuetypes[entry->key.primary]) {
496 case TYPE_INT:
497 PUT_32BIT_MSB_FIRST(data, entry->value.u.intval);
498 data += 4;
499 break;
500 case TYPE_STR:
501 len = strlen(entry->value.u.stringval);
502 memcpy(data, entry->value.u.stringval, len);
503 data += len;
504 *data++ = 0;
505 break;
506 case TYPE_FILENAME:
507 memcpy(data, &entry->value.u.fileval,
508 sizeof(entry->value.u.fileval));
509 data += sizeof(entry->value.u.fileval);
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 int primary, used;
526 unsigned char *zero;
527
528 while (maxsize >= 4) {
529 primary = GET_32BIT_MSB_FIRST(data);
530 data += 4, maxsize -= 4;
531
532 if ((unsigned)primary >= N_CONFIG_OPTIONS)
533 break;
534
535 entry = snew(struct conf_entry);
536 entry->key.primary = primary;
537
538 switch (subkeytypes[entry->key.primary]) {
539 case TYPE_INT:
540 if (maxsize < 4) {
541 sfree(entry);
542 goto done;
543 }
544 entry->key.secondary.i = GET_32BIT_MSB_FIRST(data);
545 data += 4, maxsize -= 4;
546 break;
547 case TYPE_STR:
548 zero = memchr(data, 0, maxsize);
549 if (!zero) {
550 sfree(entry);
551 goto done;
552 }
553 entry->key.secondary.s = dupstr((char *)data);
554 maxsize -= (zero + 1 - data);
555 data = zero + 1;
556 break;
557 }
558
559 switch (valuetypes[entry->key.primary]) {
560 case TYPE_INT:
561 if (maxsize < 4) {
562 if (subkeytypes[entry->key.primary] == TYPE_STR)
563 sfree(entry->key.secondary.s);
564 sfree(entry);
565 goto done;
566 }
567 entry->value.u.intval = GET_32BIT_MSB_FIRST(data);
568 data += 4, maxsize -= 4;
569 break;
570 case TYPE_STR:
571 zero = memchr(data, 0, maxsize);
572 if (!zero) {
573 if (subkeytypes[entry->key.primary] == TYPE_STR)
574 sfree(entry->key.secondary.s);
575 sfree(entry);
576 goto done;
577 }
578 entry->value.u.stringval = dupstr((char *)data);
579 maxsize -= (zero + 1 - data);
580 data = zero + 1;
581 break;
582 case TYPE_FILENAME:
583 if (maxsize < sizeof(entry->value.u.fileval)) {
584 if (subkeytypes[entry->key.primary] == TYPE_STR)
585 sfree(entry->key.secondary.s);
586 sfree(entry);
587 goto done;
588 }
589 memcpy(&entry->value.u.fileval, data,
590 sizeof(entry->value.u.fileval));
591 data += sizeof(entry->value.u.fileval);
592 maxsize -= sizeof(entry->value.u.fileval);
593 break;
594 case TYPE_FONT:
595 entry->value.u.fontval =
596 fontspec_deserialise(data, maxsize, &used);
597 if (!entry->value.u.fontval) {
598 if (subkeytypes[entry->key.primary] == TYPE_STR)
599 sfree(entry->key.secondary.s);
600 sfree(entry);
601 goto done;
602 }
603 data += used;
604 maxsize -= used;
605 break;
606 }
607 conf_insert(conf, entry);
608 }
609
610 done:
611 return (int)(data - start);
612 }