]> git.ipfire.org Git - thirdparty/newt.git/blame - whiptail.c
0.52.13
[thirdparty/newt.git] / whiptail.c
CommitLineData
feef2cb5 1#include "config.h"
9be6d60e 2#include <fcntl.h>
6c049389 3#include <popt.h>
9be6d60e 4#include <stdio.h>
5#include <string.h>
6#include <stdlib.h>
feef2cb5 7#include <signal.h>
9be6d60e 8#include <unistd.h>
feef2cb5 9#include <wchar.h>
10#include <slang.h>
83fe9fde 11#include <sys/stat.h>
9be6d60e 12
feef2cb5 13#include "nls.h"
649a0152 14#include "dialogboxes.h"
9be6d60e 15#include "newt.h"
feef2cb5 16#include "newt_pr.h"
17
18enum { NO_ERROR = 0, WAS_ERROR = 1 };
9be6d60e 19
139f06bc 20enum mode { MODE_NONE, MODE_INFOBOX, MODE_MSGBOX, MODE_YESNO, MODE_CHECKLIST,
feef2cb5 21 MODE_INPUTBOX, MODE_RADIOLIST, MODE_MENU, MODE_GAUGE ,
22 MODE_TEXTBOX, MODE_PASSWORDBOX};
9be6d60e 23
6f481af2 24#define OPT_MSGBOX 1000
25#define OPT_CHECKLIST 1001
26#define OPT_YESNO 1002
27#define OPT_INPUTBOX 1003
28#define OPT_FULLBUTTONS 1004
29#define OPT_MENU 1005
30#define OPT_RADIOLIST 1006
31#define OPT_GAUGE 1007
32#define OPT_INFOBOX 1008
feef2cb5 33#define OPT_TEXTBOX 1009
34#define OPT_PASSWORDBOX 1010
35
36static void usage(int err) {
37 newtFinished();
38 fprintf (err ? stderr : stdout,
39 _("Box options: \n"
40 "\t--msgbox <text> <height> <width>\n"
41 "\t--yesno <text> <height> <width>\n"
42 "\t--infobox <text> <height> <width>\n"
43 "\t--inputbox <text> <height> <width> [init] \n"
44 "\t--passwordbox <text> <height> <width> [init] \n"
45 "\t--textbox <file> <height> <width>\n"
46 "\t--menu <text> <height> <width> <listheight> [tag item] ...\n"
47 "\t--checklist <text> <height> <width> <listheight> [tag item status]...\n"
30cfd38a 48 "\t--radiolist <text> <height> <width> <listheight> [tag item status]...\n"
feef2cb5 49 "\t--gauge <text> <height> <width> <percent>\n"
50 "Options: (depend on box-option)\n"
51 "\t--clear clear screen on exit\n"
45bffa7f 52 "\t--defaultno default no button\n"
feef2cb5 53 "\t--default-item <string> set default string\n"
54 "\t--fb use full buttons\n"
55 "\t--nocancel no cancel button\n"
60d3c50b 56 "\t--yes-button <text> set text of yes button\n"
57 "\t--no-button <text> set text of no button\n"
58 "\t--ok-button <text> set text of ok button\n"
59 "\t--cancel-button <text> set text of cancel button\n"
feef2cb5 60 "\t--noitem display tags only\n"
f0e9344a 61 "\t--separate-output output one line at a time\n"
feef2cb5 62 "\t--output-fd <fd> output to fd, not stdout\n"
63 "\t--title <title> display title\n"
64 "\t--backtitle <backtitle> display backtitle\n"
c3a3b1e8 65 "\t--scrolltext force vertical scrollbars\n"
45bffa7f 66 "\t--topleft put window in top-left corner\n\n"));
feef2cb5 67 exit(err ? DLG_ERROR : 0 );
68}
69
70static void print_version(void) {
71 fprintf (stdout, _("whiptail (newt): %s\n"), VERSION);
72}
73
feef2cb5 74#if 0
75/* FIXME Copied from newt.c
76 * Place somewhere better -- dialogboxes? -- amck
77 */
78int wstrlen(const char *str, int len) {
79 mbstate_t ps;
80 wchar_t tmp;
81 int nchars = 0;
82
83 if (!str) return 0;
84 if (!len) return 0;
85 if (len < 0) len = strlen(str);
86 memset(&ps,0,sizeof(mbstate_t));
87 while (len > 0) {
88 int x,y;
89
90 x = mbrtowc(&tmp,str,len,&ps);
91 if (x >0) {
92 str += x;
93 len -= x;
94 y = wcwidth(tmp);
95 if (y>0)
96 nchars+=y;
97 } else break;
98 }
99 return nchars;
100}
101#endif
102
103/*
104 * The value of *width is increased if it is not as large as the width of
105 * the line.
106 */
107static const char * lineWidth(int * width, const char * line, int *chrs)
108{
109 const char * s = line;
110
111 if ( line == NULL )
112 return 0;
113
114 while ( *s != '\0' && *s != '\n' )
115 s++;
116
117 if ( *s == '\n' )
118 s++;
119
120 *chrs = _newt_wstrlen (line, s - line );
121 *width = max(*width, *chrs);
9be6d60e 122
feef2cb5 123 return s;
124}
125
126
127/*
128 * cleanNewlines
129 * Handle newlines in text. Hack.
130 */
131void cleanNewlines (char *text)
132{
ba84e71b
ML
133 char *p, *q;
134
135 for (p = q = text; *p; p++, q++)
136 if (*p == '\\' && p[1] == 'n') {
137 p++;
138 *q = '\n';
139 } else
140 *q = *p;
141 *q = '\0';
feef2cb5 142}
143
144/*
145 * The height of a text string is added to height, and width is increased
146 * if it is not big enough to store the text string.
147 */
148static const char * textSize(int * height, int * width,
149 int maxWidth,
150 const char * text)
151{
152 int h = 0;
153 int w = 0;
154 int chrs = 0;
155
156
157 if ( text == NULL )
158 return 0;
159
160 while ( *text != '\0' ) {
161 h++;
162 text = lineWidth(width, text, &chrs);
163 /* Allow for text overflowing. May overestimate a bit */
164 h += chrs / maxWidth;
165 }
166
167 h += 2;
168 w += 2;
169
170 *height += h;
171 *width += w;
172
173 *width = min(*width, maxWidth);
174 return text;
175}
176
177/*
178 * Add space for buttons.
179 * NOTE: when this is internationalized, the button width might change.
180 */
181static void spaceForButtons(int * height, int * width, int count, int full) {
182 /* Make space for the buttons */
183 if ( full ) {
184 *height += 4;
185 if ( count == 1 )
186 *width = max(*width, 7);
187 else
188 *width = max(*width, 20);
189 }
190 else {
191 *height += 2;
192 if ( count == 1 )
193 *width = max(*width, 7);
194 else
195 *width = max(*width, 19);
196 }
197}
198
199static int menuSize(int * height, int * width, enum mode mode,
200 poptContext options) {
c00a8d1b 201 const char ** argv = poptGetArgs(options);
202 const char ** items = argv;
feef2cb5 203 int h = 0;
204 int tagWidth = 0;
205 int descriptionWidth = 0;
206 int overhead = 10;
207 static char buf[20];
208
209 if ( argv == 0 || *argv == 0 )
210 return 0;
211
212 argv++;
213 if ( mode == MODE_MENU )
214 overhead = 5;
215
216 while ( argv[0] != 0 && argv[1] ) {
217 tagWidth = max(tagWidth, strlen(argv[0]));
218 descriptionWidth = max(descriptionWidth, strlen(argv[1]));
219
220 if ( mode == MODE_MENU )
221 argv += 2;
222 else
223 argv += 3;
224 h++;
225 }
226
227 *width = max(*width, tagWidth + descriptionWidth + overhead);
228 *width = min(*width, SLtt_Screen_Cols);
229
230 h = min(h, SLtt_Screen_Rows - *height - 4);
231 *height = *height + h + 1;
232 sprintf(buf, "%d", h);
233 *items = buf;
234 return 0;
235}
236
237/*
238 * Guess the size of a window, given what will be displayed within it.
239 */
240static void guessSize(int * height, int * width, enum mode mode,
241 int * flags, int fullButtons,
242 const char * title, const char * text,
243 poptContext options) {
244
245 int w = 0, h = 0, chrs = 0;
246
247 textSize(&h, &w, SLtt_Screen_Cols -4 , text); /* Width and height for text */
248 lineWidth(&w, title, &chrs); /* Width for title */
249
250 if ( w > 0 )
251 w += 4;
252
253 switch ( mode ) {
254 case MODE_CHECKLIST:
255 case MODE_RADIOLIST:
256 case MODE_MENU:
257 spaceForButtons(&h, &w, *flags & FLAG_NOCANCEL ? 1 : 2,
258 fullButtons);
259 menuSize(&h, &w, mode, options);
260 break;
261 case MODE_YESNO:
262 case MODE_MSGBOX:
263 spaceForButtons(&h, &w, 1, fullButtons);
264 break;
265 case MODE_INPUTBOX:
266 spaceForButtons(&h, &w, *flags & FLAG_NOCANCEL ? 1 : 2,
267 fullButtons);
268 h += 1;
269 break;
270 case MODE_GAUGE:
271 h += 2;
272 break;
273 case MODE_NONE:
274 break;
275 default:
276 break;
277 };
278
279 /*
280 * Fixed window-border overhead.
281 * NOTE: This will change if we add a way to turn off drop-shadow and/or
282 * box borders. That would be desirable for display-sized screens.
283 */
284 w += 2;
285 h += 2;
286
287 if ( h > SLtt_Screen_Rows - 1 ) {
288 h = SLtt_Screen_Rows - 1;
289 *flags |= FLAG_SCROLL_TEXT;
290 w += 2; /* Add width of slider - is this right? */
291 }
292
293 *width = min(max(*width, w), SLtt_Screen_Cols);
294 *height = max(*height, h);
9be6d60e 295}
296
585f5503 297char *
298readTextFile(const char * filename)
299{
300 int fd = open(filename, O_RDONLY, 0);
301 struct stat s;
302 char * buf;
303
304 if ( fd < 0 || fstat(fd, &s) != 0 ) {
305 perror(filename);
306 exit(DLG_ERROR);
307 }
308
3341bdc5 309 if ( (buf = malloc(s.st_size + 1)) == 0 ) {
feef2cb5 310 fprintf(stderr, _("%s: too large to display.\n"), filename);
3341bdc5
ML
311 exit(DLG_ERROR);
312 }
585f5503 313
314 if ( read(fd, buf, s.st_size) != s.st_size ) {
315 perror(filename);
316 exit(DLG_ERROR);
317 }
318 close(fd);
659af8f0 319 buf[s.st_size] = '\0';
585f5503 320 return buf;
321}
322
eb0a0545 323int main(int argc, const char ** argv) {
9be6d60e 324 enum mode mode = MODE_NONE;
325 poptContext optCon;
326 int arg;
feef2cb5 327 char * text;
eb0a0545 328 const char * nextArg;
9be6d60e 329 char * end;
330 int height;
331 int width;
649a0152 332 int fd = -1;
333 int needSpace = 0;
9be6d60e 334 int noCancel = 0;
57cb49e7 335 int noTags = 0;
9be6d60e 336 int noItem = 0;
337 int clear = 0;
338 int scrollText = 0;
feef2cb5 339 int rc = DLG_CANCEL;
9be6d60e 340 int flags = 0;
ff3d94b9 341 int defaultNo = 0;
9be6d60e 342 int separateOutput = 0;
feef2cb5 343 int fullButtons = 0;
9623bfae 344 int outputfd = 2;
45bffa7f 345 int topLeft = 0;
9623bfae 346 FILE *output = stderr;
d78245ab 347 const char * result;
348 const char ** selections, ** next;
9be6d60e 349 char * title = NULL;
feef2cb5 350 char *default_item = NULL;
9be6d60e 351 char * backtitle = NULL;
60d3c50b 352 char * yes_button = NULL;
353 char * no_button = NULL;
354 char * ok_button = NULL;
355 char * cancel_button = NULL;
feef2cb5 356 int help = 0, version = 0;
9be6d60e 357 struct poptOption optionsTable[] = {
6f481af2 358 { "backtitle", '\0', POPT_ARG_STRING, &backtitle, 0 },
359 { "checklist", '\0', 0, 0, OPT_CHECKLIST },
360 { "clear", '\0', 0, &clear, 0 },
361 { "defaultno", '\0', 0, &defaultNo, 0 },
362 { "inputbox", '\0', 0, 0, OPT_INPUTBOX },
363 { "fb", '\0', 0, 0, OPT_FULLBUTTONS },
364 { "fullbuttons", '\0', 0, 0, OPT_FULLBUTTONS },
365 { "gauge", '\0', 0, 0, OPT_GAUGE },
366 { "infobox", '\0', 0, 0, OPT_INFOBOX },
367 { "menu", '\0', 0, 0, OPT_MENU },
368 { "msgbox", '\0', 0, 0, OPT_MSGBOX },
369 { "nocancel", '\0', 0, &noCancel, 0 },
370 { "noitem", '\0', 0, &noItem, 0 },
feef2cb5 371 { "default-item", '\0', POPT_ARG_STRING, &default_item, 0},
6f481af2 372 { "notags", '\0', 0, &noTags, 0 },
373 { "radiolist", '\0', 0, 0, OPT_RADIOLIST },
374 { "scrolltext", '\0', 0, &scrollText, 0 },
375 { "separate-output", '\0', 0, &separateOutput, 0 },
376 { "title", '\0', POPT_ARG_STRING, &title, 0 },
6f481af2 377 { "textbox", '\0', 0, 0, OPT_TEXTBOX },
45bffa7f 378 { "topleft", '\0', 0, &topLeft, 0 },
feef2cb5 379 { "yesno", '\0', 0, 0, OPT_YESNO },
380 { "passwordbox", '\0', 0, 0, OPT_PASSWORDBOX },
381 { "output-fd", '\0', POPT_ARG_INT, &outputfd, 0 },
60d3c50b 382 { "yes-button", '\0', POPT_ARG_STRING, &yes_button, 0},
383 { "no-button", '\0', POPT_ARG_STRING, &no_button, 0},
384 { "ok-button", '\0', POPT_ARG_STRING, &ok_button, 0},
385 { "cancel-button", '\0', POPT_ARG_STRING, &cancel_button, 0},
feef2cb5 386 { "help", 'h', 0, &help, 0, NULL, NULL },
387 { "version", 'v', 0, &version, 0, NULL, NULL },
6f481af2 388 { 0, 0, 0, 0, 0 }
9be6d60e 389 };
feef2cb5 390
970ce9d5 391#ifdef ENABLE_NLS
feef2cb5 392 setlocale (LC_ALL, "");
393 bindtextdomain (PACKAGE, LOCALEDIR);
394 textdomain (PACKAGE);
970ce9d5 395#endif
feef2cb5 396
649a0152 397 optCon = poptGetContext("whiptail", argc, argv, optionsTable, 0);
9be6d60e 398
399 while ((arg = poptGetNextOpt(optCon)) > 0) {
6f481af2 400 switch (arg) {
401 case OPT_INFOBOX:
feef2cb5 402 if (mode != MODE_NONE) usage(WAS_ERROR);
6f481af2 403 mode = MODE_INFOBOX;
404 break;
405
406 case OPT_MENU:
feef2cb5 407 if (mode != MODE_NONE) usage(WAS_ERROR);
6f481af2 408 mode = MODE_MENU;
409 break;
410
411 case OPT_MSGBOX:
feef2cb5 412 if (mode != MODE_NONE) usage(WAS_ERROR);
6f481af2 413 mode = MODE_MSGBOX;
414 break;
feef2cb5 415
416 case OPT_TEXTBOX:
417 if (mode != MODE_NONE) usage(WAS_ERROR);
418 mode = MODE_TEXTBOX;
419 break;
420
421 case OPT_PASSWORDBOX:
422 if (mode != MODE_NONE) usage(WAS_ERROR);
423 mode = MODE_PASSWORDBOX;
424 break;
425
6f481af2 426 case OPT_RADIOLIST:
feef2cb5 427 if (mode != MODE_NONE) usage(WAS_ERROR);
6f481af2 428 mode = MODE_RADIOLIST;
429 break;
430
431 case OPT_CHECKLIST:
feef2cb5 432 if (mode != MODE_NONE) usage(WAS_ERROR);
6f481af2 433 mode = MODE_CHECKLIST;
434 break;
435
436 case OPT_FULLBUTTONS:
feef2cb5 437 fullButtons = 1;
6f481af2 438 useFullButtons(1);
439 break;
440
441 case OPT_YESNO:
feef2cb5 442 if (mode != MODE_NONE) usage(WAS_ERROR);
6f481af2 443 mode = MODE_YESNO;
444 break;
445
446 case OPT_GAUGE:
feef2cb5 447 if (mode != MODE_NONE) usage(WAS_ERROR);
6f481af2 448 mode = MODE_GAUGE;
449 break;
450
451 case OPT_INPUTBOX:
feef2cb5 452 if (mode != MODE_NONE) usage(WAS_ERROR);
6f481af2 453 mode = MODE_INPUTBOX;
454 break;
455 }
9be6d60e 456 }
457
feef2cb5 458 if (help) {
459 usage(NO_ERROR);
460 exit(0);
461 }
462 if (version) {
463 print_version();
464 exit(0);
465 }
466
9be6d60e 467 if (arg < -1) {
6f481af2 468 fprintf(stderr, "%s: %s\n",
469 poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
470 poptStrerror(arg));
471 exit(1);
9be6d60e 472 }
473
9623bfae 474 output = fdopen (outputfd, "w");
475 if (output == NULL ) {
476 perror ("Cannot open output-fd\n");
477 exit (DLG_ERROR);
478 }
479
feef2cb5 480 if (mode == MODE_NONE) usage(WAS_ERROR);
9be6d60e 481
c00a8d1b 482 if (!(nextArg = poptGetArg(optCon))) usage(WAS_ERROR);
483 text = strdup(nextArg);
9be6d60e 484
3341bdc5
ML
485 if (mode == MODE_TEXTBOX) {
486 char *t = text;
487 text = readTextFile(t);
488 free(t);
489 }
585f5503 490
feef2cb5 491 if (!(nextArg = poptGetArg(optCon))) usage(WAS_ERROR);
9be6d60e 492 height = strtoul(nextArg, &end, 10);
feef2cb5 493 if (*end) usage(WAS_ERROR);
9be6d60e 494
feef2cb5 495 if (!(nextArg = poptGetArg(optCon))) usage(WAS_ERROR);
9be6d60e 496 width = strtoul(nextArg, &end, 10);
feef2cb5 497 if (*end) usage(WAS_ERROR);
9be6d60e 498
499 if (mode == MODE_GAUGE) {
6f481af2 500 fd = dup(0);
3341bdc5
ML
501 if (fd < 0 || close(0) < 0) {
502 perror("dup/close stdin");
503 exit(DLG_ERROR);
504 }
505 if (open("/dev/tty", O_RDWR) != 0) {
506 perror("open /dev/tty");
507 exit(DLG_ERROR);
508 }
9be6d60e 509 }
510
511 newtInit();
512 newtCls();
feef2cb5 513
514 cleanNewlines(text);
515
516 if ( height <= 0 || width <= 0 )
517 guessSize(&height, &width, mode, &flags, fullButtons, title, text,
518 optCon);
519
9be6d60e 520 width -= 2;
521 height -= 2;
9be6d60e 522
45bffa7f 523 newtOpenWindow(topLeft ? 1 : (SLtt_Screen_Cols - width) / 2,
524 topLeft ? 1 : (SLtt_Screen_Rows - height) / 2, width, height, title);
9be6d60e 525 if (backtitle)
6f481af2 526 newtDrawRootText(0, 0, backtitle);
9be6d60e 527
60d3c50b 528 if (ok_button)
529 setButtonText(ok_button, BUTTON_OK);
530 if (cancel_button)
531 setButtonText(cancel_button, BUTTON_CANCEL);
532 if (yes_button)
533 setButtonText(yes_button, BUTTON_YES);
534 if (no_button)
535 setButtonText(no_button, BUTTON_NO);
536
9be6d60e 537 if (noCancel) flags |= FLAG_NOCANCEL;
538 if (noItem) flags |= FLAG_NOITEM;
57cb49e7 539 if (noTags) flags |= FLAG_NOTAGS;
9be6d60e 540 if (scrollText) flags |= FLAG_SCROLL_TEXT;
ff3d94b9 541 if (defaultNo) flags |= FLAG_DEFAULT_NO;
9be6d60e 542
543 switch (mode) {
544 case MODE_MSGBOX:
585f5503 545 case MODE_TEXTBOX:
6f481af2 546 rc = messageBox(text, height, width, MSGBOX_MSG, flags);
547 break;
9be6d60e 548
139f06bc 549 case MODE_INFOBOX:
6f481af2 550 rc = messageBox(text, height, width, MSGBOX_INFO, flags);
551 break;
139f06bc 552
9be6d60e 553 case MODE_YESNO:
6f481af2 554 rc = messageBox(text, height, width, MSGBOX_YESNO, flags);
555 break;
9be6d60e 556
557 case MODE_INPUTBOX:
6f481af2 558 rc = inputBox(text, height, width, optCon, flags, &result);
feef2cb5 559 if (rc == DLG_OKAY) fprintf(output, "%s", result);
560 break;
561
562 case MODE_PASSWORDBOX:
563 rc = inputBox(text, height, width, optCon, flags | FLAG_PASSWORD,
564 &result);
565 if (rc == DLG_OKAY) fprintf (output, "%s", result);
6f481af2 566 break;
9be6d60e 567
568 case MODE_MENU:
feef2cb5 569 rc = listBox(text, height, width, optCon, flags, default_item, &result);
6f481af2 570 if (rc == DLG_OKAY) fprintf(output, "%s", result);
571 break;
9be6d60e 572
573 case MODE_RADIOLIST:
6f481af2 574 rc = checkList(text, height, width, optCon, 1, flags, &selections);
feef2cb5 575 if (rc == DLG_OKAY) {
5dd92b24 576 if (selections[0])
577 fprintf(output, "%s", selections[0]);
6f481af2 578 free(selections);
579 }
580 break;
9be6d60e 581
582 case MODE_CHECKLIST:
6f481af2 583 rc = checkList(text, height, width, optCon, 0, flags, &selections);
584
585 if (!rc) {
586 for (next = selections; *next; next++) {
587 if (!separateOutput) {
588 if (needSpace) putc(' ', output);
589 fprintf(output, "\"%s\"", *next);
590 needSpace = 1;
591 } else {
592 fprintf(output, "%s\n", *next);
593 }
594 }
595
596 free(selections);
597 }
598 break;
9be6d60e 599
600 case MODE_GAUGE:
6f481af2 601 rc = gauge(text, height, width, optCon, fd, flags);
602 break;
9be6d60e 603
604 default:
feef2cb5 605 usage(WAS_ERROR);
9be6d60e 606 }
607
feef2cb5 608 if (rc == DLG_ERROR) usage(WAS_ERROR);
649a0152 609
9be6d60e 610 if (clear)
6f481af2 611 newtPopWindow();
9be6d60e 612 newtFinished();
613
3341bdc5
ML
614 free(text);
615 poptFreeContext(optCon);
616
feef2cb5 617 return ( rc == DLG_ESCAPE ) ? -1 : rc;
9be6d60e 618}