📄 util.c
字号:
/* * $Id: util.c,v 1.118 2003/12/01 00:32:44 tom Exp $ * * util.c -- miscellaneous utilities for dialog * * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) * and: Thomas E. Dickey * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include "dialog.h"#include <stdarg.h>#include <sys/types.h>#include <sys/stat.h>#include <time.h>#ifdef NCURSES_VERSION#if defined(HAVE_NCURSESW_TERM_H)#include <ncursesw/term.h>#elif defined(HAVE_NCURSES_TERM_H)#include <ncurses/term.h>#else#include <term.h>#endif#endif#ifndef O_BINARY#define O_BINARY 0#endif#ifndef S_IRUSR#define S_IRUSR 0400#endif#ifndef S_IWUSR#define S_IWUSR 0200#endif#ifndef DIALOG_TMPDIR#define DIALOG_TMPDIR NULL#endif#define LOCK_PERMITS (S_IRUSR | S_IWUSR)#define LOCK_TIMEOUT 10 /* timeout for locking, in seconds *//* globals */DIALOG_STATE dialog_state;DIALOG_VARS dialog_vars;#define concat(a,b) a##b#ifdef HAVE_RC_FILE#define RC_DATA(name,comment) , #name "_color", comment " color"#else#define RC_DATA(name,comment) /*nothing */#endif#ifdef HAVE_COLOR#include <colors.h>#define COLOR_DATA(upr) , concat(upr,_FG), concat(upr,_BG), concat(upr,_HL)#else#define COLOR_DATA(upr) /*nothing */#endif#define DATA(atr,upr,lwr,cmt) { atr COLOR_DATA(upr) RC_DATA(lwr,cmt) }/* * Table of color and attribute values, default is for mono display. *//* *INDENT-OFF* */DIALOG_COLORS dlg_color_table[] ={ DATA(A_NORMAL, SCREEN, screen, "Screen"), DATA(A_NORMAL, SHADOW, shadow, "Shadow"), DATA(A_REVERSE, DIALOG, dialog, "Dialog box"), DATA(A_REVERSE, TITLE, title, "Dialog box title"), DATA(A_REVERSE, BORDER, border, "Dialog box border"), DATA(A_BOLD, BUTTON_ACTIVE, button_active, "Active button"), DATA(A_DIM, BUTTON_INACTIVE, button_inactive, "Inactive button"), DATA(A_UNDERLINE, BUTTON_KEY_ACTIVE, button_key_active, "Active button key"), DATA(A_UNDERLINE, BUTTON_KEY_INACTIVE, button_key_inactive, "Inactive button key"), DATA(A_NORMAL, BUTTON_LABEL_ACTIVE, button_label_active, "Active button label"), DATA(A_NORMAL, BUTTON_LABEL_INACTIVE, button_label_inactive, "Inactive button label"), DATA(A_REVERSE, INPUTBOX, inputbox, "Input box"), DATA(A_REVERSE, INPUTBOX_BORDER, inputbox_border, "Input box border"), DATA(A_REVERSE, SEARCHBOX, searchbox, "Search box"), DATA(A_REVERSE, SEARCHBOX_TITLE, searchbox_title, "Search box title"), DATA(A_REVERSE, SEARCHBOX_BORDER, searchbox_border, "Search box border"), DATA(A_REVERSE, POSITION_INDICATOR, position_indicator, "File position indicator"), DATA(A_REVERSE, MENUBOX, menubox, "Menu box"), DATA(A_REVERSE, MENUBOX_BORDER, menubox_border, "Menu box border"), DATA(A_REVERSE, ITEM, item, "Item"), DATA(A_NORMAL, ITEM_SELECTED, item_selected, "Selected item"), DATA(A_REVERSE, TAG, tag, "Tag"), DATA(A_REVERSE, TAG_SELECTED, tag_selected, "Selected tag"), DATA(A_NORMAL, TAG_KEY, tag_key, "Tag key"), DATA(A_BOLD, TAG_KEY_SELECTED, tag_key_selected, "Selected tag key"), DATA(A_REVERSE, CHECK, check, "Check box"), DATA(A_REVERSE, CHECK_SELECTED, check_selected, "Selected check box"), DATA(A_REVERSE, UARROW, uarrow, "Up arrow"), DATA(A_REVERSE, DARROW, darrow, "Down arrow"), DATA(A_NORMAL, ITEMHELP, itemhelp, "Item help-text"), DATA(A_BOLD, FORM_ACTIVE_TEXT, form_active_text, "Active form text"), DATA(A_REVERSE, FORM_TEXT, form_text, "Form text"),};/* *INDENT-ON* *//* * Display background title if it exists ... */voiddlg_put_backtitle(void){ int i; if (dialog_vars.backtitle != NULL) { chtype attr = A_NORMAL; wattrset(stdscr, screen_attr); (void) wmove(stdscr, 0, 1); dlg_print_text(stdscr, dialog_vars.backtitle, COLS - 2, &attr); for (i = 0; i < COLS - (int) strlen(dialog_vars.backtitle); i++) (void) waddch(stdscr, ' '); (void) wmove(stdscr, 1, 1); for (i = 0; i < COLS - 2; i++) (void) waddch(stdscr, ACS_HLINE); } (void) wnoutrefresh(stdscr);}/* * Set window to attribute 'attr'. There are more efficient ways to do this, * but will not work on older/buggy ncurses versions. */voiddlg_attr_clear(WINDOW *win, int height, int width, chtype attr){ int i, j; wattrset(win, attr); for (i = 0; i < height; i++) { (void) wmove(win, i, 0); for (j = 0; j < width; j++) (void) waddch(win, ' '); } (void) touchwin(win);}voiddlg_clear(void){ dlg_attr_clear(stdscr, LINES, COLS, screen_attr);}#define isprivate(s) ((s) != 0 && strstr(s, "\033[?") != 0)#define TTY_DEVICE "/dev/tty"/* * If $DIALOG_TTY exists, allow the program to try to open the terminal * directly when stdout is redirected. By default we require the "--stdout" * option to be given, but some scripts were written making use of the * behavior of dialog which tried opening the terminal anyway. */static char *dialog_tty(void){ char *result = getenv("DIALOG_TTY"); if (result != 0 && atoi(result)) result = 0; return result;}/* * Open the terminal directly. If one of stdin, stdout or stderr really points * to a tty, use it. Otherwise give up and open /dev/tty. */static intopen_terminal(char **result, int mode){ const char *device = TTY_DEVICE; if (!isatty(fileno(stderr)) || (device = ttyname(fileno(stderr))) == 0) { if (!isatty(fileno(stdout)) || (device = ttyname(fileno(stdout))) == 0) { if (!isatty(fileno(stdin)) || (device = ttyname(fileno(stdin))) == 0) { device = TTY_DEVICE; } } } *result = dlg_strclone(device); return open(device, mode);}/* * Do some initialization for dialog. * * 'input' is the real tty input of dialog. Usually it is stdin, but if * --input-fd option is used, it may be anything. * * 'output' is where dialog will send its result. Usually it is stderr, but * if --stdout or --output-fd is used, it may be anything. We are concerned * mainly with the case where it happens to be the same as stdout. */voidinit_dialog(FILE *input, FILE *output){ int fd1, fd2; char *device = 0; dialog_state.output = output; dialog_state.tab_len = TAB_LEN; dialog_state.aspect_ratio = DEFAULT_ASPECT_RATIO;#ifdef HAVE_COLOR dialog_state.use_colors = USE_COLORS; /* use colors by default? */ dialog_state.use_shadow = USE_SHADOW; /* shadow dialog boxes by default? */#endif#ifdef HAVE_RC_FILE if (dlg_parse_rc() == -1) /* Read the configuration file */ dlg_exiterr("init_dialog: dlg_parse_rc");#endif /* * Some widgets (such as gauge) may read from the standard input. Pipes * only connect stdout/stdin, so there is not much choice. But reading a * pipe would get in the way of curses' normal reading stdin for getch. * * As in the --stdout (see below), reopening the terminal does not always * work properly. dialog provides a --pipe-fd option for this purpose. We * test that case first (differing fileno's for input/stdin). If the * fileno's are equal, but we're not reading from a tty, see if we can open * /dev/tty. */ dialog_state.pipe_input = stdin; if (fileno(input) != fileno(stdin)) { if ((fd1 = dup(fileno(input))) >= 0 && (fd2 = dup(fileno(stdin))) >= 0) { *stdin = *fdopen(fd1, "r"); dialog_state.pipe_input = fdopen(fd2, "r"); if (fileno(stdin) != 0) /* some functions may read fd #0 */ (void) dup2(fileno(stdin), 0); } else dlg_exiterr("cannot open tty-input"); } else if (!isatty(fileno(stdin))) { if ((fd1 = open_terminal(&device, O_RDONLY)) >= 0 && (fd2 = dup(fileno(stdin))) >= 0) { dialog_state.pipe_input = fdopen(fd2, "r"); *stdin = *freopen(device, "r", stdin); if (fileno(stdin) != 0) /* some functions may read fd #0 */ (void) dup2(fileno(stdin), 0); } free(device); } /* * If stdout is not a tty and dialog is called with the --stdout option, we * have to provide for a way to write to the screen. * * The curses library normally writes its output to stdout, leaving stderr * free for scripting. Scripts are simpler when stdout is redirected. The * newterm function is useful; it allows us to specify where the output * goes. Reopening the terminal is not portable since several * configurations do not allow this to work properly: * * a) some getty implementations (and possibly broken tty drivers, e.g., on * HPUX 10 and 11) cause stdin to act as if it is still in cooked mode * even though results from ioctl's state that it is successfully * altered to raw mode. Broken is the proper term. * * b) the user may not have permissions on the device, e.g., if one su's * from the login user to another non-privileged user. */ if (!isatty(fileno(stdout)) && (fileno(stdout) == fileno(output) || dialog_tty())) { if ((fd1 = open_terminal(&device, O_WRONLY)) >= 0 && (dialog_state.screen_output = fdopen(fd1, "w")) != 0) { if (newterm(NULL, dialog_state.screen_output, stdin) == 0) { dlg_exiterr("cannot initialize curses"); } free(device); } else { dlg_exiterr("cannot open tty-output"); } } else { dialog_state.screen_output = stdout; (void) initscr(); }#ifdef NCURSES_VERSION /* * Cancel xterm's alternate-screen mode. */ if ((dialog_state.screen_output != stdout || isatty(fileno(dialog_state.screen_output))) && key_mouse != 0 /* xterm and kindred */ && isprivate(enter_ca_mode) && isprivate(exit_ca_mode)) { /* * initscr() or newterm() already did putp(enter_ca_mode) as a side * effect of initializing the screen. It would be nice to not even * do that, but we do not really have access to the correct copy of * the terminfo description until those functions have been invoked. */ (void) putp(exit_ca_mode); (void) putp(clear_screen); /* * Prevent ncurses from switching "back" to the normal screen when * exiting from dialog. That would move the cursor to the original * location saved in xterm. Normally curses sets the cursor position * to the first line after the display, but the alternate screen * switching is done after that point. * * Cancelling the strings altogether also works around the buggy * implementation of alternate-screen in rxvt, etc., which clear * more of the display than they should. */ enter_ca_mode = 0; exit_ca_mode = 0; }#endif (void) keypad(stdscr, TRUE); (void) cbreak(); (void) noecho(); mouse_open(); dialog_state.screen_initialized = 1;#ifdef HAVE_COLOR if (dialog_state.use_colors || dialog_state.use_shadow) dlg_color_setup(); /* Set up colors */#endif /* Set screen to screen attribute */ dlg_clear();}#ifdef HAVE_COLORstatic int defined_colors = 0;/* * Setup for color display */voiddlg_color_setup(void){ unsigned i; if (has_colors()) { /* Terminal supports color? */ (void) start_color(); for (i = 0; i < sizeof(dlg_color_table) / sizeof(dlg_color_table[0]); i++) { /* Initialize color pairs */ (void) init_pair(i + 1, dlg_color_table[i].fg, dlg_color_table[i].bg); /* Setup color attributes */ dlg_color_table[i].atr = C_ATTR(dlg_color_table[i].hilite, i + 1); } defined_colors = i + 1; }}intdlg_color_count(void){ return sizeof(dlg_color_table) / sizeof(dlg_color_table[0]);}/* * Reuse color pairs (they are limited), returning a COLOR_PAIR() value if we * have (or can) define a pair with the given color as foreground on the * window's defined background. */static chtypedefine_color(WINDOW *win, int foreground){ chtype result = 0; chtype attrs = getattrs(win); int pair; short fg, bg, background; bool found = FALSE; if ((pair = PAIR_NUMBER(attrs)) != 0 && pair_content(pair, &fg, &bg) != ERR && bg != foreground) { background = bg; } else { background = COLOR_BLACK; } for (pair = 0; pair < defined_colors; ++pair) { if (pair_content(pair, &fg, &bg) != ERR && fg == foreground && bg == background) { result = COLOR_PAIR(pair); found = TRUE; break; } } if (!found && (defined_colors + 1) < COLOR_PAIRS) { pair = defined_colors++; (void) init_pair(pair, foreground, background); result = COLOR_PAIR(pair); } return result;}#endif/* * End using dialog functions. */voidend_dialog(void){ if (dialog_state.screen_initialized) { dialog_state.screen_initialized = 0; mouse_close(); (void) endwin(); (void) fflush(stdout); }}#define isOurEscape(p) (((p)[0] == '\\') && ((p)[1] == 'Z') && ((p)[2] != 0))static intcentered(int width, const char *string){ int len = strlen(string); int left; int hide = 0; int n; if (dialog_vars.colors) { for (n = 0; n < len; ++n) { if (isOurEscape(string + n)) { hide += 3; } } } left = (width - (len - hide)) / 2 - 1; if (left < 0) left = 0; return left;}/* * Print up to 'len' bytes from 'text', optionally rendering our escape * sequence for attributes and color. */voiddlg_print_text(WINDOW *win, const char *txt, int len, chtype *attr){ while (len > 0 && (*txt != '\0')) { if (dialog_vars.colors) { while (isOurEscape(txt)) { int code; txt += 2; switch (code = CharOf(*txt)) {#ifdef HAVE_COLOR case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': *attr &= ~A_COLOR; *attr |= define_color(win, code - '0'); break;#endif case 'B': *attr &= ~A_BOLD; break; case 'b': *attr = A_BOLD; break; case 'R': *attr &= ~A_REVERSE; break; case 'r': *attr = A_REVERSE; break; case 'U': *attr &= ~A_UNDERLINE; break; case 'u': *attr = A_UNDERLINE; break; case 'n': *attr = A_NORMAL; break; } ++txt; } if (*txt == '\n') break; } (void) waddch(win, CharOf(*txt++) | *attr); --len; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -