Doc: Fix broken link
[TortoiseGit.git] / src / TortoisePlink / CONF.C
blobb0a18b87ac82ca770599b5f6d1fd1fe85ce360f2
1 /*\r
2  * conf.c: implementation of the internal storage format used for\r
3  * the configuration of a PuTTY session.\r
4  */\r
5 \r
6 #include <stdio.h>\r
7 #include <stddef.h>\r
8 #include <assert.h>\r
9 \r
10 #include "tree234.h"\r
11 #include "putty.h"\r
13 /*\r
14  * Enumeration of types used in keys and values.\r
15  */\r
16 typedef enum { TYPE_NONE, TYPE_INT, TYPE_STR, TYPE_FILENAME, TYPE_FONT } Type;\r
18 /*\r
19  * Arrays which allow us to look up the subkey and value types for a\r
20  * given primary key id.\r
21  */\r
22 #define CONF_SUBKEYTYPE_DEF(valtype, keytype, keyword) TYPE_ ## keytype,\r
23 static int subkeytypes[] = { CONFIG_OPTIONS(CONF_SUBKEYTYPE_DEF) };\r
24 #define CONF_VALUETYPE_DEF(valtype, keytype, keyword) TYPE_ ## valtype,\r
25 static int valuetypes[] = { CONFIG_OPTIONS(CONF_VALUETYPE_DEF) };\r
27 /*\r
28  * Configuration keys are primarily integers (big enum of all the\r
29  * different configurable options); some keys have string-designated\r
30  * subkeys, such as the list of environment variables (subkeys\r
31  * defined by the variable names); some have integer-designated\r
32  * subkeys (wordness, colours, preference lists).\r
33  */\r
34 struct key {\r
35     int primary;\r
36     union {\r
37         int i;\r
38         char *s;\r
39     } secondary;\r
40 };\r
42 /* Variant form of struct key which doesn't contain dynamic data, used\r
43  * for lookups. */\r
44 struct constkey {\r
45     int primary;\r
46     union {\r
47         int i;\r
48         const char *s;\r
49     } secondary;\r
50 };\r
52 struct value {\r
53     union {\r
54         int intval;\r
55         char *stringval;\r
56         Filename *fileval;\r
57         FontSpec *fontval;\r
58     } u;\r
59 };\r
61 struct conf_entry {\r
62     struct key key;\r
63     struct value value;\r
64 };\r
66 struct conf_tag {\r
67     tree234 *tree;\r
68 };\r
70 /*\r
71  * Because 'struct key' is the first element in 'struct conf_entry',\r
72  * it's safe (guaranteed by the C standard) to cast arbitrarily back\r
73  * and forth between the two types. Therefore, we only need one\r
74  * comparison function, which can double as a main sort function for\r
75  * the tree (comparing two conf_entry structures with each other)\r
76  * and a search function (looking up an externally supplied key).\r
77  */\r
78 static int conf_cmp(void *av, void *bv)\r
79 {\r
80     struct key *a = (struct key *)av;\r
81     struct key *b = (struct key *)bv;\r
83     if (a->primary < b->primary)\r
84         return -1;\r
85     else if (a->primary > b->primary)\r
86         return +1;\r
87     switch (subkeytypes[a->primary]) {\r
88       case TYPE_INT:\r
89         if (a->secondary.i < b->secondary.i)\r
90             return -1;\r
91         else if (a->secondary.i > b->secondary.i)\r
92             return +1;\r
93         return 0;\r
94       case TYPE_STR:\r
95         return strcmp(a->secondary.s, b->secondary.s);\r
96       default:\r
97         return 0;\r
98     }\r
99 }\r
101 static int conf_cmp_constkey(void *av, void *bv)\r
103     struct key *a = (struct key *)av;\r
104     struct constkey *b = (struct constkey *)bv;\r
106     if (a->primary < b->primary)\r
107         return -1;\r
108     else if (a->primary > b->primary)\r
109         return +1;\r
110     switch (subkeytypes[a->primary]) {\r
111       case TYPE_INT:\r
112         if (a->secondary.i < b->secondary.i)\r
113             return -1;\r
114         else if (a->secondary.i > b->secondary.i)\r
115             return +1;\r
116         return 0;\r
117       case TYPE_STR:\r
118         return strcmp(a->secondary.s, b->secondary.s);\r
119       default:\r
120         return 0;\r
121     }\r
124 /*\r
125  * Free any dynamic data items pointed to by a 'struct key'. We\r
126  * don't free the structure itself, since it's probably part of a\r
127  * larger allocated block.\r
128  */\r
129 static void free_key(struct key *key)\r
131     if (subkeytypes[key->primary] == TYPE_STR)\r
132         sfree(key->secondary.s);\r
135 /*\r
136  * Copy a 'struct key' into another one, copying its dynamic data\r
137  * if necessary.\r
138  */\r
139 static void copy_key(struct key *to, struct key *from)\r
141     to->primary = from->primary;\r
142     switch (subkeytypes[to->primary]) {\r
143       case TYPE_INT:\r
144         to->secondary.i = from->secondary.i;\r
145         break;\r
146       case TYPE_STR:\r
147         to->secondary.s = dupstr(from->secondary.s);\r
148         break;\r
149     }\r
152 /*\r
153  * Free any dynamic data items pointed to by a 'struct value'. We\r
154  * don't free the value itself, since it's probably part of a larger\r
155  * allocated block.\r
156  */\r
157 static void free_value(struct value *val, int type)\r
159     if (type == TYPE_STR)\r
160         sfree(val->u.stringval);\r
161     else if (type == TYPE_FILENAME)\r
162         filename_free(val->u.fileval);\r
163     else if (type == TYPE_FONT)\r
164         fontspec_free(val->u.fontval);\r
167 /*\r
168  * Copy a 'struct value' into another one, copying its dynamic data\r
169  * if necessary.\r
170  */\r
171 static void copy_value(struct value *to, struct value *from, int type)\r
173     switch (type) {\r
174       case TYPE_INT:\r
175         to->u.intval = from->u.intval;\r
176         break;\r
177       case TYPE_STR:\r
178         to->u.stringval = dupstr(from->u.stringval);\r
179         break;\r
180       case TYPE_FILENAME:\r
181         to->u.fileval = filename_copy(from->u.fileval);\r
182         break;\r
183       case TYPE_FONT:\r
184         to->u.fontval = fontspec_copy(from->u.fontval);\r
185         break;\r
186     }\r
189 /*\r
190  * Free an entire 'struct conf_entry' and its dynamic data.\r
191  */\r
192 static void free_entry(struct conf_entry *entry)\r
194     free_key(&entry->key);\r
195     free_value(&entry->value, valuetypes[entry->key.primary]);\r
196     sfree(entry);\r
199 Conf *conf_new(void)\r
201     Conf *conf = snew(struct conf_tag);\r
203     conf->tree = newtree234(conf_cmp);\r
205     return conf;\r
208 static void conf_clear(Conf *conf)\r
210     struct conf_entry *entry;\r
212     while ((entry = delpos234(conf->tree, 0)) != NULL)\r
213         free_entry(entry);\r
216 void conf_free(Conf *conf)\r
218     conf_clear(conf);\r
219     freetree234(conf->tree);\r
220     sfree(conf);\r
223 static void conf_insert(Conf *conf, struct conf_entry *entry)\r
225     struct conf_entry *oldentry = add234(conf->tree, entry);\r
226     if (oldentry && oldentry != entry) {\r
227         del234(conf->tree, oldentry);\r
228         free_entry(oldentry);\r
229         oldentry = add234(conf->tree, entry);\r
230         assert(oldentry == entry);\r
231     }\r
234 void conf_copy_into(Conf *newconf, Conf *oldconf)\r
236     struct conf_entry *entry, *entry2;\r
237     int i;\r
239     conf_clear(newconf);\r
241     for (i = 0; (entry = index234(oldconf->tree, i)) != NULL; i++) {\r
242         entry2 = snew(struct conf_entry);\r
243         copy_key(&entry2->key, &entry->key);\r
244         copy_value(&entry2->value, &entry->value,\r
245                    valuetypes[entry->key.primary]);\r
246         add234(newconf->tree, entry2);\r
247     }\r
250 Conf *conf_copy(Conf *oldconf)\r
252     Conf *newconf = conf_new();\r
254     conf_copy_into(newconf, oldconf);\r
256     return newconf;\r
259 int conf_get_int(Conf *conf, int primary)\r
261     struct key key;\r
262     struct conf_entry *entry;\r
264     assert(subkeytypes[primary] == TYPE_NONE);\r
265     assert(valuetypes[primary] == TYPE_INT);\r
266     key.primary = primary;\r
267     entry = find234(conf->tree, &key, NULL);\r
268     assert(entry);\r
269     return entry->value.u.intval;\r
272 int conf_get_int_int(Conf *conf, int primary, int secondary)\r
274     struct key key;\r
275     struct conf_entry *entry;\r
277     assert(subkeytypes[primary] == TYPE_INT);\r
278     assert(valuetypes[primary] == TYPE_INT);\r
279     key.primary = primary;\r
280     key.secondary.i = secondary;\r
281     entry = find234(conf->tree, &key, NULL);\r
282     assert(entry);\r
283     return entry->value.u.intval;\r
286 char *conf_get_str(Conf *conf, int primary)\r
288     struct key key;\r
289     struct conf_entry *entry;\r
291     assert(subkeytypes[primary] == TYPE_NONE);\r
292     assert(valuetypes[primary] == TYPE_STR);\r
293     key.primary = primary;\r
294     entry = find234(conf->tree, &key, NULL);\r
295     assert(entry);\r
296     return entry->value.u.stringval;\r
299 char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary)\r
301     struct key key;\r
302     struct conf_entry *entry;\r
304     assert(subkeytypes[primary] == TYPE_STR);\r
305     assert(valuetypes[primary] == TYPE_STR);\r
306     key.primary = primary;\r
307     key.secondary.s = (char *)secondary;\r
308     entry = find234(conf->tree, &key, NULL);\r
309     return entry ? entry->value.u.stringval : NULL;\r
312 char *conf_get_str_str(Conf *conf, int primary, const char *secondary)\r
314     char *ret = conf_get_str_str_opt(conf, primary, secondary);\r
315     assert(ret);\r
316     return ret;\r
319 char *conf_get_str_strs(Conf *conf, int primary,\r
320                        char *subkeyin, char **subkeyout)\r
322     struct constkey key;\r
323     struct conf_entry *entry;\r
325     assert(subkeytypes[primary] == TYPE_STR);\r
326     assert(valuetypes[primary] == TYPE_STR);\r
327     key.primary = primary;\r
328     if (subkeyin) {\r
329         key.secondary.s = subkeyin;\r
330         entry = findrel234(conf->tree, &key, NULL, REL234_GT);\r
331     } else {\r
332         key.secondary.s = "";\r
333         entry = findrel234(conf->tree, &key, conf_cmp_constkey, REL234_GE);\r
334     }\r
335     if (!entry || entry->key.primary != primary)\r
336         return NULL;\r
337     *subkeyout = entry->key.secondary.s;\r
338     return entry->value.u.stringval;\r
341 char *conf_get_str_nthstrkey(Conf *conf, int primary, int n)\r
343     struct constkey key;\r
344     struct conf_entry *entry;\r
345     int index;\r
347     assert(subkeytypes[primary] == TYPE_STR);\r
348     assert(valuetypes[primary] == TYPE_STR);\r
349     key.primary = primary;\r
350     key.secondary.s = "";\r
351     entry = findrelpos234(conf->tree, &key, conf_cmp_constkey,\r
352                           REL234_GE, &index);\r
353     if (!entry || entry->key.primary != primary)\r
354         return NULL;\r
355     entry = index234(conf->tree, index + n);\r
356     if (!entry || entry->key.primary != primary)\r
357         return NULL;\r
358     return entry->key.secondary.s;\r
361 Filename *conf_get_filename(Conf *conf, int primary)\r
363     struct key key;\r
364     struct conf_entry *entry;\r
366     assert(subkeytypes[primary] == TYPE_NONE);\r
367     assert(valuetypes[primary] == TYPE_FILENAME);\r
368     key.primary = primary;\r
369     entry = find234(conf->tree, &key, NULL);\r
370     assert(entry);\r
371     return entry->value.u.fileval;\r
374 FontSpec *conf_get_fontspec(Conf *conf, int primary)\r
376     struct key key;\r
377     struct conf_entry *entry;\r
379     assert(subkeytypes[primary] == TYPE_NONE);\r
380     assert(valuetypes[primary] == TYPE_FONT);\r
381     key.primary = primary;\r
382     entry = find234(conf->tree, &key, NULL);\r
383     assert(entry);\r
384     return entry->value.u.fontval;\r
387 void conf_set_int(Conf *conf, int primary, int value)\r
389     struct conf_entry *entry = snew(struct conf_entry);\r
391     assert(subkeytypes[primary] == TYPE_NONE);\r
392     assert(valuetypes[primary] == TYPE_INT);\r
393     entry->key.primary = primary;\r
394     entry->value.u.intval = value; \r
395     conf_insert(conf, entry);\r
398 void conf_set_int_int(Conf *conf, int primary, int secondary, int value)\r
400     struct conf_entry *entry = snew(struct conf_entry);\r
402     assert(subkeytypes[primary] == TYPE_INT);\r
403     assert(valuetypes[primary] == TYPE_INT);\r
404     entry->key.primary = primary;\r
405     entry->key.secondary.i = secondary;\r
406     entry->value.u.intval = value;\r
407     conf_insert(conf, entry);\r
410 void conf_set_str(Conf *conf, int primary, const char *value)\r
412     struct conf_entry *entry = snew(struct conf_entry);\r
414     assert(subkeytypes[primary] == TYPE_NONE);\r
415     assert(valuetypes[primary] == TYPE_STR);\r
416     entry->key.primary = primary;\r
417     entry->value.u.stringval = dupstr(value);\r
418     conf_insert(conf, entry);\r
421 void conf_set_str_str(Conf *conf, int primary, const char *secondary,\r
422                       const char *value)\r
424     struct conf_entry *entry = snew(struct conf_entry);\r
426     assert(subkeytypes[primary] == TYPE_STR);\r
427     assert(valuetypes[primary] == TYPE_STR);\r
428     entry->key.primary = primary;\r
429     entry->key.secondary.s = dupstr(secondary);\r
430     entry->value.u.stringval = dupstr(value);\r
431     conf_insert(conf, entry);\r
434 void conf_del_str_str(Conf *conf, int primary, const char *secondary)\r
436     struct key key;\r
437     struct conf_entry *entry;\r
439     assert(subkeytypes[primary] == TYPE_STR);\r
440     assert(valuetypes[primary] == TYPE_STR);\r
441     key.primary = primary;\r
442     key.secondary.s = (char *)secondary;\r
443     entry = find234(conf->tree, &key, NULL);\r
444     if (entry) {\r
445         del234(conf->tree, entry);\r
446         free_entry(entry);\r
447     }\r
448  }\r
450 void conf_set_filename(Conf *conf, int primary, const Filename *value)\r
452     struct conf_entry *entry = snew(struct conf_entry);\r
454     assert(subkeytypes[primary] == TYPE_NONE);\r
455     assert(valuetypes[primary] == TYPE_FILENAME);\r
456     entry->key.primary = primary;\r
457     entry->value.u.fileval = filename_copy(value);\r
458     conf_insert(conf, entry);\r
461 void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value)\r
463     struct conf_entry *entry = snew(struct conf_entry);\r
465     assert(subkeytypes[primary] == TYPE_NONE);\r
466     assert(valuetypes[primary] == TYPE_FONT);\r
467     entry->key.primary = primary;\r
468     entry->value.u.fontval = fontspec_copy(value);\r
469     conf_insert(conf, entry);\r
472 int conf_serialised_size(Conf *conf)\r
474     int i;\r
475     struct conf_entry *entry;\r
476     int size = 0;\r
478     for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {\r
479         size += 4;   /* primary key */\r
480         switch (subkeytypes[entry->key.primary]) {\r
481           case TYPE_INT:\r
482             size += 4;\r
483             break;\r
484           case TYPE_STR:\r
485             size += 1 + strlen(entry->key.secondary.s);\r
486             break;\r
487         }\r
488         switch (valuetypes[entry->key.primary]) {\r
489           case TYPE_INT:\r
490             size += 4;\r
491             break;\r
492           case TYPE_STR:\r
493             size += 1 + strlen(entry->value.u.stringval);\r
494             break;\r
495           case TYPE_FILENAME:\r
496             size += filename_serialise(entry->value.u.fileval, NULL);\r
497             break;\r
498           case TYPE_FONT:\r
499             size += fontspec_serialise(entry->value.u.fontval, NULL);\r
500             break;\r
501         }\r
502     }\r
504     size += 4;                         /* terminator value */\r
506     return size;\r
509 void conf_serialise(Conf *conf, void *vdata)\r
511     unsigned char *data = (unsigned char *)vdata;\r
512     int i, len;\r
513     struct conf_entry *entry;\r
515     for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {\r
516         PUT_32BIT_MSB_FIRST(data, entry->key.primary);\r
517         data += 4;\r
519         switch (subkeytypes[entry->key.primary]) {\r
520           case TYPE_INT:\r
521             PUT_32BIT_MSB_FIRST(data, entry->key.secondary.i);\r
522             data += 4;\r
523             break;\r
524           case TYPE_STR:\r
525             len = strlen(entry->key.secondary.s);\r
526             memcpy(data, entry->key.secondary.s, len);\r
527             data += len;\r
528             *data++ = 0;\r
529             break;\r
530         }\r
531         switch (valuetypes[entry->key.primary]) {\r
532           case TYPE_INT:\r
533             PUT_32BIT_MSB_FIRST(data, entry->value.u.intval);\r
534             data += 4;\r
535             break;\r
536           case TYPE_STR:\r
537             len = strlen(entry->value.u.stringval);\r
538             memcpy(data, entry->value.u.stringval, len);\r
539             data += len;\r
540             *data++ = 0;\r
541             break;\r
542           case TYPE_FILENAME:\r
543             data += filename_serialise(entry->value.u.fileval, data);\r
544             break;\r
545           case TYPE_FONT:\r
546             data += fontspec_serialise(entry->value.u.fontval, data);\r
547             break;\r
548         }\r
549     }\r
551     PUT_32BIT_MSB_FIRST(data, 0xFFFFFFFFU);\r
554 int conf_deserialise(Conf *conf, void *vdata, int maxsize)\r
556     unsigned char *data = (unsigned char *)vdata;\r
557     unsigned char *start = data;\r
558     struct conf_entry *entry;\r
559     unsigned primary;\r
560     int used;\r
561     unsigned char *zero;\r
563     while (maxsize >= 4) {\r
564         primary = GET_32BIT_MSB_FIRST(data);\r
565         data += 4, maxsize -= 4;\r
567         if (primary >= N_CONFIG_OPTIONS)\r
568             break;\r
570         entry = snew(struct conf_entry);\r
571         entry->key.primary = primary;\r
573         switch (subkeytypes[entry->key.primary]) {\r
574           case TYPE_INT:\r
575             if (maxsize < 4) {\r
576                 sfree(entry);\r
577                 goto done;\r
578             }\r
579             entry->key.secondary.i = toint(GET_32BIT_MSB_FIRST(data));\r
580             data += 4, maxsize -= 4;\r
581             break;\r
582           case TYPE_STR:\r
583             zero = memchr(data, 0, maxsize);\r
584             if (!zero) {\r
585                 sfree(entry);\r
586                 goto done;\r
587             }\r
588             entry->key.secondary.s = dupstr((char *)data);\r
589             maxsize -= (zero + 1 - data);\r
590             data = zero + 1;\r
591             break;\r
592         }\r
594         switch (valuetypes[entry->key.primary]) {\r
595           case TYPE_INT:\r
596             if (maxsize < 4) {\r
597                 if (subkeytypes[entry->key.primary] == TYPE_STR)\r
598                     sfree(entry->key.secondary.s);\r
599                 sfree(entry);\r
600                 goto done;\r
601             }\r
602             entry->value.u.intval = toint(GET_32BIT_MSB_FIRST(data));\r
603             data += 4, maxsize -= 4;\r
604             break;\r
605           case TYPE_STR:\r
606             zero = memchr(data, 0, maxsize);\r
607             if (!zero) {\r
608                 if (subkeytypes[entry->key.primary] == TYPE_STR)\r
609                     sfree(entry->key.secondary.s);\r
610                 sfree(entry);\r
611                 goto done;\r
612             }\r
613             entry->value.u.stringval = dupstr((char *)data);\r
614             maxsize -= (zero + 1 - data);\r
615             data = zero + 1;\r
616             break;\r
617           case TYPE_FILENAME:\r
618             entry->value.u.fileval =\r
619                 filename_deserialise(data, maxsize, &used);\r
620             if (!entry->value.u.fileval) {\r
621                 if (subkeytypes[entry->key.primary] == TYPE_STR)\r
622                     sfree(entry->key.secondary.s);\r
623                 sfree(entry);\r
624                 goto done;\r
625             }\r
626             data += used;\r
627             maxsize -= used;\r
628             break;\r
629           case TYPE_FONT:\r
630             entry->value.u.fontval =\r
631                 fontspec_deserialise(data, maxsize, &used);\r
632             if (!entry->value.u.fontval) {\r
633                 if (subkeytypes[entry->key.primary] == TYPE_STR)\r
634                     sfree(entry->key.secondary.s);\r
635                 sfree(entry);\r
636                 goto done;\r
637             }\r
638             data += used;\r
639             maxsize -= used;\r
640             break;\r
641         }\r
642         conf_insert(conf, entry);\r
643     }\r
645     done:\r
646     return (int)(data - start);\r