From e2b89cbdcba7475047ca3c46fc7d03825355b3ff Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Wed, 10 Aug 2011 12:50:43 +0900 Subject: [PATCH] Add wide-char support to pinentry-curses. --- configure.ac | 2 +- m4/curses.m4 | 20 +++++- pinentry/pinentry-curses.c | 150 +++++++++++++++++++++++++++++++++++++-------- 3 files changed, 143 insertions(+), 29 deletions(-) diff --git a/configure.ac b/configure.ac index 4b8ed79..bcbe26e 100644 --- a/configure.ac +++ b/configure.ac @@ -158,7 +158,7 @@ fi # Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADERS(string.h unistd.h langinfo.h termio.h locale.h utime.h) +AC_CHECK_HEADERS(string.h unistd.h langinfo.h termio.h locale.h utime.h wchar.h) dnl Checks for library functions. AC_CHECK_FUNCS(seteuid stpcpy mmap) diff --git a/m4/curses.m4 b/m4/curses.m4 index 1e1cb4f..3a01881 100644 --- a/m4/curses.m4 +++ b/m4/curses.m4 @@ -28,7 +28,13 @@ AC_DEFUN([IU_LIB_NCURSES], [ AC_ARG_ENABLE(ncurses, [ --disable-ncurses don't prefer -lncurses over -lcurses], , enable_ncurses=yes) if test "$enable_ncurses" = yes; then - AC_CHECK_LIB(ncurses, initscr, LIBNCURSES="-lncurses") + AC_CHECK_LIB(ncursesw, initscr, LIBNCURSES="-lncursesw", + AC_CHECK_LIB(ncurses, initscr, LIBNCURSES="-lncurses")) + if test "$ac_cv_lib_ncursesw_initscr" = yes; then + have_ncursesw=yes + else + have_ncursesw=no + fi if test "$LIBNCURSES"; then # Use ncurses header files instead of the ordinary ones, if possible; # is there a better way of doing this, that avoids looking in specific @@ -53,9 +59,14 @@ AC_DEFUN([IU_LIB_NCURSES], [ else AC_CACHE_CHECK(for ncurses include dir, inetutils_cv_includedir_ncurses, + if test "$have_ncursesw" = yes; then + ncursesdir=ncursesw + else + ncursesdir=ncurses + fi for D in $includedir $prefix/include /local/include /usr/local/include /include /usr/include; do - if test -d $D/ncurses; then - inetutils_cv_includedir_ncurses="$D/ncurses" + if test -d $D/$ncursesdir; then + inetutils_cv_includedir_ncurses="$D/$ncursesdir" break fi test "$inetutils_cv_includedir_ncurses" \ @@ -68,6 +79,9 @@ AC_DEFUN([IU_LIB_NCURSES], [ NCURSES_INCLUDE="-I$inetutils_cv_includedir_ncurses" fi fi + if test $have_ncursesw = yes; then + AC_DEFINE(HAVE_NCURSESW, 1, [Define if you have working ncursesw]) + fi fi AC_SUBST(NCURSES_INCLUDE) AC_SUBST(LIBNCURSES)])dnl diff --git a/pinentry/pinentry-curses.c b/pinentry/pinentry-curses.c index 76ddbdd..585059f 100644 --- a/pinentry/pinentry-curses.c +++ b/pinentry/pinentry-curses.c @@ -42,6 +42,10 @@ #include +#ifdef HAVE_WCHAR_H +#include +#endif /*HAVE_WCHAR_H*/ + #include "pinentry.h" /* FIXME: We should allow configuration of these button labels and in @@ -94,6 +98,24 @@ struct dialog typedef struct dialog *dialog_t; +#ifdef HAVE_NCURSESW +typedef wchar_t CH; +#define STRLEN(x) wcslen (x) +#define ADDCH(x) addnwstr (&x, 1); +#define CHWIDTH(x) wcwidth (x) +#define NULLCH L'\0' +#define NLCH L'\n' +#define SPCH L' ' +#else +typedef char CH; +#define STRLEN(x) strlen (x) +#define ADDCH(x) addch ((unsigned char) x) +#define CHWIDTH(x) 1 +#define NULLCH '\0' +#define NLCH '\n' +#define SPCH ' ' +#endif + /* Return the next line up to MAXLEN columns wide in START and LEN. The first invocation should have 0 as *LEN. If the line ends with a \n, it is a normal line that will be continued. If it is a '\0' @@ -101,40 +123,95 @@ typedef struct dialog *dialog_t; there is a forced line break. A full line is returned and will be continued in the next line. */ static void -collect_line (int maxlen, char **start_p, int *len_p) +collect_line (int maxwidth, wchar_t **start_p, int *len_p) { int last_space = 0; int len = *len_p; - char *end; + int width = 0; + CH *end; /* Skip to next line. */ *start_p += len; /* Skip leading space. */ - while (**start_p == ' ') + while (**start_p == SPCH) (*start_p)++; end = *start_p; len = 0; - while (len < maxlen - 1 && *end && *end != '\n') + while (width < maxwidth - 1 && *end != NULLCH && *end != NLCH) { len++; end++; - if (*end == ' ') + if (*end == SPCH) last_space = len; + width += CHWIDTH (*end); } - if (*end && *end != '\n' && last_space != 0) + if (*end != NULLCH && *end != NLCH && last_space != 0) { /* We reached the end of the available space, but still have characters to go in this line. We can break the line into two parts at a space. */ len = last_space; - (*start_p)[len] = '\n'; + (*start_p)[len] = NLCH; } *len_p = len + 1; } +#ifdef HAVE_NCURSESW +static CH * +utf8_to_local (char *lc_ctype, char *string) +{ + mbstate_t ps; + size_t len; + char *local; + const char *p; + wchar_t *wcs = NULL; + char *old_ctype = NULL; + + local = pinentry_utf8_to_local (lc_ctype, string); + if (!local) + return NULL; + + old_ctype = strdup (setlocale (LC_CTYPE, NULL)); + setlocale (LC_CTYPE, lc_ctype? lc_ctype : ""); + + p = local; + memset (&ps, 0, sizeof(mbstate_t)); + len = mbsrtowcs (NULL, &p, strlen (string), &ps); + if (len == (size_t)-1) + { + free (local); + goto leave; + } + wcs = calloc (len + 1, sizeof(wchar_t)); + if (!wcs) + { + free (local); + goto leave; + } + + p = local; + memset (&ps, 0, sizeof(mbstate_t)); + mbsrtowcs (wcs, &p, len, &ps); + + leave: + if (old_ctype) + { + setlocale (LC_CTYPE, old_ctype); + free (old_ctype); + } + + return wcs; +} +#else +static CH * +utf8_to_local (const char *lc_ctype, const char *string) +{ + return pinentry_utf8_to_local (lc_ctype, string); +} +#endif static int dialog_create (pinentry_t pinentry, dialog_t dialog) @@ -148,16 +225,15 @@ dialog_create (pinentry_t pinentry, dialog_t dialog) int xpos; int description_x = 0; int error_x = 0; - char *description = NULL; - char *error = NULL; - char *prompt = NULL; + CH *description = NULL; + CH *error = NULL; + CH *prompt = NULL; #define COPY_OUT(what) \ do \ if (pinentry->what) \ { \ - what = pinentry_utf8_to_local (pinentry->lc_ctype, \ - pinentry->what); \ + what = utf8_to_local (pinentry->lc_ctype, pinentry->what); \ if (!what) \ { \ err = 1; \ @@ -214,7 +290,7 @@ dialog_create (pinentry_t pinentry, dialog_t dialog) y = 1; /* Top frame. */ if (description) { - char *start = description; + CH *start = description; int len = 0; do @@ -232,7 +308,7 @@ dialog_create (pinentry_t pinentry, dialog_t dialog) { if (error) { - char *p = error; + CH *p = error; int err_x = 0; while (*p) @@ -287,7 +363,9 @@ dialog_create (pinentry_t pinentry, dialog_t dialog) new_x = MIN_PINENTRY_LENGTH; if (prompt) - new_x += strlen (prompt) + 1; /* One space after prompt. */ + { + new_x += STRLEN (prompt) + 1; /* One space after prompt. */ + } if (new_x > size_x - 4) new_x = size_x - 4; if (new_x > x) @@ -335,7 +413,7 @@ dialog_create (pinentry_t pinentry, dialog_t dialog) ypos++; if (description) { - char *start = description; + CH *start = description; int len = 0; do @@ -347,9 +425,11 @@ dialog_create (pinentry_t pinentry, dialog_t dialog) addch (' '); collect_line (size_x - 4, &start, &len); for (i = 0; i < len - 1; i++) - addch ((unsigned char) start[i]); - if (start[len - 1] && start[len - 1] != '\n') - addch ((unsigned char) start[len - 1]); + { + ADDCH (start[i]); + } + if (start[len - 1] != NULLCH && start[len - 1] != NLCH) + ADDCH (start[len - 1]); ypos++; } while (start[len - 1]); @@ -363,7 +443,7 @@ dialog_create (pinentry_t pinentry, dialog_t dialog) if (error) { - char *p = error; + CH *p = error; i = 0; while (*p) @@ -378,11 +458,11 @@ dialog_create (pinentry_t pinentry, dialog_t dialog) } else standout (); - for (;*p && *p != '\n'; p++) + for (;*p && *p != NLCH; p++) if (i < x - 4) { i++; - addch ((unsigned char) *p); + ADDCH (*p); } if (USE_COLORS && pinentry->color_so != PINENTRY_COLOR_NONE) { @@ -410,14 +490,16 @@ dialog_create (pinentry_t pinentry, dialog_t dialog) dialog->pin_size = x - 4; if (prompt) { - char *p = prompt; - i = strlen (prompt); + CH *p = prompt; + i = STRLEN (prompt); if (i > x - 4 - MIN_PINENTRY_LENGTH) i = x - 4 - MIN_PINENTRY_LENGTH; dialog->pin_x += i + 1; dialog->pin_size -= i + 1; while (i-- > 0) - addch ((unsigned char) *(p++)); + { + ADDCH (*(p++)); + } addch (' '); } for (i = 0; i < dialog->pin_size; i++) @@ -631,6 +713,17 @@ dialog_run (pinentry_t pinentry, const char *tty_name, const char *tty_type) SCREEN *screen = 0; int done = 0; char *pin_utf8; +#ifdef HAVE_NCURSESW + char *old_ctype = NULL; + + if (pinentry->lc_ctype) + { + old_ctype = strdup (setlocale (LC_CTYPE, NULL)); + setlocale (LC_CTYPE, pinentry->lc_ctype); + } + else + setlocale (LC_CTYPE, ""); +#endif /* Open the desired terminal if necessary. */ if (tty_name) @@ -804,6 +897,13 @@ dialog_run (pinentry_t pinentry, const char *tty_name, const char *tty_type) if (screen) delscreen (screen); +#ifdef HAVE_NCURSESW + if (old_ctype) + { + setlocale (LC_CTYPE, old_ctype); + free (old_ctype); + } +#endif if (ttyfi) fclose (ttyfi); if (ttyfo) -- 1.8.3.1