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