]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - conf.c
conf_copy_into must empty the entire target conf before filling it
[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     conf_clear(newconf);
203
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);
210     }
211 }
212
213 Conf *conf_copy(Conf *oldconf)
214 {
215     Conf *newconf = conf_new();
216
217     conf_copy_into(newconf, oldconf);
218
219     return newconf;
220 }
221
222 int conf_get_int(Conf *conf, int primary)
223 {
224     struct key key;
225     struct conf_entry *entry;
226
227     assert(subkeytypes[primary] == TYPE_NONE);
228     assert(valuetypes[primary] == TYPE_INT);
229     key.primary = primary;
230     entry = find234(conf->tree, &key, NULL);
231     assert(entry);
232     return entry->value.u.intval;
233 }
234
235 int conf_get_int_int(Conf *conf, int primary, int secondary)
236 {
237     struct key key;
238     struct conf_entry *entry;
239
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);
245     assert(entry);
246     return entry->value.u.intval;
247 }
248
249 char *conf_get_str(Conf *conf, int primary)
250 {
251     struct key key;
252     struct conf_entry *entry;
253
254     assert(subkeytypes[primary] == TYPE_NONE);
255     assert(valuetypes[primary] == TYPE_STR);
256     key.primary = primary;
257     entry = find234(conf->tree, &key, NULL);
258     assert(entry);
259     return entry->value.u.stringval;
260 }
261
262 char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary)
263 {
264     struct key key;
265     struct conf_entry *entry;
266
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;
273 }
274
275 char *conf_get_str_str(Conf *conf, int primary, const char *secondary)
276 {
277     char *ret = conf_get_str_str_opt(conf, primary, secondary);
278     assert(ret);
279     return ret;
280 }
281
282 char *conf_get_str_strs(Conf *conf, int primary,
283                        char *subkeyin, char **subkeyout)
284 {
285     struct key key;
286     struct conf_entry *entry;
287
288     assert(subkeytypes[primary] == TYPE_STR);
289     assert(valuetypes[primary] == TYPE_STR);
290     key.primary = primary;
291     if (subkeyin) {
292         key.secondary.s = subkeyin;
293         entry = findrel234(conf->tree, &key, NULL, REL234_GT);
294     } else {
295         key.secondary.s = "";
296         entry = findrel234(conf->tree, &key, NULL, REL234_GE);
297     }
298     if (!entry || entry->key.primary != primary)
299         return NULL;
300     *subkeyout = entry->key.secondary.s;
301     return entry->value.u.stringval;
302 }
303
304 char *conf_get_str_nthstrkey(Conf *conf, int primary, int n)
305 {
306     struct key key;
307     struct conf_entry *entry;
308     int index;
309
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)
316         return NULL;
317     entry = index234(conf->tree, index + n);
318     if (!entry || entry->key.primary != primary)
319         return NULL;
320     return entry->key.secondary.s;
321 }
322
323 Filename *conf_get_filename(Conf *conf, int primary)
324 {
325     struct key key;
326     struct conf_entry *entry;
327
328     assert(subkeytypes[primary] == TYPE_NONE);
329     assert(valuetypes[primary] == TYPE_FILENAME);
330     key.primary = primary;
331     entry = find234(conf->tree, &key, NULL);
332     assert(entry);
333     return &entry->value.u.fileval;
334 }
335
336 FontSpec *conf_get_fontspec(Conf *conf, int primary)
337 {
338     struct key key;
339     struct conf_entry *entry;
340
341     assert(subkeytypes[primary] == TYPE_NONE);
342     assert(valuetypes[primary] == TYPE_FONT);
343     key.primary = primary;
344     entry = find234(conf->tree, &key, NULL);
345     assert(entry);
346     return &entry->value.u.fontval;
347 }
348
349 void conf_set_int(Conf *conf, int primary, int value)
350 {
351     struct conf_entry *entry = snew(struct conf_entry);
352
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);
358 }
359
360 void conf_set_int_int(Conf *conf, int primary, int secondary, int value)
361 {
362     struct conf_entry *entry = snew(struct conf_entry);
363
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);
370 }
371
372 void conf_set_str(Conf *conf, int primary, const char *value)
373 {
374     struct conf_entry *entry = snew(struct conf_entry);
375
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);
381 }
382
383 void conf_set_str_str(Conf *conf, int primary, const char *secondary,
384                       const char *value)
385 {
386     struct conf_entry *entry = snew(struct conf_entry);
387
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);
394 }
395
396 void conf_del_str_str(Conf *conf, int primary, const char *secondary)
397 {
398     struct key key;
399     struct conf_entry *entry;
400
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);
406     if (entry) {
407         del234(conf->tree, entry);
408         free_entry(entry);
409     }
410  }
411
412 void conf_set_filename(Conf *conf, int primary, const Filename *value)
413 {
414     struct conf_entry *entry = snew(struct conf_entry);
415
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);
421 }
422
423 void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value)
424 {
425     struct conf_entry *entry = snew(struct conf_entry);
426
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);
432 }
433
434 int conf_serialised_size(Conf *conf)
435 {
436     int i;
437     struct conf_entry *entry;
438     int size = 0;
439
440     for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
441         size += 4;   /* primary key */
442         switch (subkeytypes[entry->key.primary]) {
443           case TYPE_INT:
444             size += 4;
445             break;
446           case TYPE_STR:
447             size += 1 + strlen(entry->key.secondary.s);
448             break;
449         }
450         switch (valuetypes[entry->key.primary]) {
451           case TYPE_INT:
452             size += 4;
453             break;
454           case TYPE_STR:
455             size += 1 + strlen(entry->value.u.stringval);
456             break;
457           case TYPE_FILENAME:
458             size += sizeof(entry->value.u.fileval);
459             break;
460           case TYPE_FONT:
461             size += sizeof(entry->value.u.fontval);
462             break;
463         }
464     }
465
466     size += 4;                         /* terminator value */
467
468     return size;
469 }
470
471 void conf_serialise(Conf *conf, void *vdata)
472 {
473     unsigned char *data = (unsigned char *)vdata;
474     int i, len;
475     struct conf_entry *entry;
476
477     for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
478         PUT_32BIT_MSB_FIRST(data, entry->key.primary);
479         data += 4;
480
481         switch (subkeytypes[entry->key.primary]) {
482           case TYPE_INT:
483             PUT_32BIT_MSB_FIRST(data, entry->key.secondary.i);
484             data += 4;
485             break;
486           case TYPE_STR:
487             len = strlen(entry->key.secondary.s);
488             memcpy(data, entry->key.secondary.s, len);
489             data += len;
490             *data++ = 0;
491             break;
492         }
493         switch (valuetypes[entry->key.primary]) {
494           case TYPE_INT:
495             PUT_32BIT_MSB_FIRST(data, entry->value.u.intval);
496             data += 4;
497             break;
498           case TYPE_STR:
499             len = strlen(entry->value.u.stringval);
500             memcpy(data, entry->value.u.stringval, len);
501             data += len;
502             *data++ = 0;
503             break;
504           case TYPE_FILENAME:
505             memcpy(data, &entry->value.u.fileval,
506                    sizeof(entry->value.u.fileval));
507             data += sizeof(entry->value.u.fileval);
508             break;
509           case TYPE_FONT:
510             memcpy(data, &entry->value.u.fontval,
511                    sizeof(entry->value.u.fontval));
512             data += sizeof(entry->value.u.fontval);
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;
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             if (maxsize < sizeof(entry->value.u.fontval)) {
596                 if (subkeytypes[entry->key.primary] == TYPE_STR)
597                     sfree(entry->key.secondary.s);
598                 sfree(entry);
599                 goto done;
600             }
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);
605             break;
606         }
607         conf_insert(conf, entry);
608     }
609
610     done:
611     return (int)(data - start);
612 }