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;
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);
211 Conf *conf_copy(Conf *oldconf)
213 Conf *newconf = conf_new();
215 conf_copy_into(newconf, oldconf);
220 int conf_get_int(Conf *conf, int primary)
223 struct conf_entry *entry;
225 assert(subkeytypes[primary] == TYPE_NONE);
226 assert(valuetypes[primary] == TYPE_INT);
227 key.primary = primary;
228 entry = find234(conf->tree, &key, NULL);
230 return entry->value.u.intval;
233 int conf_get_int_int(Conf *conf, int primary, int secondary)
236 struct conf_entry *entry;
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);
244 return entry->value.u.intval;
247 char *conf_get_str(Conf *conf, int primary)
250 struct conf_entry *entry;
252 assert(subkeytypes[primary] == TYPE_NONE);
253 assert(valuetypes[primary] == TYPE_STR);
254 key.primary = primary;
255 entry = find234(conf->tree, &key, NULL);
257 return entry->value.u.stringval;
260 char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary)
263 struct conf_entry *entry;
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;
273 char *conf_get_str_str(Conf *conf, int primary, const char *secondary)
275 char *ret = conf_get_str_str_opt(conf, primary, secondary);
280 char *conf_get_str_strs(Conf *conf, int primary,
281 char *subkeyin, char **subkeyout)
284 struct conf_entry *entry;
286 assert(subkeytypes[primary] == TYPE_STR);
287 assert(valuetypes[primary] == TYPE_STR);
288 key.primary = primary;
290 key.secondary.s = subkeyin;
291 entry = findrel234(conf->tree, &key, NULL, REL234_GT);
293 key.secondary.s = "";
294 entry = findrel234(conf->tree, &key, NULL, REL234_GE);
296 if (!entry || entry->key.primary != primary)
298 *subkeyout = entry->key.secondary.s;
299 return entry->value.u.stringval;
302 char *conf_get_str_nthstrkey(Conf *conf, int primary, int n)
305 struct conf_entry *entry;
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)
315 entry = index234(conf->tree, index + n);
316 if (!entry || entry->key.primary != primary)
318 return entry->key.secondary.s;
321 Filename *conf_get_filename(Conf *conf, int primary)
324 struct conf_entry *entry;
326 assert(subkeytypes[primary] == TYPE_NONE);
327 assert(valuetypes[primary] == TYPE_FILENAME);
328 key.primary = primary;
329 entry = find234(conf->tree, &key, NULL);
331 return &entry->value.u.fileval;
334 FontSpec *conf_get_fontspec(Conf *conf, int primary)
337 struct conf_entry *entry;
339 assert(subkeytypes[primary] == TYPE_NONE);
340 assert(valuetypes[primary] == TYPE_FONT);
341 key.primary = primary;
342 entry = find234(conf->tree, &key, NULL);
344 return &entry->value.u.fontval;
347 void conf_set_int(Conf *conf, int primary, int value)
349 struct conf_entry *entry = snew(struct conf_entry);
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);
358 void conf_set_int_int(Conf *conf, int primary, int secondary, int value)
360 struct conf_entry *entry = snew(struct conf_entry);
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);
370 void conf_set_str(Conf *conf, int primary, const char *value)
372 struct conf_entry *entry = snew(struct conf_entry);
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);
381 void conf_set_str_str(Conf *conf, int primary, const char *secondary,
384 struct conf_entry *entry = snew(struct conf_entry);
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);
394 void conf_del_str_str(Conf *conf, int primary, const char *secondary)
397 struct conf_entry *entry;
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);
405 del234(conf->tree, entry);
410 void conf_set_filename(Conf *conf, int primary, const Filename *value)
412 struct conf_entry *entry = snew(struct conf_entry);
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);
421 void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value)
423 struct conf_entry *entry = snew(struct conf_entry);
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);
432 int conf_serialised_size(Conf *conf)
435 struct conf_entry *entry;
438 for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
439 size += 4; /* primary key */
440 switch (subkeytypes[entry->key.primary]) {
445 size += 1 + strlen(entry->key.secondary.s);
448 switch (valuetypes[entry->key.primary]) {
453 size += 1 + strlen(entry->value.u.stringval);
456 size += sizeof(entry->value.u.fileval);
459 size += sizeof(entry->value.u.fontval);
464 size += 4; /* terminator value */
469 void conf_serialise(Conf *conf, void *vdata)
471 unsigned char *data = (unsigned char *)vdata;
473 struct conf_entry *entry;
475 for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
476 PUT_32BIT_MSB_FIRST(data, entry->key.primary);
479 switch (subkeytypes[entry->key.primary]) {
481 PUT_32BIT_MSB_FIRST(data, entry->key.secondary.i);
485 len = strlen(entry->key.secondary.s);
486 memcpy(data, entry->key.secondary.s, len);
491 switch (valuetypes[entry->key.primary]) {
493 PUT_32BIT_MSB_FIRST(data, entry->value.u.intval);
497 len = strlen(entry->value.u.stringval);
498 memcpy(data, entry->value.u.stringval, len);
503 memcpy(data, &entry->value.u.fileval,
504 sizeof(entry->value.u.fileval));
505 data += sizeof(entry->value.u.fileval);
508 memcpy(data, &entry->value.u.fontval,
509 sizeof(entry->value.u.fontval));
510 data += sizeof(entry->value.u.fontval);
515 PUT_32BIT_MSB_FIRST(data, 0xFFFFFFFFU);
518 int conf_deserialise(Conf *conf, void *vdata, int maxsize)
520 unsigned char *data = (unsigned char *)vdata;
521 unsigned char *start = data;
522 struct conf_entry *entry;
526 while (maxsize >= 4) {
527 primary = GET_32BIT_MSB_FIRST(data);
528 data += 4, maxsize -= 4;
530 if ((unsigned)primary >= N_CONFIG_OPTIONS)
533 entry = snew(struct conf_entry);
534 entry->key.primary = primary;
536 switch (subkeytypes[entry->key.primary]) {
542 entry->key.secondary.i = GET_32BIT_MSB_FIRST(data);
543 data += 4, maxsize -= 4;
546 zero = memchr(data, 0, maxsize);
551 entry->key.secondary.s = dupstr((char *)data);
552 maxsize -= (zero + 1 - data);
557 switch (valuetypes[entry->key.primary]) {
560 if (subkeytypes[entry->key.primary] == TYPE_STR)
561 sfree(entry->key.secondary.s);
565 entry->value.u.intval = GET_32BIT_MSB_FIRST(data);
566 data += 4, maxsize -= 4;
569 zero = memchr(data, 0, maxsize);
571 if (subkeytypes[entry->key.primary] == TYPE_STR)
572 sfree(entry->key.secondary.s);
576 entry->value.u.stringval = dupstr((char *)data);
577 maxsize -= (zero + 1 - data);
581 if (maxsize < sizeof(entry->value.u.fileval)) {
582 if (subkeytypes[entry->key.primary] == TYPE_STR)
583 sfree(entry->key.secondary.s);
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);
593 if (maxsize < sizeof(entry->value.u.fontval)) {
594 if (subkeytypes[entry->key.primary] == TYPE_STR)
595 sfree(entry->key.secondary.s);
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);
605 conf_insert(conf, entry);
609 return (int)(data - start);