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
);
131 * Copy a 'struct value' into another one, copying its dynamic data
134 static void copy_value(struct value
*to
, struct value
*from
, int type
)
138 to
->u
.intval
= from
->u
.intval
;
141 to
->u
.stringval
= dupstr(from
->u
.stringval
);
144 to
->u
.fileval
= from
->u
.fileval
;
147 to
->u
.fontval
= from
->u
.fontval
;
153 * Free an entire 'struct conf_entry' and its dynamic data.
155 static void free_entry(struct conf_entry
*entry
)
157 free_key(&entry
->key
);
158 free_value(&entry
->value
, valuetypes
[entry
->key
.primary
]);
164 Conf
*conf
= snew(struct conf_tag
);
166 conf
->tree
= newtree234(conf_cmp
);
171 static void conf_clear(Conf
*conf
)
173 struct conf_entry
*entry
;
175 while ((entry
= delpos234(conf
->tree
, 0)) != NULL
)
179 void conf_free(Conf
*conf
)
182 freetree234(conf
->tree
);
186 static void conf_insert(Conf
*conf
, struct conf_entry
*entry
)
188 struct conf_entry
*oldentry
= add234(conf
->tree
, entry
);
189 if (oldentry
&& oldentry
!= entry
) {
190 del234(conf
->tree
, oldentry
);
191 free_entry(oldentry
);
192 oldentry
= add234(conf
->tree
, entry
);
193 assert(oldentry
== entry
);
197 void conf_copy_into(Conf
*newconf
, Conf
*oldconf
)
199 struct conf_entry
*entry
, *entry2
;
204 for (i
= 0; (entry
= index234(oldconf
->tree
, i
)) != NULL
; i
++) {
205 entry2
= snew(struct conf_entry
);
206 copy_key(&entry2
->key
, &entry
->key
);
207 copy_value(&entry2
->value
, &entry
->value
,
208 valuetypes
[entry
->key
.primary
]);
209 add234(newconf
->tree
, entry2
);
213 Conf
*conf_copy(Conf
*oldconf
)
215 Conf
*newconf
= conf_new();
217 conf_copy_into(newconf
, oldconf
);
222 int conf_get_int(Conf
*conf
, int primary
)
225 struct conf_entry
*entry
;
227 assert(subkeytypes
[primary
] == TYPE_NONE
);
228 assert(valuetypes
[primary
] == TYPE_INT
);
229 key
.primary
= primary
;
230 entry
= find234(conf
->tree
, &key
, NULL
);
232 return entry
->value
.u
.intval
;
235 int conf_get_int_int(Conf
*conf
, int primary
, int secondary
)
238 struct conf_entry
*entry
;
240 assert(subkeytypes
[primary
] == TYPE_INT
);
241 assert(valuetypes
[primary
] == TYPE_INT
);
242 key
.primary
= primary
;
243 key
.secondary
.i
= secondary
;
244 entry
= find234(conf
->tree
, &key
, NULL
);
246 return entry
->value
.u
.intval
;
249 char *conf_get_str(Conf
*conf
, int primary
)
252 struct conf_entry
*entry
;
254 assert(subkeytypes
[primary
] == TYPE_NONE
);
255 assert(valuetypes
[primary
] == TYPE_STR
);
256 key
.primary
= primary
;
257 entry
= find234(conf
->tree
, &key
, NULL
);
259 return entry
->value
.u
.stringval
;
262 char *conf_get_str_str_opt(Conf
*conf
, int primary
, const char *secondary
)
265 struct conf_entry
*entry
;
267 assert(subkeytypes
[primary
] == TYPE_STR
);
268 assert(valuetypes
[primary
] == TYPE_STR
);
269 key
.primary
= primary
;
270 key
.secondary
.s
= (char *)secondary
;
271 entry
= find234(conf
->tree
, &key
, NULL
);
272 return entry ? entry
->value
.u
.stringval
: NULL
;
275 char *conf_get_str_str(Conf
*conf
, int primary
, const char *secondary
)
277 char *ret
= conf_get_str_str_opt(conf
, primary
, secondary
);
282 char *conf_get_str_strs(Conf
*conf
, int primary
,
283 char *subkeyin
, char **subkeyout
)
286 struct conf_entry
*entry
;
288 assert(subkeytypes
[primary
] == TYPE_STR
);
289 assert(valuetypes
[primary
] == TYPE_STR
);
290 key
.primary
= primary
;
292 key
.secondary
.s
= subkeyin
;
293 entry
= findrel234(conf
->tree
, &key
, NULL
, REL234_GT
);
295 key
.secondary
.s
= "";
296 entry
= findrel234(conf
->tree
, &key
, NULL
, REL234_GE
);
298 if (!entry
|| entry
->key
.primary
!= primary
)
300 *subkeyout
= entry
->key
.secondary
.s
;
301 return entry
->value
.u
.stringval
;
304 char *conf_get_str_nthstrkey(Conf
*conf
, int primary
, int n
)
307 struct conf_entry
*entry
;
310 assert(subkeytypes
[primary
] == TYPE_STR
);
311 assert(valuetypes
[primary
] == TYPE_STR
);
312 key
.primary
= primary
;
313 key
.secondary
.s
= "";
314 entry
= findrelpos234(conf
->tree
, &key
, NULL
, REL234_GE
, &index
);
315 if (!entry
|| entry
->key
.primary
!= primary
)
317 entry
= index234(conf
->tree
, index
+ n
);
318 if (!entry
|| entry
->key
.primary
!= primary
)
320 return entry
->key
.secondary
.s
;
323 Filename
*conf_get_filename(Conf
*conf
, int primary
)
326 struct conf_entry
*entry
;
328 assert(subkeytypes
[primary
] == TYPE_NONE
);
329 assert(valuetypes
[primary
] == TYPE_FILENAME
);
330 key
.primary
= primary
;
331 entry
= find234(conf
->tree
, &key
, NULL
);
333 return &entry
->value
.u
.fileval
;
336 FontSpec
*conf_get_fontspec(Conf
*conf
, int primary
)
339 struct conf_entry
*entry
;
341 assert(subkeytypes
[primary
] == TYPE_NONE
);
342 assert(valuetypes
[primary
] == TYPE_FONT
);
343 key
.primary
= primary
;
344 entry
= find234(conf
->tree
, &key
, NULL
);
346 return &entry
->value
.u
.fontval
;
349 void conf_set_int(Conf
*conf
, int primary
, int value
)
351 struct conf_entry
*entry
= snew(struct conf_entry
);
353 assert(subkeytypes
[primary
] == TYPE_NONE
);
354 assert(valuetypes
[primary
] == TYPE_INT
);
355 entry
->key
.primary
= primary
;
356 entry
->value
.u
.intval
= value
;
357 conf_insert(conf
, entry
);
360 void conf_set_int_int(Conf
*conf
, int primary
, int secondary
, int value
)
362 struct conf_entry
*entry
= snew(struct conf_entry
);
364 assert(subkeytypes
[primary
] == TYPE_INT
);
365 assert(valuetypes
[primary
] == TYPE_INT
);
366 entry
->key
.primary
= primary
;
367 entry
->key
.secondary
.i
= secondary
;
368 entry
->value
.u
.intval
= value
;
369 conf_insert(conf
, entry
);
372 void conf_set_str(Conf
*conf
, int primary
, const char *value
)
374 struct conf_entry
*entry
= snew(struct conf_entry
);
376 assert(subkeytypes
[primary
] == TYPE_NONE
);
377 assert(valuetypes
[primary
] == TYPE_STR
);
378 entry
->key
.primary
= primary
;
379 entry
->value
.u
.stringval
= dupstr(value
);
380 conf_insert(conf
, entry
);
383 void conf_set_str_str(Conf
*conf
, int primary
, const char *secondary
,
386 struct conf_entry
*entry
= snew(struct conf_entry
);
388 assert(subkeytypes
[primary
] == TYPE_STR
);
389 assert(valuetypes
[primary
] == TYPE_STR
);
390 entry
->key
.primary
= primary
;
391 entry
->key
.secondary
.s
= dupstr(secondary
);
392 entry
->value
.u
.stringval
= dupstr(value
);
393 conf_insert(conf
, entry
);
396 void conf_del_str_str(Conf
*conf
, int primary
, const char *secondary
)
399 struct conf_entry
*entry
;
401 assert(subkeytypes
[primary
] == TYPE_STR
);
402 assert(valuetypes
[primary
] == TYPE_STR
);
403 key
.primary
= primary
;
404 key
.secondary
.s
= (char *)secondary
;
405 entry
= find234(conf
->tree
, &key
, NULL
);
407 del234(conf
->tree
, entry
);
412 void conf_set_filename(Conf
*conf
, int primary
, const Filename
*value
)
414 struct conf_entry
*entry
= snew(struct conf_entry
);
416 assert(subkeytypes
[primary
] == TYPE_NONE
);
417 assert(valuetypes
[primary
] == TYPE_FILENAME
);
418 entry
->key
.primary
= primary
;
419 entry
->value
.u
.fileval
= *value
; /* structure copy */
420 conf_insert(conf
, entry
);
423 void conf_set_fontspec(Conf
*conf
, int primary
, const FontSpec
*value
)
425 struct conf_entry
*entry
= snew(struct conf_entry
);
427 assert(subkeytypes
[primary
] == TYPE_NONE
);
428 assert(valuetypes
[primary
] == TYPE_FONT
);
429 entry
->key
.primary
= primary
;
430 entry
->value
.u
.fontval
= *value
; /* structure copy */
431 conf_insert(conf
, entry
);
434 int conf_serialised_size(Conf
*conf
)
437 struct conf_entry
*entry
;
440 for (i
= 0; (entry
= index234(conf
->tree
, i
)) != NULL
; i
++) {
441 size
+= 4; /* primary key */
442 switch (subkeytypes
[entry
->key
.primary
]) {
447 size
+= 1 + strlen(entry
->key
.secondary
.s
);
450 switch (valuetypes
[entry
->key
.primary
]) {
455 size
+= 1 + strlen(entry
->value
.u
.stringval
);
458 size
+= sizeof(entry
->value
.u
.fileval
);
461 size
+= sizeof(entry
->value
.u
.fontval
);
466 size
+= 4; /* terminator value */
471 void conf_serialise(Conf
*conf
, void *vdata
)
473 unsigned char *data
= (unsigned char *)vdata
;
475 struct conf_entry
*entry
;
477 for (i
= 0; (entry
= index234(conf
->tree
, i
)) != NULL
; i
++) {
478 PUT_32BIT_MSB_FIRST(data
, entry
->key
.primary
);
481 switch (subkeytypes
[entry
->key
.primary
]) {
483 PUT_32BIT_MSB_FIRST(data
, entry
->key
.secondary
.i
);
487 len
= strlen(entry
->key
.secondary
.s
);
488 memcpy(data
, entry
->key
.secondary
.s
, len
);
493 switch (valuetypes
[entry
->key
.primary
]) {
495 PUT_32BIT_MSB_FIRST(data
, entry
->value
.u
.intval
);
499 len
= strlen(entry
->value
.u
.stringval
);
500 memcpy(data
, entry
->value
.u
.stringval
, len
);
505 memcpy(data
, &entry
->value
.u
.fileval
,
506 sizeof(entry
->value
.u
.fileval
));
507 data
+= sizeof(entry
->value
.u
.fileval
);
510 memcpy(data
, &entry
->value
.u
.fontval
,
511 sizeof(entry
->value
.u
.fontval
));
512 data
+= sizeof(entry
->value
.u
.fontval
);
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 if (maxsize
< sizeof(entry
->value
.u
.fontval
)) {
596 if (subkeytypes
[entry
->key
.primary
] == TYPE_STR
)
597 sfree(entry
->key
.secondary
.s
);
601 memcpy(&entry
->value
.u
.fontval
, data
,
602 sizeof(entry
->value
.u
.fontval
));
603 data
+= sizeof(entry
->value
.u
.fontval
);
604 maxsize
-= sizeof(entry
->value
.u
.fontval
);
607 conf_insert(conf
, entry
);
611 return (int)(data
- start
);