2 * conf.c: implementation of the internal storage format used for
\r
3 * the configuration of a PuTTY session.
\r
10 #include "tree234.h"
\r
14 * Enumeration of types used in keys and values.
\r
16 typedef enum { TYPE_NONE, TYPE_INT, TYPE_STR, TYPE_FILENAME, TYPE_FONT } Type;
\r
19 * Arrays which allow us to look up the subkey and value types for a
\r
20 * given primary key id.
\r
22 #define CONF_SUBKEYTYPE_DEF(valtype, keytype, keyword) TYPE_ ## keytype,
\r
23 static int subkeytypes[] = { CONFIG_OPTIONS(CONF_SUBKEYTYPE_DEF) };
\r
24 #define CONF_VALUETYPE_DEF(valtype, keytype, keyword) TYPE_ ## valtype,
\r
25 static int valuetypes[] = { CONFIG_OPTIONS(CONF_VALUETYPE_DEF) };
\r
28 * Configuration keys are primarily integers (big enum of all the
\r
29 * different configurable options); some keys have string-designated
\r
30 * subkeys, such as the list of environment variables (subkeys
\r
31 * defined by the variable names); some have integer-designated
\r
32 * subkeys (wordness, colours, preference lists).
\r
61 * Because 'struct key' is the first element in 'struct conf_entry',
\r
62 * it's safe (guaranteed by the C standard) to cast arbitrarily back
\r
63 * and forth between the two types. Therefore, we only need one
\r
64 * comparison function, which can double as a main sort function for
\r
65 * the tree (comparing two conf_entry structures with each other)
\r
66 * and a search function (looking up an externally supplied key).
\r
68 static int conf_cmp(void *av, void *bv)
\r
70 struct key *a = (struct key *)av;
\r
71 struct key *b = (struct key *)bv;
\r
73 if (a->primary < b->primary)
\r
75 else if (a->primary > b->primary)
\r
77 switch (subkeytypes[a->primary]) {
\r
79 if (a->secondary.i < b->secondary.i)
\r
81 else if (a->secondary.i > b->secondary.i)
\r
85 return strcmp(a->secondary.s, b->secondary.s);
\r
92 * Free any dynamic data items pointed to by a 'struct key'. We
\r
93 * don't free the structure itself, since it's probably part of a
\r
94 * larger allocated block.
\r
96 static void free_key(struct key *key)
\r
98 if (subkeytypes[key->primary] == TYPE_STR)
\r
99 sfree(key->secondary.s);
\r
103 * Copy a 'struct key' into another one, copying its dynamic data
\r
106 static void copy_key(struct key *to, struct key *from)
\r
108 to->primary = from->primary;
\r
109 switch (subkeytypes[to->primary]) {
\r
111 to->secondary.i = from->secondary.i;
\r
114 to->secondary.s = dupstr(from->secondary.s);
\r
120 * Free any dynamic data items pointed to by a 'struct value'. We
\r
121 * don't free the value itself, since it's probably part of a larger
\r
124 static void free_value(struct value *val, int type)
\r
126 if (type == TYPE_STR)
\r
127 sfree(val->u.stringval);
\r
128 else if (type == TYPE_FILENAME)
\r
129 filename_free(val->u.fileval);
\r
130 else if (type == TYPE_FONT)
\r
131 fontspec_free(val->u.fontval);
\r
135 * Copy a 'struct value' into another one, copying its dynamic data
\r
138 static void copy_value(struct value *to, struct value *from, int type)
\r
142 to->u.intval = from->u.intval;
\r
145 to->u.stringval = dupstr(from->u.stringval);
\r
147 case TYPE_FILENAME:
\r
148 to->u.fileval = filename_copy(from->u.fileval);
\r
151 to->u.fontval = fontspec_copy(from->u.fontval);
\r
157 * Free an entire 'struct conf_entry' and its dynamic data.
\r
159 static void free_entry(struct conf_entry *entry)
\r
161 free_key(&entry->key);
\r
162 free_value(&entry->value, valuetypes[entry->key.primary]);
\r
166 Conf *conf_new(void)
\r
168 Conf *conf = snew(struct conf_tag);
\r
170 conf->tree = newtree234(conf_cmp);
\r
175 static void conf_clear(Conf *conf)
\r
177 struct conf_entry *entry;
\r
179 while ((entry = delpos234(conf->tree, 0)) != NULL)
\r
183 void conf_free(Conf *conf)
\r
186 freetree234(conf->tree);
\r
190 static void conf_insert(Conf *conf, struct conf_entry *entry)
\r
192 struct conf_entry *oldentry = add234(conf->tree, entry);
\r
193 if (oldentry && oldentry != entry) {
\r
194 del234(conf->tree, oldentry);
\r
195 free_entry(oldentry);
\r
196 oldentry = add234(conf->tree, entry);
\r
197 assert(oldentry == entry);
\r
201 void conf_copy_into(Conf *newconf, Conf *oldconf)
\r
203 struct conf_entry *entry, *entry2;
\r
206 conf_clear(newconf);
\r
208 for (i = 0; (entry = index234(oldconf->tree, i)) != NULL; i++) {
\r
209 entry2 = snew(struct conf_entry);
\r
210 copy_key(&entry2->key, &entry->key);
\r
211 copy_value(&entry2->value, &entry->value,
\r
212 valuetypes[entry->key.primary]);
\r
213 add234(newconf->tree, entry2);
\r
217 Conf *conf_copy(Conf *oldconf)
\r
219 Conf *newconf = conf_new();
\r
221 conf_copy_into(newconf, oldconf);
\r
226 int conf_get_int(Conf *conf, int primary)
\r
229 struct conf_entry *entry;
\r
231 assert(subkeytypes[primary] == TYPE_NONE);
\r
232 assert(valuetypes[primary] == TYPE_INT);
\r
233 key.primary = primary;
\r
234 entry = find234(conf->tree, &key, NULL);
\r
236 return entry->value.u.intval;
\r
239 int conf_get_int_int(Conf *conf, int primary, int secondary)
\r
242 struct conf_entry *entry;
\r
244 assert(subkeytypes[primary] == TYPE_INT);
\r
245 assert(valuetypes[primary] == TYPE_INT);
\r
246 key.primary = primary;
\r
247 key.secondary.i = secondary;
\r
248 entry = find234(conf->tree, &key, NULL);
\r
250 return entry->value.u.intval;
\r
253 char *conf_get_str(Conf *conf, int primary)
\r
256 struct conf_entry *entry;
\r
258 assert(subkeytypes[primary] == TYPE_NONE);
\r
259 assert(valuetypes[primary] == TYPE_STR);
\r
260 key.primary = primary;
\r
261 entry = find234(conf->tree, &key, NULL);
\r
263 return entry->value.u.stringval;
\r
266 char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary)
\r
269 struct conf_entry *entry;
\r
271 assert(subkeytypes[primary] == TYPE_STR);
\r
272 assert(valuetypes[primary] == TYPE_STR);
\r
273 key.primary = primary;
\r
274 key.secondary.s = (char *)secondary;
\r
275 entry = find234(conf->tree, &key, NULL);
\r
276 return entry ? entry->value.u.stringval : NULL;
\r
279 char *conf_get_str_str(Conf *conf, int primary, const char *secondary)
\r
281 char *ret = conf_get_str_str_opt(conf, primary, secondary);
\r
286 char *conf_get_str_strs(Conf *conf, int primary,
\r
287 char *subkeyin, char **subkeyout)
\r
290 struct conf_entry *entry;
\r
292 assert(subkeytypes[primary] == TYPE_STR);
\r
293 assert(valuetypes[primary] == TYPE_STR);
\r
294 key.primary = primary;
\r
296 key.secondary.s = subkeyin;
\r
297 entry = findrel234(conf->tree, &key, NULL, REL234_GT);
\r
299 key.secondary.s = "";
\r
300 entry = findrel234(conf->tree, &key, NULL, REL234_GE);
\r
302 if (!entry || entry->key.primary != primary)
\r
304 *subkeyout = entry->key.secondary.s;
\r
305 return entry->value.u.stringval;
\r
308 char *conf_get_str_nthstrkey(Conf *conf, int primary, int n)
\r
311 struct conf_entry *entry;
\r
314 assert(subkeytypes[primary] == TYPE_STR);
\r
315 assert(valuetypes[primary] == TYPE_STR);
\r
316 key.primary = primary;
\r
317 key.secondary.s = "";
\r
318 entry = findrelpos234(conf->tree, &key, NULL, REL234_GE, &index);
\r
319 if (!entry || entry->key.primary != primary)
\r
321 entry = index234(conf->tree, index + n);
\r
322 if (!entry || entry->key.primary != primary)
\r
324 return entry->key.secondary.s;
\r
327 Filename *conf_get_filename(Conf *conf, int primary)
\r
330 struct conf_entry *entry;
\r
332 assert(subkeytypes[primary] == TYPE_NONE);
\r
333 assert(valuetypes[primary] == TYPE_FILENAME);
\r
334 key.primary = primary;
\r
335 entry = find234(conf->tree, &key, NULL);
\r
337 return entry->value.u.fileval;
\r
340 FontSpec *conf_get_fontspec(Conf *conf, int primary)
\r
343 struct conf_entry *entry;
\r
345 assert(subkeytypes[primary] == TYPE_NONE);
\r
346 assert(valuetypes[primary] == TYPE_FONT);
\r
347 key.primary = primary;
\r
348 entry = find234(conf->tree, &key, NULL);
\r
350 return entry->value.u.fontval;
\r
353 void conf_set_int(Conf *conf, int primary, int value)
\r
355 struct conf_entry *entry = snew(struct conf_entry);
\r
357 assert(subkeytypes[primary] == TYPE_NONE);
\r
358 assert(valuetypes[primary] == TYPE_INT);
\r
359 entry->key.primary = primary;
\r
360 entry->value.u.intval = value;
\r
361 conf_insert(conf, entry);
\r
364 void conf_set_int_int(Conf *conf, int primary, int secondary, int value)
\r
366 struct conf_entry *entry = snew(struct conf_entry);
\r
368 assert(subkeytypes[primary] == TYPE_INT);
\r
369 assert(valuetypes[primary] == TYPE_INT);
\r
370 entry->key.primary = primary;
\r
371 entry->key.secondary.i = secondary;
\r
372 entry->value.u.intval = value;
\r
373 conf_insert(conf, entry);
\r
376 void conf_set_str(Conf *conf, int primary, const char *value)
\r
378 struct conf_entry *entry = snew(struct conf_entry);
\r
380 assert(subkeytypes[primary] == TYPE_NONE);
\r
381 assert(valuetypes[primary] == TYPE_STR);
\r
382 entry->key.primary = primary;
\r
383 entry->value.u.stringval = dupstr(value);
\r
384 conf_insert(conf, entry);
\r
387 void conf_set_str_str(Conf *conf, int primary, const char *secondary,
\r
390 struct conf_entry *entry = snew(struct conf_entry);
\r
392 assert(subkeytypes[primary] == TYPE_STR);
\r
393 assert(valuetypes[primary] == TYPE_STR);
\r
394 entry->key.primary = primary;
\r
395 entry->key.secondary.s = dupstr(secondary);
\r
396 entry->value.u.stringval = dupstr(value);
\r
397 conf_insert(conf, entry);
\r
400 void conf_del_str_str(Conf *conf, int primary, const char *secondary)
\r
403 struct conf_entry *entry;
\r
405 assert(subkeytypes[primary] == TYPE_STR);
\r
406 assert(valuetypes[primary] == TYPE_STR);
\r
407 key.primary = primary;
\r
408 key.secondary.s = (char *)secondary;
\r
409 entry = find234(conf->tree, &key, NULL);
\r
411 del234(conf->tree, entry);
\r
416 void conf_set_filename(Conf *conf, int primary, const Filename *value)
\r
418 struct conf_entry *entry = snew(struct conf_entry);
\r
420 assert(subkeytypes[primary] == TYPE_NONE);
\r
421 assert(valuetypes[primary] == TYPE_FILENAME);
\r
422 entry->key.primary = primary;
\r
423 entry->value.u.fileval = filename_copy(value);
\r
424 conf_insert(conf, entry);
\r
427 void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value)
\r
429 struct conf_entry *entry = snew(struct conf_entry);
\r
431 assert(subkeytypes[primary] == TYPE_NONE);
\r
432 assert(valuetypes[primary] == TYPE_FONT);
\r
433 entry->key.primary = primary;
\r
434 entry->value.u.fontval = fontspec_copy(value);
\r
435 conf_insert(conf, entry);
\r
438 int conf_serialised_size(Conf *conf)
\r
441 struct conf_entry *entry;
\r
444 for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
\r
445 size += 4; /* primary key */
\r
446 switch (subkeytypes[entry->key.primary]) {
\r
451 size += 1 + strlen(entry->key.secondary.s);
\r
454 switch (valuetypes[entry->key.primary]) {
\r
459 size += 1 + strlen(entry->value.u.stringval);
\r
461 case TYPE_FILENAME:
\r
462 size += filename_serialise(entry->value.u.fileval, NULL);
\r
465 size += fontspec_serialise(entry->value.u.fontval, NULL);
\r
470 size += 4; /* terminator value */
\r
475 void conf_serialise(Conf *conf, void *vdata)
\r
477 unsigned char *data = (unsigned char *)vdata;
\r
479 struct conf_entry *entry;
\r
481 for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
\r
482 PUT_32BIT_MSB_FIRST(data, entry->key.primary);
\r
485 switch (subkeytypes[entry->key.primary]) {
\r
487 PUT_32BIT_MSB_FIRST(data, entry->key.secondary.i);
\r
491 len = strlen(entry->key.secondary.s);
\r
492 memcpy(data, entry->key.secondary.s, len);
\r
497 switch (valuetypes[entry->key.primary]) {
\r
499 PUT_32BIT_MSB_FIRST(data, entry->value.u.intval);
\r
503 len = strlen(entry->value.u.stringval);
\r
504 memcpy(data, entry->value.u.stringval, len);
\r
508 case TYPE_FILENAME:
\r
509 data += filename_serialise(entry->value.u.fileval, data);
\r
512 data += fontspec_serialise(entry->value.u.fontval, data);
\r
517 PUT_32BIT_MSB_FIRST(data, 0xFFFFFFFFU);
\r
520 int conf_deserialise(Conf *conf, void *vdata, int maxsize)
\r
522 unsigned char *data = (unsigned char *)vdata;
\r
523 unsigned char *start = data;
\r
524 struct conf_entry *entry;
\r
527 unsigned char *zero;
\r
529 while (maxsize >= 4) {
\r
530 primary = GET_32BIT_MSB_FIRST(data);
\r
531 data += 4, maxsize -= 4;
\r
533 if (primary >= N_CONFIG_OPTIONS)
\r
536 entry = snew(struct conf_entry);
\r
537 entry->key.primary = primary;
\r
539 switch (subkeytypes[entry->key.primary]) {
\r
545 entry->key.secondary.i = toint(GET_32BIT_MSB_FIRST(data));
\r
546 data += 4, maxsize -= 4;
\r
549 zero = memchr(data, 0, maxsize);
\r
554 entry->key.secondary.s = dupstr((char *)data);
\r
555 maxsize -= (zero + 1 - data);
\r
560 switch (valuetypes[entry->key.primary]) {
\r
563 if (subkeytypes[entry->key.primary] == TYPE_STR)
\r
564 sfree(entry->key.secondary.s);
\r
568 entry->value.u.intval = toint(GET_32BIT_MSB_FIRST(data));
\r
569 data += 4, maxsize -= 4;
\r
572 zero = memchr(data, 0, maxsize);
\r
574 if (subkeytypes[entry->key.primary] == TYPE_STR)
\r
575 sfree(entry->key.secondary.s);
\r
579 entry->value.u.stringval = dupstr((char *)data);
\r
580 maxsize -= (zero + 1 - data);
\r
583 case TYPE_FILENAME:
\r
584 entry->value.u.fileval =
\r
585 filename_deserialise(data, maxsize, &used);
\r
586 if (!entry->value.u.fileval) {
\r
587 if (subkeytypes[entry->key.primary] == TYPE_STR)
\r
588 sfree(entry->key.secondary.s);
\r
596 entry->value.u.fontval =
\r
597 fontspec_deserialise(data, maxsize, &used);
\r
598 if (!entry->value.u.fontval) {
\r
599 if (subkeytypes[entry->key.primary] == TYPE_STR)
\r
600 sfree(entry->key.secondary.s);
\r
608 conf_insert(conf, entry);
\r
612 return (int)(data - start);
\r