X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/d87d258a8f5d8fc9c43e73cc59ff4258b3755485..4a693cfc5c3ee0e639bbee0215345e921715ab04:/conf.c diff --git a/conf.c b/conf.c new file mode 100644 index 00000000..89bf5c3b --- /dev/null +++ b/conf.c @@ -0,0 +1,610 @@ +/* + * conf.c: implementation of the internal storage format used for + * the configuration of a PuTTY session. + */ + +#include +#include +#include + +#include "tree234.h" +#include "putty.h" + +/* + * Enumeration of types used in keys and values. + */ +typedef enum { TYPE_NONE, TYPE_INT, TYPE_STR, TYPE_FILENAME, TYPE_FONT } Type; + +/* + * Arrays which allow us to look up the subkey and value types for a + * given primary key id. + */ +#define CONF_SUBKEYTYPE_DEF(valtype, keytype, keyword) TYPE_ ## keytype, +static int subkeytypes[] = { CONFIG_OPTIONS(CONF_SUBKEYTYPE_DEF) }; +#define CONF_VALUETYPE_DEF(valtype, keytype, keyword) TYPE_ ## valtype, +static int valuetypes[] = { CONFIG_OPTIONS(CONF_VALUETYPE_DEF) }; + +/* + * Configuration keys are primarily integers (big enum of all the + * different configurable options); some keys have string-designated + * subkeys, such as the list of environment variables (subkeys + * defined by the variable names); some have integer-designated + * subkeys (wordness, colours, preference lists). + */ +struct key { + int primary; + union { + int i; + char *s; + } secondary; +}; + +struct value { + union { + int intval; + char *stringval; + Filename fileval; + FontSpec fontval; + } u; +}; + +struct conf_entry { + struct key key; + struct value value; +}; + +struct conf_tag { + tree234 *tree; +}; + +/* + * Because 'struct key' is the first element in 'struct conf_entry', + * it's safe (guaranteed by the C standard) to cast arbitrarily back + * and forth between the two types. Therefore, we only need one + * comparison function, which can double as a main sort function for + * the tree (comparing two conf_entry structures with each other) + * and a search function (looking up an externally supplied key). + */ +static int conf_cmp(void *av, void *bv) +{ + struct key *a = (struct key *)av; + struct key *b = (struct key *)bv; + + if (a->primary < b->primary) + return -1; + else if (a->primary > b->primary) + return +1; + switch (subkeytypes[a->primary]) { + case TYPE_INT: + if (a->secondary.i < b->secondary.i) + return -1; + else if (a->secondary.i > b->secondary.i) + return +1; + return 0; + case TYPE_STR: + return strcmp(a->secondary.s, b->secondary.s); + default: + return 0; + } +} + +/* + * Free any dynamic data items pointed to by a 'struct key'. We + * don't free the structure itself, since it's probably part of a + * larger allocated block. + */ +static void free_key(struct key *key) +{ + if (subkeytypes[key->primary] == TYPE_STR) + sfree(key->secondary.s); +} + +/* + * Copy a 'struct key' into another one, copying its dynamic data + * if necessary. + */ +static void copy_key(struct key *to, struct key *from) +{ + to->primary = from->primary; + switch (subkeytypes[to->primary]) { + case TYPE_INT: + to->secondary.i = from->secondary.i; + break; + case TYPE_STR: + to->secondary.s = dupstr(from->secondary.s); + break; + } +} + +/* + * Free any dynamic data items pointed to by a 'struct value'. We + * don't free the value itself, since it's probably part of a larger + * allocated block. + */ +static void free_value(struct value *val, int type) +{ + if (type == TYPE_STR) + sfree(val->u.stringval); +} + +/* + * Copy a 'struct value' into another one, copying its dynamic data + * if necessary. + */ +static void copy_value(struct value *to, struct value *from, int type) +{ + switch (type) { + case TYPE_INT: + to->u.intval = from->u.intval; + break; + case TYPE_STR: + to->u.stringval = dupstr(from->u.stringval); + break; + case TYPE_FILENAME: + to->u.fileval = from->u.fileval; + break; + case TYPE_FONT: + to->u.fontval = from->u.fontval; + break; + } +} + +/* + * Free an entire 'struct conf_entry' and its dynamic data. + */ +static void free_entry(struct conf_entry *entry) +{ + free_key(&entry->key); + free_value(&entry->value, valuetypes[entry->key.primary]); + sfree(entry); +} + +Conf *conf_new(void) +{ + Conf *conf = snew(struct conf_tag); + + conf->tree = newtree234(conf_cmp); + + return conf; +} + +static void conf_clear(Conf *conf) +{ + struct conf_entry *entry; + + while ((entry = delpos234(conf->tree, 0)) != NULL) + free_entry(entry); +} + +void conf_free(Conf *conf) +{ + conf_clear(conf); + freetree234(conf->tree); + sfree(conf); +} + +static void conf_insert(Conf *conf, struct conf_entry *entry) +{ + struct conf_entry *oldentry = add234(conf->tree, entry); + if (oldentry && oldentry != entry) { + del234(conf->tree, oldentry); + free_entry(oldentry); + oldentry = add234(conf->tree, entry); + assert(oldentry == entry); + } +} + +void conf_copy_into(Conf *newconf, Conf *oldconf) +{ + struct conf_entry *entry, *entry2; + int i; + + for (i = 0; (entry = index234(oldconf->tree, i)) != NULL; i++) { + entry2 = snew(struct conf_entry); + copy_key(&entry2->key, &entry->key); + copy_value(&entry2->value, &entry->value, + valuetypes[entry->key.primary]); + add234(newconf->tree, entry2); + } +} + +Conf *conf_copy(Conf *oldconf) +{ + Conf *newconf = conf_new(); + + conf_copy_into(newconf, oldconf); + + return newconf; +} + +int conf_get_int(Conf *conf, int primary) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_INT); + key.primary = primary; + entry = find234(conf->tree, &key, NULL); + assert(entry); + return entry->value.u.intval; +} + +int conf_get_int_int(Conf *conf, int primary, int secondary) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_INT); + assert(valuetypes[primary] == TYPE_INT); + key.primary = primary; + key.secondary.i = secondary; + entry = find234(conf->tree, &key, NULL); + assert(entry); + return entry->value.u.intval; +} + +char *conf_get_str(Conf *conf, int primary) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_STR); + key.primary = primary; + entry = find234(conf->tree, &key, NULL); + assert(entry); + return entry->value.u.stringval; +} + +char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_STR); + assert(valuetypes[primary] == TYPE_STR); + key.primary = primary; + key.secondary.s = (char *)secondary; + entry = find234(conf->tree, &key, NULL); + return entry ? entry->value.u.stringval : NULL; +} + +char *conf_get_str_str(Conf *conf, int primary, const char *secondary) +{ + char *ret = conf_get_str_str_opt(conf, primary, secondary); + assert(ret); + return ret; +} + +char *conf_get_str_strs(Conf *conf, int primary, + char *subkeyin, char **subkeyout) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_STR); + assert(valuetypes[primary] == TYPE_STR); + key.primary = primary; + if (subkeyin) { + key.secondary.s = subkeyin; + entry = findrel234(conf->tree, &key, NULL, REL234_GT); + } else { + key.secondary.s = ""; + entry = findrel234(conf->tree, &key, NULL, REL234_GE); + } + if (!entry || entry->key.primary != primary) + return NULL; + *subkeyout = entry->key.secondary.s; + return entry->value.u.stringval; +} + +char *conf_get_str_nthstrkey(Conf *conf, int primary, int n) +{ + struct key key; + struct conf_entry *entry; + int index; + + assert(subkeytypes[primary] == TYPE_STR); + assert(valuetypes[primary] == TYPE_STR); + key.primary = primary; + key.secondary.s = ""; + entry = findrelpos234(conf->tree, &key, NULL, REL234_GE, &index); + if (!entry || entry->key.primary != primary) + return NULL; + entry = index234(conf->tree, index + n); + if (!entry || entry->key.primary != primary) + return NULL; + return entry->key.secondary.s; +} + +Filename *conf_get_filename(Conf *conf, int primary) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_FILENAME); + key.primary = primary; + entry = find234(conf->tree, &key, NULL); + assert(entry); + return &entry->value.u.fileval; +} + +FontSpec *conf_get_fontspec(Conf *conf, int primary) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_FONT); + key.primary = primary; + entry = find234(conf->tree, &key, NULL); + assert(entry); + return &entry->value.u.fontval; +} + +void conf_set_int(Conf *conf, int primary, int value) +{ + struct conf_entry *entry = snew(struct conf_entry); + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_INT); + entry->key.primary = primary; + entry->value.u.intval = value; + conf_insert(conf, entry); +} + +void conf_set_int_int(Conf *conf, int primary, int secondary, int value) +{ + struct conf_entry *entry = snew(struct conf_entry); + + assert(subkeytypes[primary] == TYPE_INT); + assert(valuetypes[primary] == TYPE_INT); + entry->key.primary = primary; + entry->key.secondary.i = secondary; + entry->value.u.intval = value; + conf_insert(conf, entry); +} + +void conf_set_str(Conf *conf, int primary, const char *value) +{ + struct conf_entry *entry = snew(struct conf_entry); + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_STR); + entry->key.primary = primary; + entry->value.u.stringval = dupstr(value); + conf_insert(conf, entry); +} + +void conf_set_str_str(Conf *conf, int primary, const char *secondary, + const char *value) +{ + struct conf_entry *entry = snew(struct conf_entry); + + assert(subkeytypes[primary] == TYPE_STR); + assert(valuetypes[primary] == TYPE_STR); + entry->key.primary = primary; + entry->key.secondary.s = dupstr(secondary); + entry->value.u.stringval = dupstr(value); + conf_insert(conf, entry); +} + +void conf_del_str_str(Conf *conf, int primary, const char *secondary) +{ + struct key key; + struct conf_entry *entry; + + assert(subkeytypes[primary] == TYPE_STR); + assert(valuetypes[primary] == TYPE_STR); + key.primary = primary; + key.secondary.s = (char *)secondary; + entry = find234(conf->tree, &key, NULL); + if (entry) { + del234(conf->tree, entry); + free_entry(entry); + } + } + +void conf_set_filename(Conf *conf, int primary, const Filename *value) +{ + struct conf_entry *entry = snew(struct conf_entry); + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_FILENAME); + entry->key.primary = primary; + entry->value.u.fileval = *value; /* structure copy */ + conf_insert(conf, entry); +} + +void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value) +{ + struct conf_entry *entry = snew(struct conf_entry); + + assert(subkeytypes[primary] == TYPE_NONE); + assert(valuetypes[primary] == TYPE_FONT); + entry->key.primary = primary; + entry->value.u.fontval = *value; /* structure copy */ + conf_insert(conf, entry); +} + +int conf_serialised_size(Conf *conf) +{ + int i; + struct conf_entry *entry; + int size = 0; + + for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) { + size += 4; /* primary key */ + switch (subkeytypes[entry->key.primary]) { + case TYPE_INT: + size += 4; + break; + case TYPE_STR: + size += 1 + strlen(entry->key.secondary.s); + break; + } + switch (valuetypes[entry->key.primary]) { + case TYPE_INT: + size += 4; + break; + case TYPE_STR: + size += 1 + strlen(entry->value.u.stringval); + break; + case TYPE_FILENAME: + size += sizeof(entry->value.u.fileval); + break; + case TYPE_FONT: + size += sizeof(entry->value.u.fontval); + break; + } + } + + size += 4; /* terminator value */ + + return size; +} + +void conf_serialise(Conf *conf, void *vdata) +{ + unsigned char *data = (unsigned char *)vdata; + int i, len; + struct conf_entry *entry; + + for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) { + PUT_32BIT_MSB_FIRST(data, entry->key.primary); + data += 4; + + switch (subkeytypes[entry->key.primary]) { + case TYPE_INT: + PUT_32BIT_MSB_FIRST(data, entry->key.secondary.i); + data += 4; + break; + case TYPE_STR: + len = strlen(entry->key.secondary.s); + memcpy(data, entry->key.secondary.s, len); + data += len; + *data++ = 0; + break; + } + switch (valuetypes[entry->key.primary]) { + case TYPE_INT: + PUT_32BIT_MSB_FIRST(data, entry->value.u.intval); + data += 4; + break; + case TYPE_STR: + len = strlen(entry->value.u.stringval); + memcpy(data, entry->value.u.stringval, len); + data += len; + *data++ = 0; + break; + case TYPE_FILENAME: + memcpy(data, &entry->value.u.fileval, + sizeof(entry->value.u.fileval)); + data += sizeof(entry->value.u.fileval); + break; + case TYPE_FONT: + memcpy(data, &entry->value.u.fontval, + sizeof(entry->value.u.fontval)); + data += sizeof(entry->value.u.fontval); + break; + } + } + + PUT_32BIT_MSB_FIRST(data, 0xFFFFFFFFU); +} + +int conf_deserialise(Conf *conf, void *vdata, int maxsize) +{ + unsigned char *data = (unsigned char *)vdata; + unsigned char *start = data; + struct conf_entry *entry; + int primary; + unsigned char *zero; + + while (maxsize >= 4) { + primary = GET_32BIT_MSB_FIRST(data); + data += 4, maxsize -= 4; + + if ((unsigned)primary >= N_CONFIG_OPTIONS) + break; + + entry = snew(struct conf_entry); + entry->key.primary = primary; + + switch (subkeytypes[entry->key.primary]) { + case TYPE_INT: + if (maxsize < 4) { + sfree(entry); + goto done; + } + entry->key.secondary.i = GET_32BIT_MSB_FIRST(data); + data += 4, maxsize -= 4; + break; + case TYPE_STR: + zero = memchr(data, 0, maxsize); + if (!zero) { + sfree(entry); + goto done; + } + entry->key.secondary.s = dupstr((char *)data); + maxsize -= (zero + 1 - data); + data = zero + 1; + break; + } + + switch (valuetypes[entry->key.primary]) { + case TYPE_INT: + if (maxsize < 4) { + if (subkeytypes[entry->key.primary] == TYPE_STR) + sfree(entry->key.secondary.s); + sfree(entry); + goto done; + } + entry->value.u.intval = GET_32BIT_MSB_FIRST(data); + data += 4, maxsize -= 4; + break; + case TYPE_STR: + zero = memchr(data, 0, maxsize); + if (!zero) { + if (subkeytypes[entry->key.primary] == TYPE_STR) + sfree(entry->key.secondary.s); + sfree(entry); + goto done; + } + entry->value.u.stringval = dupstr((char *)data); + maxsize -= (zero + 1 - data); + data = zero + 1; + break; + case TYPE_FILENAME: + if (maxsize < sizeof(entry->value.u.fileval)) { + if (subkeytypes[entry->key.primary] == TYPE_STR) + sfree(entry->key.secondary.s); + sfree(entry); + goto done; + } + memcpy(&entry->value.u.fileval, data, + sizeof(entry->value.u.fileval)); + data += sizeof(entry->value.u.fileval); + maxsize -= sizeof(entry->value.u.fileval); + break; + case TYPE_FONT: + if (maxsize < sizeof(entry->value.u.fontval)) { + if (subkeytypes[entry->key.primary] == TYPE_STR) + sfree(entry->key.secondary.s); + sfree(entry); + goto done; + } + memcpy(&entry->value.u.fontval, data, + sizeof(entry->value.u.fontval)); + data += sizeof(entry->value.u.fontval); + maxsize -= sizeof(entry->value.u.fontval); + break; + } + conf_insert(conf, entry); + } + + done: + return (int)(data - start); +}