]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - conf.c
first pass
[PuTTY.git] / conf.c
1 /*
2  * conf.c: implementation of the internal storage format used for
3  * the configuration of a PuTTY session.
4  */
5
6 #include <stdio.h>
7 #include <stddef.h>
8 #include <assert.h>
9
10 #include "tree234.h"
11 #include "putty.h"
12
13 /*
14  * Enumeration of types used in keys and values.
15  */
16 typedef enum { TYPE_NONE, TYPE_INT, TYPE_STR, TYPE_FILENAME, TYPE_FONT } Type;
17
18 /*
19  * Arrays which allow us to look up the subkey and value types for a
20  * given primary key id.
21  */
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) };
26
27 /*
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).
33  */
34 struct key {
35     int primary;
36     union {
37         int i;
38         char *s;
39     } secondary;
40 };
41
42 /* Variant form of struct key which doesn't contain dynamic data, used
43  * for lookups. */
44 struct constkey {
45     int primary;
46     union {
47         int i;
48         const char *s;
49     } secondary;
50 };
51
52 struct value {
53     union {
54         int intval;
55         char *stringval;
56         Filename *fileval;
57         FontSpec *fontval;
58     } u;
59 };
60
61 struct conf_entry {
62     struct key key;
63     struct value value;
64 };
65
66 struct conf_tag {
67     tree234 *tree;
68 };
69
70 /*
71  * Because 'struct key' is the first element in 'struct conf_entry',
72  * it's safe (guaranteed by the C standard) to cast arbitrarily back
73  * and forth between the two types. Therefore, we only need one
74  * comparison function, which can double as a main sort function for
75  * the tree (comparing two conf_entry structures with each other)
76  * and a search function (looking up an externally supplied key).
77  */
78 static int conf_cmp(void *av, void *bv)
79 {
80     struct key *a = (struct key *)av;
81     struct key *b = (struct key *)bv;
82
83     if (a->primary < b->primary)
84         return -1;
85     else if (a->primary > b->primary)
86         return +1;
87     switch (subkeytypes[a->primary]) {
88       case TYPE_INT:
89         if (a->secondary.i < b->secondary.i)
90             return -1;
91         else if (a->secondary.i > b->secondary.i)
92             return +1;
93         return 0;
94       case TYPE_STR:
95         return strcmp(a->secondary.s, b->secondary.s);
96       default:
97         return 0;
98     }
99 }
100
101 static int conf_cmp_constkey(void *av, void *bv)
102 {
103     struct key *a = (struct key *)av;
104     struct constkey *b = (struct constkey *)bv;
105
106     if (a->primary < b->primary)
107         return -1;
108     else if (a->primary > b->primary)
109         return +1;
110     switch (subkeytypes[a->primary]) {
111       case TYPE_INT:
112         if (a->secondary.i < b->secondary.i)
113             return -1;
114         else if (a->secondary.i > b->secondary.i)
115             return +1;
116         return 0;
117       case TYPE_STR:
118         return strcmp(a->secondary.s, b->secondary.s);
119       default:
120         return 0;
121     }
122 }
123
124 /*
125  * Free any dynamic data items pointed to by a 'struct key'. We
126  * don't free the structure itself, since it's probably part of a
127  * larger allocated block.
128  */
129 static void free_key(struct key *key)
130 {
131     if (subkeytypes[key->primary] == TYPE_STR)
132         sfree(key->secondary.s);
133 }
134
135 /*
136  * Copy a 'struct key' into another one, copying its dynamic data
137  * if necessary.
138  */
139 static void copy_key(struct key *to, struct key *from)
140 {
141     to->primary = from->primary;
142     switch (subkeytypes[to->primary]) {
143       case TYPE_INT:
144         to->secondary.i = from->secondary.i;
145         break;
146       case TYPE_STR:
147         to->secondary.s = dupstr(from->secondary.s);
148         break;
149     }
150 }
151
152 /*
153  * Free any dynamic data items pointed to by a 'struct value'. We
154  * don't free the value itself, since it's probably part of a larger
155  * allocated block.
156  */
157 static void free_value(struct value *val, int type)
158 {
159     if (type == TYPE_STR)
160         sfree(val->u.stringval);
161     else if (type == TYPE_FILENAME)
162         filename_free(val->u.fileval);
163     else if (type == TYPE_FONT)
164         fontspec_free(val->u.fontval);
165 }
166
167 /*
168  * Copy a 'struct value' into another one, copying its dynamic data
169  * if necessary.
170  */
171 static void copy_value(struct value *to, struct value *from, int type)
172 {
173     switch (type) {
174       case TYPE_INT:
175         to->u.intval = from->u.intval;
176         break;
177       case TYPE_STR:
178         to->u.stringval = dupstr(from->u.stringval);
179         break;
180       case TYPE_FILENAME:
181         to->u.fileval = filename_copy(from->u.fileval);
182         break;
183       case TYPE_FONT:
184         to->u.fontval = fontspec_copy(from->u.fontval);
185         break;
186     }
187 }
188
189 /*
190  * Free an entire 'struct conf_entry' and its dynamic data.
191  */
192 static void free_entry(struct conf_entry *entry)
193 {
194     free_key(&entry->key);
195     free_value(&entry->value, valuetypes[entry->key.primary]);
196     sfree(entry);
197 }
198
199 Conf *conf_new(void)
200 {
201     Conf *conf = snew(struct conf_tag);
202
203     conf->tree = newtree234(conf_cmp);
204
205     return conf;
206 }
207
208 static void conf_clear(Conf *conf)
209 {
210     struct conf_entry *entry;
211
212     while ((entry = delpos234(conf->tree, 0)) != NULL)
213         free_entry(entry);
214 }
215
216 void conf_free(Conf *conf)
217 {
218     conf_clear(conf);
219     freetree234(conf->tree);
220     sfree(conf);
221 }
222
223 static void conf_insert(Conf *conf, struct conf_entry *entry)
224 {
225     struct conf_entry *oldentry = add234(conf->tree, entry);
226     if (oldentry && oldentry != entry) {
227         del234(conf->tree, oldentry);
228         free_entry(oldentry);
229         oldentry = add234(conf->tree, entry);
230         assert(oldentry == entry);
231     }
232 }
233
234 void conf_copy_into(Conf *newconf, Conf *oldconf)
235 {
236     struct conf_entry *entry, *entry2;
237     int i;
238
239     conf_clear(newconf);
240
241     for (i = 0; (entry = index234(oldconf->tree, i)) != NULL; i++) {
242         entry2 = snew(struct conf_entry);
243         copy_key(&entry2->key, &entry->key);
244         copy_value(&entry2->value, &entry->value,
245                    valuetypes[entry->key.primary]);
246         add234(newconf->tree, entry2);
247     }
248 }
249
250 Conf *conf_copy(Conf *oldconf)
251 {
252     Conf *newconf = conf_new();
253
254     conf_copy_into(newconf, oldconf);
255
256     return newconf;
257 }
258
259 int conf_get_int(Conf *conf, int primary)
260 {
261     struct key key;
262     struct conf_entry *entry;
263
264     assert(subkeytypes[primary] == TYPE_NONE);
265     assert(valuetypes[primary] == TYPE_INT);
266     key.primary = primary;
267     entry = find234(conf->tree, &key, NULL);
268     assert(entry);
269     return entry->value.u.intval;
270 }
271
272 int conf_get_int_int(Conf *conf, int primary, int secondary)
273 {
274     struct key key;
275     struct conf_entry *entry;
276
277     assert(subkeytypes[primary] == TYPE_INT);
278     assert(valuetypes[primary] == TYPE_INT);
279     key.primary = primary;
280     key.secondary.i = secondary;
281     entry = find234(conf->tree, &key, NULL);
282     assert(entry);
283     return entry->value.u.intval;
284 }
285
286 char *conf_get_str(Conf *conf, int primary)
287 {
288     struct key key;
289     struct conf_entry *entry;
290
291     assert(subkeytypes[primary] == TYPE_NONE);
292     assert(valuetypes[primary] == TYPE_STR);
293     key.primary = primary;
294     entry = find234(conf->tree, &key, NULL);
295     assert(entry);
296     return entry->value.u.stringval;
297 }
298
299 char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary)
300 {
301     struct key key;
302     struct conf_entry *entry;
303
304     assert(subkeytypes[primary] == TYPE_STR);
305     assert(valuetypes[primary] == TYPE_STR);
306     key.primary = primary;
307     key.secondary.s = (char *)secondary;
308     entry = find234(conf->tree, &key, NULL);
309     return entry ? entry->value.u.stringval : NULL;
310 }
311
312 char *conf_get_str_str(Conf *conf, int primary, const char *secondary)
313 {
314     char *ret = conf_get_str_str_opt(conf, primary, secondary);
315     assert(ret);
316     return ret;
317 }
318
319 char *conf_get_str_strs(Conf *conf, int primary,
320                        char *subkeyin, char **subkeyout)
321 {
322     struct constkey key;
323     struct conf_entry *entry;
324
325     assert(subkeytypes[primary] == TYPE_STR);
326     assert(valuetypes[primary] == TYPE_STR);
327     key.primary = primary;
328     if (subkeyin) {
329         key.secondary.s = subkeyin;
330         entry = findrel234(conf->tree, &key, NULL, REL234_GT);
331     } else {
332         key.secondary.s = "";
333         entry = findrel234(conf->tree, &key, conf_cmp_constkey, REL234_GE);
334     }
335     if (!entry || entry->key.primary != primary)
336         return NULL;
337     *subkeyout = entry->key.secondary.s;
338     return entry->value.u.stringval;
339 }
340
341 char *conf_get_str_nthstrkey(Conf *conf, int primary, int n)
342 {
343     struct constkey key;
344     struct conf_entry *entry;
345     int index;
346
347     assert(subkeytypes[primary] == TYPE_STR);
348     assert(valuetypes[primary] == TYPE_STR);
349     key.primary = primary;
350     key.secondary.s = "";
351     entry = findrelpos234(conf->tree, &key, conf_cmp_constkey,
352                           REL234_GE, &index);
353     if (!entry || entry->key.primary != primary)
354         return NULL;
355     entry = index234(conf->tree, index + n);
356     if (!entry || entry->key.primary != primary)
357         return NULL;
358     return entry->key.secondary.s;
359 }
360
361 Filename *conf_get_filename(Conf *conf, int primary)
362 {
363     struct key key;
364     struct conf_entry *entry;
365
366     assert(subkeytypes[primary] == TYPE_NONE);
367     assert(valuetypes[primary] == TYPE_FILENAME);
368     key.primary = primary;
369     entry = find234(conf->tree, &key, NULL);
370     assert(entry);
371     return entry->value.u.fileval;
372 }
373
374 FontSpec *conf_get_fontspec(Conf *conf, int primary)
375 {
376     struct key key;
377     struct conf_entry *entry;
378
379     assert(subkeytypes[primary] == TYPE_NONE);
380     assert(valuetypes[primary] == TYPE_FONT);
381     key.primary = primary;
382     entry = find234(conf->tree, &key, NULL);
383     assert(entry);
384     return entry->value.u.fontval;
385 }
386
387 void conf_set_int(Conf *conf, int primary, int value)
388 {
389     struct conf_entry *entry = snew(struct conf_entry);
390
391     assert(subkeytypes[primary] == TYPE_NONE);
392     assert(valuetypes[primary] == TYPE_INT);
393     entry->key.primary = primary;
394     entry->value.u.intval = value; 
395     conf_insert(conf, entry);
396 }
397
398 void conf_set_int_int(Conf *conf, int primary, int secondary, int value)
399 {
400     struct conf_entry *entry = snew(struct conf_entry);
401
402     assert(subkeytypes[primary] == TYPE_INT);
403     assert(valuetypes[primary] == TYPE_INT);
404     entry->key.primary = primary;
405     entry->key.secondary.i = secondary;
406     entry->value.u.intval = value;
407     conf_insert(conf, entry);
408 }
409
410 void conf_set_str(Conf *conf, int primary, const char *value)
411 {
412     struct conf_entry *entry = snew(struct conf_entry);
413
414     assert(subkeytypes[primary] == TYPE_NONE);
415     assert(valuetypes[primary] == TYPE_STR);
416     entry->key.primary = primary;
417     entry->value.u.stringval = dupstr(value);
418     conf_insert(conf, entry);
419 }
420
421 void conf_set_str_str(Conf *conf, int primary, const char *secondary,
422                       const char *value)
423 {
424     struct conf_entry *entry = snew(struct conf_entry);
425
426     assert(subkeytypes[primary] == TYPE_STR);
427     assert(valuetypes[primary] == TYPE_STR);
428     entry->key.primary = primary;
429     entry->key.secondary.s = dupstr(secondary);
430     entry->value.u.stringval = dupstr(value);
431     conf_insert(conf, entry);
432 }
433
434 void conf_del_str_str(Conf *conf, int primary, const char *secondary)
435 {
436     struct key key;
437     struct conf_entry *entry;
438
439     assert(subkeytypes[primary] == TYPE_STR);
440     assert(valuetypes[primary] == TYPE_STR);
441     key.primary = primary;
442     key.secondary.s = (char *)secondary;
443     entry = find234(conf->tree, &key, NULL);
444     if (entry) {
445         del234(conf->tree, entry);
446         free_entry(entry);
447     }
448  }
449
450 void conf_set_filename(Conf *conf, int primary, const Filename *value)
451 {
452     struct conf_entry *entry = snew(struct conf_entry);
453
454     assert(subkeytypes[primary] == TYPE_NONE);
455     assert(valuetypes[primary] == TYPE_FILENAME);
456     entry->key.primary = primary;
457     entry->value.u.fileval = filename_copy(value);
458     conf_insert(conf, entry);
459 }
460
461 void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value)
462 {
463     struct conf_entry *entry = snew(struct conf_entry);
464
465     assert(subkeytypes[primary] == TYPE_NONE);
466     assert(valuetypes[primary] == TYPE_FONT);
467     entry->key.primary = primary;
468     entry->value.u.fontval = fontspec_copy(value);
469     conf_insert(conf, entry);
470 }
471
472 int conf_serialised_size(Conf *conf)
473 {
474     int i;
475     struct conf_entry *entry;
476     int size = 0;
477
478     for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
479         size += 4;   /* primary key */
480         switch (subkeytypes[entry->key.primary]) {
481           case TYPE_INT:
482             size += 4;
483             break;
484           case TYPE_STR:
485             size += 1 + strlen(entry->key.secondary.s);
486             break;
487         }
488         switch (valuetypes[entry->key.primary]) {
489           case TYPE_INT:
490             size += 4;
491             break;
492           case TYPE_STR:
493             size += 1 + strlen(entry->value.u.stringval);
494             break;
495           case TYPE_FILENAME:
496             size += filename_serialise(entry->value.u.fileval, NULL);
497             break;
498           case TYPE_FONT:
499             size += fontspec_serialise(entry->value.u.fontval, NULL);
500             break;
501         }
502     }
503
504     size += 4;                         /* terminator value */
505
506     return size;
507 }
508
509 void conf_serialise(Conf *conf, void *vdata)
510 {
511     unsigned char *data = (unsigned char *)vdata;
512     int i, len;
513     struct conf_entry *entry;
514
515     for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
516         PUT_32BIT_MSB_FIRST(data, entry->key.primary);
517         data += 4;
518
519         switch (subkeytypes[entry->key.primary]) {
520           case TYPE_INT:
521             PUT_32BIT_MSB_FIRST(data, entry->key.secondary.i);
522             data += 4;
523             break;
524           case TYPE_STR:
525             len = strlen(entry->key.secondary.s);
526             memcpy(data, entry->key.secondary.s, len);
527             data += len;
528             *data++ = 0;
529             break;
530         }
531         switch (valuetypes[entry->key.primary]) {
532           case TYPE_INT:
533             PUT_32BIT_MSB_FIRST(data, entry->value.u.intval);
534             data += 4;
535             break;
536           case TYPE_STR:
537             len = strlen(entry->value.u.stringval);
538             memcpy(data, entry->value.u.stringval, len);
539             data += len;
540             *data++ = 0;
541             break;
542           case TYPE_FILENAME:
543             data += filename_serialise(entry->value.u.fileval, data);
544             break;
545           case TYPE_FONT:
546             data += fontspec_serialise(entry->value.u.fontval, data);
547             break;
548         }
549     }
550
551     PUT_32BIT_MSB_FIRST(data, 0xFFFFFFFFU);
552 }
553
554 int conf_deserialise(Conf *conf, void *vdata, int maxsize)
555 {
556     unsigned char *data = (unsigned char *)vdata;
557     unsigned char *start = data;
558     struct conf_entry *entry;
559     unsigned primary;
560     int used;
561     unsigned char *zero;
562
563     while (maxsize >= 4) {
564         primary = GET_32BIT_MSB_FIRST(data);
565         data += 4, maxsize -= 4;
566
567         if (primary >= N_CONFIG_OPTIONS)
568             break;
569
570         entry = snew(struct conf_entry);
571         entry->key.primary = primary;
572
573         switch (subkeytypes[entry->key.primary]) {
574           case TYPE_INT:
575             if (maxsize < 4) {
576                 sfree(entry);
577                 goto done;
578             }
579             entry->key.secondary.i = toint(GET_32BIT_MSB_FIRST(data));
580             data += 4, maxsize -= 4;
581             break;
582           case TYPE_STR:
583             zero = memchr(data, 0, maxsize);
584             if (!zero) {
585                 sfree(entry);
586                 goto done;
587             }
588             entry->key.secondary.s = dupstr((char *)data);
589             maxsize -= (zero + 1 - data);
590             data = zero + 1;
591             break;
592         }
593
594         switch (valuetypes[entry->key.primary]) {
595           case TYPE_INT:
596             if (maxsize < 4) {
597                 if (subkeytypes[entry->key.primary] == TYPE_STR)
598                     sfree(entry->key.secondary.s);
599                 sfree(entry);
600                 goto done;
601             }
602             entry->value.u.intval = toint(GET_32BIT_MSB_FIRST(data));
603             data += 4, maxsize -= 4;
604             break;
605           case TYPE_STR:
606             zero = memchr(data, 0, maxsize);
607             if (!zero) {
608                 if (subkeytypes[entry->key.primary] == TYPE_STR)
609                     sfree(entry->key.secondary.s);
610                 sfree(entry);
611                 goto done;
612             }
613             entry->value.u.stringval = dupstr((char *)data);
614             maxsize -= (zero + 1 - data);
615             data = zero + 1;
616             break;
617           case TYPE_FILENAME:
618             entry->value.u.fileval =
619                 filename_deserialise(data, maxsize, &used);
620             if (!entry->value.u.fileval) {
621                 if (subkeytypes[entry->key.primary] == TYPE_STR)
622                     sfree(entry->key.secondary.s);
623                 sfree(entry);
624                 goto done;
625             }
626             data += used;
627             maxsize -= used;
628             break;
629           case TYPE_FONT:
630             entry->value.u.fontval =
631                 fontspec_deserialise(data, maxsize, &used);
632             if (!entry->value.u.fontval) {
633                 if (subkeytypes[entry->key.primary] == TYPE_STR)
634                     sfree(entry->key.secondary.s);
635                 sfree(entry);
636                 goto done;
637             }
638             data += used;
639             maxsize -= used;
640             break;
641         }
642         conf_insert(conf, entry);
643     }
644
645     done:
646     return (int)(data - start);
647 }