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
);
128 else if (type
== TYPE_FILENAME
)
129 filename_free(val
->u
.fileval
);
130 else if (type
== TYPE_FONT
)
131 fontspec_free(val
->u
.fontval
);
135 * Copy a 'struct value' into another one, copying its dynamic data
138 static void copy_value(struct value
*to
, struct value
*from
, int type
)
142 to
->u
.intval
= from
->u
.intval
;
145 to
->u
.stringval
= dupstr(from
->u
.stringval
);
148 to
->u
.fileval
= filename_copy(from
->u
.fileval
);
151 to
->u
.fontval
= fontspec_copy(from
->u
.fontval
);
157 * Free an entire 'struct conf_entry' and its dynamic data.
159 static void free_entry(struct conf_entry
*entry
)
161 free_key(&entry
->key
);
162 free_value(&entry
->value
, valuetypes
[entry
->key
.primary
]);
168 Conf
*conf
= snew(struct conf_tag
);
170 conf
->tree
= newtree234(conf_cmp
);
175 static void conf_clear(Conf
*conf
)
177 struct conf_entry
*entry
;
179 while ((entry
= delpos234(conf
->tree
, 0)) != NULL
)
183 void conf_free(Conf
*conf
)
186 freetree234(conf
->tree
);
190 static void conf_insert(Conf
*conf
, struct conf_entry
*entry
)
192 struct conf_entry
*oldentry
= add234(conf
->tree
, entry
);
193 if (oldentry
&& oldentry
!= entry
) {
194 del234(conf
->tree
, oldentry
);
195 free_entry(oldentry
);
196 oldentry
= add234(conf
->tree
, entry
);
197 assert(oldentry
== entry
);
201 void conf_copy_into(Conf
*newconf
, Conf
*oldconf
)
203 struct conf_entry
*entry
, *entry2
;
208 for (i
= 0; (entry
= index234(oldconf
->tree
, i
)) != NULL
; i
++) {
209 entry2
= snew(struct conf_entry
);
210 copy_key(&entry2
->key
, &entry
->key
);
211 copy_value(&entry2
->value
, &entry
->value
,
212 valuetypes
[entry
->key
.primary
]);
213 add234(newconf
->tree
, entry2
);
217 Conf
*conf_copy(Conf
*oldconf
)
219 Conf
*newconf
= conf_new();
221 conf_copy_into(newconf
, oldconf
);
226 int conf_get_int(Conf
*conf
, int primary
)
229 struct conf_entry
*entry
;
231 assert(subkeytypes
[primary
] == TYPE_NONE
);
232 assert(valuetypes
[primary
] == TYPE_INT
);
233 key
.primary
= primary
;
234 entry
= find234(conf
->tree
, &key
, NULL
);
236 return entry
->value
.u
.intval
;
239 int conf_get_int_int(Conf
*conf
, int primary
, int secondary
)
242 struct conf_entry
*entry
;
244 assert(subkeytypes
[primary
] == TYPE_INT
);
245 assert(valuetypes
[primary
] == TYPE_INT
);
246 key
.primary
= primary
;
247 key
.secondary
.i
= secondary
;
248 entry
= find234(conf
->tree
, &key
, NULL
);
250 return entry
->value
.u
.intval
;
253 char *conf_get_str(Conf
*conf
, int primary
)
256 struct conf_entry
*entry
;
258 assert(subkeytypes
[primary
] == TYPE_NONE
);
259 assert(valuetypes
[primary
] == TYPE_STR
);
260 key
.primary
= primary
;
261 entry
= find234(conf
->tree
, &key
, NULL
);
263 return entry
->value
.u
.stringval
;
266 char *conf_get_str_str_opt(Conf
*conf
, int primary
, const char *secondary
)
269 struct conf_entry
*entry
;
271 assert(subkeytypes
[primary
] == TYPE_STR
);
272 assert(valuetypes
[primary
] == TYPE_STR
);
273 key
.primary
= primary
;
274 key
.secondary
.s
= (char *)secondary
;
275 entry
= find234(conf
->tree
, &key
, NULL
);
276 return entry ? entry
->value
.u
.stringval
: NULL
;
279 char *conf_get_str_str(Conf
*conf
, int primary
, const char *secondary
)
281 char *ret
= conf_get_str_str_opt(conf
, primary
, secondary
);
286 char *conf_get_str_strs(Conf
*conf
, int primary
,
287 char *subkeyin
, char **subkeyout
)
290 struct conf_entry
*entry
;
292 assert(subkeytypes
[primary
] == TYPE_STR
);
293 assert(valuetypes
[primary
] == TYPE_STR
);
294 key
.primary
= primary
;
296 key
.secondary
.s
= subkeyin
;
297 entry
= findrel234(conf
->tree
, &key
, NULL
, REL234_GT
);
299 key
.secondary
.s
= "";
300 entry
= findrel234(conf
->tree
, &key
, NULL
, REL234_GE
);
302 if (!entry
|| entry
->key
.primary
!= primary
)
304 *subkeyout
= entry
->key
.secondary
.s
;
305 return entry
->value
.u
.stringval
;
308 char *conf_get_str_nthstrkey(Conf
*conf
, int primary
, int n
)
311 struct conf_entry
*entry
;
314 assert(subkeytypes
[primary
] == TYPE_STR
);
315 assert(valuetypes
[primary
] == TYPE_STR
);
316 key
.primary
= primary
;
317 key
.secondary
.s
= "";
318 entry
= findrelpos234(conf
->tree
, &key
, NULL
, REL234_GE
, &index
);
319 if (!entry
|| entry
->key
.primary
!= primary
)
321 entry
= index234(conf
->tree
, index
+ n
);
322 if (!entry
|| entry
->key
.primary
!= primary
)
324 return entry
->key
.secondary
.s
;
327 Filename
*conf_get_filename(Conf
*conf
, int primary
)
330 struct conf_entry
*entry
;
332 assert(subkeytypes
[primary
] == TYPE_NONE
);
333 assert(valuetypes
[primary
] == TYPE_FILENAME
);
334 key
.primary
= primary
;
335 entry
= find234(conf
->tree
, &key
, NULL
);
337 return entry
->value
.u
.fileval
;
340 FontSpec
*conf_get_fontspec(Conf
*conf
, int primary
)
343 struct conf_entry
*entry
;
345 assert(subkeytypes
[primary
] == TYPE_NONE
);
346 assert(valuetypes
[primary
] == TYPE_FONT
);
347 key
.primary
= primary
;
348 entry
= find234(conf
->tree
, &key
, NULL
);
350 return entry
->value
.u
.fontval
;
353 void conf_set_int(Conf
*conf
, int primary
, int value
)
355 struct conf_entry
*entry
= snew(struct conf_entry
);
357 assert(subkeytypes
[primary
] == TYPE_NONE
);
358 assert(valuetypes
[primary
] == TYPE_INT
);
359 entry
->key
.primary
= primary
;
360 entry
->value
.u
.intval
= value
;
361 conf_insert(conf
, entry
);
364 void conf_set_int_int(Conf
*conf
, int primary
, int secondary
, int value
)
366 struct conf_entry
*entry
= snew(struct conf_entry
);
368 assert(subkeytypes
[primary
] == TYPE_INT
);
369 assert(valuetypes
[primary
] == TYPE_INT
);
370 entry
->key
.primary
= primary
;
371 entry
->key
.secondary
.i
= secondary
;
372 entry
->value
.u
.intval
= value
;
373 conf_insert(conf
, entry
);
376 void conf_set_str(Conf
*conf
, int primary
, const char *value
)
378 struct conf_entry
*entry
= snew(struct conf_entry
);
380 assert(subkeytypes
[primary
] == TYPE_NONE
);
381 assert(valuetypes
[primary
] == TYPE_STR
);
382 entry
->key
.primary
= primary
;
383 entry
->value
.u
.stringval
= dupstr(value
);
384 conf_insert(conf
, entry
);
387 void conf_set_str_str(Conf
*conf
, int primary
, const char *secondary
,
390 struct conf_entry
*entry
= snew(struct conf_entry
);
392 assert(subkeytypes
[primary
] == TYPE_STR
);
393 assert(valuetypes
[primary
] == TYPE_STR
);
394 entry
->key
.primary
= primary
;
395 entry
->key
.secondary
.s
= dupstr(secondary
);
396 entry
->value
.u
.stringval
= dupstr(value
);
397 conf_insert(conf
, entry
);
400 void conf_del_str_str(Conf
*conf
, int primary
, const char *secondary
)
403 struct conf_entry
*entry
;
405 assert(subkeytypes
[primary
] == TYPE_STR
);
406 assert(valuetypes
[primary
] == TYPE_STR
);
407 key
.primary
= primary
;
408 key
.secondary
.s
= (char *)secondary
;
409 entry
= find234(conf
->tree
, &key
, NULL
);
411 del234(conf
->tree
, entry
);
416 void conf_set_filename(Conf
*conf
, int primary
, const Filename
*value
)
418 struct conf_entry
*entry
= snew(struct conf_entry
);
420 assert(subkeytypes
[primary
] == TYPE_NONE
);
421 assert(valuetypes
[primary
] == TYPE_FILENAME
);
422 entry
->key
.primary
= primary
;
423 entry
->value
.u
.fileval
= filename_copy(value
);
424 conf_insert(conf
, entry
);
427 void conf_set_fontspec(Conf
*conf
, int primary
, const FontSpec
*value
)
429 struct conf_entry
*entry
= snew(struct conf_entry
);
431 assert(subkeytypes
[primary
] == TYPE_NONE
);
432 assert(valuetypes
[primary
] == TYPE_FONT
);
433 entry
->key
.primary
= primary
;
434 entry
->value
.u
.fontval
= fontspec_copy(value
);
435 conf_insert(conf
, entry
);
438 int conf_serialised_size(Conf
*conf
)
441 struct conf_entry
*entry
;
444 for (i
= 0; (entry
= index234(conf
->tree
, i
)) != NULL
; i
++) {
445 size
+= 4; /* primary key */
446 switch (subkeytypes
[entry
->key
.primary
]) {
451 size
+= 1 + strlen(entry
->key
.secondary
.s
);
454 switch (valuetypes
[entry
->key
.primary
]) {
459 size
+= 1 + strlen(entry
->value
.u
.stringval
);
462 size
+= filename_serialise(entry
->value
.u
.fileval
, NULL
);
465 size
+= fontspec_serialise(entry
->value
.u
.fontval
, NULL
);
470 size
+= 4; /* terminator value */
475 void conf_serialise(Conf
*conf
, void *vdata
)
477 unsigned char *data
= (unsigned char *)vdata
;
479 struct conf_entry
*entry
;
481 for (i
= 0; (entry
= index234(conf
->tree
, i
)) != NULL
; i
++) {
482 PUT_32BIT_MSB_FIRST(data
, entry
->key
.primary
);
485 switch (subkeytypes
[entry
->key
.primary
]) {
487 PUT_32BIT_MSB_FIRST(data
, entry
->key
.secondary
.i
);
491 len
= strlen(entry
->key
.secondary
.s
);
492 memcpy(data
, entry
->key
.secondary
.s
, len
);
497 switch (valuetypes
[entry
->key
.primary
]) {
499 PUT_32BIT_MSB_FIRST(data
, entry
->value
.u
.intval
);
503 len
= strlen(entry
->value
.u
.stringval
);
504 memcpy(data
, entry
->value
.u
.stringval
, len
);
509 data
+= filename_serialise(entry
->value
.u
.fileval
, data
);
512 data
+= fontspec_serialise(entry
->value
.u
.fontval
, data
);
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 entry
->value
.u
.fileval
=
584 filename_deserialise(data
, maxsize
, &used
);
585 if (!entry
->value
.u
.fileval
) {
586 if (subkeytypes
[entry
->key
.primary
] == TYPE_STR
)
587 sfree(entry
->key
.secondary
.s
);
595 entry
->value
.u
.fontval
=
596 fontspec_deserialise(data
, maxsize
, &used
);
597 if (!entry
->value
.u
.fontval
) {
598 if (subkeytypes
[entry
->key
.primary
] == TYPE_STR
)
599 sfree(entry
->key
.secondary
.s
);
607 conf_insert(conf
, entry
);
611 return (int)(data
- start
);