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