dhcpcd: update README.DRAGONFLY
[dragonfly.git] / contrib / dialog / guage.c
blobc53e37c17a6e80e9611a8bdebf81dda487ba1f01
1 /*
2 * $Id: guage.c,v 1.85 2022/04/03 22:38:16 tom Exp $
4 * guage.c -- implements the gauge dialog
6 * Copyright 2000-2021,2022 Thomas E. Dickey
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License, version 2.1
10 * as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program; if not, write to
19 * Free Software Foundation, Inc.
20 * 51 Franklin St., Fifth Floor
21 * Boston, MA 02110, USA.
23 * An earlier version of this program lists as authors
24 * Marc Ewing, Red Hat Software
27 #include <dlg_internals.h>
29 #define MY_LEN (MAX_LEN)/2
31 #define MIN_HIGH (4)
32 #define MIN_WIDE (10 + 2 * (2 + MARGIN))
34 #define isMarker(buf) !strncmp(buf, "XXX", (size_t) 3)
36 typedef struct _my_obj {
37 DIALOG_CALLBACK obj; /* has to be first in struct */
38 struct _my_obj *next;
39 WINDOW *text;
40 char *title;
41 char *prompt;
42 char prompt_buf[MY_LEN];
43 int percent;
44 int height;
45 int width;
46 char line[MAX_LEN + 1];
47 } MY_OBJ;
49 static MY_OBJ *all_objects;
51 static int
52 valid(MY_OBJ * obj)
54 MY_OBJ *list = all_objects;
55 int result = 0;
57 while (list != 0) {
58 if (list == obj) {
59 result = 1;
60 break;
62 list = list->next;
64 return result;
67 static void
68 delink(MY_OBJ * obj)
70 MY_OBJ *p = all_objects;
71 MY_OBJ *q = 0;
72 while (p != 0) {
73 if (p == obj) {
74 if (q != 0) {
75 q->next = p->next;
76 } else {
77 all_objects = p->next;
79 break;
81 q = p;
82 p = p->next;
86 static int
87 read_data(char *buffer, FILE *fp)
89 int result;
91 if (feof(fp)) {
92 result = 0;
93 } else if (fgets(buffer, MY_LEN, fp) != 0) {
94 DLG_TRACE(("read_data:%s", buffer));
95 buffer[MY_LEN] = '\0';
96 dlg_trim_string(buffer);
97 result = 1;
98 } else {
99 result = -1;
101 return result;
104 static int
105 decode_percent(char *buffer)
107 char *tmp = 0;
108 long value = strtol(buffer, &tmp, 10);
110 if (tmp != 0 && (*tmp == 0 || isspace(UCH(*tmp))) && value >= 0) {
111 return TRUE;
113 return FALSE;
116 static void
117 repaint_text(MY_OBJ * obj)
119 WINDOW *dialog = obj->obj.win;
121 if (dialog != 0) {
122 int i, x;
124 (void) werase(dialog);
125 dlg_draw_box2(dialog, 0, 0, obj->height, obj->width, dialog_attr,
126 border_attr, border2_attr);
128 dlg_draw_title(dialog, obj->title);
130 dlg_attrset(dialog, dialog_attr);
131 dlg_draw_helpline(dialog, FALSE);
132 dlg_print_autowrap(dialog, obj->prompt, obj->height, obj->width);
134 dlg_draw_box2(dialog,
135 obj->height - 4, 2 + MARGIN,
136 2 + MARGIN, obj->width - 2 * (2 + MARGIN),
137 dialog_attr,
138 border_attr,
139 border2_attr);
142 * Clear the area for the progress bar by filling it with spaces
143 * in the gauge-attribute, and write the percentage with that
144 * attribute.
146 (void) wmove(dialog, obj->height - 3, 4);
147 dlg_attrset(dialog, gauge_attr);
149 for (i = 0; i < (obj->width - 2 * (3 + MARGIN)); i++)
150 (void) waddch(dialog, ' ');
152 (void) wmove(dialog, obj->height - 3, (obj->width / 2) - 2);
153 (void) wprintw(dialog, "%3d%%", obj->percent);
156 * Now draw a bar in reverse, relative to the background.
157 * The window attribute was useful for painting the background,
158 * but requires some tweaks to reverse it.
160 x = (obj->percent * (obj->width - 2 * (3 + MARGIN))) / 100;
161 if ((gauge_attr & A_REVERSE) != 0) {
162 dlg_attroff(dialog, A_REVERSE);
163 } else {
164 dlg_attrset(dialog, A_REVERSE);
166 (void) wmove(dialog, obj->height - 3, 4);
167 for (i = 0; i < x; i++) {
168 chtype ch2 = winch(dialog);
169 if (gauge_attr & A_REVERSE) {
170 ch2 &= ~A_REVERSE;
172 (void) waddch(dialog, ch2);
175 (void) wrefresh(dialog);
179 static bool
180 handle_input(DIALOG_CALLBACK * cb)
182 MY_OBJ *obj = (MY_OBJ *) cb;
183 FILE *my_input = ((obj != NULL && obj->obj.input != NULL)
184 ? obj->obj.input
185 : dialog_state.pipe_input);
186 bool result;
187 bool cleanup = FALSE;
188 int status;
189 char buf[MY_LEN + 1];
191 if (my_input == NULL) {
192 status = -1;
193 cleanup = TRUE;
194 } else if ((status = read_data(buf, my_input)) > 0) {
196 if (isMarker(buf)) {
198 * Historically, next line should be percentage, but one of the
199 * worse-written clones of 'dialog' assumes the number is missing.
200 * (Gresham's Law applied to software).
202 if ((status = read_data(buf, my_input)) > 0) {
204 obj->prompt_buf[0] = '\0';
205 if (decode_percent(buf))
206 obj->percent = atoi(buf);
207 else
208 strcpy(obj->prompt_buf, buf);
210 /* Rest is message text */
211 while ((status = read_data(buf, my_input)) > 0
212 && !isMarker(buf)) {
213 if (strlen(obj->prompt_buf) + strlen(buf) <
214 sizeof(obj->prompt_buf) - 1) {
215 strcat(obj->prompt_buf, buf);
219 if (obj->prompt != obj->prompt_buf)
220 free(obj->prompt);
221 obj->prompt = obj->prompt_buf;
223 } else if (decode_percent(buf)) {
224 obj->percent = atoi(buf);
226 } else {
227 if (feof(my_input) ||
228 (ferror(my_input) && errno != EINTR)) {
229 cleanup = TRUE;
233 repaint_text(obj);
234 if (status > 0) {
235 result = TRUE;
236 } else {
237 result = FALSE;
238 if (cleanup) {
239 dlg_remove_callback(cb);
240 delink(obj);
244 return result;
247 static bool
248 handle_my_getc(DIALOG_CALLBACK * cb, int ch, int fkey, int *result)
250 bool status = TRUE;
252 *result = DLG_EXIT_OK;
253 if (cb != 0) {
254 if (!fkey && (ch == ERR)) {
255 (void) handle_input(cb);
256 /* cb might be freed in handle_input */
257 status = (valid((MY_OBJ *) cb) && (cb->input != 0));
259 } else {
260 status = FALSE;
262 return status;
265 static void
266 my_cleanup(DIALOG_CALLBACK * cb)
268 MY_OBJ *obj = (MY_OBJ *) cb;
270 if (valid(obj)) {
271 if (obj->prompt != obj->prompt_buf) {
272 free(obj->prompt);
273 obj->prompt = obj->prompt_buf;
275 free(obj->title);
276 dlg_del_window(obj->obj.win);
277 delink(obj);
281 void
282 dlg_update_gauge(void *objptr, int percent)
284 MY_OBJ *obj = (MY_OBJ *) objptr;
285 bool save_finish_string = dialog_state.finish_string;
287 dialog_state.finish_string = TRUE;
288 curs_set(0);
289 obj->percent = percent;
290 repaint_text(obj);
291 dialog_state.finish_string = save_finish_string;
295 * (Re)Allocates an object and fills it as per the arguments
297 void *
298 dlg_reallocate_gauge(void *objptr,
299 const char *title,
300 const char *cprompt,
301 int height,
302 int width,
303 int percent)
305 char *prompt = dlg_strclone(cprompt);
306 MY_OBJ *obj;
307 bool save_finish_string = dialog_state.finish_string;
309 dialog_state.finish_string = TRUE;
310 dlg_tab_correct_str(prompt);
312 if (objptr == 0) {
313 /* create a new object */
314 obj = dlg_calloc(MY_OBJ, 1);
315 assert_ptr(obj, "dialog_gauge");
317 dlg_auto_size(title, prompt, &height, &width, MIN_HIGH, MIN_WIDE);
318 dlg_print_size(height, width);
319 dlg_ctl_size(height, width);
321 } else {
322 /* reuse an existing object */
323 obj = objptr;
324 height = obj->height;
325 width = obj->width;
328 if (obj->obj.win == 0) {
329 /* center dialog box on screen */
330 int x = dlg_box_x_ordinate(width);
331 int y = dlg_box_y_ordinate(height);
332 WINDOW *dialog = dlg_new_window(height, width, y, x);
333 obj->obj.win = dialog;
336 obj->obj.input = dialog_state.pipe_input;
337 obj->obj.keep_win = TRUE;
338 obj->obj.bg_task = TRUE;
339 obj->obj.handle_getc = handle_my_getc;
340 obj->obj.handle_input = handle_input;
342 if (obj->title == 0 || strcmp(obj->title, title)) {
343 dlg_finish_string(obj->title);
344 free(obj->title);
345 obj->title = dlg_strclone(title);
348 dlg_finish_string(obj->prompt);
349 free(obj->prompt);
351 obj->prompt = prompt;
352 obj->percent = percent;
353 obj->height = height;
354 obj->width = width;
356 /* if this was a new object, link it into the list */
357 if (objptr == 0) {
358 obj->next = all_objects;
359 all_objects = obj;
362 dialog_state.finish_string = save_finish_string;
363 return (void *) obj;
366 void *
367 dlg_allocate_gauge(const char *title,
368 const char *cprompt,
369 int height,
370 int width,
371 int percent)
373 return dlg_reallocate_gauge(NULL, title, cprompt, height, width, percent);
376 void
377 dlg_free_gauge(void *objptr)
379 MY_OBJ *obj = (MY_OBJ *) objptr;
381 if (valid(obj)) {
382 if (obj->title)
383 free(obj->title);
384 if (obj->prompt)
385 free(obj->prompt);
386 obj->obj.keep_win = FALSE;
387 dlg_remove_callback(&(obj->obj));
388 delink(obj);
390 curs_set(1);
394 * Display a gauge, or progress meter. Starts at percent% and reads stdin. If
395 * stdin is not XXX, then it is interpreted as a percentage, and the display is
396 * updated accordingly. Otherwise the next line is the percentage, and
397 * subsequent lines up to another XXX are used for the new prompt. Note that
398 * the size of the window never changes, so the prompt can not get any larger
399 * than the height and width specified.
402 dialog_gauge(const char *title,
403 const char *cprompt,
404 int height,
405 int width,
406 int percent)
408 int fkey;
409 int ch, result;
410 void *objptr = dlg_allocate_gauge(title, cprompt, height, width, percent);
411 MY_OBJ *obj = (MY_OBJ *) objptr;
413 DLG_TRACE(("# gauge args:\n"));
414 DLG_TRACE2S("title", title);
415 DLG_TRACE2S("message", cprompt);
416 DLG_TRACE2N("height", height);
417 DLG_TRACE2N("width", width);
418 DLG_TRACE2N("percent", percent);
420 dlg_add_callback_ref((DIALOG_CALLBACK **) & obj, my_cleanup);
421 dlg_update_gauge(obj, percent);
423 dlg_trace_win(obj->obj.win);
424 do {
425 ch = dlg_getc(obj->obj.win, &fkey);
426 #ifdef KEY_RESIZE
427 if (fkey && ch == KEY_RESIZE) {
428 MY_OBJ *oldobj = obj;
430 dlg_will_resize(obj->obj.win);
432 obj = dlg_allocate_gauge(title,
433 cprompt,
434 height,
435 width,
436 oldobj->percent);
438 /* avoid breaking new window in dlg_remove_callback */
439 oldobj->obj.caller = 0;
440 oldobj->obj.input = 0;
441 oldobj->obj.keep_win = FALSE;
443 /* remove the old version of the gauge */
444 _dlg_resize_cleanup(oldobj->obj.win);
445 dlg_remove_callback(&(oldobj->obj));
447 dlg_add_callback_ref((DIALOG_CALLBACK **) & obj, my_cleanup);
448 dlg_update_gauge(obj, obj->percent);
450 #endif
452 while (valid(obj) && handle_my_getc(&(obj->obj), ch, fkey, &result));
454 dlg_free_gauge(obj);
456 return (DLG_EXIT_OK);