| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647 | /* * conf.c: implementation of the internal storage format used for * the configuration of a PuTTY session. */#include <stdio.h>#include <stddef.h>#include <assert.h>#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;};/* Variant form of struct key which doesn't contain dynamic data, used * for lookups. */struct constkey {    int primary;    union {	int i;	const 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;    }}static int conf_cmp_constkey(void *av, void *bv){    struct key *a = (struct key *)av;    struct constkey *b = (struct constkey *)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);    else if (type == TYPE_FILENAME)	filename_free(val->u.fileval);    else if (type == TYPE_FONT)	fontspec_free(val->u.fontval);}/* * 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 = filename_copy(from->u.fileval);	break;      case TYPE_FONT:	to->u.fontval = fontspec_copy(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;    conf_clear(newconf);    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 constkey 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, conf_cmp_constkey, 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 constkey 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, conf_cmp_constkey,                          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 = filename_copy(value);    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 = fontspec_copy(value);    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 += filename_serialise(entry->value.u.fileval, NULL);	    break;	  case TYPE_FONT:	    size += fontspec_serialise(entry->value.u.fontval, NULL);	    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:            data += filename_serialise(entry->value.u.fileval, data);	    break;	  case TYPE_FONT:            data += fontspec_serialise(entry->value.u.fontval, data);	    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;    unsigned primary;    int used;    unsigned char *zero;    while (maxsize >= 4) {	primary = GET_32BIT_MSB_FIRST(data);	data += 4, maxsize -= 4;	if (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 = toint(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 = toint(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:            entry->value.u.fileval =                filename_deserialise(data, maxsize, &used);            if (!entry->value.u.fileval) {		if (subkeytypes[entry->key.primary] == TYPE_STR)		    sfree(entry->key.secondary.s);		sfree(entry);		goto done;	    }	    data += used;	    maxsize -= used;	    break;	  case TYPE_FONT:            entry->value.u.fontval =                fontspec_deserialise(data, maxsize, &used);            if (!entry->value.u.fontval) {		if (subkeytypes[entry->key.primary] == TYPE_STR)		    sfree(entry->key.secondary.s);		sfree(entry);		goto done;	    }	    data += used;	    maxsize -= used;	    break;	}	conf_insert(conf, entry);    }    done:    return (int)(data - start);}
 |