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