From 3d4e8889889e5e36302454225999f7e146d3219c Mon Sep 17 00:00:00 2001
From: nwhitehorn <nwhitehorn@FreeBSD.org>
Date: Wed, 12 Jan 2011 14:55:02 +0000
Subject: Update dialog to version 20100428. This changes the license under
 which dialog is distributed from GPLv2 to LGPLv2 and introduces a number of
 new features and a new and better libdialog API. The existing libdialog will
 be kept temporarily as libodialog for compatibility purposes until sade,
 sysinstall and tzsetup have been either updated or replaced.

__FreeBSD_version is now 900030.

Discussed on:	-current
Approved by:	core
Obtained from:	http://invisible-island.net/dialog
---
 contrib/dialog/inputstr.c | 751 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 751 insertions(+)
 create mode 100644 contrib/dialog/inputstr.c

(limited to 'contrib/dialog/inputstr.c')

diff --git a/contrib/dialog/inputstr.c b/contrib/dialog/inputstr.c
new file mode 100644
index 0000000..4d95e68
--- /dev/null
+++ b/contrib/dialog/inputstr.c
@@ -0,0 +1,751 @@
+/*
+ * $Id: inputstr.c,v 1.66 2010/01/15 23:13:36 tom Exp $
+ *
+ * inputstr.c -- functions for input/display of a string
+ *
+ * Copyright 2000-2009,2010 Thomas E. Dickey
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License, version 2.1
+ *  as published by the Free Software Foundation.
+ *
+ *  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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to
+ *	Free Software Foundation, Inc.
+ *	51 Franklin St., Fifth Floor
+ *	Boston, MA 02110, USA.
+ */
+
+#include <dialog.h>
+#include <dlg_keys.h>
+
+#include <errno.h>
+
+#ifdef HAVE_SETLOCALE
+#include <locale.h>
+#endif
+
+#if defined(HAVE_SEARCH_H) && defined(HAVE_TSEARCH)
+#include <search.h>
+#else
+#undef HAVE_TSEARCH
+#endif
+
+#ifdef NEED_WCHAR_H
+#include <wchar.h>
+#endif
+
+#if defined(USE_WIDE_CURSES)
+#define USE_CACHING 1
+#elif defined(HAVE_XDIALOG)
+#define USE_CACHING 1		/* editbox really needs caching! */
+#else
+#define USE_CACHING 0
+#endif
+
+typedef struct _cache {
+    struct _cache *next;
+#if USE_CACHING
+    struct _cache *cache_at;	/* unique: associate caches by CACHE */
+    const char *string_at;	/* unique: associate caches by char* */
+#endif
+    unsigned s_len;		/* strlen(string) - we add 1 for EOS */
+    unsigned i_len;		/* length(list) - we add 1 for EOS */
+    char *string;		/* a copy of the last-processed string */
+    int *list;			/* indices into the string */
+} CACHE;
+
+#if USE_CACHING
+#define SAME_CACHE(c,s,l) (c->string != 0 && memcmp(c->string,s,l) == 0)
+
+static CACHE *cache_list;
+
+#ifdef HAVE_TSEARCH
+static void *sorted_cache;
+#endif
+
+#ifdef USE_WIDE_CURSES
+static int
+have_locale(void)
+{
+    static int result = -1;
+    if (result < 0) {
+	char *test = setlocale(LC_ALL, 0);
+	if (test == 0 || *test == 0) {
+	    result = FALSE;
+	} else if (strcmp(test, "C") && strcmp(test, "POSIX")) {
+	    result = TRUE;
+	} else {
+	    result = FALSE;
+	}
+    }
+    return result;
+}
+#endif
+
+#ifdef HAVE_TSEARCH
+static int
+compare_cache(const void *a, const void *b)
+{
+    const CACHE *p = (const CACHE *) a;
+    const CACHE *q = (const CACHE *) b;
+    int result = 0;
+    result = p->cache_at - q->cache_at;
+    if (result == 0)
+	result = p->string_at - q->string_at;
+    return result;
+}
+#endif
+
+static CACHE *
+find_cache(CACHE * cache, const char *string)
+{
+    CACHE *p;
+
+#ifdef HAVE_TSEARCH
+    void *pp;
+    CACHE find;
+
+    memset(&find, 0, sizeof(find));
+    find.cache_at = cache;
+    find.string_at = string;
+
+    if ((pp = tfind(&find, &sorted_cache, compare_cache)) != 0) {
+	p = *(CACHE **) pp;
+    } else {
+	p = 0;
+    }
+#else
+    for (p = cache_list; p != 0; p = p->next) {
+	if (p->cache_at == cache
+	    && p->string_at == string) {
+	    break;
+	}
+    }
+#endif
+    return p;
+}
+
+static void
+make_cache(CACHE * cache, const char *string)
+{
+    CACHE *p;
+
+    p = dlg_calloc(CACHE, 1);
+    assert_ptr(p, "load_cache");
+    p->next = cache_list;
+    cache_list = p;
+
+    p->cache_at = cache;
+    p->string_at = string;
+
+    *cache = *p;
+#ifdef HAVE_TSEARCH
+    (void) tsearch(p, &sorted_cache, compare_cache);
+#endif
+}
+
+static void
+load_cache(CACHE * cache, const char *string)
+{
+    CACHE *p;
+
+    if ((p = find_cache(cache, string)) != 0) {
+	*cache = *p;
+    } else {
+	make_cache(cache, string);
+    }
+}
+
+static void
+save_cache(CACHE * cache, const char *string)
+{
+    CACHE *p;
+
+    if ((p = find_cache(cache, string)) != 0) {
+	CACHE *q = p->next;
+	*p = *cache;
+	p->next = q;
+    }
+}
+#else
+#define SAME_CACHE(c,s,l) (c->string != 0)
+#define load_cache(cache, string)	/* nothing */
+#define save_cache(cache, string)	/* nothing */
+#endif /* USE_WIDE_CURSES */
+
+/*
+ * If the given string has not changed, we do not need to update the index.
+ * If we need to update the index, allocate enough memory for it.
+ */
+static bool
+same_cache2(CACHE * cache, const char *string, unsigned i_len)
+{
+    unsigned need;
+    unsigned s_len = strlen(string);
+
+    if (cache->s_len != 0
+	&& cache->s_len >= s_len
+	&& cache->list != 0
+	&& SAME_CACHE(cache, string, s_len)) {
+	return TRUE;
+    }
+
+    need = (i_len + 1);
+    if (cache->list == 0) {
+	cache->list = dlg_malloc(int, need);
+    } else if (cache->i_len < i_len) {
+	cache->list = dlg_realloc(int, need, cache->list);
+    }
+    cache->i_len = i_len;
+
+    if (cache->s_len >= s_len && cache->string != 0) {
+	strcpy(cache->string, string);
+    } else {
+	if (cache->string != 0)
+	    free(cache->string);
+	cache->string = dlg_strclone(string);
+    }
+    cache->s_len = s_len;
+
+    return FALSE;
+}
+
+#ifdef USE_WIDE_CURSES
+/*
+ * Like same_cache2(), but we are only concerned about caching a copy of the
+ * string and its associated length.
+ */
+static bool
+same_cache1(CACHE * cache, const char *string, unsigned i_len)
+{
+    unsigned s_len = strlen(string);
+
+    if (cache->s_len == s_len
+	&& SAME_CACHE(cache, string, s_len)) {
+	return TRUE;
+    }
+
+    if (cache->s_len >= s_len && cache->string != 0) {
+	strcpy(cache->string, string);
+    } else {
+	if (cache->string != 0)
+	    free(cache->string);
+	cache->string = dlg_strclone(string);
+    }
+    cache->s_len = s_len;
+    cache->i_len = i_len;
+
+    return FALSE;
+}
+#endif /* USE_CACHING */
+
+/*
+ * Counts the number of bytes that make up complete wide-characters, up to byte
+ * 'len'.  If there is no locale set, simply return the original length.
+ */
+#ifdef USE_WIDE_CURSES
+static int
+dlg_count_wcbytes(const char *string, size_t len)
+{
+    int result;
+
+    if (have_locale()) {
+	static CACHE cache;
+
+	load_cache(&cache, string);
+	if (!same_cache1(&cache, string, len)) {
+	    while (len != 0) {
+		int part = 0;
+		size_t code = 0;
+		const char *src = cache.string;
+		mbstate_t state;
+		char save = cache.string[len];
+
+		cache.string[len] = '\0';
+		memset(&state, 0, sizeof(state));
+		code = mbsrtowcs((wchar_t *) 0, &src, len, &state);
+		cache.string[len] = save;
+		if ((int) code >= 0) {
+		    break;
+		}
+		++part;
+		--len;
+	    }
+	    cache.i_len = len;
+	    save_cache(&cache, string);
+	}
+	result = (int) cache.i_len;
+    } else {
+	result = (int) len;
+    }
+    return result;
+}
+#endif /* USE_WIDE_CURSES */
+
+/*
+ * Counts the number of wide-characters in the string.
+ */
+int
+dlg_count_wchars(const char *string)
+{
+    int result;
+
+#ifdef USE_WIDE_CURSES
+    if (have_locale()) {
+	static CACHE cache;
+	size_t len = strlen(string);
+
+	load_cache(&cache, string);
+	if (!same_cache1(&cache, string, len)) {
+	    const char *src = cache.string;
+	    mbstate_t state;
+	    int part = dlg_count_wcbytes(cache.string, len);
+	    char save = cache.string[part];
+	    size_t code;
+	    wchar_t *temp = dlg_calloc(wchar_t, len + 1);
+
+	    cache.string[part] = '\0';
+	    memset(&state, 0, sizeof(state));
+	    code = mbsrtowcs(temp, &src, (size_t) part, &state);
+	    cache.i_len = ((int) code >= 0) ? wcslen(temp) : 0;
+	    cache.string[part] = save;
+	    free(temp);
+	    save_cache(&cache, string);
+	}
+	result = (int) cache.i_len;
+    } else
+#endif /* USE_WIDE_CURSES */
+    {
+	result = (int) strlen(string);
+    }
+    return result;
+}
+
+/*
+ * Build an index of the wide-characters in the string, so we can easily tell
+ * which byte-offset begins a given wide-character.
+ */
+const int *
+dlg_index_wchars(const char *string)
+{
+    static CACHE cache;
+    unsigned len = (unsigned) dlg_count_wchars(string);
+    unsigned inx;
+
+    load_cache(&cache, string);
+    if (!same_cache2(&cache, string, len)) {
+	const char *current = string;
+
+	cache.list[0] = 0;
+	for (inx = 1; inx <= len; ++inx) {
+#ifdef USE_WIDE_CURSES
+	    if (have_locale()) {
+		mbstate_t state;
+		int width;
+		memset(&state, 0, sizeof(state));
+		width = (int) mbrlen(current, strlen(current), &state);
+		if (width <= 0)
+		    width = 1;	/* FIXME: what if we have a control-char? */
+		current += width;
+		cache.list[inx] = cache.list[inx - 1] + width;
+	    } else
+#endif /* USE_WIDE_CURSES */
+	    {
+		(void) current;
+		cache.list[inx] = (int) inx;
+	    }
+	}
+	save_cache(&cache, string);
+    }
+    return cache.list;
+}
+
+/*
+ * Given the character-offset to find in the list, return the corresponding
+ * array index.
+ */
+int
+dlg_find_index(const int *list, int limit, int to_find)
+{
+    int result;
+    for (result = 0; result <= limit; ++result) {
+	if (to_find == list[result]
+	    || result == limit
+	    || to_find < list[result + 1])
+	    break;
+    }
+    return result;
+}
+
+/*
+ * Build a list of the display-columns for the given string's characters.
+ */
+const int *
+dlg_index_columns(const char *string)
+{
+    static CACHE cache;
+    unsigned len = (unsigned) dlg_count_wchars(string);
+    unsigned inx;
+
+    load_cache(&cache, string);
+    if (!same_cache2(&cache, string, len)) {
+	cache.list[0] = 0;
+#ifdef USE_WIDE_CURSES
+	if (have_locale()) {
+	    size_t num_bytes = strlen(string);
+	    const int *inx_wchars = dlg_index_wchars(string);
+	    mbstate_t state;
+
+	    for (inx = 0; inx < len; ++inx) {
+		wchar_t temp[2];
+		size_t check;
+		int result;
+
+		if (string[inx_wchars[inx]] == TAB) {
+		    result = ((cache.list[inx] | 7) + 1) - cache.list[inx];
+		} else {
+		    memset(&state, 0, sizeof(state));
+		    memset(temp, 0, sizeof(temp));
+		    check = mbrtowc(temp,
+				    string + inx_wchars[inx],
+				    num_bytes - (size_t) inx_wchars[inx],
+				    &state);
+		    if ((int) check <= 0) {
+			result = 1;
+		    } else {
+			result = wcwidth(temp[0]);
+		    }
+		    if (result < 0) {
+			wchar_t *printable;
+			cchar_t temp2;
+			setcchar(&temp2, temp, 0, 0, 0);
+			printable = wunctrl(&temp2);
+			result = printable ? (int) wcslen(printable) : 1;
+		    }
+		}
+		cache.list[inx + 1] = result;
+		if (inx != 0)
+		    cache.list[inx + 1] += cache.list[inx];
+	    }
+	} else
+#endif /* USE_WIDE_CURSES */
+	{
+	    for (inx = 0; inx < len; ++inx) {
+		chtype ch = UCH(string[inx]);
+
+		if (ch == TAB)
+		    cache.list[inx + 1] =
+			((cache.list[inx] | 7) + 1) - cache.list[inx];
+		else if (isprint(ch))
+		    cache.list[inx + 1] = 1;
+		else {
+		    const char *printable;
+		    printable = unctrl(ch);
+		    cache.list[inx + 1] = (printable
+					   ? (int) strlen(printable)
+					   : 1);
+		}
+		if (inx != 0)
+		    cache.list[inx + 1] += cache.list[inx];
+	    }
+	}
+	save_cache(&cache, string);
+    }
+    return cache.list;
+}
+
+/*
+ * Returns the number of columns used for a string.  That happens to be the
+ * end-value of the cols[] array.
+ */
+int
+dlg_count_columns(const char *string)
+{
+    int result = 0;
+    int limit = dlg_count_wchars(string);
+    if (limit > 0) {
+	const int *cols = dlg_index_columns(string);
+	result = cols[limit];
+    } else {
+	result = (int) strlen(string);
+    }
+    return result;
+}
+
+/*
+ * Given a column limit, count the number of wide characters that can fit
+ * into that limit.  The offset is used to skip over a leading character
+ * that was already written.
+ */
+int
+dlg_limit_columns(const char *string, int limit, int offset)
+{
+    const int *cols = dlg_index_columns(string);
+    int result = dlg_count_wchars(string);
+
+    while (result > 0 && (cols[result] - cols[offset]) > limit)
+	--result;
+    return result;
+}
+
+/*
+ * Updates the string and character-offset, given various editing characters
+ * or literal characters which are inserted at the character-offset.
+ */
+bool
+dlg_edit_string(char *string, int *chr_offset, int key, int fkey, bool force)
+{
+    int i;
+    int len = (int) strlen(string);
+    int limit = dlg_count_wchars(string);
+    const int *indx = dlg_index_wchars(string);
+    int offset = dlg_find_index(indx, limit, *chr_offset);
+    int max_len = dlg_max_input(MAX_LEN);
+    bool edit = TRUE;
+
+    /* transform editing characters into equivalent function-keys */
+    if (!fkey) {
+	fkey = TRUE;		/* assume we transform */
+	switch (key) {
+	case 0:
+	    break;
+	case ESC:
+	case TAB:
+	    fkey = FALSE;	/* this is used for navigation */
+	    break;
+	default:
+	    fkey = FALSE;	/* ...no, we did not transform */
+	    break;
+	}
+    }
+
+    if (fkey) {
+	switch (key) {
+	case 0:		/* special case for loop entry */
+	    edit = force;
+	    break;
+	case DLGK_GRID_LEFT:
+	    if (*chr_offset)
+		*chr_offset = indx[offset - 1];
+	    break;
+	case DLGK_GRID_RIGHT:
+	    if (offset < limit)
+		*chr_offset = indx[offset + 1];
+	    break;
+	case DLGK_BEGIN:
+	    if (*chr_offset)
+		*chr_offset = 0;
+	    break;
+	case DLGK_FINAL:
+	    if (offset < limit)
+		*chr_offset = indx[limit];
+	    break;
+	case DLGK_DELETE_LEFT:
+	    if (offset) {
+		int gap = indx[offset] - indx[offset - 1];
+		*chr_offset = indx[offset - 1];
+		if (gap > 0) {
+		    for (i = *chr_offset;
+			 (string[i] = string[i + gap]) != '\0';
+			 i++) {
+			;
+		    }
+		}
+	    }
+	    break;
+	case DLGK_DELETE_RIGHT:
+	    if (limit) {
+		if (--limit == 0) {
+		    string[*chr_offset = 0] = '\0';
+		} else {
+		    int gap = ((offset <= limit)
+			       ? (indx[offset + 1] - indx[offset])
+			       : 0);
+		    if (gap > 0) {
+			for (i = indx[offset];
+			     (string[i] = string[i + gap]) != '\0';
+			     i++) {
+			    ;
+			}
+		    } else if (offset > 0) {
+			string[indx[offset - 1]] = '\0';
+		    }
+		    if (*chr_offset > indx[limit])
+			*chr_offset = indx[limit];
+		}
+	    }
+	    break;
+	case DLGK_DELETE_ALL:
+	    string[*chr_offset = 0] = '\0';
+	    break;
+	case DLGK_ENTER:
+	    edit = 0;
+	    break;
+#ifdef KEY_RESIZE
+	case KEY_RESIZE:
+	    edit = 0;
+	    break;
+#endif
+	case DLGK_GRID_UP:
+	case DLGK_GRID_DOWN:
+	case DLGK_FIELD_NEXT:
+	case DLGK_FIELD_PREV:
+	    edit = 0;
+	    break;
+	case ERR:
+	    edit = 0;
+	    break;
+	default:
+	    beep();
+	    break;
+	}
+    } else {
+	if (key == ESC || key == ERR) {
+	    edit = 0;
+	} else {
+	    if (len < max_len) {
+		for (i = ++len; i > *chr_offset; i--)
+		    string[i] = string[i - 1];
+		string[*chr_offset] = (char) key;
+		*chr_offset += 1;
+	    } else {
+		(void) beep();
+	    }
+	}
+    }
+    return edit;
+}
+
+static void
+compute_edit_offset(const char *string,
+		    int chr_offset,
+		    int x_last,
+		    int *p_dpy_column,
+		    int *p_scroll_amt)
+{
+    const int *cols = dlg_index_columns(string);
+    const int *indx = dlg_index_wchars(string);
+    int limit = dlg_count_wchars(string);
+    int offset = dlg_find_index(indx, limit, chr_offset);
+    int offset2;
+    int dpy_column;
+    int n;
+
+    for (n = offset2 = 0; n <= offset; ++n) {
+	if ((cols[offset] - cols[n]) < x_last
+	    && (offset == limit || (cols[offset + 1] - cols[n]) < x_last)) {
+	    offset2 = n;
+	    break;
+	}
+    }
+
+    dpy_column = cols[offset] - cols[offset2];
+
+    if (p_dpy_column != 0)
+	*p_dpy_column = dpy_column;
+    if (p_scroll_amt != 0)
+	*p_scroll_amt = offset2;
+}
+
+/*
+ * Given the character-offset in the string, returns the display-offset where
+ * we will position the cursor.
+ */
+int
+dlg_edit_offset(char *string, int chr_offset, int x_last)
+{
+    int result;
+
+    compute_edit_offset(string, chr_offset, x_last, &result, 0);
+
+    return result;
+}
+
+/*
+ * Displays the string, shifted as necessary, to fit within the box and show
+ * the current character-offset.
+ */
+void
+dlg_show_string(WINDOW *win,
+		const char *string,	/* string to display (may be multibyte) */
+		int chr_offset,	/* character (not bytes) offset */
+		chtype attr,	/* window-attributes */
+		int y_base,	/* beginning row on screen */
+		int x_base,	/* beginning column on screen */
+		int x_last,	/* number of columns on screen */
+		bool hidden,	/* if true, do not echo */
+		bool force)	/* if true, force repaint */
+{
+    x_last = MIN(x_last + x_base, getmaxx(win)) - x_base;
+
+    if (hidden && !dialog_vars.insecure) {
+	if (force) {
+	    (void) wmove(win, y_base, x_base);
+	    wrefresh(win);
+	}
+    } else {
+	const int *cols = dlg_index_columns(string);
+	const int *indx = dlg_index_wchars(string);
+	int limit = dlg_count_wchars(string);
+
+	int i, j, k;
+	int input_x;
+	int scrollamt;
+
+	compute_edit_offset(string, chr_offset, x_last, &input_x, &scrollamt);
+
+	wattrset(win, attr);
+	(void) wmove(win, y_base, x_base);
+	for (i = scrollamt, k = 0; i < limit && k < x_last; ++i) {
+	    int check = cols[i + 1] - cols[scrollamt];
+	    if (check <= x_last) {
+		for (j = indx[i]; j < indx[i + 1]; ++j) {
+		    chtype ch = UCH(string[j]);
+		    if (hidden && dialog_vars.insecure) {
+			waddch(win, '*');
+		    } else if (ch == TAB) {
+			int count = cols[i + 1] - cols[i];
+			while (--count >= 0)
+			    waddch(win, ' ');
+		    } else {
+			waddch(win, ch);
+		    }
+		}
+		k = check;
+	    } else {
+		break;
+	    }
+	}
+	while (k++ < x_last)
+	    waddch(win, ' ');
+	(void) wmove(win, y_base, x_base + input_x);
+	wrefresh(win);
+    }
+}
+
+#ifdef NO_LEAKS
+void
+_dlg_inputstr_leaks(void)
+{
+#if USE_CACHING
+    while (cache_list != 0) {
+	CACHE *next = cache_list->next;
+#ifdef HAVE_TSEARCH
+	tdelete(cache_list, &sorted_cache, compare_cache);
+#endif
+	if (cache_list->string != 0)
+	    free(cache_list->string);
+	if (cache_list->list != 0)
+	    free(cache_list->list);
+	free(cache_list);
+	cache_list = next;
+    }
+#endif /* USE_CACHING */
+}
+#endif /* NO_LEAKS */
-- 
cgit v1.1