]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - conf.c
Change the semantics of 'FontSpec' so that it's a dynamically
[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 struct value {
43     union {
44         int intval;
45         char *stringval;
46         Filename fileval;
47         FontSpec *fontval;
48     } u;
49 };
50
51 struct conf_entry {
52     struct key key;
53     struct value value;
54 };
55
56 struct conf_tag {
57     tree234 *tree;
58 };
59
60 /*
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).
67  */
68 static int conf_cmp(void *av, void *bv)
69 {
70     struct key *a = (struct key *)av;
71     struct key *b = (struct key *)bv;
72
73     if (a->primary < b->primary)
74         return -1;
75     else if (a->primary > b->primary)
76         return +1;
77     switch (subkeytypes[a->primary]) {
78       case TYPE_INT:
79         if (a->secondary.i < b->secondary.i)
80             return -1;
81         else if (a->secondary.i > b->secondary.i)
82             return +1;
83         return 0;
84       case TYPE_STR:
85         return strcmp(a->secondary.s, b->secondary.s);
86       default:
87         return 0;
88     }
89 }
90
91 /*
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.
95  */
96 static void free_key(struct key *key)
97 {
98     if (subkeytypes[key->primary] == TYPE_STR)
99         sfree(key->secondary.s);
100 }
101
102 /*
103  * Copy a 'struct key' into another one, copying its dynamic data
104  * if necessary.
105  */
106 static void copy_key(struct key *to, struct key *from)
107 {
108     to->primary = from->primary;
109     switch (subkeytypes[to->primary]) {
110       case TYPE_INT:
111         to->secondary.i = from->secondary.i;
112         break;
113       case TYPE_STR:
114         to->secondary.s = dupstr(from->secondary.s);
115         break;
116     }
117 }
118
119 /*
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
122  * allocated block.
123  */
124 static void free_value(struct value *val, int type)
125 {
126     if (type == TYPE_STR)
127         sfree(val->u.stringval);
128     else if (type == TYPE_FONT)
129         fontspec_free(val->u.fontval);
130 }
131
132 /*
133  * Copy a 'struct value' into another one, copying its dynamic data
134  * if necessary.
135  */
136 static void copy_value(struct value *to, struct value *from, int type)
137 {
138     switch (type) {
139       case TYPE_INT:
140         to->u.intval = from->u.intval;
141         break;
142       case TYPE_STR:
143         to->u.stringval = dupstr(from->u.stringval);
144         break;
145       case TYPE_FILENAME:
146         to->u.fileval = from->u.fileval;
147         break;
148       case TYPE_FONT:
149         to->u.fontval = fontspec_copy(from->u.fontval);
150         break;
151     }
152 }
153
154 /*
155  * Free an entire 'struct conf_entry' and its dynamic data.
156  */
157 static void free_entry(struct conf_entry *entry)
158 {
159     free_key(&entry->key);
160     free_value(&entry->value, valuetypes[entry->key.primary]);
161     sfree(entry);
162 }
163
164 Conf *conf_new(void)
165 {
166     Conf *conf = snew(struct conf_tag);
167
168     conf->tree = newtree234(conf_cmp);
169
170     return conf;
171 }
172
173 static void conf_clear(Conf *conf)
174 {
175     struct conf_entry *entry;
176
177     while ((entry = delpos234(conf->tree, 0)) != NULL)
178         free_entry(entry);
179 }
180
181 void conf_free(Conf *conf)
182 {
183     conf_clear(conf);
184     freetree234(conf->tree);
185     sfree(conf);
186 }
187
188 static void conf_insert(Conf *conf, struct conf_entry *entry)
189 {
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);
196     }
197 }
198
199 void conf_copy_into(Conf *newconf, Conf *oldconf)
200 {
201     struct conf_entry *entry, *entry2;
202     int i;
203
204     conf_clear(newconf);
205
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);
212     }
213 }
214
215 Conf *conf_copy(Conf *oldconf)
216 {
217     Conf *newconf = conf_new();
218
219     conf_copy_into(newconf, oldconf);
220
221     return newconf;
222 }
223
224 int conf_get_int(Conf *conf, int primary)
225 {
226     struct key key;
227     struct conf_entry *entry;
228
229     assert(subkeytypes[primary] == TYPE_NONE);
230     assert(valuetypes[primary] == TYPE_INT);
231     key.primary = primary;
232     entry = find234(conf->tree, &key, NULL);
233     assert(entry);
234     return entry->value.u.intval;
235 }
236
237 int conf_get_int_int(Conf *conf, int primary, int secondary)
238 {
239     struct key key;
240     struct conf_entry *entry;
241
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);
247     assert(entry);
248     return entry->value.u.intval;
249 }
250
251 char *conf_get_str(Conf *conf, int primary)
252 {
253     struct key key;
254     struct conf_entry *entry;
255
256     assert(subkeytypes[primary] == TYPE_NONE);
257     assert(valuetypes[primary] == TYPE_STR);
258     key.primary = primary;
259     entry = find234(conf->tree, &key, NULL);
260     assert(entry);
261     return entry->value.u.stringval;
262 }
263
264 char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary)
265 {
266     struct key key;
267     struct conf_entry *entry;
268
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;
275 }
276
277 char *conf_get_str_str(Conf *conf, int primary, const char *secondary)
278 {
279     char *ret = conf_get_str_str_opt(conf, primary, secondary);
280     assert(ret);
281     return ret;
282 }
283
284 char *conf_get_str_strs(Conf *conf, int primary,
285                        char *subkeyin, char **subkeyout)
286 {
287     struct key key;
288     struct conf_entry *entry;
289
290     assert(subkeytypes[primary] == TYPE_STR);
291     assert(valuetypes[primary] == TYPE_STR);
292     key.primary = primary;
293     if (subkeyin) {
294         key.secondary.s = subkeyin;
295         entry = findrel234(conf->tree, &key, NULL, REL234_GT);
296     } else {
297         key.secondary.s = "";
298         entry = findrel234(conf->tree, &key, NULL, REL234_GE);
299     }
300     if (!entry || entry->key.primary != primary)
301         return NULL;
302     *subkeyout = entry->key.secondary.s;
303     return entry->value.u.stringval;
304 }
305
306 char *conf_get_str_nthstrkey(Conf *conf, int primary, int n)
307 {
308     struct key key;
309     struct conf_entry *entry;
310     int index;
311
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)
318         return NULL;
319     entry = index234(conf->tree, index + n);
320     if (!entry || entry->key.primary != primary)
321         return NULL;
322     return entry->key.secondary.s;
323 }
324
325 Filename *conf_get_filename(Conf *conf, int primary)
326 {
327     struct key key;
328     struct conf_entry *entry;
329
330     assert(subkeytypes[primary] == TYPE_NONE);
331     assert(valuetypes[primary] == TYPE_FILENAME);
332     key.primary = primary;
333     entry = find234(conf->tree, &key, NULL);
334     assert(entry);
335     return &entry->value.u.fileval;
336 }
337
338 FontSpec *conf_get_fontspec(Conf *conf, int primary)
339 {
340     struct key key;
341     struct conf_entry *entry;
342
343     assert(subkeytypes[primary] == TYPE_NONE);
344     assert(valuetypes[primary] == TYPE_FONT);
345     key.primary = primary;
346     entry = find234(conf->tree, &key, NULL);
347     assert(entry);
348     return entry->value.u.fontval;
349 }
350
351 void conf_set_int(Conf *conf, int primary, int value)
352 {
353     struct conf_entry *entry = snew(struct conf_entry);
354
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);
360 }
361
362 void conf_set_int_int(Conf *conf, int primary, int secondary, int value)
363 {
364     struct conf_entry *entry = snew(struct conf_entry);
365
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);
372 }
373
374 void conf_set_str(Conf *conf, int primary, const char *value)
375 {
376     struct conf_entry *entry = snew(struct conf_entry);
377
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);
383 }
384
385 void conf_set_str_str(Conf *conf, int primary, const char *secondary,
386                       const char *value)
387 {
388     struct conf_entry *entry = snew(struct conf_entry);
389
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);
396 }
397
398 void conf_del_str_str(Conf *conf, int primary, const char *secondary)
399 {
400     struct key key;
401     struct conf_entry *entry;
402
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);
408     if (entry) {
409         del234(conf->tree, entry);
410         free_entry(entry);
411     }
412  }
413
414 void conf_set_filename(Conf *conf, int primary, const Filename *value)
415 {
416     struct conf_entry *entry = snew(struct conf_entry);
417
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);
423 }
424
425 void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value)
426 {
427     struct conf_entry *entry = snew(struct conf_entry);
428
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);
434 }
435
436 int conf_serialised_size(Conf *conf)
437 {
438     int i;
439     struct conf_entry *entry;
440     int size = 0;
441
442     for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
443         size += 4;   /* primary key */
444         switch (subkeytypes[entry->key.primary]) {
445           case TYPE_INT:
446             size += 4;
447             break;
448           case TYPE_STR:
449             size += 1 + strlen(entry->key.secondary.s);
450             break;
451         }
452         switch (valuetypes[entry->key.primary]) {
453           case TYPE_INT:
454             size += 4;
455             break;
456           case TYPE_STR:
457             size += 1 + strlen(entry->value.u.stringval);
458             break;
459           case TYPE_FILENAME:
460             size += sizeof(entry->value.u.fileval);
461             break;
462           case TYPE_FONT:
463             size += fontspec_serialise(entry->value.u.fontval, NULL);
464             break;
465         }
466     }
467
468     size += 4;                         /* terminator value */
469
470     return size;
471 }
472
473 void conf_serialise(Conf *conf, void *vdata)
474 {
475     unsigned char *data = (unsigned char *)vdata;
476     int i, len;
477     struct conf_entry *entry;
478
479     for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
480         PUT_32BIT_MSB_FIRST(data, entry->key.primary);
481         data += 4;
482
483         switch (subkeytypes[entry->key.primary]) {
484           case TYPE_INT:
485             PUT_32BIT_MSB_FIRST(data, entry->key.secondary.i);
486             data += 4;
487             break;
488           case TYPE_STR:
489             len = strlen(entry->key.secondary.s);
490             memcpy(data, entry->key.secondary.s, len);
491             data += len;
492             *data++ = 0;
493             break;
494         }
495         switch (valuetypes[entry->key.primary]) {
496           case TYPE_INT:
497             PUT_32BIT_MSB_FIRST(data, entry->value.u.intval);
498             data += 4;
499             break;
500           case TYPE_STR:
501             len = strlen(entry->value.u.stringval);
502             memcpy(data, entry->value.u.stringval, len);
503             data += len;
504             *data++ = 0;
505             break;
506           case TYPE_FILENAME:
507             memcpy(data, &entry->value.u.fileval,
508                    sizeof(entry->value.u.fileval));
509             data += sizeof(entry->value.u.fileval);
510             break;
511           case TYPE_FONT:
512             data += fontspec_serialise(entry->value.u.fontval, data);
513             break;
514         }
515     }
516
517     PUT_32BIT_MSB_FIRST(data, 0xFFFFFFFFU);
518 }
519
520 int conf_deserialise(Conf *conf, void *vdata, int maxsize)
521 {
522     unsigned char *data = (unsigned char *)vdata;
523     unsigned char *start = data;
524     struct conf_entry *entry;
525     int primary, used;
526     unsigned char *zero;
527
528     while (maxsize >= 4) {
529         primary = GET_32BIT_MSB_FIRST(data);
530         data += 4, maxsize -= 4;
531
532         if ((unsigned)primary >= N_CONFIG_OPTIONS)
533             break;
534
535         entry = snew(struct conf_entry);
536         entry->key.primary = primary;
537
538         switch (subkeytypes[entry->key.primary]) {
539           case TYPE_INT:
540             if (maxsize < 4) {
541                 sfree(entry);
542                 goto done;
543             }
544             entry->key.secondary.i = GET_32BIT_MSB_FIRST(data);
545             data += 4, maxsize -= 4;
546             break;
547           case TYPE_STR:
548             zero = memchr(data, 0, maxsize);
549             if (!zero) {
550                 sfree(entry);
551                 goto done;
552             }
553             entry->key.secondary.s = dupstr((char *)data);
554             maxsize -= (zero + 1 - data);
555             data = zero + 1;
556             break;
557         }
558
559         switch (valuetypes[entry->key.primary]) {
560           case TYPE_INT:
561             if (maxsize < 4) {
562                 if (subkeytypes[entry->key.primary] == TYPE_STR)
563                     sfree(entry->key.secondary.s);
564                 sfree(entry);
565                 goto done;
566             }
567             entry->value.u.intval = GET_32BIT_MSB_FIRST(data);
568             data += 4, maxsize -= 4;
569             break;
570           case TYPE_STR:
571             zero = memchr(data, 0, maxsize);
572             if (!zero) {
573                 if (subkeytypes[entry->key.primary] == TYPE_STR)
574                     sfree(entry->key.secondary.s);
575                 sfree(entry);
576                 goto done;
577             }
578             entry->value.u.stringval = dupstr((char *)data);
579             maxsize -= (zero + 1 - data);
580             data = zero + 1;
581             break;
582           case TYPE_FILENAME:
583             if (maxsize < sizeof(entry->value.u.fileval)) {
584                 if (subkeytypes[entry->key.primary] == TYPE_STR)
585                     sfree(entry->key.secondary.s);
586                 sfree(entry);
587                 goto done;
588             }
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);
593             break;
594           case TYPE_FONT:
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);
600                 sfree(entry);
601                 goto done;
602             }
603             data += used;
604             maxsize -= used;
605             break;
606         }
607         conf_insert(conf, entry);
608     }
609
610     done:
611     return (int)(data - start);
612 }