2 * conf.c: implementation of the internal storage format used for
3 * the configuration of a PuTTY session.
14 * Enumeration of types used in keys and values.
16 typedef enum { TYPE_NONE, TYPE_INT, TYPE_STR, TYPE_FILENAME, TYPE_FONT } Type;
19 * Arrays which allow us to look up the subkey and value types for a
20 * given primary key id.
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) };
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).
42 /* Variant form of struct key which doesn't contain dynamic data, used
71 * Because 'struct key' is the first element in 'struct conf_entry',
72 * it's safe (guaranteed by the C standard) to cast arbitrarily back
73 * and forth between the two types. Therefore, we only need one
74 * comparison function, which can double as a main sort function for
75 * the tree (comparing two conf_entry structures with each other)
76 * and a search function (looking up an externally supplied key).
78 static int conf_cmp(void *av, void *bv)
80 struct key *a = (struct key *)av;
81 struct key *b = (struct key *)bv;
83 if (a->primary < b->primary)
85 else if (a->primary > b->primary)
87 switch (subkeytypes[a->primary]) {
89 if (a->secondary.i < b->secondary.i)
91 else if (a->secondary.i > b->secondary.i)
95 return strcmp(a->secondary.s, b->secondary.s);
101 static int conf_cmp_constkey(void *av, void *bv)
103 struct key *a = (struct key *)av;
104 struct constkey *b = (struct constkey *)bv;
106 if (a->primary < b->primary)
108 else if (a->primary > b->primary)
110 switch (subkeytypes[a->primary]) {
112 if (a->secondary.i < b->secondary.i)
114 else if (a->secondary.i > b->secondary.i)
118 return strcmp(a->secondary.s, b->secondary.s);
125 * Free any dynamic data items pointed to by a 'struct key'. We
126 * don't free the structure itself, since it's probably part of a
127 * larger allocated block.
129 static void free_key(struct key *key)
131 if (subkeytypes[key->primary] == TYPE_STR)
132 sfree(key->secondary.s);
136 * Copy a 'struct key' into another one, copying its dynamic data
139 static void copy_key(struct key *to, struct key *from)
141 to->primary = from->primary;
142 switch (subkeytypes[to->primary]) {
144 to->secondary.i = from->secondary.i;
147 to->secondary.s = dupstr(from->secondary.s);
153 * Free any dynamic data items pointed to by a 'struct value'. We
154 * don't free the value itself, since it's probably part of a larger
157 static void free_value(struct value *val, int type)
159 if (type == TYPE_STR)
160 sfree(val->u.stringval);
161 else if (type == TYPE_FILENAME)
162 filename_free(val->u.fileval);
163 else if (type == TYPE_FONT)
164 fontspec_free(val->u.fontval);
168 * Copy a 'struct value' into another one, copying its dynamic data
171 static void copy_value(struct value *to, struct value *from, int type)
175 to->u.intval = from->u.intval;
178 to->u.stringval = dupstr(from->u.stringval);
181 to->u.fileval = filename_copy(from->u.fileval);
184 to->u.fontval = fontspec_copy(from->u.fontval);
190 * Free an entire 'struct conf_entry' and its dynamic data.
192 static void free_entry(struct conf_entry *entry)
194 free_key(&entry->key);
195 free_value(&entry->value, valuetypes[entry->key.primary]);
201 Conf *conf = snew(struct conf_tag);
203 conf->tree = newtree234(conf_cmp);
208 static void conf_clear(Conf *conf)
210 struct conf_entry *entry;
212 while ((entry = delpos234(conf->tree, 0)) != NULL)
216 void conf_free(Conf *conf)
219 freetree234(conf->tree);
223 static void conf_insert(Conf *conf, struct conf_entry *entry)
225 struct conf_entry *oldentry = add234(conf->tree, entry);
226 if (oldentry && oldentry != entry) {
227 del234(conf->tree, oldentry);
228 free_entry(oldentry);
229 oldentry = add234(conf->tree, entry);
230 assert(oldentry == entry);
234 void conf_copy_into(Conf *newconf, Conf *oldconf)
236 struct conf_entry *entry, *entry2;
241 for (i = 0; (entry = index234(oldconf->tree, i)) != NULL; i++) {
242 entry2 = snew(struct conf_entry);
243 copy_key(&entry2->key, &entry->key);
244 copy_value(&entry2->value, &entry->value,
245 valuetypes[entry->key.primary]);
246 add234(newconf->tree, entry2);
250 Conf *conf_copy(Conf *oldconf)
252 Conf *newconf = conf_new();
254 conf_copy_into(newconf, oldconf);
259 int conf_get_int(Conf *conf, int primary)
262 struct conf_entry *entry;
264 assert(subkeytypes[primary] == TYPE_NONE);
265 assert(valuetypes[primary] == TYPE_INT);
266 key.primary = primary;
267 entry = find234(conf->tree, &key, NULL);
269 return entry->value.u.intval;
272 int conf_get_int_int(Conf *conf, int primary, int secondary)
275 struct conf_entry *entry;
277 assert(subkeytypes[primary] == TYPE_INT);
278 assert(valuetypes[primary] == TYPE_INT);
279 key.primary = primary;
280 key.secondary.i = secondary;
281 entry = find234(conf->tree, &key, NULL);
283 return entry->value.u.intval;
286 char *conf_get_str(Conf *conf, int primary)
289 struct conf_entry *entry;
291 assert(subkeytypes[primary] == TYPE_NONE);
292 assert(valuetypes[primary] == TYPE_STR);
293 key.primary = primary;
294 entry = find234(conf->tree, &key, NULL);
296 return entry->value.u.stringval;
299 char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary)
302 struct conf_entry *entry;
304 assert(subkeytypes[primary] == TYPE_STR);
305 assert(valuetypes[primary] == TYPE_STR);
306 key.primary = primary;
307 key.secondary.s = (char *)secondary;
308 entry = find234(conf->tree, &key, NULL);
309 return entry ? entry->value.u.stringval : NULL;
312 char *conf_get_str_str(Conf *conf, int primary, const char *secondary)
314 char *ret = conf_get_str_str_opt(conf, primary, secondary);
319 char *conf_get_str_strs(Conf *conf, int primary,
320 char *subkeyin, char **subkeyout)
323 struct conf_entry *entry;
325 assert(subkeytypes[primary] == TYPE_STR);
326 assert(valuetypes[primary] == TYPE_STR);
327 key.primary = primary;
329 key.secondary.s = subkeyin;
330 entry = findrel234(conf->tree, &key, NULL, REL234_GT);
332 key.secondary.s = "";
333 entry = findrel234(conf->tree, &key, conf_cmp_constkey, REL234_GE);
335 if (!entry || entry->key.primary != primary)
337 *subkeyout = entry->key.secondary.s;
338 return entry->value.u.stringval;
341 char *conf_get_str_nthstrkey(Conf *conf, int primary, int n)
344 struct conf_entry *entry;
347 assert(subkeytypes[primary] == TYPE_STR);
348 assert(valuetypes[primary] == TYPE_STR);
349 key.primary = primary;
350 key.secondary.s = "";
351 entry = findrelpos234(conf->tree, &key, conf_cmp_constkey,
353 if (!entry || entry->key.primary != primary)
355 entry = index234(conf->tree, index + n);
356 if (!entry || entry->key.primary != primary)
358 return entry->key.secondary.s;
361 Filename *conf_get_filename(Conf *conf, int primary)
364 struct conf_entry *entry;
366 assert(subkeytypes[primary] == TYPE_NONE);
367 assert(valuetypes[primary] == TYPE_FILENAME);
368 key.primary = primary;
369 entry = find234(conf->tree, &key, NULL);
371 return entry->value.u.fileval;
374 FontSpec *conf_get_fontspec(Conf *conf, int primary)
377 struct conf_entry *entry;
379 assert(subkeytypes[primary] == TYPE_NONE);
380 assert(valuetypes[primary] == TYPE_FONT);
381 key.primary = primary;
382 entry = find234(conf->tree, &key, NULL);
384 return entry->value.u.fontval;
387 void conf_set_int(Conf *conf, int primary, int value)
389 struct conf_entry *entry = snew(struct conf_entry);
391 assert(subkeytypes[primary] == TYPE_NONE);
392 assert(valuetypes[primary] == TYPE_INT);
393 entry->key.primary = primary;
394 entry->value.u.intval = value;
395 conf_insert(conf, entry);
398 void conf_set_int_int(Conf *conf, int primary, int secondary, int value)
400 struct conf_entry *entry = snew(struct conf_entry);
402 assert(subkeytypes[primary] == TYPE_INT);
403 assert(valuetypes[primary] == TYPE_INT);
404 entry->key.primary = primary;
405 entry->key.secondary.i = secondary;
406 entry->value.u.intval = value;
407 conf_insert(conf, entry);
410 void conf_set_str(Conf *conf, int primary, const char *value)
412 struct conf_entry *entry = snew(struct conf_entry);
414 assert(subkeytypes[primary] == TYPE_NONE);
415 assert(valuetypes[primary] == TYPE_STR);
416 entry->key.primary = primary;
417 entry->value.u.stringval = dupstr(value);
418 conf_insert(conf, entry);
421 void conf_set_str_str(Conf *conf, int primary, const char *secondary,
424 struct conf_entry *entry = snew(struct conf_entry);
426 assert(subkeytypes[primary] == TYPE_STR);
427 assert(valuetypes[primary] == TYPE_STR);
428 entry->key.primary = primary;
429 entry->key.secondary.s = dupstr(secondary);
430 entry->value.u.stringval = dupstr(value);
431 conf_insert(conf, entry);
434 void conf_del_str_str(Conf *conf, int primary, const char *secondary)
437 struct conf_entry *entry;
439 assert(subkeytypes[primary] == TYPE_STR);
440 assert(valuetypes[primary] == TYPE_STR);
441 key.primary = primary;
442 key.secondary.s = (char *)secondary;
443 entry = find234(conf->tree, &key, NULL);
445 del234(conf->tree, entry);
450 void conf_set_filename(Conf *conf, int primary, const Filename *value)
452 struct conf_entry *entry = snew(struct conf_entry);
454 assert(subkeytypes[primary] == TYPE_NONE);
455 assert(valuetypes[primary] == TYPE_FILENAME);
456 entry->key.primary = primary;
457 entry->value.u.fileval = filename_copy(value);
458 conf_insert(conf, entry);
461 void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value)
463 struct conf_entry *entry = snew(struct conf_entry);
465 assert(subkeytypes[primary] == TYPE_NONE);
466 assert(valuetypes[primary] == TYPE_FONT);
467 entry->key.primary = primary;
468 entry->value.u.fontval = fontspec_copy(value);
469 conf_insert(conf, entry);
472 int conf_serialised_size(Conf *conf)
475 struct conf_entry *entry;
478 for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
479 size += 4; /* primary key */
480 switch (subkeytypes[entry->key.primary]) {
485 size += 1 + strlen(entry->key.secondary.s);
488 switch (valuetypes[entry->key.primary]) {
493 size += 1 + strlen(entry->value.u.stringval);
496 size += filename_serialise(entry->value.u.fileval, NULL);
499 size += fontspec_serialise(entry->value.u.fontval, NULL);
504 size += 4; /* terminator value */
509 void conf_serialise(Conf *conf, void *vdata)
511 unsigned char *data = (unsigned char *)vdata;
513 struct conf_entry *entry;
515 for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
516 PUT_32BIT_MSB_FIRST(data, entry->key.primary);
519 switch (subkeytypes[entry->key.primary]) {
521 PUT_32BIT_MSB_FIRST(data, entry->key.secondary.i);
525 len = strlen(entry->key.secondary.s);
526 memcpy(data, entry->key.secondary.s, len);
531 switch (valuetypes[entry->key.primary]) {
533 PUT_32BIT_MSB_FIRST(data, entry->value.u.intval);
537 len = strlen(entry->value.u.stringval);
538 memcpy(data, entry->value.u.stringval, len);
543 data += filename_serialise(entry->value.u.fileval, data);
546 data += fontspec_serialise(entry->value.u.fontval, data);
551 PUT_32BIT_MSB_FIRST(data, 0xFFFFFFFFU);
554 int conf_deserialise(Conf *conf, void *vdata, int maxsize)
556 unsigned char *data = (unsigned char *)vdata;
557 unsigned char *start = data;
558 struct conf_entry *entry;
563 while (maxsize >= 4) {
564 primary = GET_32BIT_MSB_FIRST(data);
565 data += 4, maxsize -= 4;
567 if (primary >= N_CONFIG_OPTIONS)
570 entry = snew(struct conf_entry);
571 entry->key.primary = primary;
573 switch (subkeytypes[entry->key.primary]) {
579 entry->key.secondary.i = toint(GET_32BIT_MSB_FIRST(data));
580 data += 4, maxsize -= 4;
583 zero = memchr(data, 0, maxsize);
588 entry->key.secondary.s = dupstr((char *)data);
589 maxsize -= (zero + 1 - data);
594 switch (valuetypes[entry->key.primary]) {
597 if (subkeytypes[entry->key.primary] == TYPE_STR)
598 sfree(entry->key.secondary.s);
602 entry->value.u.intval = toint(GET_32BIT_MSB_FIRST(data));
603 data += 4, maxsize -= 4;
606 zero = memchr(data, 0, maxsize);
608 if (subkeytypes[entry->key.primary] == TYPE_STR)
609 sfree(entry->key.secondary.s);
613 entry->value.u.stringval = dupstr((char *)data);
614 maxsize -= (zero + 1 - data);
618 entry->value.u.fileval =
619 filename_deserialise(data, maxsize, &used);
620 if (!entry->value.u.fileval) {
621 if (subkeytypes[entry->key.primary] == TYPE_STR)
622 sfree(entry->key.secondary.s);
630 entry->value.u.fontval =
631 fontspec_deserialise(data, maxsize, &used);
632 if (!entry->value.u.fontval) {
633 if (subkeytypes[entry->key.primary] == TYPE_STR)
634 sfree(entry->key.secondary.s);
642 conf_insert(conf, entry);
646 return (int)(data - start);