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).
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).
68 static int conf_cmp(void *av, void *bv)
70 struct key *a = (struct key *)av;
71 struct key *b = (struct key *)bv;
73 if (a->primary < b->primary)
75 else if (a->primary > b->primary)
77 switch (subkeytypes[a->primary]) {
79 if (a->secondary.i < b->secondary.i)
81 else if (a->secondary.i > b->secondary.i)
85 return strcmp(a->secondary.s, b->secondary.s);
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.
96 static void free_key(struct key *key)
98 if (subkeytypes[key->primary] == TYPE_STR)
99 sfree(key->secondary.s);
103 * Copy a 'struct key' into another one, copying its dynamic data
106 static void copy_key(struct key *to, struct key *from)
108 to->primary = from->primary;
109 switch (subkeytypes[to->primary]) {
111 to->secondary.i = from->secondary.i;
114 to->secondary.s = dupstr(from->secondary.s);
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
124 static void free_value(struct value *val, int type)
126 if (type == TYPE_STR)
127 sfree(val->u.stringval);
128 else if (type == TYPE_FONT)
129 fontspec_free(val->u.fontval);
133 * Copy a 'struct value' into another one, copying its dynamic data
136 static void copy_value(struct value *to, struct value *from, int type)
140 to->u.intval = from->u.intval;
143 to->u.stringval = dupstr(from->u.stringval);
146 to->u.fileval = from->u.fileval;
149 to->u.fontval = fontspec_copy(from->u.fontval);
155 * Free an entire 'struct conf_entry' and its dynamic data.
157 static void free_entry(struct conf_entry *entry)
159 free_key(&entry->key);
160 free_value(&entry->value, valuetypes[entry->key.primary]);
166 Conf *conf = snew(struct conf_tag);
168 conf->tree = newtree234(conf_cmp);
173 static void conf_clear(Conf *conf)
175 struct conf_entry *entry;
177 while ((entry = delpos234(conf->tree, 0)) != NULL)
181 void conf_free(Conf *conf)
184 freetree234(conf->tree);
188 static void conf_insert(Conf *conf, struct conf_entry *entry)
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);
199 void conf_copy_into(Conf *newconf, Conf *oldconf)
201 struct conf_entry *entry, *entry2;
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);
215 Conf *conf_copy(Conf *oldconf)
217 Conf *newconf = conf_new();
219 conf_copy_into(newconf, oldconf);
224 int conf_get_int(Conf *conf, int primary)
227 struct conf_entry *entry;
229 assert(subkeytypes[primary] == TYPE_NONE);
230 assert(valuetypes[primary] == TYPE_INT);
231 key.primary = primary;
232 entry = find234(conf->tree, &key, NULL);
234 return entry->value.u.intval;
237 int conf_get_int_int(Conf *conf, int primary, int secondary)
240 struct conf_entry *entry;
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);
248 return entry->value.u.intval;
251 char *conf_get_str(Conf *conf, int primary)
254 struct conf_entry *entry;
256 assert(subkeytypes[primary] == TYPE_NONE);
257 assert(valuetypes[primary] == TYPE_STR);
258 key.primary = primary;
259 entry = find234(conf->tree, &key, NULL);
261 return entry->value.u.stringval;
264 char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary)
267 struct conf_entry *entry;
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;
277 char *conf_get_str_str(Conf *conf, int primary, const char *secondary)
279 char *ret = conf_get_str_str_opt(conf, primary, secondary);
284 char *conf_get_str_strs(Conf *conf, int primary,
285 char *subkeyin, char **subkeyout)
288 struct conf_entry *entry;
290 assert(subkeytypes[primary] == TYPE_STR);
291 assert(valuetypes[primary] == TYPE_STR);
292 key.primary = primary;
294 key.secondary.s = subkeyin;
295 entry = findrel234(conf->tree, &key, NULL, REL234_GT);
297 key.secondary.s = "";
298 entry = findrel234(conf->tree, &key, NULL, REL234_GE);
300 if (!entry || entry->key.primary != primary)
302 *subkeyout = entry->key.secondary.s;
303 return entry->value.u.stringval;
306 char *conf_get_str_nthstrkey(Conf *conf, int primary, int n)
309 struct conf_entry *entry;
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)
319 entry = index234(conf->tree, index + n);
320 if (!entry || entry->key.primary != primary)
322 return entry->key.secondary.s;
325 Filename *conf_get_filename(Conf *conf, int primary)
328 struct conf_entry *entry;
330 assert(subkeytypes[primary] == TYPE_NONE);
331 assert(valuetypes[primary] == TYPE_FILENAME);
332 key.primary = primary;
333 entry = find234(conf->tree, &key, NULL);
335 return &entry->value.u.fileval;
338 FontSpec *conf_get_fontspec(Conf *conf, int primary)
341 struct conf_entry *entry;
343 assert(subkeytypes[primary] == TYPE_NONE);
344 assert(valuetypes[primary] == TYPE_FONT);
345 key.primary = primary;
346 entry = find234(conf->tree, &key, NULL);
348 return entry->value.u.fontval;
351 void conf_set_int(Conf *conf, int primary, int value)
353 struct conf_entry *entry = snew(struct conf_entry);
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);
362 void conf_set_int_int(Conf *conf, int primary, int secondary, int value)
364 struct conf_entry *entry = snew(struct conf_entry);
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);
374 void conf_set_str(Conf *conf, int primary, const char *value)
376 struct conf_entry *entry = snew(struct conf_entry);
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);
385 void conf_set_str_str(Conf *conf, int primary, const char *secondary,
388 struct conf_entry *entry = snew(struct conf_entry);
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);
398 void conf_del_str_str(Conf *conf, int primary, const char *secondary)
401 struct conf_entry *entry;
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);
409 del234(conf->tree, entry);
414 void conf_set_filename(Conf *conf, int primary, const Filename *value)
416 struct conf_entry *entry = snew(struct conf_entry);
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);
425 void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value)
427 struct conf_entry *entry = snew(struct conf_entry);
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);
436 int conf_serialised_size(Conf *conf)
439 struct conf_entry *entry;
442 for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
443 size += 4; /* primary key */
444 switch (subkeytypes[entry->key.primary]) {
449 size += 1 + strlen(entry->key.secondary.s);
452 switch (valuetypes[entry->key.primary]) {
457 size += 1 + strlen(entry->value.u.stringval);
460 size += sizeof(entry->value.u.fileval);
463 size += fontspec_serialise(entry->value.u.fontval, NULL);
468 size += 4; /* terminator value */
473 void conf_serialise(Conf *conf, void *vdata)
475 unsigned char *data = (unsigned char *)vdata;
477 struct conf_entry *entry;
479 for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
480 PUT_32BIT_MSB_FIRST(data, entry->key.primary);
483 switch (subkeytypes[entry->key.primary]) {
485 PUT_32BIT_MSB_FIRST(data, entry->key.secondary.i);
489 len = strlen(entry->key.secondary.s);
490 memcpy(data, entry->key.secondary.s, len);
495 switch (valuetypes[entry->key.primary]) {
497 PUT_32BIT_MSB_FIRST(data, entry->value.u.intval);
501 len = strlen(entry->value.u.stringval);
502 memcpy(data, entry->value.u.stringval, len);
507 memcpy(data, &entry->value.u.fileval,
508 sizeof(entry->value.u.fileval));
509 data += sizeof(entry->value.u.fileval);
512 data += fontspec_serialise(entry->value.u.fontval, data);
517 PUT_32BIT_MSB_FIRST(data, 0xFFFFFFFFU);
520 int conf_deserialise(Conf *conf, void *vdata, int maxsize)
522 unsigned char *data = (unsigned char *)vdata;
523 unsigned char *start = data;
524 struct conf_entry *entry;
528 while (maxsize >= 4) {
529 primary = GET_32BIT_MSB_FIRST(data);
530 data += 4, maxsize -= 4;
532 if ((unsigned)primary >= N_CONFIG_OPTIONS)
535 entry = snew(struct conf_entry);
536 entry->key.primary = primary;
538 switch (subkeytypes[entry->key.primary]) {
544 entry->key.secondary.i = GET_32BIT_MSB_FIRST(data);
545 data += 4, maxsize -= 4;
548 zero = memchr(data, 0, maxsize);
553 entry->key.secondary.s = dupstr((char *)data);
554 maxsize -= (zero + 1 - data);
559 switch (valuetypes[entry->key.primary]) {
562 if (subkeytypes[entry->key.primary] == TYPE_STR)
563 sfree(entry->key.secondary.s);
567 entry->value.u.intval = GET_32BIT_MSB_FIRST(data);
568 data += 4, maxsize -= 4;
571 zero = memchr(data, 0, maxsize);
573 if (subkeytypes[entry->key.primary] == TYPE_STR)
574 sfree(entry->key.secondary.s);
578 entry->value.u.stringval = dupstr((char *)data);
579 maxsize -= (zero + 1 - data);
583 if (maxsize < sizeof(entry->value.u.fileval)) {
584 if (subkeytypes[entry->key.primary] == TYPE_STR)
585 sfree(entry->key.secondary.s);
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);
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);
607 conf_insert(conf, entry);
611 return (int)(data - start);