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);
131 * Copy a 'struct value' into another one, copying its dynamic data
134 static void copy_value(struct value *to, struct value *from, int type)
138 to->u.intval = from->u.intval;
141 to->u.stringval = dupstr(from->u.stringval);
144 to->u.fileval = from->u.fileval;
147 to->u.fontval = from->u.fontval;
153 * Free an entire 'struct conf_entry' and its dynamic data.
155 static void free_entry(struct conf_entry *entry)
157 free_key(&entry->key);
158 free_value(&entry->value, valuetypes[entry->key.primary]);
164 Conf *conf = snew(struct conf_tag);
166 conf->tree = newtree234(conf_cmp);
171 static void conf_clear(Conf *conf)
173 struct conf_entry *entry;
175 while ((entry = delpos234(conf->tree, 0)) != NULL)
179 void conf_free(Conf *conf)
182 freetree234(conf->tree);
186 static void conf_insert(Conf *conf, struct conf_entry *entry)
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);
197 void conf_copy_into(Conf *newconf, Conf *oldconf)
199 struct conf_entry *entry, *entry2;
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);
213 Conf *conf_copy(Conf *oldconf)
215 Conf *newconf = conf_new();
217 conf_copy_into(newconf, oldconf);
222 int conf_get_int(Conf *conf, int primary)
225 struct conf_entry *entry;
227 assert(subkeytypes[primary] == TYPE_NONE);
228 assert(valuetypes[primary] == TYPE_INT);
229 key.primary = primary;
230 entry = find234(conf->tree, &key, NULL);
232 return entry->value.u.intval;
235 int conf_get_int_int(Conf *conf, int primary, int secondary)
238 struct conf_entry *entry;
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);
246 return entry->value.u.intval;
249 char *conf_get_str(Conf *conf, int primary)
252 struct conf_entry *entry;
254 assert(subkeytypes[primary] == TYPE_NONE);
255 assert(valuetypes[primary] == TYPE_STR);
256 key.primary = primary;
257 entry = find234(conf->tree, &key, NULL);
259 return entry->value.u.stringval;
262 char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary)
265 struct conf_entry *entry;
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;
275 char *conf_get_str_str(Conf *conf, int primary, const char *secondary)
277 char *ret = conf_get_str_str_opt(conf, primary, secondary);
282 char *conf_get_str_strs(Conf *conf, int primary,
283 char *subkeyin, char **subkeyout)
286 struct conf_entry *entry;
288 assert(subkeytypes[primary] == TYPE_STR);
289 assert(valuetypes[primary] == TYPE_STR);
290 key.primary = primary;
292 key.secondary.s = subkeyin;
293 entry = findrel234(conf->tree, &key, NULL, REL234_GT);
295 key.secondary.s = "";
296 entry = findrel234(conf->tree, &key, NULL, REL234_GE);
298 if (!entry || entry->key.primary != primary)
300 *subkeyout = entry->key.secondary.s;
301 return entry->value.u.stringval;
304 char *conf_get_str_nthstrkey(Conf *conf, int primary, int n)
307 struct conf_entry *entry;
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)
317 entry = index234(conf->tree, index + n);
318 if (!entry || entry->key.primary != primary)
320 return entry->key.secondary.s;
323 Filename *conf_get_filename(Conf *conf, int primary)
326 struct conf_entry *entry;
328 assert(subkeytypes[primary] == TYPE_NONE);
329 assert(valuetypes[primary] == TYPE_FILENAME);
330 key.primary = primary;
331 entry = find234(conf->tree, &key, NULL);
333 return &entry->value.u.fileval;
336 FontSpec *conf_get_fontspec(Conf *conf, int primary)
339 struct conf_entry *entry;
341 assert(subkeytypes[primary] == TYPE_NONE);
342 assert(valuetypes[primary] == TYPE_FONT);
343 key.primary = primary;
344 entry = find234(conf->tree, &key, NULL);
346 return &entry->value.u.fontval;
349 void conf_set_int(Conf *conf, int primary, int value)
351 struct conf_entry *entry = snew(struct conf_entry);
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);
360 void conf_set_int_int(Conf *conf, int primary, int secondary, int value)
362 struct conf_entry *entry = snew(struct conf_entry);
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);
372 void conf_set_str(Conf *conf, int primary, const char *value)
374 struct conf_entry *entry = snew(struct conf_entry);
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);
383 void conf_set_str_str(Conf *conf, int primary, const char *secondary,
386 struct conf_entry *entry = snew(struct conf_entry);
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);
396 void conf_del_str_str(Conf *conf, int primary, const char *secondary)
399 struct conf_entry *entry;
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);
407 del234(conf->tree, entry);
412 void conf_set_filename(Conf *conf, int primary, const Filename *value)
414 struct conf_entry *entry = snew(struct conf_entry);
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);
423 void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value)
425 struct conf_entry *entry = snew(struct conf_entry);
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);
434 int conf_serialised_size(Conf *conf)
437 struct conf_entry *entry;
440 for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
441 size += 4; /* primary key */
442 switch (subkeytypes[entry->key.primary]) {
447 size += 1 + strlen(entry->key.secondary.s);
450 switch (valuetypes[entry->key.primary]) {
455 size += 1 + strlen(entry->value.u.stringval);
458 size += sizeof(entry->value.u.fileval);
461 size += sizeof(entry->value.u.fontval);
466 size += 4; /* terminator value */
471 void conf_serialise(Conf *conf, void *vdata)
473 unsigned char *data = (unsigned char *)vdata;
475 struct conf_entry *entry;
477 for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
478 PUT_32BIT_MSB_FIRST(data, entry->key.primary);
481 switch (subkeytypes[entry->key.primary]) {
483 PUT_32BIT_MSB_FIRST(data, entry->key.secondary.i);
487 len = strlen(entry->key.secondary.s);
488 memcpy(data, entry->key.secondary.s, len);
493 switch (valuetypes[entry->key.primary]) {
495 PUT_32BIT_MSB_FIRST(data, entry->value.u.intval);
499 len = strlen(entry->value.u.stringval);
500 memcpy(data, entry->value.u.stringval, len);
505 memcpy(data, &entry->value.u.fileval,
506 sizeof(entry->value.u.fileval));
507 data += sizeof(entry->value.u.fileval);
510 memcpy(data, &entry->value.u.fontval,
511 sizeof(entry->value.u.fontval));
512 data += sizeof(entry->value.u.fontval);
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 if (maxsize < sizeof(entry->value.u.fontval)) {
596 if (subkeytypes[entry->key.primary] == TYPE_STR)
597 sfree(entry->key.secondary.s);
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);
607 conf_insert(conf, entry);
611 return (int)(data - start);