conf.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613
  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. * Enumeration of types used in keys and values.
  12. */
  13. typedef enum { TYPE_NONE, TYPE_INT, TYPE_STR, TYPE_FILENAME, TYPE_FONT } Type;
  14. /*
  15. * Arrays which allow us to look up the subkey and value types for a
  16. * given primary key id.
  17. */
  18. #define CONF_SUBKEYTYPE_DEF(valtype, keytype, keyword) TYPE_ ## keytype,
  19. static int subkeytypes[] = { CONFIG_OPTIONS(CONF_SUBKEYTYPE_DEF) };
  20. #define CONF_VALUETYPE_DEF(valtype, keytype, keyword) TYPE_ ## valtype,
  21. static int valuetypes[] = { CONFIG_OPTIONS(CONF_VALUETYPE_DEF) };
  22. /*
  23. * Configuration keys are primarily integers (big enum of all the
  24. * different configurable options); some keys have string-designated
  25. * subkeys, such as the list of environment variables (subkeys
  26. * defined by the variable names); some have integer-designated
  27. * subkeys (wordness, colours, preference lists).
  28. */
  29. struct key {
  30. int primary;
  31. union {
  32. int i;
  33. char *s;
  34. } secondary;
  35. };
  36. struct value {
  37. union {
  38. int intval;
  39. char *stringval;
  40. Filename *fileval;
  41. FontSpec *fontval;
  42. } u;
  43. };
  44. struct conf_entry {
  45. struct key key;
  46. struct value value;
  47. };
  48. struct conf_tag {
  49. tree234 *tree;
  50. };
  51. /*
  52. * Because 'struct key' is the first element in 'struct conf_entry',
  53. * it's safe (guaranteed by the C standard) to cast arbitrarily back
  54. * and forth between the two types. Therefore, we only need one
  55. * comparison function, which can double as a main sort function for
  56. * the tree (comparing two conf_entry structures with each other)
  57. * and a search function (looking up an externally supplied key).
  58. */
  59. static int conf_cmp(void *av, void *bv)
  60. {
  61. struct key *a = (struct key *)av;
  62. struct key *b = (struct key *)bv;
  63. if (a->primary < b->primary)
  64. return -1;
  65. else if (a->primary > b->primary)
  66. return +1;
  67. switch (subkeytypes[a->primary]) {
  68. case TYPE_INT:
  69. if (a->secondary.i < b->secondary.i)
  70. return -1;
  71. else if (a->secondary.i > b->secondary.i)
  72. return +1;
  73. return 0;
  74. case TYPE_STR:
  75. return strcmp(a->secondary.s, b->secondary.s);
  76. default:
  77. return 0;
  78. }
  79. }
  80. /*
  81. * Free any dynamic data items pointed to by a 'struct key'. We
  82. * don't free the structure itself, since it's probably part of a
  83. * larger allocated block.
  84. */
  85. static void free_key(struct key *key)
  86. {
  87. if (subkeytypes[key->primary] == TYPE_STR)
  88. sfree(key->secondary.s);
  89. }
  90. /*
  91. * Copy a 'struct key' into another one, copying its dynamic data
  92. * if necessary.
  93. */
  94. static void copy_key(struct key *to, struct key *from)
  95. {
  96. to->primary = from->primary;
  97. switch (subkeytypes[to->primary]) {
  98. case TYPE_INT:
  99. to->secondary.i = from->secondary.i;
  100. break;
  101. case TYPE_STR:
  102. to->secondary.s = dupstr(from->secondary.s);
  103. break;
  104. }
  105. }
  106. /*
  107. * Free any dynamic data items pointed to by a 'struct value'. We
  108. * don't free the value itself, since it's probably part of a larger
  109. * allocated block.
  110. */
  111. static void free_value(struct value *val, int type)
  112. {
  113. if (type == TYPE_STR)
  114. sfree(val->u.stringval);
  115. else if (type == TYPE_FILENAME)
  116. filename_free(val->u.fileval);
  117. else if (type == TYPE_FONT)
  118. fontspec_free(val->u.fontval);
  119. }
  120. /*
  121. * Copy a 'struct value' into another one, copying its dynamic data
  122. * if necessary.
  123. */
  124. static void copy_value(struct value *to, struct value *from, int type)
  125. {
  126. switch (type) {
  127. case TYPE_INT:
  128. to->u.intval = from->u.intval;
  129. break;
  130. case TYPE_STR:
  131. to->u.stringval = dupstr(from->u.stringval);
  132. break;
  133. case TYPE_FILENAME:
  134. to->u.fileval = filename_copy(from->u.fileval);
  135. break;
  136. case TYPE_FONT:
  137. to->u.fontval = fontspec_copy(from->u.fontval);
  138. break;
  139. }
  140. }
  141. /*
  142. * Free an entire 'struct conf_entry' and its dynamic data.
  143. */
  144. static void free_entry(struct conf_entry *entry)
  145. {
  146. free_key(&entry->key);
  147. free_value(&entry->value, valuetypes[entry->key.primary]);
  148. sfree(entry);
  149. }
  150. Conf *conf_new(void)
  151. {
  152. Conf *conf = snew(struct conf_tag);
  153. conf->tree = newtree234(conf_cmp);
  154. return conf;
  155. }
  156. static void conf_clear(Conf *conf)
  157. {
  158. struct conf_entry *entry;
  159. while ((entry = delpos234(conf->tree, 0)) != NULL)
  160. free_entry(entry);
  161. }
  162. void conf_free(Conf *conf)
  163. {
  164. conf_clear(conf);
  165. freetree234(conf->tree);
  166. sfree(conf);
  167. }
  168. static void conf_insert(Conf *conf, struct conf_entry *entry)
  169. {
  170. struct conf_entry *oldentry = add234(conf->tree, entry);
  171. if (oldentry && oldentry != entry) {
  172. del234(conf->tree, oldentry);
  173. free_entry(oldentry);
  174. oldentry = add234(conf->tree, entry);
  175. assert(oldentry == entry);
  176. }
  177. }
  178. void conf_copy_into(Conf *newconf, Conf *oldconf)
  179. {
  180. struct conf_entry *entry, *entry2;
  181. int i;
  182. conf_clear(newconf);
  183. for (i = 0; (entry = index234(oldconf->tree, i)) != NULL; i++) {
  184. entry2 = snew(struct conf_entry);
  185. copy_key(&entry2->key, &entry->key);
  186. copy_value(&entry2->value, &entry->value,
  187. valuetypes[entry->key.primary]);
  188. add234(newconf->tree, entry2);
  189. }
  190. }
  191. Conf *conf_copy(Conf *oldconf)
  192. {
  193. Conf *newconf = conf_new();
  194. conf_copy_into(newconf, oldconf);
  195. return newconf;
  196. }
  197. int conf_get_int(Conf *conf, int primary)
  198. {
  199. struct key key;
  200. struct conf_entry *entry;
  201. assert(subkeytypes[primary] == TYPE_NONE);
  202. assert(valuetypes[primary] == TYPE_INT);
  203. key.primary = primary;
  204. entry = find234(conf->tree, &key, NULL);
  205. assert(entry);
  206. return entry->value.u.intval;
  207. }
  208. int conf_get_int_int(Conf *conf, int primary, int secondary)
  209. {
  210. struct key key;
  211. struct conf_entry *entry;
  212. assert(subkeytypes[primary] == TYPE_INT);
  213. assert(valuetypes[primary] == TYPE_INT);
  214. key.primary = primary;
  215. key.secondary.i = secondary;
  216. entry = find234(conf->tree, &key, NULL);
  217. assert(entry);
  218. return entry->value.u.intval;
  219. }
  220. char *conf_get_str(Conf *conf, int primary)
  221. {
  222. struct key key;
  223. struct conf_entry *entry;
  224. assert(subkeytypes[primary] == TYPE_NONE);
  225. assert(valuetypes[primary] == TYPE_STR);
  226. key.primary = primary;
  227. entry = find234(conf->tree, &key, NULL);
  228. assert(entry);
  229. return entry->value.u.stringval;
  230. }
  231. char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary)
  232. {
  233. struct key key;
  234. struct conf_entry *entry;
  235. assert(subkeytypes[primary] == TYPE_STR);
  236. assert(valuetypes[primary] == TYPE_STR);
  237. key.primary = primary;
  238. key.secondary.s = (char *)secondary;
  239. entry = find234(conf->tree, &key, NULL);
  240. return entry ? entry->value.u.stringval : NULL;
  241. }
  242. char *conf_get_str_str(Conf *conf, int primary, const char *secondary)
  243. {
  244. char *ret = conf_get_str_str_opt(conf, primary, secondary);
  245. assert(ret);
  246. return ret;
  247. }
  248. char *conf_get_str_strs(Conf *conf, int primary,
  249. char *subkeyin, char **subkeyout)
  250. {
  251. struct key key;
  252. struct conf_entry *entry;
  253. assert(subkeytypes[primary] == TYPE_STR);
  254. assert(valuetypes[primary] == TYPE_STR);
  255. key.primary = primary;
  256. if (subkeyin) {
  257. key.secondary.s = subkeyin;
  258. entry = findrel234(conf->tree, &key, NULL, REL234_GT);
  259. } else {
  260. key.secondary.s = "";
  261. entry = findrel234(conf->tree, &key, NULL, REL234_GE);
  262. }
  263. if (!entry || entry->key.primary != primary)
  264. return NULL;
  265. *subkeyout = entry->key.secondary.s;
  266. return entry->value.u.stringval;
  267. }
  268. char *conf_get_str_nthstrkey(Conf *conf, int primary, int n)
  269. {
  270. struct key key;
  271. struct conf_entry *entry;
  272. int index;
  273. assert(subkeytypes[primary] == TYPE_STR);
  274. assert(valuetypes[primary] == TYPE_STR);
  275. key.primary = primary;
  276. key.secondary.s = "";
  277. entry = findrelpos234(conf->tree, &key, NULL, REL234_GE, &index);
  278. if (!entry || entry->key.primary != primary)
  279. return NULL;
  280. entry = index234(conf->tree, index + n);
  281. if (!entry || entry->key.primary != primary)
  282. return NULL;
  283. return entry->key.secondary.s;
  284. }
  285. Filename *conf_get_filename(Conf *conf, int primary)
  286. {
  287. struct key key;
  288. struct conf_entry *entry;
  289. assert(subkeytypes[primary] == TYPE_NONE);
  290. assert(valuetypes[primary] == TYPE_FILENAME);
  291. key.primary = primary;
  292. entry = find234(conf->tree, &key, NULL);
  293. assert(entry);
  294. return entry->value.u.fileval;
  295. }
  296. FontSpec *conf_get_fontspec(Conf *conf, int primary)
  297. {
  298. struct key key;
  299. struct conf_entry *entry;
  300. assert(subkeytypes[primary] == TYPE_NONE);
  301. assert(valuetypes[primary] == TYPE_FONT);
  302. key.primary = primary;
  303. entry = find234(conf->tree, &key, NULL);
  304. assert(entry);
  305. return entry->value.u.fontval;
  306. }
  307. void conf_set_int(Conf *conf, int primary, int value)
  308. {
  309. struct conf_entry *entry = snew(struct conf_entry);
  310. assert(subkeytypes[primary] == TYPE_NONE);
  311. assert(valuetypes[primary] == TYPE_INT);
  312. entry->key.primary = primary;
  313. entry->value.u.intval = value;
  314. conf_insert(conf, entry);
  315. }
  316. void conf_set_int_int(Conf *conf, int primary, int secondary, int value)
  317. {
  318. struct conf_entry *entry = snew(struct conf_entry);
  319. assert(subkeytypes[primary] == TYPE_INT);
  320. assert(valuetypes[primary] == TYPE_INT);
  321. entry->key.primary = primary;
  322. entry->key.secondary.i = secondary;
  323. entry->value.u.intval = value;
  324. conf_insert(conf, entry);
  325. }
  326. void conf_set_str(Conf *conf, int primary, const char *value)
  327. {
  328. struct conf_entry *entry = snew(struct conf_entry);
  329. assert(subkeytypes[primary] == TYPE_NONE);
  330. assert(valuetypes[primary] == TYPE_STR);
  331. entry->key.primary = primary;
  332. entry->value.u.stringval = dupstr(value);
  333. conf_insert(conf, entry);
  334. }
  335. void conf_set_str_str(Conf *conf, int primary, const char *secondary,
  336. const char *value)
  337. {
  338. struct conf_entry *entry = snew(struct conf_entry);
  339. assert(subkeytypes[primary] == TYPE_STR);
  340. assert(valuetypes[primary] == TYPE_STR);
  341. entry->key.primary = primary;
  342. entry->key.secondary.s = dupstr(secondary);
  343. entry->value.u.stringval = dupstr(value);
  344. conf_insert(conf, entry);
  345. }
  346. void conf_del_str_str(Conf *conf, int primary, const char *secondary)
  347. {
  348. struct key key;
  349. struct conf_entry *entry;
  350. assert(subkeytypes[primary] == TYPE_STR);
  351. assert(valuetypes[primary] == TYPE_STR);
  352. key.primary = primary;
  353. key.secondary.s = (char *)secondary;
  354. entry = find234(conf->tree, &key, NULL);
  355. if (entry) {
  356. del234(conf->tree, entry);
  357. free_entry(entry);
  358. }
  359. }
  360. void conf_set_filename(Conf *conf, int primary, const Filename *value)
  361. {
  362. struct conf_entry *entry = snew(struct conf_entry);
  363. assert(subkeytypes[primary] == TYPE_NONE);
  364. assert(valuetypes[primary] == TYPE_FILENAME);
  365. entry->key.primary = primary;
  366. entry->value.u.fileval = filename_copy(value);
  367. conf_insert(conf, entry);
  368. }
  369. void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value)
  370. {
  371. struct conf_entry *entry = snew(struct conf_entry);
  372. assert(subkeytypes[primary] == TYPE_NONE);
  373. assert(valuetypes[primary] == TYPE_FONT);
  374. entry->key.primary = primary;
  375. entry->value.u.fontval = fontspec_copy(value);
  376. conf_insert(conf, entry);
  377. }
  378. int conf_serialised_size(Conf *conf)
  379. {
  380. int i;
  381. struct conf_entry *entry;
  382. int size = 0;
  383. for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
  384. size += 4; /* primary key */
  385. switch (subkeytypes[entry->key.primary]) {
  386. case TYPE_INT:
  387. size += 4;
  388. break;
  389. case TYPE_STR:
  390. size += 1 + strlen(entry->key.secondary.s);
  391. break;
  392. }
  393. switch (valuetypes[entry->key.primary]) {
  394. case TYPE_INT:
  395. size += 4;
  396. break;
  397. case TYPE_STR:
  398. size += 1 + strlen(entry->value.u.stringval);
  399. break;
  400. case TYPE_FILENAME:
  401. size += filename_serialise(entry->value.u.fileval, NULL);
  402. break;
  403. case TYPE_FONT:
  404. size += fontspec_serialise(entry->value.u.fontval, NULL);
  405. break;
  406. }
  407. }
  408. size += 4; /* terminator value */
  409. return size;
  410. }
  411. void conf_serialise(Conf *conf, void *vdata)
  412. {
  413. unsigned char *data = (unsigned char *)vdata;
  414. int i, len;
  415. struct conf_entry *entry;
  416. for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
  417. PUT_32BIT_MSB_FIRST(data, entry->key.primary);
  418. data += 4;
  419. switch (subkeytypes[entry->key.primary]) {
  420. case TYPE_INT:
  421. PUT_32BIT_MSB_FIRST(data, entry->key.secondary.i);
  422. data += 4;
  423. break;
  424. case TYPE_STR:
  425. len = strlen(entry->key.secondary.s);
  426. memcpy(data, entry->key.secondary.s, len);
  427. data += len;
  428. *data++ = 0;
  429. break;
  430. }
  431. switch (valuetypes[entry->key.primary]) {
  432. case TYPE_INT:
  433. PUT_32BIT_MSB_FIRST(data, entry->value.u.intval);
  434. data += 4;
  435. break;
  436. case TYPE_STR:
  437. len = strlen(entry->value.u.stringval);
  438. memcpy(data, entry->value.u.stringval, len);
  439. data += len;
  440. *data++ = 0;
  441. break;
  442. case TYPE_FILENAME:
  443. data += filename_serialise(entry->value.u.fileval, data);
  444. break;
  445. case TYPE_FONT:
  446. data += fontspec_serialise(entry->value.u.fontval, data);
  447. break;
  448. }
  449. }
  450. PUT_32BIT_MSB_FIRST(data, 0xFFFFFFFFU);
  451. }
  452. int conf_deserialise(Conf *conf, void *vdata, int maxsize)
  453. {
  454. unsigned char *data = (unsigned char *)vdata;
  455. unsigned char *start = data;
  456. struct conf_entry *entry;
  457. unsigned primary;
  458. int used;
  459. unsigned char *zero;
  460. while (maxsize >= 4) {
  461. primary = GET_32BIT_MSB_FIRST(data);
  462. data += 4, maxsize -= 4;
  463. if (primary >= N_CONFIG_OPTIONS)
  464. break;
  465. entry = snew(struct conf_entry);
  466. entry->key.primary = primary;
  467. switch (subkeytypes[entry->key.primary]) {
  468. case TYPE_INT:
  469. if (maxsize < 4) {
  470. sfree(entry);
  471. goto done;
  472. }
  473. entry->key.secondary.i = toint(GET_32BIT_MSB_FIRST(data));
  474. data += 4, maxsize -= 4;
  475. break;
  476. case TYPE_STR:
  477. zero = memchr(data, 0, maxsize);
  478. if (!zero) {
  479. sfree(entry);
  480. goto done;
  481. }
  482. entry->key.secondary.s = dupstr((char *)data);
  483. maxsize -= (zero + 1 - data);
  484. data = zero + 1;
  485. break;
  486. }
  487. switch (valuetypes[entry->key.primary]) {
  488. case TYPE_INT:
  489. if (maxsize < 4) {
  490. if (subkeytypes[entry->key.primary] == TYPE_STR)
  491. sfree(entry->key.secondary.s);
  492. sfree(entry);
  493. goto done;
  494. }
  495. entry->value.u.intval = toint(GET_32BIT_MSB_FIRST(data));
  496. data += 4, maxsize -= 4;
  497. break;
  498. case TYPE_STR:
  499. zero = memchr(data, 0, maxsize);
  500. if (!zero) {
  501. if (subkeytypes[entry->key.primary] == TYPE_STR)
  502. sfree(entry->key.secondary.s);
  503. sfree(entry);
  504. goto done;
  505. }
  506. entry->value.u.stringval = dupstr((char *)data);
  507. maxsize -= (zero + 1 - data);
  508. data = zero + 1;
  509. break;
  510. case TYPE_FILENAME:
  511. entry->value.u.fileval =
  512. filename_deserialise(data, maxsize, &used);
  513. if (!entry->value.u.fileval) {
  514. if (subkeytypes[entry->key.primary] == TYPE_STR)
  515. sfree(entry->key.secondary.s);
  516. sfree(entry);
  517. goto done;
  518. }
  519. data += used;
  520. maxsize -= used;
  521. break;
  522. case TYPE_FONT:
  523. entry->value.u.fontval =
  524. fontspec_deserialise(data, maxsize, &used);
  525. if (!entry->value.u.fontval) {
  526. if (subkeytypes[entry->key.primary] == TYPE_STR)
  527. sfree(entry->key.secondary.s);
  528. sfree(entry);
  529. goto done;
  530. }
  531. data += used;
  532. maxsize -= used;
  533. break;
  534. }
  535. conf_insert(conf, entry);
  536. }
  537. done:
  538. return (int)(data - start);
  539. }