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