]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - conf.c
Post-release destabilisation! Completely remove the struct type
[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 }
129
130 /*
131  * Copy a 'struct value' into another one, copying its dynamic data
132  * if necessary.
133  */
134 static void copy_value(struct value *to, struct value *from, int type)
135 {
136     switch (type) {
137       case TYPE_INT:
138         to->u.intval = from->u.intval;
139         break;
140       case TYPE_STR:
141         to->u.stringval = dupstr(from->u.stringval);
142         break;
143       case TYPE_FILENAME:
144         to->u.fileval = from->u.fileval;
145         break;
146       case TYPE_FONT:
147         to->u.fontval = from->u.fontval;
148         break;
149     }
150 }
151
152 /*
153  * Free an entire 'struct conf_entry' and its dynamic data.
154  */
155 static void free_entry(struct conf_entry *entry)
156 {
157     free_key(&entry->key);
158     free_value(&entry->value, valuetypes[entry->key.primary]);
159     sfree(entry);
160 }
161
162 Conf *conf_new(void)
163 {
164     Conf *conf = snew(struct conf_tag);
165
166     conf->tree = newtree234(conf_cmp);
167
168     return conf;
169 }
170
171 static void conf_clear(Conf *conf)
172 {
173     struct conf_entry *entry;
174
175     while ((entry = delpos234(conf->tree, 0)) != NULL)
176         free_entry(entry);
177 }
178
179 void conf_free(Conf *conf)
180 {
181     conf_clear(conf);
182     freetree234(conf->tree);
183     sfree(conf);
184 }
185
186 static void conf_insert(Conf *conf, struct conf_entry *entry)
187 {
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);
194     }
195 }
196
197 void conf_copy_into(Conf *newconf, Conf *oldconf)
198 {
199     struct conf_entry *entry, *entry2;
200     int i;
201
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);
208     }
209 }
210
211 Conf *conf_copy(Conf *oldconf)
212 {
213     Conf *newconf = conf_new();
214
215     conf_copy_into(newconf, oldconf);
216
217     return newconf;
218 }
219
220 int conf_get_int(Conf *conf, int primary)
221 {
222     struct key key;
223     struct conf_entry *entry;
224
225     assert(subkeytypes[primary] == TYPE_NONE);
226     assert(valuetypes[primary] == TYPE_INT);
227     key.primary = primary;
228     entry = find234(conf->tree, &key, NULL);
229     assert(entry);
230     return entry->value.u.intval;
231 }
232
233 int conf_get_int_int(Conf *conf, int primary, int secondary)
234 {
235     struct key key;
236     struct conf_entry *entry;
237
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);
243     assert(entry);
244     return entry->value.u.intval;
245 }
246
247 char *conf_get_str(Conf *conf, int primary)
248 {
249     struct key key;
250     struct conf_entry *entry;
251
252     assert(subkeytypes[primary] == TYPE_NONE);
253     assert(valuetypes[primary] == TYPE_STR);
254     key.primary = primary;
255     entry = find234(conf->tree, &key, NULL);
256     assert(entry);
257     return entry->value.u.stringval;
258 }
259
260 char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary)
261 {
262     struct key key;
263     struct conf_entry *entry;
264
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;
271 }
272
273 char *conf_get_str_str(Conf *conf, int primary, const char *secondary)
274 {
275     char *ret = conf_get_str_str_opt(conf, primary, secondary);
276     assert(ret);
277     return ret;
278 }
279
280 char *conf_get_str_strs(Conf *conf, int primary,
281                        char *subkeyin, char **subkeyout)
282 {
283     struct key key;
284     struct conf_entry *entry;
285
286     assert(subkeytypes[primary] == TYPE_STR);
287     assert(valuetypes[primary] == TYPE_STR);
288     key.primary = primary;
289     if (subkeyin) {
290         key.secondary.s = subkeyin;
291         entry = findrel234(conf->tree, &key, NULL, REL234_GT);
292     } else {
293         key.secondary.s = "";
294         entry = findrel234(conf->tree, &key, NULL, REL234_GE);
295     }
296     if (!entry || entry->key.primary != primary)
297         return NULL;
298     *subkeyout = entry->key.secondary.s;
299     return entry->value.u.stringval;
300 }
301
302 char *conf_get_str_nthstrkey(Conf *conf, int primary, int n)
303 {
304     struct key key;
305     struct conf_entry *entry;
306     int index;
307
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)
314         return NULL;
315     entry = index234(conf->tree, index + n);
316     if (!entry || entry->key.primary != primary)
317         return NULL;
318     return entry->key.secondary.s;
319 }
320
321 Filename *conf_get_filename(Conf *conf, int primary)
322 {
323     struct key key;
324     struct conf_entry *entry;
325
326     assert(subkeytypes[primary] == TYPE_NONE);
327     assert(valuetypes[primary] == TYPE_FILENAME);
328     key.primary = primary;
329     entry = find234(conf->tree, &key, NULL);
330     assert(entry);
331     return &entry->value.u.fileval;
332 }
333
334 FontSpec *conf_get_fontspec(Conf *conf, int primary)
335 {
336     struct key key;
337     struct conf_entry *entry;
338
339     assert(subkeytypes[primary] == TYPE_NONE);
340     assert(valuetypes[primary] == TYPE_FONT);
341     key.primary = primary;
342     entry = find234(conf->tree, &key, NULL);
343     assert(entry);
344     return &entry->value.u.fontval;
345 }
346
347 void conf_set_int(Conf *conf, int primary, int value)
348 {
349     struct conf_entry *entry = snew(struct conf_entry);
350
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);
356 }
357
358 void conf_set_int_int(Conf *conf, int primary, int secondary, int value)
359 {
360     struct conf_entry *entry = snew(struct conf_entry);
361
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);
368 }
369
370 void conf_set_str(Conf *conf, int primary, const char *value)
371 {
372     struct conf_entry *entry = snew(struct conf_entry);
373
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);
379 }
380
381 void conf_set_str_str(Conf *conf, int primary, const char *secondary,
382                       const char *value)
383 {
384     struct conf_entry *entry = snew(struct conf_entry);
385
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);
392 }
393
394 void conf_del_str_str(Conf *conf, int primary, const char *secondary)
395 {
396     struct key key;
397     struct conf_entry *entry;
398
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);
404     if (entry) {
405         del234(conf->tree, entry);
406         free_entry(entry);
407     }
408  }
409
410 void conf_set_filename(Conf *conf, int primary, const Filename *value)
411 {
412     struct conf_entry *entry = snew(struct conf_entry);
413
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);
419 }
420
421 void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value)
422 {
423     struct conf_entry *entry = snew(struct conf_entry);
424
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);
430 }
431
432 int conf_serialised_size(Conf *conf)
433 {
434     int i;
435     struct conf_entry *entry;
436     int size = 0;
437
438     for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
439         size += 4;   /* primary key */
440         switch (subkeytypes[entry->key.primary]) {
441           case TYPE_INT:
442             size += 4;
443             break;
444           case TYPE_STR:
445             size += 1 + strlen(entry->key.secondary.s);
446             break;
447         }
448         switch (valuetypes[entry->key.primary]) {
449           case TYPE_INT:
450             size += 4;
451             break;
452           case TYPE_STR:
453             size += 1 + strlen(entry->value.u.stringval);
454             break;
455           case TYPE_FILENAME:
456             size += sizeof(entry->value.u.fileval);
457             break;
458           case TYPE_FONT:
459             size += sizeof(entry->value.u.fontval);
460             break;
461         }
462     }
463
464     size += 4;                         /* terminator value */
465
466     return size;
467 }
468
469 void conf_serialise(Conf *conf, void *vdata)
470 {
471     unsigned char *data = (unsigned char *)vdata;
472     int i, len;
473     struct conf_entry *entry;
474
475     for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
476         PUT_32BIT_MSB_FIRST(data, entry->key.primary);
477         data += 4;
478
479         switch (subkeytypes[entry->key.primary]) {
480           case TYPE_INT:
481             PUT_32BIT_MSB_FIRST(data, entry->key.secondary.i);
482             data += 4;
483             break;
484           case TYPE_STR:
485             len = strlen(entry->key.secondary.s);
486             memcpy(data, entry->key.secondary.s, len);
487             data += len;
488             *data++ = 0;
489             break;
490         }
491         switch (valuetypes[entry->key.primary]) {
492           case TYPE_INT:
493             PUT_32BIT_MSB_FIRST(data, entry->value.u.intval);
494             data += 4;
495             break;
496           case TYPE_STR:
497             len = strlen(entry->value.u.stringval);
498             memcpy(data, entry->value.u.stringval, len);
499             data += len;
500             *data++ = 0;
501             break;
502           case TYPE_FILENAME:
503             memcpy(data, &entry->value.u.fileval,
504                    sizeof(entry->value.u.fileval));
505             data += sizeof(entry->value.u.fileval);
506             break;
507           case TYPE_FONT:
508             memcpy(data, &entry->value.u.fontval,
509                    sizeof(entry->value.u.fontval));
510             data += sizeof(entry->value.u.fontval);
511             break;
512         }
513     }
514
515     PUT_32BIT_MSB_FIRST(data, 0xFFFFFFFFU);
516 }
517
518 int conf_deserialise(Conf *conf, void *vdata, int maxsize)
519 {
520     unsigned char *data = (unsigned char *)vdata;
521     unsigned char *start = data;
522     struct conf_entry *entry;
523     int primary;
524     unsigned char *zero;
525
526     while (maxsize >= 4) {
527         primary = GET_32BIT_MSB_FIRST(data);
528         data += 4, maxsize -= 4;
529
530         if ((unsigned)primary >= N_CONFIG_OPTIONS)
531             break;
532
533         entry = snew(struct conf_entry);
534         entry->key.primary = primary;
535
536         switch (subkeytypes[entry->key.primary]) {
537           case TYPE_INT:
538             if (maxsize < 4) {
539                 sfree(entry);
540                 goto done;
541             }
542             entry->key.secondary.i = GET_32BIT_MSB_FIRST(data);
543             data += 4, maxsize -= 4;
544             break;
545           case TYPE_STR:
546             zero = memchr(data, 0, maxsize);
547             if (!zero) {
548                 sfree(entry);
549                 goto done;
550             }
551             entry->key.secondary.s = dupstr((char *)data);
552             maxsize -= (zero + 1 - data);
553             data = zero + 1;
554             break;
555         }
556
557         switch (valuetypes[entry->key.primary]) {
558           case TYPE_INT:
559             if (maxsize < 4) {
560                 if (subkeytypes[entry->key.primary] == TYPE_STR)
561                     sfree(entry->key.secondary.s);
562                 sfree(entry);
563                 goto done;
564             }
565             entry->value.u.intval = GET_32BIT_MSB_FIRST(data);
566             data += 4, maxsize -= 4;
567             break;
568           case TYPE_STR:
569             zero = memchr(data, 0, maxsize);
570             if (!zero) {
571                 if (subkeytypes[entry->key.primary] == TYPE_STR)
572                     sfree(entry->key.secondary.s);
573                 sfree(entry);
574                 goto done;
575             }
576             entry->value.u.stringval = dupstr((char *)data);
577             maxsize -= (zero + 1 - data);
578             data = zero + 1;
579             break;
580           case TYPE_FILENAME:
581             if (maxsize < sizeof(entry->value.u.fileval)) {
582                 if (subkeytypes[entry->key.primary] == TYPE_STR)
583                     sfree(entry->key.secondary.s);
584                 sfree(entry);
585                 goto done;
586             }
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);
591             break;
592           case TYPE_FONT:
593             if (maxsize < sizeof(entry->value.u.fontval)) {
594                 if (subkeytypes[entry->key.primary] == TYPE_STR)
595                     sfree(entry->key.secondary.s);
596                 sfree(entry);
597                 goto done;
598             }
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);
603             break;
604         }
605         conf_insert(conf, entry);
606     }
607
608     done:
609     return (int)(data - start);
610 }