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