PageRenderTime 27ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/dialog.c

https://gitlab.com/imxieke/Putty
C | 478 lines | 391 code | 38 blank | 49 comment | 51 complexity | 17f5402817f0195a694b2dcc228f9d40 MD5 | raw file
  1. /*
  2. * dialog.c - a reasonably platform-independent mechanism for
  3. * describing dialog boxes.
  4. */
  5. #include <assert.h>
  6. #include <limits.h>
  7. #include <stdarg.h>
  8. #include <stdlib.h>
  9. #define DEFINE_INTORPTR_FNS
  10. #include "putty.h"
  11. #include "dialog.h"
  12. int ctrl_path_elements(const char *path)
  13. {
  14. int i = 1;
  15. while (*path) {
  16. if (*path == '/') i++;
  17. path++;
  18. }
  19. return i;
  20. }
  21. /* Return the number of matching path elements at the starts of p1 and p2,
  22. * or INT_MAX if the paths are identical. */
  23. int ctrl_path_compare(const char *p1, const char *p2)
  24. {
  25. int i = 0;
  26. while (*p1 || *p2) {
  27. if ((*p1 == '/' || *p1 == '\0') &&
  28. (*p2 == '/' || *p2 == '\0'))
  29. i++; /* a whole element matches, ooh */
  30. if (*p1 != *p2)
  31. return i; /* mismatch */
  32. p1++, p2++;
  33. }
  34. return INT_MAX; /* exact match */
  35. }
  36. struct controlbox *ctrl_new_box(void)
  37. {
  38. struct controlbox *ret = snew(struct controlbox);
  39. ret->nctrlsets = ret->ctrlsetsize = 0;
  40. ret->ctrlsets = NULL;
  41. ret->nfrees = ret->freesize = 0;
  42. ret->frees = NULL;
  43. ret->freefuncs = NULL;
  44. return ret;
  45. }
  46. void ctrl_free_box(struct controlbox *b)
  47. {
  48. int i;
  49. for (i = 0; i < b->nctrlsets; i++) {
  50. ctrl_free_set(b->ctrlsets[i]);
  51. }
  52. for (i = 0; i < b->nfrees; i++)
  53. b->freefuncs[i](b->frees[i]);
  54. sfree(b->ctrlsets);
  55. sfree(b->frees);
  56. sfree(b->freefuncs);
  57. sfree(b);
  58. }
  59. void ctrl_free_set(struct controlset *s)
  60. {
  61. int i;
  62. sfree(s->pathname);
  63. sfree(s->boxname);
  64. sfree(s->boxtitle);
  65. for (i = 0; i < s->ncontrols; i++) {
  66. ctrl_free(s->ctrls[i]);
  67. }
  68. sfree(s->ctrls);
  69. sfree(s);
  70. }
  71. /*
  72. * Find the index of first controlset in a controlbox for a given
  73. * path. If that path doesn't exist, return the index where it
  74. * should be inserted.
  75. */
  76. static int ctrl_find_set(struct controlbox *b, const char *path, int start)
  77. {
  78. int i, last, thisone;
  79. last = 0;
  80. for (i = 0; i < b->nctrlsets; i++) {
  81. thisone = ctrl_path_compare(path, b->ctrlsets[i]->pathname);
  82. /*
  83. * If `start' is true and there exists a controlset with
  84. * exactly the path we've been given, we should return the
  85. * index of the first such controlset we find. Otherwise,
  86. * we should return the index of the first entry in which
  87. * _fewer_ path elements match than they did last time.
  88. */
  89. if ((start && thisone == INT_MAX) || thisone < last)
  90. return i;
  91. last = thisone;
  92. }
  93. return b->nctrlsets; /* insert at end */
  94. }
  95. /*
  96. * Find the index of next controlset in a controlbox for a given
  97. * path, or -1 if no such controlset exists. If -1 is passed as
  98. * input, finds the first.
  99. */
  100. int ctrl_find_path(struct controlbox *b, const char *path, int index)
  101. {
  102. if (index < 0)
  103. index = ctrl_find_set(b, path, 1);
  104. else
  105. index++;
  106. if (index < b->nctrlsets && !strcmp(path, b->ctrlsets[index]->pathname))
  107. return index;
  108. else
  109. return -1;
  110. }
  111. /* Set up a panel title. */
  112. struct controlset *ctrl_settitle(struct controlbox *b,
  113. const char *path, const char *title)
  114. {
  115. struct controlset *s = snew(struct controlset);
  116. int index = ctrl_find_set(b, path, 1);
  117. s->pathname = dupstr(path);
  118. s->boxname = NULL;
  119. s->boxtitle = dupstr(title);
  120. s->ncontrols = s->ctrlsize = 0;
  121. s->ncolumns = 0; /* this is a title! */
  122. s->ctrls = NULL;
  123. if (b->nctrlsets >= b->ctrlsetsize) {
  124. b->ctrlsetsize = b->nctrlsets + 32;
  125. b->ctrlsets = sresize(b->ctrlsets, b->ctrlsetsize,struct controlset *);
  126. }
  127. if (index < b->nctrlsets)
  128. memmove(&b->ctrlsets[index+1], &b->ctrlsets[index],
  129. (b->nctrlsets-index) * sizeof(*b->ctrlsets));
  130. b->ctrlsets[index] = s;
  131. b->nctrlsets++;
  132. return s;
  133. }
  134. /* Retrieve a pointer to a controlset, creating it if absent. */
  135. struct controlset *ctrl_getset(struct controlbox *b, const char *path,
  136. const char *name, const char *boxtitle)
  137. {
  138. struct controlset *s;
  139. int index = ctrl_find_set(b, path, 1);
  140. while (index < b->nctrlsets &&
  141. !strcmp(b->ctrlsets[index]->pathname, path)) {
  142. if (b->ctrlsets[index]->boxname &&
  143. !strcmp(b->ctrlsets[index]->boxname, name))
  144. return b->ctrlsets[index];
  145. index++;
  146. }
  147. s = snew(struct controlset);
  148. s->pathname = dupstr(path);
  149. s->boxname = dupstr(name);
  150. s->boxtitle = boxtitle ? dupstr(boxtitle) : NULL;
  151. s->ncolumns = 1;
  152. s->ncontrols = s->ctrlsize = 0;
  153. s->ctrls = NULL;
  154. if (b->nctrlsets >= b->ctrlsetsize) {
  155. b->ctrlsetsize = b->nctrlsets + 32;
  156. b->ctrlsets = sresize(b->ctrlsets, b->ctrlsetsize,struct controlset *);
  157. }
  158. if (index < b->nctrlsets)
  159. memmove(&b->ctrlsets[index+1], &b->ctrlsets[index],
  160. (b->nctrlsets-index) * sizeof(*b->ctrlsets));
  161. b->ctrlsets[index] = s;
  162. b->nctrlsets++;
  163. return s;
  164. }
  165. /* Allocate some private data in a controlbox. */
  166. void *ctrl_alloc_with_free(struct controlbox *b, size_t size,
  167. ctrl_freefn_t freefunc)
  168. {
  169. void *p;
  170. /*
  171. * This is an internal allocation routine, so it's allowed to
  172. * use smalloc directly.
  173. */
  174. p = smalloc(size);
  175. if (b->nfrees >= b->freesize) {
  176. b->freesize = b->nfrees + 32;
  177. b->frees = sresize(b->frees, b->freesize, void *);
  178. b->freefuncs = sresize(b->freefuncs, b->freesize, ctrl_freefn_t);
  179. }
  180. b->frees[b->nfrees] = p;
  181. b->freefuncs[b->nfrees] = freefunc;
  182. b->nfrees++;
  183. return p;
  184. }
  185. static void ctrl_default_free(void *p)
  186. {
  187. sfree(p);
  188. }
  189. void *ctrl_alloc(struct controlbox *b, size_t size)
  190. {
  191. return ctrl_alloc_with_free(b, size, ctrl_default_free);
  192. }
  193. static union control *ctrl_new(struct controlset *s, int type,
  194. intorptr helpctx, handler_fn handler,
  195. intorptr context)
  196. {
  197. union control *c = snew(union control);
  198. if (s->ncontrols >= s->ctrlsize) {
  199. s->ctrlsize = s->ncontrols + 32;
  200. s->ctrls = sresize(s->ctrls, s->ctrlsize, union control *);
  201. }
  202. s->ctrls[s->ncontrols++] = c;
  203. /*
  204. * Fill in the standard fields.
  205. */
  206. c->generic.type = type;
  207. c->generic.tabdelay = 0;
  208. c->generic.column = COLUMN_FIELD(0, s->ncolumns);
  209. c->generic.helpctx = helpctx;
  210. c->generic.handler = handler;
  211. c->generic.context = context;
  212. c->generic.label = NULL;
  213. return c;
  214. }
  215. /* `ncolumns' is followed by that many percentages, as integers. */
  216. union control *ctrl_columns(struct controlset *s, int ncolumns, ...)
  217. {
  218. union control *c = ctrl_new(s, CTRL_COLUMNS, P(NULL), NULL, P(NULL));
  219. assert(s->ncolumns == 1 || ncolumns == 1);
  220. c->columns.ncols = ncolumns;
  221. s->ncolumns = ncolumns;
  222. if (ncolumns == 1) {
  223. c->columns.percentages = NULL;
  224. } else {
  225. va_list ap;
  226. int i;
  227. c->columns.percentages = snewn(ncolumns, int);
  228. va_start(ap, ncolumns);
  229. for (i = 0; i < ncolumns; i++)
  230. c->columns.percentages[i] = va_arg(ap, int);
  231. va_end(ap);
  232. }
  233. return c;
  234. }
  235. union control *ctrl_editbox(struct controlset *s, const char *label,
  236. char shortcut, int percentage,
  237. intorptr helpctx, handler_fn handler,
  238. intorptr context, intorptr context2)
  239. {
  240. union control *c = ctrl_new(s, CTRL_EDITBOX, helpctx, handler, context);
  241. c->editbox.label = label ? dupstr(label) : NULL;
  242. c->editbox.shortcut = shortcut;
  243. c->editbox.percentwidth = percentage;
  244. c->editbox.password = 0;
  245. c->editbox.has_list = 0;
  246. c->editbox.context2 = context2;
  247. return c;
  248. }
  249. union control *ctrl_combobox(struct controlset *s, const char *label,
  250. char shortcut, int percentage,
  251. intorptr helpctx, handler_fn handler,
  252. intorptr context, intorptr context2)
  253. {
  254. union control *c = ctrl_new(s, CTRL_EDITBOX, helpctx, handler, context);
  255. c->editbox.label = label ? dupstr(label) : NULL;
  256. c->editbox.shortcut = shortcut;
  257. c->editbox.percentwidth = percentage;
  258. c->editbox.password = 0;
  259. c->editbox.has_list = 1;
  260. c->editbox.context2 = context2;
  261. return c;
  262. }
  263. /*
  264. * `ncolumns' is followed by (alternately) radio button titles and
  265. * intorptrs, until a NULL in place of a title string is seen. Each
  266. * title is expected to be followed by a shortcut _iff_ `shortcut'
  267. * is NO_SHORTCUT.
  268. */
  269. union control *ctrl_radiobuttons(struct controlset *s, const char *label,
  270. char shortcut, int ncolumns, intorptr helpctx,
  271. handler_fn handler, intorptr context, ...)
  272. {
  273. va_list ap;
  274. int i;
  275. union control *c = ctrl_new(s, CTRL_RADIO, helpctx, handler, context);
  276. c->radio.label = label ? dupstr(label) : NULL;
  277. c->radio.shortcut = shortcut;
  278. c->radio.ncolumns = ncolumns;
  279. /*
  280. * Initial pass along variable argument list to count the
  281. * buttons.
  282. */
  283. va_start(ap, context);
  284. i = 0;
  285. while (va_arg(ap, char *) != NULL) {
  286. i++;
  287. if (c->radio.shortcut == NO_SHORTCUT)
  288. (void)va_arg(ap, int); /* char promotes to int in arg lists */
  289. (void)va_arg(ap, intorptr);
  290. }
  291. va_end(ap);
  292. c->radio.nbuttons = i;
  293. if (c->radio.shortcut == NO_SHORTCUT)
  294. c->radio.shortcuts = snewn(c->radio.nbuttons, char);
  295. else
  296. c->radio.shortcuts = NULL;
  297. c->radio.buttons = snewn(c->radio.nbuttons, char *);
  298. c->radio.buttondata = snewn(c->radio.nbuttons, intorptr);
  299. /*
  300. * Second pass along variable argument list to actually fill in
  301. * the structure.
  302. */
  303. va_start(ap, context);
  304. for (i = 0; i < c->radio.nbuttons; i++) {
  305. c->radio.buttons[i] = dupstr(va_arg(ap, char *));
  306. if (c->radio.shortcut == NO_SHORTCUT)
  307. c->radio.shortcuts[i] = va_arg(ap, int);
  308. /* char promotes to int in arg lists */
  309. c->radio.buttondata[i] = va_arg(ap, intorptr);
  310. }
  311. va_end(ap);
  312. return c;
  313. }
  314. union control *ctrl_pushbutton(struct controlset *s, const char *label,
  315. char shortcut, intorptr helpctx,
  316. handler_fn handler, intorptr context)
  317. {
  318. union control *c = ctrl_new(s, CTRL_BUTTON, helpctx, handler, context);
  319. c->button.label = label ? dupstr(label) : NULL;
  320. c->button.shortcut = shortcut;
  321. c->button.isdefault = 0;
  322. c->button.iscancel = 0;
  323. return c;
  324. }
  325. union control *ctrl_listbox(struct controlset *s, const char *label,
  326. char shortcut, intorptr helpctx,
  327. handler_fn handler, intorptr context)
  328. {
  329. union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context);
  330. c->listbox.label = label ? dupstr(label) : NULL;
  331. c->listbox.shortcut = shortcut;
  332. c->listbox.height = 5; /* *shrug* a plausible default */
  333. c->listbox.draglist = 0;
  334. c->listbox.multisel = 0;
  335. c->listbox.percentwidth = 100;
  336. c->listbox.ncols = 0;
  337. c->listbox.percentages = NULL;
  338. c->listbox.hscroll = TRUE;
  339. return c;
  340. }
  341. union control *ctrl_droplist(struct controlset *s, const char *label,
  342. char shortcut, int percentage, intorptr helpctx,
  343. handler_fn handler, intorptr context)
  344. {
  345. union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context);
  346. c->listbox.label = label ? dupstr(label) : NULL;
  347. c->listbox.shortcut = shortcut;
  348. c->listbox.height = 0; /* means it's a drop-down list */
  349. c->listbox.draglist = 0;
  350. c->listbox.multisel = 0;
  351. c->listbox.percentwidth = percentage;
  352. c->listbox.ncols = 0;
  353. c->listbox.percentages = NULL;
  354. c->listbox.hscroll = FALSE;
  355. return c;
  356. }
  357. union control *ctrl_draglist(struct controlset *s, const char *label,
  358. char shortcut, intorptr helpctx,
  359. handler_fn handler, intorptr context)
  360. {
  361. union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context);
  362. c->listbox.label = label ? dupstr(label) : NULL;
  363. c->listbox.shortcut = shortcut;
  364. c->listbox.height = 5; /* *shrug* a plausible default */
  365. c->listbox.draglist = 1;
  366. c->listbox.multisel = 0;
  367. c->listbox.percentwidth = 100;
  368. c->listbox.ncols = 0;
  369. c->listbox.percentages = NULL;
  370. c->listbox.hscroll = FALSE;
  371. return c;
  372. }
  373. union control *ctrl_filesel(struct controlset *s, const char *label,
  374. char shortcut, const char *filter, int write,
  375. const char *title, intorptr helpctx,
  376. handler_fn handler, intorptr context)
  377. {
  378. union control *c = ctrl_new(s, CTRL_FILESELECT, helpctx, handler, context);
  379. c->fileselect.label = label ? dupstr(label) : NULL;
  380. c->fileselect.shortcut = shortcut;
  381. c->fileselect.filter = filter;
  382. c->fileselect.for_writing = write;
  383. c->fileselect.title = dupstr(title);
  384. return c;
  385. }
  386. union control *ctrl_fontsel(struct controlset *s, const char *label,
  387. char shortcut, intorptr helpctx,
  388. handler_fn handler, intorptr context)
  389. {
  390. union control *c = ctrl_new(s, CTRL_FONTSELECT, helpctx, handler, context);
  391. c->fontselect.label = label ? dupstr(label) : NULL;
  392. c->fontselect.shortcut = shortcut;
  393. return c;
  394. }
  395. union control *ctrl_tabdelay(struct controlset *s, union control *ctrl)
  396. {
  397. union control *c = ctrl_new(s, CTRL_TABDELAY, P(NULL), NULL, P(NULL));
  398. c->tabdelay.ctrl = ctrl;
  399. return c;
  400. }
  401. union control *ctrl_text(struct controlset *s, const char *text,
  402. intorptr helpctx)
  403. {
  404. union control *c = ctrl_new(s, CTRL_TEXT, helpctx, NULL, P(NULL));
  405. c->text.label = dupstr(text);
  406. return c;
  407. }
  408. union control *ctrl_checkbox(struct controlset *s, const char *label,
  409. char shortcut, intorptr helpctx,
  410. handler_fn handler, intorptr context)
  411. {
  412. union control *c = ctrl_new(s, CTRL_CHECKBOX, helpctx, handler, context);
  413. c->checkbox.label = label ? dupstr(label) : NULL;
  414. c->checkbox.shortcut = shortcut;
  415. return c;
  416. }
  417. void ctrl_free(union control *ctrl)
  418. {
  419. int i;
  420. sfree(ctrl->generic.label);
  421. switch (ctrl->generic.type) {
  422. case CTRL_RADIO:
  423. for (i = 0; i < ctrl->radio.nbuttons; i++)
  424. sfree(ctrl->radio.buttons[i]);
  425. sfree(ctrl->radio.buttons);
  426. sfree(ctrl->radio.shortcuts);
  427. sfree(ctrl->radio.buttondata);
  428. break;
  429. case CTRL_COLUMNS:
  430. sfree(ctrl->columns.percentages);
  431. break;
  432. case CTRL_LISTBOX:
  433. sfree(ctrl->listbox.percentages);
  434. break;
  435. case CTRL_FILESELECT:
  436. sfree(ctrl->fileselect.title);
  437. break;
  438. }
  439. sfree(ctrl);
  440. }