conf.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. /*
  2. * conf.c: implementation of the internal storage format used for
  3. * the configuration of a PuTTY session.
  4. */
  5. #include <stdio.h>
  6. #include <stddef.h>
  7. #include <assert.h>
  8. #include "tree234.h"
  9. #include "putty.h"
  10. /*
  11. * Configuration keys are primarily integers (big enum of all the
  12. * different configurable options); some keys have string-designated
  13. * subkeys, such as the list of environment variables (subkeys
  14. * defined by the variable names); some have integer-designated
  15. * subkeys (wordness, colours, preference lists).
  16. */
  17. struct key {
  18. int primary;
  19. union {
  20. int i;
  21. char *s;
  22. } secondary;
  23. };
  24. /* Variant form of struct key which doesn't contain dynamic data, used
  25. * for lookups. */
  26. struct constkey {
  27. int primary;
  28. union {
  29. int i;
  30. const char *s;
  31. } secondary;
  32. };
  33. struct value {
  34. union {
  35. bool boolval;
  36. int intval;
  37. char *stringval;
  38. Filename *fileval;
  39. FontSpec *fontval;
  40. } u;
  41. };
  42. struct conf_entry {
  43. struct key key;
  44. struct value value;
  45. };
  46. struct conf_tag {
  47. tree234 *tree;
  48. };
  49. /*
  50. * Because 'struct key' is the first element in 'struct conf_entry',
  51. * it's safe (guaranteed by the C standard) to cast arbitrarily back
  52. * and forth between the two types. Therefore, we only need one
  53. * comparison function, which can double as a main sort function for
  54. * the tree (comparing two conf_entry structures with each other)
  55. * and a search function (looking up an externally supplied key).
  56. */
  57. static int conf_cmp(void *av, void *bv)
  58. {
  59. struct key *a = (struct key *)av;
  60. struct key *b = (struct key *)bv;
  61. if (a->primary < b->primary)
  62. return -1;
  63. else if (a->primary > b->primary)
  64. return +1;
  65. switch (conf_key_info[a->primary].subkey_type) {
  66. case CONF_TYPE_INT:
  67. if (a->secondary.i < b->secondary.i)
  68. return -1;
  69. else if (a->secondary.i > b->secondary.i)
  70. return +1;
  71. return 0;
  72. case CONF_TYPE_STR:
  73. return strcmp(a->secondary.s, b->secondary.s);
  74. default:
  75. return 0;
  76. }
  77. }
  78. static int conf_cmp_constkey(void *av, void *bv)
  79. {
  80. struct key *a = (struct key *)av;
  81. struct constkey *b = (struct constkey *)bv;
  82. if (a->primary < b->primary)
  83. return -1;
  84. else if (a->primary > b->primary)
  85. return +1;
  86. switch (conf_key_info[a->primary].subkey_type) {
  87. case CONF_TYPE_INT:
  88. if (a->secondary.i < b->secondary.i)
  89. return -1;
  90. else if (a->secondary.i > b->secondary.i)
  91. return +1;
  92. return 0;
  93. case CONF_TYPE_STR:
  94. return strcmp(a->secondary.s, b->secondary.s);
  95. default:
  96. return 0;
  97. }
  98. }
  99. /*
  100. * Free any dynamic data items pointed to by a 'struct key'. We
  101. * don't free the structure itself, since it's probably part of a
  102. * larger allocated block.
  103. */
  104. static void free_key(struct key *key)
  105. {
  106. if (conf_key_info[key->primary].subkey_type == CONF_TYPE_STR)
  107. sfree(key->secondary.s);
  108. }
  109. /*
  110. * Copy a 'struct key' into another one, copying its dynamic data
  111. * if necessary.
  112. */
  113. static void copy_key(struct key *to, struct key *from)
  114. {
  115. to->primary = from->primary;
  116. switch (conf_key_info[to->primary].subkey_type) {
  117. case CONF_TYPE_INT:
  118. to->secondary.i = from->secondary.i;
  119. break;
  120. case CONF_TYPE_STR:
  121. to->secondary.s = dupstr(from->secondary.s);
  122. break;
  123. }
  124. }
  125. /*
  126. * Free any dynamic data items pointed to by a 'struct value'. We
  127. * don't free the value itself, since it's probably part of a larger
  128. * allocated block.
  129. */
  130. static void free_value(struct value *val, int type)
  131. {
  132. if (type == CONF_TYPE_STR)
  133. sfree(val->u.stringval);
  134. else if (type == CONF_TYPE_FILENAME)
  135. filename_free(val->u.fileval);
  136. else if (type == CONF_TYPE_FONT)
  137. fontspec_free(val->u.fontval);
  138. }
  139. /*
  140. * Copy a 'struct value' into another one, copying its dynamic data
  141. * if necessary.
  142. */
  143. static void copy_value(struct value *to, struct value *from, int type)
  144. {
  145. switch (type) {
  146. case CONF_TYPE_BOOL:
  147. to->u.boolval = from->u.boolval;
  148. break;
  149. case CONF_TYPE_INT:
  150. to->u.intval = from->u.intval;
  151. break;
  152. case CONF_TYPE_STR:
  153. to->u.stringval = dupstr(from->u.stringval);
  154. break;
  155. case CONF_TYPE_FILENAME:
  156. to->u.fileval = filename_copy(from->u.fileval);
  157. break;
  158. case CONF_TYPE_FONT:
  159. to->u.fontval = fontspec_copy(from->u.fontval);
  160. break;
  161. }
  162. }
  163. /*
  164. * Free an entire 'struct conf_entry' and its dynamic data.
  165. */
  166. static void free_entry(struct conf_entry *entry)
  167. {
  168. free_key(&entry->key);
  169. free_value(&entry->value, conf_key_info[entry->key.primary].value_type);
  170. sfree(entry);
  171. }
  172. Conf *conf_new(void)
  173. {
  174. Conf *conf = snew(struct conf_tag);
  175. conf->tree = newtree234(conf_cmp);
  176. return conf;
  177. }
  178. void conf_clear(Conf *conf)
  179. {
  180. struct conf_entry *entry;
  181. while ((entry = delpos234(conf->tree, 0)) != NULL)
  182. free_entry(entry);
  183. }
  184. void conf_free(Conf *conf)
  185. {
  186. conf_clear(conf);
  187. freetree234(conf->tree);
  188. sfree(conf);
  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. void conf_copy_into(Conf *newconf, Conf *oldconf)
  201. {
  202. struct conf_entry *entry, *entry2;
  203. int i;
  204. conf_clear(newconf);
  205. for (i = 0; (entry = index234(oldconf->tree, i)) != NULL; i++) {
  206. entry2 = snew(struct conf_entry);
  207. copy_key(&entry2->key, &entry->key);
  208. copy_value(&entry2->value, &entry->value,
  209. conf_key_info[entry->key.primary].value_type);
  210. add234(newconf->tree, entry2);
  211. }
  212. }
  213. Conf *conf_copy(Conf *oldconf)
  214. {
  215. Conf *newconf = conf_new();
  216. conf_copy_into(newconf, oldconf);
  217. return newconf;
  218. }
  219. bool conf_get_bool(Conf *conf, int primary)
  220. {
  221. struct key key;
  222. struct conf_entry *entry;
  223. assert(conf_key_info[primary].subkey_type == CONF_TYPE_NONE);
  224. assert(conf_key_info[primary].value_type == CONF_TYPE_BOOL);
  225. key.primary = primary;
  226. entry = find234(conf->tree, &key, NULL);
  227. assert(entry);
  228. return entry->value.u.boolval;
  229. }
  230. int conf_get_int(Conf *conf, int primary)
  231. {
  232. struct key key;
  233. struct conf_entry *entry;
  234. assert(conf_key_info[primary].subkey_type == CONF_TYPE_NONE);
  235. assert(conf_key_info[primary].value_type == CONF_TYPE_INT);
  236. key.primary = primary;
  237. entry = find234(conf->tree, &key, NULL);
  238. assert(entry);
  239. return entry->value.u.intval;
  240. }
  241. int conf_get_int_int(Conf *conf, int primary, int secondary)
  242. {
  243. struct key key;
  244. struct conf_entry *entry;
  245. assert(conf_key_info[primary].subkey_type == CONF_TYPE_INT);
  246. assert(conf_key_info[primary].value_type == CONF_TYPE_INT);
  247. key.primary = primary;
  248. key.secondary.i = secondary;
  249. entry = find234(conf->tree, &key, NULL);
  250. assert(entry);
  251. return entry->value.u.intval;
  252. }
  253. char *conf_get_str(Conf *conf, int primary)
  254. {
  255. struct key key;
  256. struct conf_entry *entry;
  257. assert(conf_key_info[primary].subkey_type == CONF_TYPE_NONE);
  258. assert(conf_key_info[primary].value_type == CONF_TYPE_STR);
  259. key.primary = primary;
  260. entry = find234(conf->tree, &key, NULL);
  261. assert(entry);
  262. return entry->value.u.stringval;
  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. assert(conf_key_info[primary].subkey_type == CONF_TYPE_STR);
  269. assert(conf_key_info[primary].value_type == CONF_TYPE_STR);
  270. key.primary = primary;
  271. key.secondary.s = (char *)secondary;
  272. entry = find234(conf->tree, &key, NULL);
  273. return entry ? entry->value.u.stringval : NULL;
  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. char *conf_get_str_strs(Conf *conf, int primary,
  282. char *subkeyin, char **subkeyout)
  283. {
  284. struct constkey key;
  285. struct conf_entry *entry;
  286. assert(conf_key_info[primary].subkey_type == CONF_TYPE_STR);
  287. assert(conf_key_info[primary].value_type == CONF_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, conf_cmp_constkey, 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. char *conf_get_str_nthstrkey(Conf *conf, int primary, int n)
  302. {
  303. struct constkey key;
  304. struct conf_entry *entry;
  305. int index;
  306. assert(conf_key_info[primary].subkey_type == CONF_TYPE_STR);
  307. assert(conf_key_info[primary].value_type == CONF_TYPE_STR);
  308. key.primary = primary;
  309. key.secondary.s = "";
  310. entry = findrelpos234(conf->tree, &key, conf_cmp_constkey,
  311. REL234_GE, &index);
  312. if (!entry || entry->key.primary != primary)
  313. return NULL;
  314. entry = index234(conf->tree, index + n);
  315. if (!entry || entry->key.primary != primary)
  316. return NULL;
  317. return entry->key.secondary.s;
  318. }
  319. Filename *conf_get_filename(Conf *conf, int primary)
  320. {
  321. struct key key;
  322. struct conf_entry *entry;
  323. assert(conf_key_info[primary].subkey_type == CONF_TYPE_NONE);
  324. assert(conf_key_info[primary].value_type == CONF_TYPE_FILENAME);
  325. key.primary = primary;
  326. entry = find234(conf->tree, &key, NULL);
  327. assert(entry);
  328. return entry->value.u.fileval;
  329. }
  330. FontSpec *conf_get_fontspec(Conf *conf, int primary)
  331. {
  332. struct key key;
  333. struct conf_entry *entry;
  334. assert(conf_key_info[primary].subkey_type == CONF_TYPE_NONE);
  335. assert(conf_key_info[primary].value_type == CONF_TYPE_FONT);
  336. key.primary = primary;
  337. entry = find234(conf->tree, &key, NULL);
  338. assert(entry);
  339. return entry->value.u.fontval;
  340. }
  341. void conf_set_bool(Conf *conf, int primary, bool value)
  342. {
  343. struct conf_entry *entry = snew(struct conf_entry);
  344. assert(conf_key_info[primary].subkey_type == CONF_TYPE_NONE);
  345. assert(conf_key_info[primary].value_type == CONF_TYPE_BOOL);
  346. entry->key.primary = primary;
  347. entry->value.u.boolval = value;
  348. conf_insert(conf, entry);
  349. }
  350. void conf_set_int(Conf *conf, int primary, int value)
  351. {
  352. struct conf_entry *entry = snew(struct conf_entry);
  353. assert(conf_key_info[primary].subkey_type == CONF_TYPE_NONE);
  354. assert(conf_key_info[primary].value_type == CONF_TYPE_INT);
  355. entry->key.primary = primary;
  356. entry->value.u.intval = value;
  357. conf_insert(conf, entry);
  358. }
  359. void conf_set_int_int(Conf *conf, int primary,
  360. int secondary, int value)
  361. {
  362. struct conf_entry *entry = snew(struct conf_entry);
  363. assert(conf_key_info[primary].subkey_type == CONF_TYPE_INT);
  364. assert(conf_key_info[primary].value_type == CONF_TYPE_INT);
  365. entry->key.primary = primary;
  366. entry->key.secondary.i = secondary;
  367. entry->value.u.intval = value;
  368. conf_insert(conf, entry);
  369. }
  370. void conf_set_str(Conf *conf, int primary, const char *value)
  371. {
  372. struct conf_entry *entry = snew(struct conf_entry);
  373. assert(conf_key_info[primary].subkey_type == CONF_TYPE_NONE);
  374. assert(conf_key_info[primary].value_type == CONF_TYPE_STR);
  375. entry->key.primary = primary;
  376. entry->value.u.stringval = dupstr(value);
  377. conf_insert(conf, entry);
  378. }
  379. void conf_set_str_str(Conf *conf, int primary, const char *secondary,
  380. const char *value)
  381. {
  382. struct conf_entry *entry = snew(struct conf_entry);
  383. assert(conf_key_info[primary].subkey_type == CONF_TYPE_STR);
  384. assert(conf_key_info[primary].value_type == CONF_TYPE_STR);
  385. entry->key.primary = primary;
  386. entry->key.secondary.s = dupstr(secondary);
  387. entry->value.u.stringval = dupstr(value);
  388. conf_insert(conf, entry);
  389. }
  390. void conf_del_str_str(Conf *conf, int primary, const char *secondary)
  391. {
  392. struct key key;
  393. struct conf_entry *entry;
  394. assert(conf_key_info[primary].subkey_type == CONF_TYPE_STR);
  395. assert(conf_key_info[primary].value_type == CONF_TYPE_STR);
  396. key.primary = primary;
  397. key.secondary.s = (char *)secondary;
  398. entry = find234(conf->tree, &key, NULL);
  399. if (entry) {
  400. del234(conf->tree, entry);
  401. free_entry(entry);
  402. }
  403. }
  404. void conf_set_filename(Conf *conf, int primary, const Filename *value)
  405. {
  406. struct conf_entry *entry = snew(struct conf_entry);
  407. assert(conf_key_info[primary].subkey_type == CONF_TYPE_NONE);
  408. assert(conf_key_info[primary].value_type == CONF_TYPE_FILENAME);
  409. entry->key.primary = primary;
  410. entry->value.u.fileval = filename_copy(value);
  411. conf_insert(conf, entry);
  412. }
  413. void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value)
  414. {
  415. struct conf_entry *entry = snew(struct conf_entry);
  416. assert(conf_key_info[primary].subkey_type == CONF_TYPE_NONE);
  417. assert(conf_key_info[primary].value_type == CONF_TYPE_FONT);
  418. entry->key.primary = primary;
  419. entry->value.u.fontval = fontspec_copy(value);
  420. conf_insert(conf, entry);
  421. }
  422. void conf_serialise(BinarySink *bs, Conf *conf)
  423. {
  424. int i;
  425. struct conf_entry *entry;
  426. for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
  427. put_uint32(bs, entry->key.primary);
  428. switch (conf_key_info[entry->key.primary].subkey_type) {
  429. case CONF_TYPE_INT:
  430. put_uint32(bs, entry->key.secondary.i);
  431. break;
  432. case CONF_TYPE_STR:
  433. put_asciz(bs, entry->key.secondary.s);
  434. break;
  435. }
  436. switch (conf_key_info[entry->key.primary].value_type) {
  437. case CONF_TYPE_BOOL:
  438. put_bool(bs, entry->value.u.boolval);
  439. break;
  440. case CONF_TYPE_INT:
  441. put_uint32(bs, entry->value.u.intval);
  442. break;
  443. case CONF_TYPE_STR:
  444. put_asciz(bs, entry->value.u.stringval);
  445. break;
  446. case CONF_TYPE_FILENAME:
  447. filename_serialise(bs, entry->value.u.fileval);
  448. break;
  449. case CONF_TYPE_FONT:
  450. fontspec_serialise(bs, entry->value.u.fontval);
  451. break;
  452. }
  453. }
  454. put_uint32(bs, 0xFFFFFFFFU);
  455. }
  456. bool conf_deserialise(Conf *conf, BinarySource *src)
  457. {
  458. struct conf_entry *entry;
  459. unsigned primary;
  460. while (1) {
  461. primary = get_uint32(src);
  462. if (get_err(src))
  463. return false;
  464. if (primary == 0xFFFFFFFFU)
  465. return true;
  466. if (primary >= N_CONFIG_OPTIONS)
  467. return false;
  468. entry = snew(struct conf_entry);
  469. entry->key.primary = primary;
  470. switch (conf_key_info[entry->key.primary].subkey_type) {
  471. case CONF_TYPE_INT:
  472. entry->key.secondary.i = toint(get_uint32(src));
  473. break;
  474. case CONF_TYPE_STR:
  475. entry->key.secondary.s = dupstr(get_asciz(src));
  476. break;
  477. }
  478. switch (conf_key_info[entry->key.primary].value_type) {
  479. case CONF_TYPE_BOOL:
  480. entry->value.u.boolval = get_bool(src);
  481. break;
  482. case CONF_TYPE_INT:
  483. entry->value.u.intval = toint(get_uint32(src));
  484. break;
  485. case CONF_TYPE_STR:
  486. entry->value.u.stringval = dupstr(get_asciz(src));
  487. break;
  488. case CONF_TYPE_FILENAME:
  489. entry->value.u.fileval = filename_deserialise(src);
  490. break;
  491. case CONF_TYPE_FONT:
  492. entry->value.u.fontval = fontspec_deserialise(src);
  493. break;
  494. }
  495. if (get_err(src)) {
  496. free_entry(entry);
  497. return false;
  498. }
  499. conf_insert(conf, entry);
  500. }
  501. }