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_FONT
)
129 fontspec_free(val
->u
.fontval
);
133 * Copy a 'struct value' into another one, copying its dynamic data
136 static void copy_value(struct value
*to
, struct value
*from
, int type
)
140 to
->u
.intval
= from
->u
.intval
;
143 to
->u
.stringval
= dupstr(from
->u
.stringval
);
146 to
->u
.fileval
= from
->u
.fileval
;
149 to
->u
.fontval
= fontspec_copy(from
->u
.fontval
);
155 * Free an entire 'struct conf_entry' and its dynamic data.
157 static void free_entry(struct conf_entry
*entry
)
159 free_key(&entry
->key
);
160 free_value(&entry
->value
, valuetypes
[entry
->key
.primary
]);
166 Conf
*conf
= snew(struct conf_tag
);
168 conf
->tree
= newtree234(conf_cmp
);
173 static void conf_clear(Conf
*conf
)
175 struct conf_entry
*entry
;
177 while ((entry
= delpos234(conf
->tree
, 0)) != NULL
)
181 void conf_free(Conf
*conf
)
184 freetree234(conf
->tree
);
188 static void conf_insert(Conf
*conf
, struct conf_entry
*entry
)
190 struct conf_entry
*oldentry
= add234(conf
->tree
, entry
);
191 if (oldentry
&& oldentry
!= entry
) {
192 del234(conf
->tree
, oldentry
);
193 free_entry(oldentry
);
194 oldentry
= add234(conf
->tree
, entry
);
195 assert(oldentry
== entry
);
199 void conf_copy_into(Conf
*newconf
, Conf
*oldconf
)
201 struct conf_entry
*entry
, *entry2
;
206 for (i
= 0; (entry
= index234(oldconf
->tree
, i
)) != NULL
; i
++) {
207 entry2
= snew(struct conf_entry
);
208 copy_key(&entry2
->key
, &entry
->key
);
209 copy_value(&entry2
->value
, &entry
->value
,
210 valuetypes
[entry
->key
.primary
]);
211 add234(newconf
->tree
, entry2
);
215 Conf
*conf_copy(Conf
*oldconf
)
217 Conf
*newconf
= conf_new();
219 conf_copy_into(newconf
, oldconf
);
224 int conf_get_int(Conf
*conf
, int primary
)
227 struct conf_entry
*entry
;
229 assert(subkeytypes
[primary
] == TYPE_NONE
);
230 assert(valuetypes
[primary
] == TYPE_INT
);
231 key
.primary
= primary
;
232 entry
= find234(conf
->tree
, &key
, NULL
);
234 return entry
->value
.u
.intval
;
237 int conf_get_int_int(Conf
*conf
, int primary
, int secondary
)
240 struct conf_entry
*entry
;
242 assert(subkeytypes
[primary
] == TYPE_INT
);
243 assert(valuetypes
[primary
] == TYPE_INT
);
244 key
.primary
= primary
;
245 key
.secondary
.i
= secondary
;
246 entry
= find234(conf
->tree
, &key
, NULL
);
248 return entry
->value
.u
.intval
;
251 char *conf_get_str(Conf
*conf
, int primary
)
254 struct conf_entry
*entry
;
256 assert(subkeytypes
[primary
] == TYPE_NONE
);
257 assert(valuetypes
[primary
] == TYPE_STR
);
258 key
.primary
= primary
;
259 entry
= find234(conf
->tree
, &key
, NULL
);
261 return entry
->value
.u
.stringval
;
264 char *conf_get_str_str_opt(Conf
*conf
, int primary
, const char *secondary
)
267 struct conf_entry
*entry
;
269 assert(subkeytypes
[primary
] == TYPE_STR
);
270 assert(valuetypes
[primary
] == TYPE_STR
);
271 key
.primary
= primary
;
272 key
.secondary
.s
= (char *)secondary
;
273 entry
= find234(conf
->tree
, &key
, NULL
);
274 return entry ? entry
->value
.u
.stringval
: NULL
;
277 char *conf_get_str_str(Conf
*conf
, int primary
, const char *secondary
)
279 char *ret
= conf_get_str_str_opt(conf
, primary
, secondary
);
284 char *conf_get_str_strs(Conf
*conf
, int primary
,
285 char *subkeyin
, char **subkeyout
)
288 struct conf_entry
*entry
;
290 assert(subkeytypes
[primary
] == TYPE_STR
);
291 assert(valuetypes
[primary
] == TYPE_STR
);
292 key
.primary
= primary
;
294 key
.secondary
.s
= subkeyin
;
295 entry
= findrel234(conf
->tree
, &key
, NULL
, REL234_GT
);
297 key
.secondary
.s
= "";
298 entry
= findrel234(conf
->tree
, &key
, NULL
, REL234_GE
);
300 if (!entry
|| entry
->key
.primary
!= primary
)
302 *subkeyout
= entry
->key
.secondary
.s
;
303 return entry
->value
.u
.stringval
;
306 char *conf_get_str_nthstrkey(Conf
*conf
, int primary
, int n
)
309 struct conf_entry
*entry
;
312 assert(subkeytypes
[primary
] == TYPE_STR
);
313 assert(valuetypes
[primary
] == TYPE_STR
);
314 key
.primary
= primary
;
315 key
.secondary
.s
= "";
316 entry
= findrelpos234(conf
->tree
, &key
, NULL
, REL234_GE
, &index
);
317 if (!entry
|| entry
->key
.primary
!= primary
)
319 entry
= index234(conf
->tree
, index
+ n
);
320 if (!entry
|| entry
->key
.primary
!= primary
)
322 return entry
->key
.secondary
.s
;
325 Filename
*conf_get_filename(Conf
*conf
, int primary
)
328 struct conf_entry
*entry
;
330 assert(subkeytypes
[primary
] == TYPE_NONE
);
331 assert(valuetypes
[primary
] == TYPE_FILENAME
);
332 key
.primary
= primary
;
333 entry
= find234(conf
->tree
, &key
, NULL
);
335 return &entry
->value
.u
.fileval
;
338 FontSpec
*conf_get_fontspec(Conf
*conf
, int primary
)
341 struct conf_entry
*entry
;
343 assert(subkeytypes
[primary
] == TYPE_NONE
);
344 assert(valuetypes
[primary
] == TYPE_FONT
);
345 key
.primary
= primary
;
346 entry
= find234(conf
->tree
, &key
, NULL
);
348 return entry
->value
.u
.fontval
;
351 void conf_set_int(Conf
*conf
, int primary
, int value
)
353 struct conf_entry
*entry
= snew(struct conf_entry
);
355 assert(subkeytypes
[primary
] == TYPE_NONE
);
356 assert(valuetypes
[primary
] == TYPE_INT
);
357 entry
->key
.primary
= primary
;
358 entry
->value
.u
.intval
= value
;
359 conf_insert(conf
, entry
);
362 void conf_set_int_int(Conf
*conf
, int primary
, int secondary
, int value
)
364 struct conf_entry
*entry
= snew(struct conf_entry
);
366 assert(subkeytypes
[primary
] == TYPE_INT
);
367 assert(valuetypes
[primary
] == TYPE_INT
);
368 entry
->key
.primary
= primary
;
369 entry
->key
.secondary
.i
= secondary
;
370 entry
->value
.u
.intval
= value
;
371 conf_insert(conf
, entry
);
374 void conf_set_str(Conf
*conf
, int primary
, const char *value
)
376 struct conf_entry
*entry
= snew(struct conf_entry
);
378 assert(subkeytypes
[primary
] == TYPE_NONE
);
379 assert(valuetypes
[primary
] == TYPE_STR
);
380 entry
->key
.primary
= primary
;
381 entry
->value
.u
.stringval
= dupstr(value
);
382 conf_insert(conf
, entry
);
385 void conf_set_str_str(Conf
*conf
, int primary
, const char *secondary
,
388 struct conf_entry
*entry
= snew(struct conf_entry
);
390 assert(subkeytypes
[primary
] == TYPE_STR
);
391 assert(valuetypes
[primary
] == TYPE_STR
);
392 entry
->key
.primary
= primary
;
393 entry
->key
.secondary
.s
= dupstr(secondary
);
394 entry
->value
.u
.stringval
= dupstr(value
);
395 conf_insert(conf
, entry
);
398 void conf_del_str_str(Conf
*conf
, int primary
, const char *secondary
)
401 struct conf_entry
*entry
;
403 assert(subkeytypes
[primary
] == TYPE_STR
);
404 assert(valuetypes
[primary
] == TYPE_STR
);
405 key
.primary
= primary
;
406 key
.secondary
.s
= (char *)secondary
;
407 entry
= find234(conf
->tree
, &key
, NULL
);
409 del234(conf
->tree
, entry
);
414 void conf_set_filename(Conf
*conf
, int primary
, const Filename
*value
)
416 struct conf_entry
*entry
= snew(struct conf_entry
);
418 assert(subkeytypes
[primary
] == TYPE_NONE
);
419 assert(valuetypes
[primary
] == TYPE_FILENAME
);
420 entry
->key
.primary
= primary
;
421 entry
->value
.u
.fileval
= *value
; /* structure copy */
422 conf_insert(conf
, entry
);
425 void conf_set_fontspec(Conf
*conf
, int primary
, const FontSpec
*value
)
427 struct conf_entry
*entry
= snew(struct conf_entry
);
429 assert(subkeytypes
[primary
] == TYPE_NONE
);
430 assert(valuetypes
[primary
] == TYPE_FONT
);
431 entry
->key
.primary
= primary
;
432 entry
->value
.u
.fontval
= fontspec_copy(value
);
433 conf_insert(conf
, entry
);
436 int conf_serialised_size(Conf
*conf
)
439 struct conf_entry
*entry
;
442 for (i
= 0; (entry
= index234(conf
->tree
, i
)) != NULL
; i
++) {
443 size
+= 4; /* primary key */
444 switch (subkeytypes
[entry
->key
.primary
]) {
449 size
+= 1 + strlen(entry
->key
.secondary
.s
);
452 switch (valuetypes
[entry
->key
.primary
]) {
457 size
+= 1 + strlen(entry
->value
.u
.stringval
);
460 size
+= sizeof(entry
->value
.u
.fileval
);
463 size
+= fontspec_serialise(entry
->value
.u
.fontval
, NULL
);
468 size
+= 4; /* terminator value */
473 void conf_serialise(Conf
*conf
, void *vdata
)
475 unsigned char *data
= (unsigned char *)vdata
;
477 struct conf_entry
*entry
;
479 for (i
= 0; (entry
= index234(conf
->tree
, i
)) != NULL
; i
++) {
480 PUT_32BIT_MSB_FIRST(data
, entry
->key
.primary
);
483 switch (subkeytypes
[entry
->key
.primary
]) {
485 PUT_32BIT_MSB_FIRST(data
, entry
->key
.secondary
.i
);
489 len
= strlen(entry
->key
.secondary
.s
);
490 memcpy(data
, entry
->key
.secondary
.s
, len
);
495 switch (valuetypes
[entry
->key
.primary
]) {
497 PUT_32BIT_MSB_FIRST(data
, entry
->value
.u
.intval
);
501 len
= strlen(entry
->value
.u
.stringval
);
502 memcpy(data
, entry
->value
.u
.stringval
, len
);
507 memcpy(data
, &entry
->value
.u
.fileval
,
508 sizeof(entry
->value
.u
.fileval
));
509 data
+= sizeof(entry
->value
.u
.fileval
);
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 if (maxsize
< sizeof(entry
->value
.u
.fileval
)) {
584 if (subkeytypes
[entry
->key
.primary
] == TYPE_STR
)
585 sfree(entry
->key
.secondary
.s
);
589 memcpy(&entry
->value
.u
.fileval
, data
,
590 sizeof(entry
->value
.u
.fileval
));
591 data
+= sizeof(entry
->value
.u
.fileval
);
592 maxsize
-= sizeof(entry
->value
.u
.fileval
);
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
);