Post-release destabilisation! Completely remove the struct type
[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 for (i = 0; (entry = index234(oldconf->tree, i)) != NULL; i++) {
203 entry2 = snew(struct conf_entry);
204 copy_key(&entry2->key, &entry->key);
205 copy_value(&entry2->value, &entry->value,
206 valuetypes[entry->key.primary]);
207 add234(newconf->tree, entry2);
208 }
209 }
210
211 Conf *conf_copy(Conf *oldconf)
212 {
213 Conf *newconf = conf_new();
214
215 conf_copy_into(newconf, oldconf);
216
217 return newconf;
218 }
219
220 int conf_get_int(Conf *conf, int primary)
221 {
222 struct key key;
223 struct conf_entry *entry;
224
225 assert(subkeytypes[primary] == TYPE_NONE);
226 assert(valuetypes[primary] == TYPE_INT);
227 key.primary = primary;
228 entry = find234(conf->tree, &key, NULL);
229 assert(entry);
230 return entry->value.u.intval;
231 }
232
233 int conf_get_int_int(Conf *conf, int primary, int secondary)
234 {
235 struct key key;
236 struct conf_entry *entry;
237
238 assert(subkeytypes[primary] == TYPE_INT);
239 assert(valuetypes[primary] == TYPE_INT);
240 key.primary = primary;
241 key.secondary.i = secondary;
242 entry = find234(conf->tree, &key, NULL);
243 assert(entry);
244 return entry->value.u.intval;
245 }
246
247 char *conf_get_str(Conf *conf, int primary)
248 {
249 struct key key;
250 struct conf_entry *entry;
251
252 assert(subkeytypes[primary] == TYPE_NONE);
253 assert(valuetypes[primary] == TYPE_STR);
254 key.primary = primary;
255 entry = find234(conf->tree, &key, NULL);
256 assert(entry);
257 return entry->value.u.stringval;
258 }
259
260 char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary)
261 {
262 struct key key;
263 struct conf_entry *entry;
264
265 assert(subkeytypes[primary] == TYPE_STR);
266 assert(valuetypes[primary] == TYPE_STR);
267 key.primary = primary;
268 key.secondary.s = (char *)secondary;
269 entry = find234(conf->tree, &key, NULL);
270 return entry ? entry->value.u.stringval : NULL;
271 }
272
273 char *conf_get_str_str(Conf *conf, int primary, const char *secondary)
274 {
275 char *ret = conf_get_str_str_opt(conf, primary, secondary);
276 assert(ret);
277 return ret;
278 }
279
280 char *conf_get_str_strs(Conf *conf, int primary,
281 char *subkeyin, char **subkeyout)
282 {
283 struct key key;
284 struct conf_entry *entry;
285
286 assert(subkeytypes[primary] == TYPE_STR);
287 assert(valuetypes[primary] == TYPE_STR);
288 key.primary = primary;
289 if (subkeyin) {
290 key.secondary.s = subkeyin;
291 entry = findrel234(conf->tree, &key, NULL, REL234_GT);
292 } else {
293 key.secondary.s = "";
294 entry = findrel234(conf->tree, &key, NULL, REL234_GE);
295 }
296 if (!entry || entry->key.primary != primary)
297 return NULL;
298 *subkeyout = entry->key.secondary.s;
299 return entry->value.u.stringval;
300 }
301
302 char *conf_get_str_nthstrkey(Conf *conf, int primary, int n)
303 {
304 struct key key;
305 struct conf_entry *entry;
306 int index;
307
308 assert(subkeytypes[primary] == TYPE_STR);
309 assert(valuetypes[primary] == TYPE_STR);
310 key.primary = primary;
311 key.secondary.s = "";
312 entry = findrelpos234(conf->tree, &key, NULL, REL234_GE, &index);
313 if (!entry || entry->key.primary != primary)
314 return NULL;
315 entry = index234(conf->tree, index + n);
316 if (!entry || entry->key.primary != primary)
317 return NULL;
318 return entry->key.secondary.s;
319 }
320
321 Filename *conf_get_filename(Conf *conf, int primary)
322 {
323 struct key key;
324 struct conf_entry *entry;
325
326 assert(subkeytypes[primary] == TYPE_NONE);
327 assert(valuetypes[primary] == TYPE_FILENAME);
328 key.primary = primary;
329 entry = find234(conf->tree, &key, NULL);
330 assert(entry);
331 return &entry->value.u.fileval;
332 }
333
334 FontSpec *conf_get_fontspec(Conf *conf, int primary)
335 {
336 struct key key;
337 struct conf_entry *entry;
338
339 assert(subkeytypes[primary] == TYPE_NONE);
340 assert(valuetypes[primary] == TYPE_FONT);
341 key.primary = primary;
342 entry = find234(conf->tree, &key, NULL);
343 assert(entry);
344 return &entry->value.u.fontval;
345 }
346
347 void conf_set_int(Conf *conf, int primary, int value)
348 {
349 struct conf_entry *entry = snew(struct conf_entry);
350
351 assert(subkeytypes[primary] == TYPE_NONE);
352 assert(valuetypes[primary] == TYPE_INT);
353 entry->key.primary = primary;
354 entry->value.u.intval = value;
355 conf_insert(conf, entry);
356 }
357
358 void conf_set_int_int(Conf *conf, int primary, int secondary, int value)
359 {
360 struct conf_entry *entry = snew(struct conf_entry);
361
362 assert(subkeytypes[primary] == TYPE_INT);
363 assert(valuetypes[primary] == TYPE_INT);
364 entry->key.primary = primary;
365 entry->key.secondary.i = secondary;
366 entry->value.u.intval = value;
367 conf_insert(conf, entry);
368 }
369
370 void conf_set_str(Conf *conf, int primary, const char *value)
371 {
372 struct conf_entry *entry = snew(struct conf_entry);
373
374 assert(subkeytypes[primary] == TYPE_NONE);
375 assert(valuetypes[primary] == TYPE_STR);
376 entry->key.primary = primary;
377 entry->value.u.stringval = dupstr(value);
378 conf_insert(conf, entry);
379 }
380
381 void conf_set_str_str(Conf *conf, int primary, const char *secondary,
382 const char *value)
383 {
384 struct conf_entry *entry = snew(struct conf_entry);
385
386 assert(subkeytypes[primary] == TYPE_STR);
387 assert(valuetypes[primary] == TYPE_STR);
388 entry->key.primary = primary;
389 entry->key.secondary.s = dupstr(secondary);
390 entry->value.u.stringval = dupstr(value);
391 conf_insert(conf, entry);
392 }
393
394 void conf_del_str_str(Conf *conf, int primary, const char *secondary)
395 {
396 struct key key;
397 struct conf_entry *entry;
398
399 assert(subkeytypes[primary] == TYPE_STR);
400 assert(valuetypes[primary] == TYPE_STR);
401 key.primary = primary;
402 key.secondary.s = (char *)secondary;
403 entry = find234(conf->tree, &key, NULL);
404 if (entry) {
405 del234(conf->tree, entry);
406 free_entry(entry);
407 }
408 }
409
410 void conf_set_filename(Conf *conf, int primary, const Filename *value)
411 {
412 struct conf_entry *entry = snew(struct conf_entry);
413
414 assert(subkeytypes[primary] == TYPE_NONE);
415 assert(valuetypes[primary] == TYPE_FILENAME);
416 entry->key.primary = primary;
417 entry->value.u.fileval = *value; /* structure copy */
418 conf_insert(conf, entry);
419 }
420
421 void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value)
422 {
423 struct conf_entry *entry = snew(struct conf_entry);
424
425 assert(subkeytypes[primary] == TYPE_NONE);
426 assert(valuetypes[primary] == TYPE_FONT);
427 entry->key.primary = primary;
428 entry->value.u.fontval = *value; /* structure copy */
429 conf_insert(conf, entry);
430 }
431
432 int conf_serialised_size(Conf *conf)
433 {
434 int i;
435 struct conf_entry *entry;
436 int size = 0;
437
438 for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
439 size += 4; /* primary key */
440 switch (subkeytypes[entry->key.primary]) {
441 case TYPE_INT:
442 size += 4;
443 break;
444 case TYPE_STR:
445 size += 1 + strlen(entry->key.secondary.s);
446 break;
447 }
448 switch (valuetypes[entry->key.primary]) {
449 case TYPE_INT:
450 size += 4;
451 break;
452 case TYPE_STR:
453 size += 1 + strlen(entry->value.u.stringval);
454 break;
455 case TYPE_FILENAME:
456 size += sizeof(entry->value.u.fileval);
457 break;
458 case TYPE_FONT:
459 size += sizeof(entry->value.u.fontval);
460 break;
461 }
462 }
463
464 size += 4; /* terminator value */
465
466 return size;
467 }
468
469 void conf_serialise(Conf *conf, void *vdata)
470 {
471 unsigned char *data = (unsigned char *)vdata;
472 int i, len;
473 struct conf_entry *entry;
474
475 for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
476 PUT_32BIT_MSB_FIRST(data, entry->key.primary);
477 data += 4;
478
479 switch (subkeytypes[entry->key.primary]) {
480 case TYPE_INT:
481 PUT_32BIT_MSB_FIRST(data, entry->key.secondary.i);
482 data += 4;
483 break;
484 case TYPE_STR:
485 len = strlen(entry->key.secondary.s);
486 memcpy(data, entry->key.secondary.s, len);
487 data += len;
488 *data++ = 0;
489 break;
490 }
491 switch (valuetypes[entry->key.primary]) {
492 case TYPE_INT:
493 PUT_32BIT_MSB_FIRST(data, entry->value.u.intval);
494 data += 4;
495 break;
496 case TYPE_STR:
497 len = strlen(entry->value.u.stringval);
498 memcpy(data, entry->value.u.stringval, len);
499 data += len;
500 *data++ = 0;
501 break;
502 case TYPE_FILENAME:
503 memcpy(data, &entry->value.u.fileval,
504 sizeof(entry->value.u.fileval));
505 data += sizeof(entry->value.u.fileval);
506 break;
507 case TYPE_FONT:
508 memcpy(data, &entry->value.u.fontval,
509 sizeof(entry->value.u.fontval));
510 data += sizeof(entry->value.u.fontval);
511 break;
512 }
513 }
514
515 PUT_32BIT_MSB_FIRST(data, 0xFFFFFFFFU);
516 }
517
518 int conf_deserialise(Conf *conf, void *vdata, int maxsize)
519 {
520 unsigned char *data = (unsigned char *)vdata;
521 unsigned char *start = data;
522 struct conf_entry *entry;
523 int primary;
524 unsigned char *zero;
525
526 while (maxsize >= 4) {
527 primary = GET_32BIT_MSB_FIRST(data);
528 data += 4, maxsize -= 4;
529
530 if ((unsigned)primary >= N_CONFIG_OPTIONS)
531 break;
532
533 entry = snew(struct conf_entry);
534 entry->key.primary = primary;
535
536 switch (subkeytypes[entry->key.primary]) {
537 case TYPE_INT:
538 if (maxsize < 4) {
539 sfree(entry);
540 goto done;
541 }
542 entry->key.secondary.i = GET_32BIT_MSB_FIRST(data);
543 data += 4, maxsize -= 4;
544 break;
545 case TYPE_STR:
546 zero = memchr(data, 0, maxsize);
547 if (!zero) {
548 sfree(entry);
549 goto done;
550 }
551 entry->key.secondary.s = dupstr((char *)data);
552 maxsize -= (zero + 1 - data);
553 data = zero + 1;
554 break;
555 }
556
557 switch (valuetypes[entry->key.primary]) {
558 case TYPE_INT:
559 if (maxsize < 4) {
560 if (subkeytypes[entry->key.primary] == TYPE_STR)
561 sfree(entry->key.secondary.s);
562 sfree(entry);
563 goto done;
564 }
565 entry->value.u.intval = GET_32BIT_MSB_FIRST(data);
566 data += 4, maxsize -= 4;
567 break;
568 case TYPE_STR:
569 zero = memchr(data, 0, maxsize);
570 if (!zero) {
571 if (subkeytypes[entry->key.primary] == TYPE_STR)
572 sfree(entry->key.secondary.s);
573 sfree(entry);
574 goto done;
575 }
576 entry->value.u.stringval = dupstr((char *)data);
577 maxsize -= (zero + 1 - data);
578 data = zero + 1;
579 break;
580 case TYPE_FILENAME:
581 if (maxsize < sizeof(entry->value.u.fileval)) {
582 if (subkeytypes[entry->key.primary] == TYPE_STR)
583 sfree(entry->key.secondary.s);
584 sfree(entry);
585 goto done;
586 }
587 memcpy(&entry->value.u.fileval, data,
588 sizeof(entry->value.u.fileval));
589 data += sizeof(entry->value.u.fileval);
590 maxsize -= sizeof(entry->value.u.fileval);
591 break;
592 case TYPE_FONT:
593 if (maxsize < sizeof(entry->value.u.fontval)) {
594 if (subkeytypes[entry->key.primary] == TYPE_STR)
595 sfree(entry->key.secondary.s);
596 sfree(entry);
597 goto done;
598 }
599 memcpy(&entry->value.u.fontval, data,
600 sizeof(entry->value.u.fontval));
601 data += sizeof(entry->value.u.fontval);
602 maxsize -= sizeof(entry->value.u.fontval);
603 break;
604 }
605 conf_insert(conf, entry);
606 }
607
608 done:
609 return (int)(data - start);
610 }