#include "config.h" #include #include #include #include #include #include #include #include #include #include #include "nls.h" #include "dialogboxes.h" #include "newt.h" #include "newt_pr.h" enum { NO_ERROR = 0, WAS_ERROR = 1 }; enum mode { MODE_NONE, MODE_INFOBOX, MODE_MSGBOX, MODE_YESNO, MODE_CHECKLIST, MODE_INPUTBOX, MODE_RADIOLIST, MODE_MENU, MODE_GAUGE , MODE_TEXTBOX, MODE_PASSWORDBOX}; #define OPT_MSGBOX 1000 #define OPT_CHECKLIST 1001 #define OPT_YESNO 1002 #define OPT_INPUTBOX 1003 #define OPT_FULLBUTTONS 1004 #define OPT_MENU 1005 #define OPT_RADIOLIST 1006 #define OPT_GAUGE 1007 #define OPT_INFOBOX 1008 #define OPT_TEXTBOX 1009 #define OPT_PASSWORDBOX 1010 static void usage(int err) { newtFinished(); fprintf (err ? stderr : stdout, _("Box options: \n" "\t--msgbox \n" "\t--yesno \n" "\t--infobox \n" "\t--inputbox [init] \n" "\t--passwordbox [init] \n" "\t--textbox \n" "\t--menu [tag item] ...\n" "\t--checklist [tag item status]...\n" "\t--radiolist [tag item status]...\n" "\t--gauge \n" "Options: (depend on box-option)\n" "\t--clear clear screen on exit\n" "\t--defaultno default no button\n" "\t--default-item set default string\n" "\t--fb, --fullbuttons use full buttons\n" "\t--nocancel no cancel button\n" "\t--yes-button set text of yes button\n" "\t--no-button set text of no button\n" "\t--ok-button set text of ok button\n" "\t--cancel-button set text of cancel button\n" "\t--noitem don't display items\n" "\t--notags don't display tags\n" "\t--separate-output output one line at a time\n" "\t--output-fd output to fd, not stdout\n" "\t--title display title\n" "\t--backtitle <backtitle> display backtitle\n" "\t--scrolltext force vertical scrollbars\n" "\t--topleft put window in top-left corner\n" "\t-h, --help print this message\n" "\t-v, --version print version information\n\n")); exit(err ? DLG_ERROR : 0 ); } static void print_version(void) { fprintf (stdout, _("whiptail (newt): %s\n"), VERSION); } #if 0 /* FIXME Copied from newt.c * Place somewhere better -- dialogboxes? -- amck */ int wstrlen(const char *str, int len) { mbstate_t ps; wchar_t tmp; int nchars = 0; if (!str) return 0; if (!len) return 0; if (len < 0) len = strlen(str); memset(&ps,0,sizeof(mbstate_t)); while (len > 0) { int x,y; x = mbrtowc(&tmp,str,len,&ps); if (x >0) { str += x; len -= x; y = wcwidth(tmp); if (y>0) nchars+=y; } else break; } return nchars; } #endif /* * The value of *width is increased if it is not as large as the width of * the line. */ static const char * lineWidth(int * width, const char * line, int *chrs) { const char * s = line; if ( line == NULL ) return 0; while ( *s != '\0' && *s != '\n' ) s++; if ( *s == '\n' ) s++; *chrs = _newt_wstrlen (line, s - line ); *width = max(*width, *chrs); return s; } /* * cleanNewlines * Handle newlines in text. Hack. */ void cleanNewlines (char *text) { char *p, *q; for (p = q = text; *p; p++, q++) if (*p == '\\' && p[1] == 'n') { p++; *q = '\n'; } else *q = *p; *q = '\0'; } /* * The height of a text string is added to height, and width is increased * if it is not big enough to store the text string. */ static const char * textSize(int * height, int * width, int maxWidth, const char * text) { int h = 0; int w = 0; int chrs = 0; if ( text == NULL ) return 0; while ( *text != '\0' ) { h++; text = lineWidth(width, text, &chrs); /* Allow for text overflowing. May overestimate a bit */ h += chrs / maxWidth; } h += 2; w += 2; *height += h; *width += w; *width = min(*width, maxWidth); return text; } /* * Add space for buttons. * NOTE: when this is internationalized, the button width might change. */ static void spaceForButtons(int * height, int * width, int count, int full) { /* Make space for the buttons */ if ( full ) { *height += 4; if ( count == 1 ) *width = max(*width, 7); else *width = max(*width, 20); } else { *height += 2; if ( count == 1 ) *width = max(*width, 7); else *width = max(*width, 19); } } static int menuSize(int * height, int * width, enum mode mode, poptContext options) { const char ** argv = poptGetArgs(options); const char ** items = argv; int h = 0; int tagWidth = 0; int descriptionWidth = 0; int overhead = 10; static char buf[20]; if ( argv == 0 || *argv == 0 ) return 0; argv++; if ( mode == MODE_MENU ) overhead = 5; while ( argv[0] != 0 && argv[1] ) { tagWidth = max(tagWidth, strlen(argv[0])); descriptionWidth = max(descriptionWidth, strlen(argv[1])); if ( mode == MODE_MENU ) argv += 2; else argv += 3; h++; } *width = max(*width, tagWidth + descriptionWidth + overhead); *width = min(*width, SLtt_Screen_Cols); h = min(h, SLtt_Screen_Rows - *height - 4); *height = *height + h + 1; sprintf(buf, "%d", h); *items = buf; return 0; } /* * Guess the size of a window, given what will be displayed within it. */ static void guessSize(int * height, int * width, enum mode mode, int * flags, int fullButtons, const char * title, const char * text, poptContext options) { int w = 0, h = 0, chrs = 0; textSize(&h, &w, SLtt_Screen_Cols -4 , text); /* Width and height for text */ lineWidth(&w, title, &chrs); /* Width for title */ if ( w > 0 ) w += 4; switch ( mode ) { case MODE_CHECKLIST: case MODE_RADIOLIST: case MODE_MENU: spaceForButtons(&h, &w, *flags & FLAG_NOCANCEL ? 1 : 2, fullButtons); menuSize(&h, &w, mode, options); break; case MODE_YESNO: case MODE_MSGBOX: spaceForButtons(&h, &w, 1, fullButtons); break; case MODE_INPUTBOX: spaceForButtons(&h, &w, *flags & FLAG_NOCANCEL ? 1 : 2, fullButtons); h += 1; break; case MODE_GAUGE: h += 2; break; case MODE_NONE: break; default: break; }; /* * Fixed window-border overhead. * NOTE: This will change if we add a way to turn off drop-shadow and/or * box borders. That would be desirable for display-sized screens. */ w += 2; h += 2; if ( h > SLtt_Screen_Rows - 1 ) { h = SLtt_Screen_Rows - 1; *flags |= FLAG_SCROLL_TEXT; w += 2; /* Add width of slider - is this right? */ } *width = min(max(*width, w), SLtt_Screen_Cols); *height = max(*height, h); } char * readTextFile(const char * filename) { int fd = open(filename, O_RDONLY, 0); struct stat s; char * buf; if ( fd < 0 || fstat(fd, &s) != 0 ) { perror(filename); exit(DLG_ERROR); } if ( (buf = malloc(s.st_size + 1)) == 0 ) { fprintf(stderr, _("%s: too large to display.\n"), filename); exit(DLG_ERROR); } if ( read(fd, buf, s.st_size) != s.st_size ) { perror(filename); exit(DLG_ERROR); } close(fd); buf[s.st_size] = '\0'; return buf; } int main(int argc, const char ** argv) { enum mode mode = MODE_NONE; poptContext optCon; int arg; char * text; const char * nextArg; char * end; int height; int width; int fd = -1; int needSpace = 0; int noCancel = 0; int noTags = 0; int noItem = 0; int clear = 0; int scrollText = 0; int rc = DLG_CANCEL; int flags = 0; int defaultNo = 0; int separateOutput = 0; int fullButtons = 0; int outputfd = 2; int topLeft = 0; FILE *output = stderr; char * result; char ** selections, ** next; char * title = NULL; char *default_item = NULL; char * backtitle = NULL; char * yes_button = NULL; char * no_button = NULL; char * ok_button = NULL; char * cancel_button = NULL; int help = 0, version = 0; struct poptOption optionsTable[] = { { "backtitle", '\0', POPT_ARG_STRING, &backtitle, 0 }, { "checklist", '\0', 0, 0, OPT_CHECKLIST }, { "clear", '\0', 0, &clear, 0 }, { "defaultno", '\0', 0, &defaultNo, 0 }, { "inputbox", '\0', 0, 0, OPT_INPUTBOX }, { "fb", '\0', 0, 0, OPT_FULLBUTTONS }, { "fullbuttons", '\0', 0, 0, OPT_FULLBUTTONS }, { "gauge", '\0', 0, 0, OPT_GAUGE }, { "infobox", '\0', 0, 0, OPT_INFOBOX }, { "menu", '\0', 0, 0, OPT_MENU }, { "msgbox", '\0', 0, 0, OPT_MSGBOX }, { "nocancel", '\0', 0, &noCancel, 0 }, { "noitem", '\0', 0, &noItem, 0 }, { "default-item", '\0', POPT_ARG_STRING, &default_item, 0}, { "notags", '\0', 0, &noTags, 0 }, { "radiolist", '\0', 0, 0, OPT_RADIOLIST }, { "scrolltext", '\0', 0, &scrollText, 0 }, { "separate-output", '\0', 0, &separateOutput, 0 }, { "title", '\0', POPT_ARG_STRING, &title, 0 }, { "textbox", '\0', 0, 0, OPT_TEXTBOX }, { "topleft", '\0', 0, &topLeft, 0 }, { "yesno", '\0', 0, 0, OPT_YESNO }, { "passwordbox", '\0', 0, 0, OPT_PASSWORDBOX }, { "output-fd", '\0', POPT_ARG_INT, &outputfd, 0 }, { "yes-button", '\0', POPT_ARG_STRING, &yes_button, 0}, { "no-button", '\0', POPT_ARG_STRING, &no_button, 0}, { "ok-button", '\0', POPT_ARG_STRING, &ok_button, 0}, { "cancel-button", '\0', POPT_ARG_STRING, &cancel_button, 0}, { "help", 'h', 0, &help, 0, NULL, NULL }, { "version", 'v', 0, &version, 0, NULL, NULL }, { 0, 0, 0, 0, 0 } }; #ifdef ENABLE_NLS setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); #endif optCon = poptGetContext("whiptail", argc, argv, optionsTable, 0); while ((arg = poptGetNextOpt(optCon)) > 0) { switch (arg) { case OPT_INFOBOX: if (mode != MODE_NONE) usage(WAS_ERROR); mode = MODE_INFOBOX; break; case OPT_MENU: if (mode != MODE_NONE) usage(WAS_ERROR); mode = MODE_MENU; break; case OPT_MSGBOX: if (mode != MODE_NONE) usage(WAS_ERROR); mode = MODE_MSGBOX; break; case OPT_TEXTBOX: if (mode != MODE_NONE) usage(WAS_ERROR); mode = MODE_TEXTBOX; break; case OPT_PASSWORDBOX: if (mode != MODE_NONE) usage(WAS_ERROR); mode = MODE_PASSWORDBOX; break; case OPT_RADIOLIST: if (mode != MODE_NONE) usage(WAS_ERROR); mode = MODE_RADIOLIST; break; case OPT_CHECKLIST: if (mode != MODE_NONE) usage(WAS_ERROR); mode = MODE_CHECKLIST; break; case OPT_FULLBUTTONS: fullButtons = 1; useFullButtons(1); break; case OPT_YESNO: if (mode != MODE_NONE) usage(WAS_ERROR); mode = MODE_YESNO; break; case OPT_GAUGE: if (mode != MODE_NONE) usage(WAS_ERROR); mode = MODE_GAUGE; break; case OPT_INPUTBOX: if (mode != MODE_NONE) usage(WAS_ERROR); mode = MODE_INPUTBOX; break; } } if (help) { usage(NO_ERROR); exit(0); } if (version) { print_version(); exit(0); } if (arg < -1) { fprintf(stderr, "%s: %s\n", poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(arg)); exit(1); } output = fdopen (outputfd, "w"); if (output == NULL ) { perror ("Cannot open output-fd\n"); exit (DLG_ERROR); } if (mode == MODE_NONE) usage(WAS_ERROR); if (!(nextArg = poptGetArg(optCon))) usage(WAS_ERROR); text = strdup(nextArg); if (mode == MODE_TEXTBOX) { char *t = text; text = readTextFile(t); free(t); } if (!(nextArg = poptGetArg(optCon))) usage(WAS_ERROR); height = strtoul(nextArg, &end, 10); if (*end) usage(WAS_ERROR); if (!(nextArg = poptGetArg(optCon))) usage(WAS_ERROR); width = strtoul(nextArg, &end, 10); if (*end) usage(WAS_ERROR); if (mode == MODE_GAUGE) { fd = dup(0); if (fd < 0 || close(0) < 0) { perror("dup/close stdin"); exit(DLG_ERROR); } if (open("/dev/tty", O_RDWR) != 0) { perror("open /dev/tty"); exit(DLG_ERROR); } } newtInit(); newtCls(); cleanNewlines(text); if ( height <= 0 || width <= 0 ) guessSize(&height, &width, mode, &flags, fullButtons, title, text, optCon); width -= 2; height -= 2; newtOpenWindow(topLeft ? 1 : (SLtt_Screen_Cols - width) / 2, topLeft ? 1 : (SLtt_Screen_Rows - height) / 2, width, height, title); if (backtitle) newtDrawRootText(0, 0, backtitle); if (ok_button) setButtonText(ok_button, BUTTON_OK); if (cancel_button) setButtonText(cancel_button, BUTTON_CANCEL); if (yes_button) setButtonText(yes_button, BUTTON_YES); if (no_button) setButtonText(no_button, BUTTON_NO); if (noCancel) flags |= FLAG_NOCANCEL; if (noItem) flags |= FLAG_NOITEM; if (noTags) flags |= FLAG_NOTAGS; if (scrollText) flags |= FLAG_SCROLL_TEXT; if (defaultNo) flags |= FLAG_DEFAULT_NO; switch (mode) { case MODE_MSGBOX: case MODE_TEXTBOX: rc = messageBox(text, height, width, MSGBOX_MSG, flags); break; case MODE_INFOBOX: rc = messageBox(text, height, width, MSGBOX_INFO, flags); break; case MODE_YESNO: rc = messageBox(text, height, width, MSGBOX_YESNO, flags); break; case MODE_INPUTBOX: rc = inputBox(text, height, width, optCon, flags, &result); if (rc == DLG_OKAY) { fprintf(output, "%s", result); free(result); } break; case MODE_PASSWORDBOX: rc = inputBox(text, height, width, optCon, flags | FLAG_PASSWORD, &result); if (rc == DLG_OKAY) { fprintf (output, "%s", result); free(result); } break; case MODE_MENU: rc = listBox(text, height, width, optCon, flags, default_item, &result); if (rc == DLG_OKAY) { fprintf(output, "%s", result); free(result); } break; case MODE_RADIOLIST: rc = checkList(text, height, width, optCon, 1, flags, &selections); if (rc == DLG_OKAY && selections[0]) { fprintf(output, "%s", selections[0]); free(selections[0]); free(selections); } break; case MODE_CHECKLIST: rc = checkList(text, height, width, optCon, 0, flags, &selections); if (!rc) { for (next = selections; *next; next++) { if (!separateOutput) { if (needSpace) putc(' ', output); fprintf(output, "\"%s\"", *next); needSpace = 1; } else { fprintf(output, "%s\n", *next); } free(*next); } free(selections); } break; case MODE_GAUGE: rc = gauge(text, height, width, optCon, fd, flags); break; default: usage(WAS_ERROR); } if (rc == DLG_ERROR) usage(WAS_ERROR); if (clear) newtPopWindow(); newtFinished(); free(text); poptFreeContext(optCon); return ( rc == DLG_ESCAPE ) ? -1 : rc; }