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
;
202 for (i
= 0; (entry
= index234(oldconf
->tree
, i
)) != NULL
; i
++) {
203 entry2
= snew(struct conf_entry
);
204 copy_key(&entry2
->key
, &entry
->key
);
205 copy_value(&entry2
->value
, &entry
->value
,
206 valuetypes
[entry
->key
.primary
]);
207 add234(newconf
->tree
, entry2
);
211 Conf
*conf_copy(Conf
*oldconf
)
213 Conf
*newconf
= conf_new();
215 conf_copy_into(newconf
, oldconf
);
220 int conf_get_int(Conf
*conf
, int primary
)
223 struct conf_entry
*entry
;
225 assert(subkeytypes
[primary
] == TYPE_NONE
);
226 assert(valuetypes
[primary
] == TYPE_INT
);
227 key
.primary
= primary
;
228 entry
= find234(conf
->tree
, &key
, NULL
);
230 return entry
->value
.u
.intval
;
233 int conf_get_int_int(Conf
*conf
, int primary
, int secondary
)
236 struct conf_entry
*entry
;
238 assert(subkeytypes
[primary
] == TYPE_INT
);
239 assert(valuetypes
[primary
] == TYPE_INT
);
240 key
.primary
= primary
;
241 key
.secondary
.i
= secondary
;
242 entry
= find234(conf
->tree
, &key
, NULL
);
244 return entry
->value
.u
.intval
;
247 char *conf_get_str(Conf
*conf
, int primary
)
250 struct conf_entry
*entry
;
252 assert(subkeytypes
[primary
] == TYPE_NONE
);
253 assert(valuetypes
[primary
] == TYPE_STR
);
254 key
.primary
= primary
;
255 entry
= find234(conf
->tree
, &key
, NULL
);
257 return entry
->value
.u
.stringval
;
260 char *conf_get_str_str_opt(Conf
*conf
, int primary
, const char *secondary
)
263 struct conf_entry
*entry
;
265 assert(subkeytypes
[primary
] == TYPE_STR
);
266 assert(valuetypes
[primary
] == TYPE_STR
);
267 key
.primary
= primary
;
268 key
.secondary
.s
= (char *)secondary
;
269 entry
= find234(conf
->tree
, &key
, NULL
);
270 return entry ? entry
->value
.u
.stringval
: NULL
;
273 char *conf_get_str_str(Conf
*conf
, int primary
, const char *secondary
)
275 char *ret
= conf_get_str_str_opt(conf
, primary
, secondary
);
280 char *conf_get_str_strs(Conf
*conf
, int primary
,
281 char *subkeyin
, char **subkeyout
)
284 struct conf_entry
*entry
;
286 assert(subkeytypes
[primary
] == TYPE_STR
);
287 assert(valuetypes
[primary
] == TYPE_STR
);
288 key
.primary
= primary
;
290 key
.secondary
.s
= subkeyin
;
291 entry
= findrel234(conf
->tree
, &key
, NULL
, REL234_GT
);
293 key
.secondary
.s
= "";
294 entry
= findrel234(conf
->tree
, &key
, NULL
, REL234_GE
);
296 if (!entry
|| entry
->key
.primary
!= primary
)
298 *subkeyout
= entry
->key
.secondary
.s
;
299 return entry
->value
.u
.stringval
;
302 char *conf_get_str_nthstrkey(Conf
*conf
, int primary
, int n
)
305 struct conf_entry
*entry
;
308 assert(subkeytypes
[primary
] == TYPE_STR
);
309 assert(valuetypes
[primary
] == TYPE_STR
);
310 key
.primary
= primary
;
311 key
.secondary
.s
= "";
312 entry
= findrelpos234(conf
->tree
, &key
, NULL
, REL234_GE
, &index
);
313 if (!entry
|| entry
->key
.primary
!= primary
)
315 entry
= index234(conf
->tree
, index
+ n
);
316 if (!entry
|| entry
->key
.primary
!= primary
)
318 return entry
->key
.secondary
.s
;
321 Filename
*conf_get_filename(Conf
*conf
, int primary
)
324 struct conf_entry
*entry
;
326 assert(subkeytypes
[primary
] == TYPE_NONE
);
327 assert(valuetypes
[primary
] == TYPE_FILENAME
);
328 key
.primary
= primary
;
329 entry
= find234(conf
->tree
, &key
, NULL
);
331 return &entry
->value
.u
.fileval
;
334 FontSpec
*conf_get_fontspec(Conf
*conf
, int primary
)
337 struct conf_entry
*entry
;
339 assert(subkeytypes
[primary
] == TYPE_NONE
);
340 assert(valuetypes
[primary
] == TYPE_FONT
);
341 key
.primary
= primary
;
342 entry
= find234(conf
->tree
, &key
, NULL
);
344 return &entry
->value
.u
.fontval
;
347 void conf_set_int(Conf
*conf
, int primary
, int value
)
349 struct conf_entry
*entry
= snew(struct conf_entry
);
351 assert(subkeytypes
[primary
] == TYPE_NONE
);
352 assert(valuetypes
[primary
] == TYPE_INT
);
353 entry
->key
.primary
= primary
;
354 entry
->value
.u
.intval
= value
;
355 conf_insert(conf
, entry
);
358 void conf_set_int_int(Conf
*conf
, int primary
, int secondary
, int value
)
360 struct conf_entry
*entry
= snew(struct conf_entry
);
362 assert(subkeytypes
[primary
] == TYPE_INT
);
363 assert(valuetypes
[primary
] == TYPE_INT
);
364 entry
->key
.primary
= primary
;
365 entry
->key
.secondary
.i
= secondary
;
366 entry
->value
.u
.intval
= value
;
367 conf_insert(conf
, entry
);
370 void conf_set_str(Conf
*conf
, int primary
, const char *value
)
372 struct conf_entry
*entry
= snew(struct conf_entry
);
374 assert(subkeytypes
[primary
] == TYPE_NONE
);
375 assert(valuetypes
[primary
] == TYPE_STR
);
376 entry
->key
.primary
= primary
;
377 entry
->value
.u
.stringval
= dupstr(value
);
378 conf_insert(conf
, entry
);
381 void conf_set_str_str(Conf
*conf
, int primary
, const char *secondary
,
384 struct conf_entry
*entry
= snew(struct conf_entry
);
386 assert(subkeytypes
[primary
] == TYPE_STR
);
387 assert(valuetypes
[primary
] == TYPE_STR
);
388 entry
->key
.primary
= primary
;
389 entry
->key
.secondary
.s
= dupstr(secondary
);
390 entry
->value
.u
.stringval
= dupstr(value
);
391 conf_insert(conf
, entry
);
394 void conf_del_str_str(Conf
*conf
, int primary
, const char *secondary
)
397 struct conf_entry
*entry
;
399 assert(subkeytypes
[primary
] == TYPE_STR
);
400 assert(valuetypes
[primary
] == TYPE_STR
);
401 key
.primary
= primary
;
402 key
.secondary
.s
= (char *)secondary
;
403 entry
= find234(conf
->tree
, &key
, NULL
);
405 del234(conf
->tree
, entry
);
410 void conf_set_filename(Conf
*conf
, int primary
, const Filename
*value
)
412 struct conf_entry
*entry
= snew(struct conf_entry
);
414 assert(subkeytypes
[primary
] == TYPE_NONE
);
415 assert(valuetypes
[primary
] == TYPE_FILENAME
);
416 entry
->key
.primary
= primary
;
417 entry
->value
.u
.fileval
= *value
; /* structure copy */
418 conf_insert(conf
, entry
);
421 void conf_set_fontspec(Conf
*conf
, int primary
, const FontSpec
*value
)
423 struct conf_entry
*entry
= snew(struct conf_entry
);
425 assert(subkeytypes
[primary
] == TYPE_NONE
);
426 assert(valuetypes
[primary
] == TYPE_FONT
);
427 entry
->key
.primary
= primary
;
428 entry
->value
.u
.fontval
= *value
; /* structure copy */
429 conf_insert(conf
, entry
);
432 int conf_serialised_size(Conf
*conf
)
435 struct conf_entry
*entry
;
438 for (i
= 0; (entry
= index234(conf
->tree
, i
)) != NULL
; i
++) {
439 size
+= 4; /* primary key */
440 switch (subkeytypes
[entry
->key
.primary
]) {
445 size
+= 1 + strlen(entry
->key
.secondary
.s
);
448 switch (valuetypes
[entry
->key
.primary
]) {
453 size
+= 1 + strlen(entry
->value
.u
.stringval
);
456 size
+= sizeof(entry
->value
.u
.fileval
);
459 size
+= sizeof(entry
->value
.u
.fontval
);
464 size
+= 4; /* terminator value */
469 void conf_serialise(Conf
*conf
, void *vdata
)
471 unsigned char *data
= (unsigned char *)vdata
;
473 struct conf_entry
*entry
;
475 for (i
= 0; (entry
= index234(conf
->tree
, i
)) != NULL
; i
++) {
476 PUT_32BIT_MSB_FIRST(data
, entry
->key
.primary
);
479 switch (subkeytypes
[entry
->key
.primary
]) {
481 PUT_32BIT_MSB_FIRST(data
, entry
->key
.secondary
.i
);
485 len
= strlen(entry
->key
.secondary
.s
);
486 memcpy(data
, entry
->key
.secondary
.s
, len
);
491 switch (valuetypes
[entry
->key
.primary
]) {
493 PUT_32BIT_MSB_FIRST(data
, entry
->value
.u
.intval
);
497 len
= strlen(entry
->value
.u
.stringval
);
498 memcpy(data
, entry
->value
.u
.stringval
, len
);
503 memcpy(data
, &entry
->value
.u
.fileval
,
504 sizeof(entry
->value
.u
.fileval
));
505 data
+= sizeof(entry
->value
.u
.fileval
);
508 memcpy(data
, &entry
->value
.u
.fontval
,
509 sizeof(entry
->value
.u
.fontval
));
510 data
+= sizeof(entry
->value
.u
.fontval
);
515 PUT_32BIT_MSB_FIRST(data
, 0xFFFFFFFFU
);
518 int conf_deserialise(Conf
*conf
, void *vdata
, int maxsize
)
520 unsigned char *data
= (unsigned char *)vdata
;
521 unsigned char *start
= data
;
522 struct conf_entry
*entry
;
526 while (maxsize
>= 4) {
527 primary
= GET_32BIT_MSB_FIRST(data
);
528 data
+= 4, maxsize
-= 4;
530 if ((unsigned)primary
>= N_CONFIG_OPTIONS
)
533 entry
= snew(struct conf_entry
);
534 entry
->key
.primary
= primary
;
536 switch (subkeytypes
[entry
->key
.primary
]) {
542 entry
->key
.secondary
.i
= GET_32BIT_MSB_FIRST(data
);
543 data
+= 4, maxsize
-= 4;
546 zero
= memchr(data
, 0, maxsize
);
551 entry
->key
.secondary
.s
= dupstr((char *)data
);
552 maxsize
-= (zero
+ 1 - data
);
557 switch (valuetypes
[entry
->key
.primary
]) {
560 if (subkeytypes
[entry
->key
.primary
] == TYPE_STR
)
561 sfree(entry
->key
.secondary
.s
);
565 entry
->value
.u
.intval
= GET_32BIT_MSB_FIRST(data
);
566 data
+= 4, maxsize
-= 4;
569 zero
= memchr(data
, 0, maxsize
);
571 if (subkeytypes
[entry
->key
.primary
] == TYPE_STR
)
572 sfree(entry
->key
.secondary
.s
);
576 entry
->value
.u
.stringval
= dupstr((char *)data
);
577 maxsize
-= (zero
+ 1 - data
);
581 if (maxsize
< sizeof(entry
->value
.u
.fileval
)) {
582 if (subkeytypes
[entry
->key
.primary
] == TYPE_STR
)
583 sfree(entry
->key
.secondary
.s
);
587 memcpy(&entry
->value
.u
.fileval
, data
,
588 sizeof(entry
->value
.u
.fileval
));
589 data
+= sizeof(entry
->value
.u
.fileval
);
590 maxsize
-= sizeof(entry
->value
.u
.fileval
);
593 if (maxsize
< sizeof(entry
->value
.u
.fontval
)) {
594 if (subkeytypes
[entry
->key
.primary
] == TYPE_STR
)
595 sfree(entry
->key
.secondary
.s
);
599 memcpy(&entry
->value
.u
.fontval
, data
,
600 sizeof(entry
->value
.u
.fontval
));
601 data
+= sizeof(entry
->value
.u
.fontval
);
602 maxsize
-= sizeof(entry
->value
.u
.fontval
);
605 conf_insert(conf
, entry
);
609 return (int)(data
- start
);