xref: /dflybsd-src/contrib/dialog/inputstr.c (revision b2dabe2e739bd72461a68ac543307c2dedfb048c)
15382d832SPeter Avalos /*
2*a8e38dc0SAntonio Huete Jimenez  *  $Id: inputstr.c,v 1.95 2022/04/06 08:03:09 tom Exp $
35382d832SPeter Avalos  *
45382d832SPeter Avalos  *  inputstr.c -- functions for input/display of a string
55382d832SPeter Avalos  *
6*a8e38dc0SAntonio Huete Jimenez  *  Copyright 2000-2021,2022	Thomas E. Dickey
75382d832SPeter Avalos  *
85382d832SPeter Avalos  *  This program is free software; you can redistribute it and/or modify
95382d832SPeter Avalos  *  it under the terms of the GNU Lesser General Public License, version 2.1
105382d832SPeter Avalos  *  as published by the Free Software Foundation.
115382d832SPeter Avalos  *
125382d832SPeter Avalos  *  This program is distributed in the hope that it will be useful, but
135382d832SPeter Avalos  *  WITHOUT ANY WARRANTY; without even the implied warranty of
145382d832SPeter Avalos  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
155382d832SPeter Avalos  *  Lesser General Public License for more details.
165382d832SPeter Avalos  *
175382d832SPeter Avalos  *  You should have received a copy of the GNU Lesser General Public
185382d832SPeter Avalos  *  License along with this program; if not, write to
195382d832SPeter Avalos  *	Free Software Foundation, Inc.
205382d832SPeter Avalos  *	51 Franklin St., Fifth Floor
215382d832SPeter Avalos  *	Boston, MA 02110, USA.
225382d832SPeter Avalos  */
235382d832SPeter Avalos 
24*a8e38dc0SAntonio Huete Jimenez #include <dlg_internals.h>
255382d832SPeter Avalos #include <dlg_keys.h>
265382d832SPeter Avalos 
275382d832SPeter Avalos #if defined(USE_WIDE_CURSES)
285382d832SPeter Avalos #define USE_CACHING 1
295382d832SPeter Avalos #elif defined(HAVE_XDIALOG)
305382d832SPeter Avalos #define USE_CACHING 1		/* editbox really needs caching! */
315382d832SPeter Avalos #else
325382d832SPeter Avalos #define USE_CACHING 0
335382d832SPeter Avalos #endif
345382d832SPeter Avalos 
355382d832SPeter Avalos typedef struct _cache {
365382d832SPeter Avalos     struct _cache *next;
375382d832SPeter Avalos #if USE_CACHING
381ef6786aSJohn Marino     int cache_num;		/* tells what type of data is in list[] */
395382d832SPeter Avalos     const char *string_at;	/* unique: associate caches by char* */
405382d832SPeter Avalos #endif
415382d832SPeter Avalos     size_t s_len;		/* strlen(string) - we add 1 for EOS */
425382d832SPeter Avalos     size_t i_len;		/* length(list) - we add 1 for EOS */
435382d832SPeter Avalos     char *string;		/* a copy of the last-processed string */
445382d832SPeter Avalos     int *list;			/* indices into the string */
455382d832SPeter Avalos } CACHE;
465382d832SPeter Avalos 
475382d832SPeter Avalos #if USE_CACHING
485382d832SPeter Avalos #define SAME_CACHE(c,s,l) (c->string != 0 && memcmp(c->string,s,l) == 0)
495382d832SPeter Avalos 
505382d832SPeter Avalos static CACHE *cache_list;
515382d832SPeter Avalos 
521ef6786aSJohn Marino typedef enum {
531ef6786aSJohn Marino     cInxCols
541ef6786aSJohn Marino     ,cCntWideBytes
551ef6786aSJohn Marino     ,cCntWideChars
561ef6786aSJohn Marino     ,cInxWideChars
571ef6786aSJohn Marino     ,cMAX
581ef6786aSJohn Marino } CACHE_USED;
591ef6786aSJohn Marino 
605382d832SPeter Avalos #ifdef HAVE_TSEARCH
615382d832SPeter Avalos static void *sorted_cache;
625382d832SPeter Avalos #endif
635382d832SPeter Avalos 
645382d832SPeter Avalos #ifdef USE_WIDE_CURSES
655382d832SPeter Avalos static int
have_locale(void)665382d832SPeter Avalos have_locale(void)
675382d832SPeter Avalos {
685382d832SPeter Avalos     static int result = -1;
695382d832SPeter Avalos     if (result < 0) {
705382d832SPeter Avalos 	char *test = setlocale(LC_ALL, 0);
715382d832SPeter Avalos 	if (test == 0 || *test == 0) {
725382d832SPeter Avalos 	    result = FALSE;
735382d832SPeter Avalos 	} else if (strcmp(test, "C") && strcmp(test, "POSIX")) {
745382d832SPeter Avalos 	    result = TRUE;
755382d832SPeter Avalos 	} else {
765382d832SPeter Avalos 	    result = FALSE;
775382d832SPeter Avalos 	}
785382d832SPeter Avalos     }
795382d832SPeter Avalos     return result;
805382d832SPeter Avalos }
815382d832SPeter Avalos #endif
825382d832SPeter Avalos 
835382d832SPeter Avalos #ifdef HAVE_TSEARCH
841ef6786aSJohn Marino 
851ef6786aSJohn Marino #if 0
861ef6786aSJohn Marino static void
871ef6786aSJohn Marino show_tsearch(const void *nodep, const VISIT which, const int depth)
881ef6786aSJohn Marino {
891ef6786aSJohn Marino     const CACHE *p = *(CACHE * const *) nodep;
901ef6786aSJohn Marino     (void) depth;
911ef6786aSJohn Marino     if (which == postorder || which == leaf) {
925940c9abSDaniel Fojt 	DLG_TRACE(("# cache %p %p:%s\n", p, p->string, p->string));
931ef6786aSJohn Marino     }
941ef6786aSJohn Marino }
951ef6786aSJohn Marino 
961ef6786aSJohn Marino static void
971ef6786aSJohn Marino trace_cache(const char *fn, int ln)
981ef6786aSJohn Marino {
995940c9abSDaniel Fojt     DLG_TRACE(("# trace_cache %s@%d\n", fn, ln));
1001ef6786aSJohn Marino     twalk(sorted_cache, show_tsearch);
1011ef6786aSJohn Marino }
1021ef6786aSJohn Marino 
1031ef6786aSJohn Marino #else
1041ef6786aSJohn Marino #define trace_cache(fn, ln)	/* nothing */
1051ef6786aSJohn Marino #endif
1061ef6786aSJohn Marino 
1071ef6786aSJohn Marino #define CMP(a,b) (((a) > (b)) ? 1 : (((a) < (b)) ? -1 : 0))
1081ef6786aSJohn Marino 
1095382d832SPeter Avalos static int
compare_cache(const void * a,const void * b)1105382d832SPeter Avalos compare_cache(const void *a, const void *b)
1115382d832SPeter Avalos {
1125382d832SPeter Avalos     const CACHE *p = (const CACHE *) a;
1135382d832SPeter Avalos     const CACHE *q = (const CACHE *) b;
1141ef6786aSJohn Marino     int result = CMP(p->cache_num, q->cache_num);
1155382d832SPeter Avalos     if (result == 0)
1161ef6786aSJohn Marino 	result = CMP(p->string_at, q->string_at);
1175382d832SPeter Avalos     return result;
1185382d832SPeter Avalos }
1195382d832SPeter Avalos #endif
1205382d832SPeter Avalos 
1215382d832SPeter Avalos static CACHE *
find_cache(int cache_num,const char * string)1221ef6786aSJohn Marino find_cache(int cache_num, const char *string)
1235382d832SPeter Avalos {
1245382d832SPeter Avalos     CACHE *p;
1255382d832SPeter Avalos 
1265382d832SPeter Avalos #ifdef HAVE_TSEARCH
1275382d832SPeter Avalos     void *pp;
1285382d832SPeter Avalos     CACHE find;
1295382d832SPeter Avalos 
1305382d832SPeter Avalos     memset(&find, 0, sizeof(find));
1311ef6786aSJohn Marino     find.cache_num = cache_num;
1325382d832SPeter Avalos     find.string_at = string;
1335382d832SPeter Avalos 
1345382d832SPeter Avalos     if ((pp = tfind(&find, &sorted_cache, compare_cache)) != 0) {
1355382d832SPeter Avalos 	p = *(CACHE **) pp;
1365382d832SPeter Avalos     } else {
1375382d832SPeter Avalos 	p = 0;
1385382d832SPeter Avalos     }
1395382d832SPeter Avalos #else
1405382d832SPeter Avalos     for (p = cache_list; p != 0; p = p->next) {
1411ef6786aSJohn Marino 	if (p->string_at == string) {
1425382d832SPeter Avalos 	    break;
1435382d832SPeter Avalos 	}
1445382d832SPeter Avalos     }
1455382d832SPeter Avalos #endif
1465382d832SPeter Avalos     return p;
1475382d832SPeter Avalos }
1485382d832SPeter Avalos 
1491ef6786aSJohn Marino static CACHE *
make_cache(int cache_num,const char * string)1501ef6786aSJohn Marino make_cache(int cache_num, const char *string)
1515382d832SPeter Avalos {
1525382d832SPeter Avalos     CACHE *p;
1535382d832SPeter Avalos 
1545382d832SPeter Avalos     p = dlg_calloc(CACHE, 1);
1555382d832SPeter Avalos     assert_ptr(p, "load_cache");
1565382d832SPeter Avalos     p->next = cache_list;
1575382d832SPeter Avalos     cache_list = p;
1585382d832SPeter Avalos 
1591ef6786aSJohn Marino     p->cache_num = cache_num;
1605382d832SPeter Avalos     p->string_at = string;
1615382d832SPeter Avalos 
1625382d832SPeter Avalos #ifdef HAVE_TSEARCH
1635382d832SPeter Avalos     (void) tsearch(p, &sorted_cache, compare_cache);
1645382d832SPeter Avalos #endif
1651ef6786aSJohn Marino     return p;
1665382d832SPeter Avalos }
1675382d832SPeter Avalos 
1681ef6786aSJohn Marino static CACHE *
load_cache(int cache_num,const char * string)1691ef6786aSJohn Marino load_cache(int cache_num, const char *string)
1705382d832SPeter Avalos {
1715382d832SPeter Avalos     CACHE *p;
1725382d832SPeter Avalos 
1731ef6786aSJohn Marino     if ((p = find_cache(cache_num, string)) == 0) {
1741ef6786aSJohn Marino 	p = make_cache(cache_num, string);
1755382d832SPeter Avalos     }
1761ef6786aSJohn Marino     return p;
1775382d832SPeter Avalos }
1785382d832SPeter Avalos #else
1791ef6786aSJohn Marino static CACHE my_cache;
1805382d832SPeter Avalos #define SAME_CACHE(c,s,l) (c->string != 0)
1811ef6786aSJohn Marino #define load_cache(cache, string) &my_cache
1821ef6786aSJohn Marino #endif /* USE_CACHING */
1835382d832SPeter Avalos 
1845382d832SPeter Avalos /*
1855382d832SPeter Avalos  * If the given string has not changed, we do not need to update the index.
1865382d832SPeter Avalos  * If we need to update the index, allocate enough memory for it.
1875382d832SPeter Avalos  */
1885382d832SPeter Avalos static bool
same_cache2(CACHE * cache,const char * string,unsigned i_len)1895382d832SPeter Avalos same_cache2(CACHE * cache, const char *string, unsigned i_len)
1905382d832SPeter Avalos {
1915382d832SPeter Avalos     size_t s_len = strlen(string);
1921ef6786aSJohn Marino     bool result = TRUE;
1935382d832SPeter Avalos 
1941ef6786aSJohn Marino     if (cache->s_len == 0
1951ef6786aSJohn Marino 	|| cache->s_len < s_len
1961ef6786aSJohn Marino 	|| cache->list == 0
1971ef6786aSJohn Marino 	|| !SAME_CACHE(cache, string, (size_t) s_len)) {
1985940c9abSDaniel Fojt 	unsigned need = (i_len + 1);
1995382d832SPeter Avalos 
2005382d832SPeter Avalos 	if (cache->list == 0) {
2015382d832SPeter Avalos 	    cache->list = dlg_malloc(int, need);
2025382d832SPeter Avalos 	} else if (cache->i_len < i_len) {
2035382d832SPeter Avalos 	    cache->list = dlg_realloc(int, need, cache->list);
2045382d832SPeter Avalos 	}
2051ef6786aSJohn Marino 	assert_ptr(cache->list, "load_cache");
2065382d832SPeter Avalos 	cache->i_len = i_len;
2075382d832SPeter Avalos 
2085382d832SPeter Avalos 	if (cache->s_len >= s_len && cache->string != 0) {
2095382d832SPeter Avalos 	    strcpy(cache->string, string);
2105382d832SPeter Avalos 	} else {
2115382d832SPeter Avalos 	    if (cache->string != 0)
2125382d832SPeter Avalos 		free(cache->string);
2135382d832SPeter Avalos 	    cache->string = dlg_strclone(string);
2145382d832SPeter Avalos 	}
2155382d832SPeter Avalos 	cache->s_len = s_len;
2165382d832SPeter Avalos 
2171ef6786aSJohn Marino 	result = FALSE;
2181ef6786aSJohn Marino     }
2191ef6786aSJohn Marino     return result;
2205382d832SPeter Avalos }
2215382d832SPeter Avalos 
2225382d832SPeter Avalos #ifdef USE_WIDE_CURSES
2235382d832SPeter Avalos /*
2245382d832SPeter Avalos  * Like same_cache2(), but we are only concerned about caching a copy of the
2255382d832SPeter Avalos  * string and its associated length.
2265382d832SPeter Avalos  */
2275382d832SPeter Avalos static bool
same_cache1(CACHE * cache,const char * string,size_t i_len)2285382d832SPeter Avalos same_cache1(CACHE * cache, const char *string, size_t i_len)
2295382d832SPeter Avalos {
2305382d832SPeter Avalos     size_t s_len = strlen(string);
2311ef6786aSJohn Marino     bool result = TRUE;
2325382d832SPeter Avalos 
2331ef6786aSJohn Marino     if (cache->s_len != s_len
2341ef6786aSJohn Marino 	|| !SAME_CACHE(cache, string, (size_t) s_len)) {
2355382d832SPeter Avalos 
2365382d832SPeter Avalos 	if (cache->s_len >= s_len && cache->string != 0) {
2375382d832SPeter Avalos 	    strcpy(cache->string, string);
2385382d832SPeter Avalos 	} else {
2395382d832SPeter Avalos 	    if (cache->string != 0)
2405382d832SPeter Avalos 		free(cache->string);
2415382d832SPeter Avalos 	    cache->string = dlg_strclone(string);
2425382d832SPeter Avalos 	}
2435382d832SPeter Avalos 	cache->s_len = s_len;
2445382d832SPeter Avalos 	cache->i_len = i_len;
2455382d832SPeter Avalos 
2461ef6786aSJohn Marino 	result = FALSE;
2471ef6786aSJohn Marino     }
2481ef6786aSJohn Marino     return result;
2495382d832SPeter Avalos }
2505382d832SPeter Avalos #endif /* USE_CACHING */
2515382d832SPeter Avalos 
2525382d832SPeter Avalos /*
2535382d832SPeter Avalos  * Counts the number of bytes that make up complete wide-characters, up to byte
2545382d832SPeter Avalos  * 'len'.  If there is no locale set, simply return the original length.
2555382d832SPeter Avalos  */
2565382d832SPeter Avalos #ifdef USE_WIDE_CURSES
2575382d832SPeter Avalos static int
dlg_count_wcbytes(const char * string,size_t len)2585382d832SPeter Avalos dlg_count_wcbytes(const char *string, size_t len)
2595382d832SPeter Avalos {
2605382d832SPeter Avalos     int result;
2615382d832SPeter Avalos 
2625382d832SPeter Avalos     if (have_locale()) {
2631ef6786aSJohn Marino 	CACHE *cache = load_cache(cCntWideBytes, string);
2641ef6786aSJohn Marino 	if (!same_cache1(cache, string, len)) {
2655382d832SPeter Avalos 	    while (len != 0) {
2665382d832SPeter Avalos 		size_t code = 0;
2671ef6786aSJohn Marino 		const char *src = cache->string;
2685382d832SPeter Avalos 		mbstate_t state;
2691ef6786aSJohn Marino 		char save = cache->string[len];
2705382d832SPeter Avalos 
2711ef6786aSJohn Marino 		cache->string[len] = '\0';
2725382d832SPeter Avalos 		memset(&state, 0, sizeof(state));
2735382d832SPeter Avalos 		code = mbsrtowcs((wchar_t *) 0, &src, len, &state);
2741ef6786aSJohn Marino 		cache->string[len] = save;
2755382d832SPeter Avalos 		if ((int) code >= 0) {
2765382d832SPeter Avalos 		    break;
2775382d832SPeter Avalos 		}
2785382d832SPeter Avalos 		--len;
2795382d832SPeter Avalos 	    }
2801ef6786aSJohn Marino 	    cache->i_len = len;
2815382d832SPeter Avalos 	}
2821ef6786aSJohn Marino 	result = (int) cache->i_len;
2835382d832SPeter Avalos     } else {
2845382d832SPeter Avalos 	result = (int) len;
2855382d832SPeter Avalos     }
2865382d832SPeter Avalos     return result;
2875382d832SPeter Avalos }
2885382d832SPeter Avalos #endif /* USE_WIDE_CURSES */
2895382d832SPeter Avalos 
2905382d832SPeter Avalos /*
2915382d832SPeter Avalos  * Counts the number of wide-characters in the string.
2925382d832SPeter Avalos  */
2935382d832SPeter Avalos int
dlg_count_wchars(const char * string)2945382d832SPeter Avalos dlg_count_wchars(const char *string)
2955382d832SPeter Avalos {
2965382d832SPeter Avalos     int result;
2975382d832SPeter Avalos #ifdef USE_WIDE_CURSES
2985382d832SPeter Avalos 
2991ef6786aSJohn Marino     if (have_locale()) {
3001ef6786aSJohn Marino 	size_t len = strlen(string);
3011ef6786aSJohn Marino 	CACHE *cache = load_cache(cCntWideChars, string);
3021ef6786aSJohn Marino 
3031ef6786aSJohn Marino 	if (!same_cache1(cache, string, len)) {
3041ef6786aSJohn Marino 	    const char *src = cache->string;
3055382d832SPeter Avalos 	    mbstate_t state;
3061ef6786aSJohn Marino 	    int part = dlg_count_wcbytes(cache->string, len);
3071ef6786aSJohn Marino 	    char save = cache->string[part];
3085382d832SPeter Avalos 	    wchar_t *temp = dlg_calloc(wchar_t, len + 1);
3095382d832SPeter Avalos 
3105382d832SPeter Avalos 	    if (temp != 0) {
3115940c9abSDaniel Fojt 		size_t code;
3125940c9abSDaniel Fojt 
3131ef6786aSJohn Marino 		cache->string[part] = '\0';
3145382d832SPeter Avalos 		memset(&state, 0, sizeof(state));
3155382d832SPeter Avalos 		code = mbsrtowcs(temp, &src, (size_t) part, &state);
3161ef6786aSJohn Marino 		cache->i_len = ((int) code >= 0) ? wcslen(temp) : 0;
3171ef6786aSJohn Marino 		cache->string[part] = save;
3185382d832SPeter Avalos 		free(temp);
3195382d832SPeter Avalos 	    } else {
3201ef6786aSJohn Marino 		cache->i_len = 0;
3215382d832SPeter Avalos 	    }
3225382d832SPeter Avalos 	}
3231ef6786aSJohn Marino 	result = (int) cache->i_len;
3245382d832SPeter Avalos     } else
3255382d832SPeter Avalos #endif /* USE_WIDE_CURSES */
3265382d832SPeter Avalos     {
3275382d832SPeter Avalos 	result = (int) strlen(string);
3285382d832SPeter Avalos     }
3295382d832SPeter Avalos     return result;
3305382d832SPeter Avalos }
3315382d832SPeter Avalos 
3325382d832SPeter Avalos /*
3335382d832SPeter Avalos  * Build an index of the wide-characters in the string, so we can easily tell
3345382d832SPeter Avalos  * which byte-offset begins a given wide-character.
3355382d832SPeter Avalos  */
3365382d832SPeter Avalos const int *
dlg_index_wchars(const char * string)3375382d832SPeter Avalos dlg_index_wchars(const char *string)
3385382d832SPeter Avalos {
3395382d832SPeter Avalos     unsigned len = (unsigned) dlg_count_wchars(string);
3401ef6786aSJohn Marino     CACHE *cache = load_cache(cInxWideChars, string);
3415382d832SPeter Avalos 
3421ef6786aSJohn Marino     if (!same_cache2(cache, string, len)) {
3435382d832SPeter Avalos 	const char *current = string;
3445940c9abSDaniel Fojt 	unsigned inx;
3455382d832SPeter Avalos 
3461ef6786aSJohn Marino 	cache->list[0] = 0;
3475382d832SPeter Avalos 	for (inx = 1; inx <= len; ++inx) {
3485382d832SPeter Avalos #ifdef USE_WIDE_CURSES
3495382d832SPeter Avalos 	    if (have_locale()) {
3505382d832SPeter Avalos 		mbstate_t state;
3515382d832SPeter Avalos 		int width;
3525382d832SPeter Avalos 		memset(&state, 0, sizeof(state));
3535382d832SPeter Avalos 		width = (int) mbrlen(current, strlen(current), &state);
3545382d832SPeter Avalos 		if (width <= 0)
3555382d832SPeter Avalos 		    width = 1;	/* FIXME: what if we have a control-char? */
3565382d832SPeter Avalos 		current += width;
3571ef6786aSJohn Marino 		cache->list[inx] = cache->list[inx - 1] + width;
3585382d832SPeter Avalos 	    } else
3595382d832SPeter Avalos #endif /* USE_WIDE_CURSES */
3605382d832SPeter Avalos 	    {
3615382d832SPeter Avalos 		(void) current;
3621ef6786aSJohn Marino 		cache->list[inx] = (int) inx;
3635382d832SPeter Avalos 	    }
3645382d832SPeter Avalos 	}
3655382d832SPeter Avalos     }
3661ef6786aSJohn Marino     return cache->list;
3675382d832SPeter Avalos }
3685382d832SPeter Avalos 
3695382d832SPeter Avalos /*
3705382d832SPeter Avalos  * Given the character-offset to find in the list, return the corresponding
3715382d832SPeter Avalos  * array index.
3725382d832SPeter Avalos  */
3735382d832SPeter Avalos int
dlg_find_index(const int * list,int limit,int to_find)3745382d832SPeter Avalos dlg_find_index(const int *list, int limit, int to_find)
3755382d832SPeter Avalos {
3765382d832SPeter Avalos     int result;
3775382d832SPeter Avalos     for (result = 0; result <= limit; ++result) {
3785382d832SPeter Avalos 	if (to_find == list[result]
3795382d832SPeter Avalos 	    || result == limit
3805382d832SPeter Avalos 	    || ((result < limit) && (to_find < list[result + 1]))) {
3815382d832SPeter Avalos 	    break;
3825382d832SPeter Avalos 	}
3835382d832SPeter Avalos     }
3845382d832SPeter Avalos     return result;
3855382d832SPeter Avalos }
3865382d832SPeter Avalos 
3875382d832SPeter Avalos /*
3885382d832SPeter Avalos  * Build a list of the display-columns for the given string's characters.
3895382d832SPeter Avalos  */
3905382d832SPeter Avalos const int *
dlg_index_columns(const char * string)3915382d832SPeter Avalos dlg_index_columns(const char *string)
3925382d832SPeter Avalos {
3935382d832SPeter Avalos     unsigned len = (unsigned) dlg_count_wchars(string);
3941ef6786aSJohn Marino     CACHE *cache = load_cache(cInxCols, string);
3955382d832SPeter Avalos 
3961ef6786aSJohn Marino     if (!same_cache2(cache, string, len)) {
3975940c9abSDaniel Fojt 
3981ef6786aSJohn Marino 	cache->list[0] = 0;
3995382d832SPeter Avalos #ifdef USE_WIDE_CURSES
4005382d832SPeter Avalos 	if (have_locale()) {
401*a8e38dc0SAntonio Huete Jimenez 	    unsigned inx;
4025382d832SPeter Avalos 	    size_t num_bytes = strlen(string);
4035382d832SPeter Avalos 	    const int *inx_wchars = dlg_index_wchars(string);
4045382d832SPeter Avalos 	    mbstate_t state;
4055382d832SPeter Avalos 
4065382d832SPeter Avalos 	    for (inx = 0; inx < len; ++inx) {
4075382d832SPeter Avalos 		int result;
4085382d832SPeter Avalos 
4095382d832SPeter Avalos 		if (string[inx_wchars[inx]] == TAB) {
4101ef6786aSJohn Marino 		    result = ((cache->list[inx] | 7) + 1) - cache->list[inx];
4115382d832SPeter Avalos 		} else {
4125940c9abSDaniel Fojt 		    wchar_t temp[2];
4135940c9abSDaniel Fojt 		    size_t check;
4145940c9abSDaniel Fojt 
4155382d832SPeter Avalos 		    memset(&state, 0, sizeof(state));
4165382d832SPeter Avalos 		    memset(temp, 0, sizeof(temp));
4175382d832SPeter Avalos 		    check = mbrtowc(temp,
4185382d832SPeter Avalos 				    string + inx_wchars[inx],
4195382d832SPeter Avalos 				    num_bytes - (size_t) inx_wchars[inx],
4205382d832SPeter Avalos 				    &state);
4215382d832SPeter Avalos 		    if ((int) check <= 0) {
4225382d832SPeter Avalos 			result = 1;
4235382d832SPeter Avalos 		    } else {
4245382d832SPeter Avalos 			result = wcwidth(temp[0]);
4255382d832SPeter Avalos 		    }
4265382d832SPeter Avalos 		    if (result < 0) {
4275382d832SPeter Avalos 			const wchar_t *printable;
4285382d832SPeter Avalos 			cchar_t temp2, *temp2p = &temp2;
4295382d832SPeter Avalos 			setcchar(temp2p, temp, 0, 0, 0);
4305382d832SPeter Avalos 			printable = wunctrl(temp2p);
4315382d832SPeter Avalos 			result = printable ? (int) wcslen(printable) : 1;
4325382d832SPeter Avalos 		    }
4335382d832SPeter Avalos 		}
4341ef6786aSJohn Marino 		cache->list[inx + 1] = result;
4355382d832SPeter Avalos 		if (inx != 0)
4361ef6786aSJohn Marino 		    cache->list[inx + 1] += cache->list[inx];
4375382d832SPeter Avalos 	    }
4385382d832SPeter Avalos 	} else
4395382d832SPeter Avalos #endif /* USE_WIDE_CURSES */
4405382d832SPeter Avalos 	{
441*a8e38dc0SAntonio Huete Jimenez 	    unsigned inx;
442*a8e38dc0SAntonio Huete Jimenez 
4435382d832SPeter Avalos 	    for (inx = 0; inx < len; ++inx) {
4445382d832SPeter Avalos 		chtype ch = UCH(string[inx]);
4455382d832SPeter Avalos 
4465382d832SPeter Avalos 		if (ch == TAB)
4471ef6786aSJohn Marino 		    cache->list[inx + 1] =
4481ef6786aSJohn Marino 			((cache->list[inx] | 7) + 1) - cache->list[inx];
4495940c9abSDaniel Fojt 		else if (isprint(UCH(ch)))
4501ef6786aSJohn Marino 		    cache->list[inx + 1] = 1;
4515382d832SPeter Avalos 		else {
4525382d832SPeter Avalos 		    const char *printable;
4535382d832SPeter Avalos 		    printable = unctrl(ch);
4541ef6786aSJohn Marino 		    cache->list[inx + 1] = (printable
4555382d832SPeter Avalos 					    ? (int) strlen(printable)
4565382d832SPeter Avalos 					    : 1);
4575382d832SPeter Avalos 		}
4585382d832SPeter Avalos 		if (inx != 0)
4591ef6786aSJohn Marino 		    cache->list[inx + 1] += cache->list[inx];
4605382d832SPeter Avalos 	    }
4615382d832SPeter Avalos 	}
4625382d832SPeter Avalos     }
4631ef6786aSJohn Marino     return cache->list;
4645382d832SPeter Avalos }
4655382d832SPeter Avalos 
4665382d832SPeter Avalos /*
4675382d832SPeter Avalos  * Returns the number of columns used for a string.  That happens to be the
4685382d832SPeter Avalos  * end-value of the cols[] array.
4695382d832SPeter Avalos  */
4705382d832SPeter Avalos int
dlg_count_columns(const char * string)4715382d832SPeter Avalos dlg_count_columns(const char *string)
4725382d832SPeter Avalos {
4735382d832SPeter Avalos     int result = 0;
4745382d832SPeter Avalos     int limit = dlg_count_wchars(string);
4755382d832SPeter Avalos     if (limit > 0) {
4765382d832SPeter Avalos 	const int *cols = dlg_index_columns(string);
4775382d832SPeter Avalos 	result = cols[limit];
4785382d832SPeter Avalos     } else {
4795382d832SPeter Avalos 	result = (int) strlen(string);
4805382d832SPeter Avalos     }
4811ef6786aSJohn Marino     dlg_finish_string(string);
4825382d832SPeter Avalos     return result;
4835382d832SPeter Avalos }
4845382d832SPeter Avalos 
4855382d832SPeter Avalos /*
4865382d832SPeter Avalos  * Given a column limit, count the number of wide characters that can fit
4875382d832SPeter Avalos  * into that limit.  The offset is used to skip over a leading character
4885382d832SPeter Avalos  * that was already written.
4895382d832SPeter Avalos  */
4905382d832SPeter Avalos int
dlg_limit_columns(const char * string,int limit,int offset)4915382d832SPeter Avalos dlg_limit_columns(const char *string, int limit, int offset)
4925382d832SPeter Avalos {
4935382d832SPeter Avalos     const int *cols = dlg_index_columns(string);
4945382d832SPeter Avalos     int result = dlg_count_wchars(string);
4955382d832SPeter Avalos 
4965382d832SPeter Avalos     while (result > 0 && (cols[result] - cols[offset]) > limit)
4975382d832SPeter Avalos 	--result;
4985382d832SPeter Avalos     return result;
4995382d832SPeter Avalos }
5005382d832SPeter Avalos 
5015382d832SPeter Avalos /*
5025382d832SPeter Avalos  * Updates the string and character-offset, given various editing characters
5035382d832SPeter Avalos  * or literal characters which are inserted at the character-offset.
5045382d832SPeter Avalos  */
5055382d832SPeter Avalos bool
dlg_edit_string(char * string,int * chr_offset,int key,int fkey,bool force)5065382d832SPeter Avalos dlg_edit_string(char *string, int *chr_offset, int key, int fkey, bool force)
5075382d832SPeter Avalos {
5085382d832SPeter Avalos     int i;
5095382d832SPeter Avalos     int len = (int) strlen(string);
5105382d832SPeter Avalos     int limit = dlg_count_wchars(string);
5115382d832SPeter Avalos     const int *indx = dlg_index_wchars(string);
5125382d832SPeter Avalos     int offset = dlg_find_index(indx, limit, *chr_offset);
5135382d832SPeter Avalos     bool edit = TRUE;
5145382d832SPeter Avalos 
5155382d832SPeter Avalos     /* transform editing characters into equivalent function-keys */
5165382d832SPeter Avalos     if (!fkey) {
5175382d832SPeter Avalos 	fkey = TRUE;		/* assume we transform */
5185382d832SPeter Avalos 	switch (key) {
5195382d832SPeter Avalos 	case 0:
5205382d832SPeter Avalos 	    break;
5215382d832SPeter Avalos 	case ESC:
5225382d832SPeter Avalos 	case TAB:
5235382d832SPeter Avalos 	    fkey = FALSE;	/* this is used for navigation */
5245382d832SPeter Avalos 	    break;
5255382d832SPeter Avalos 	default:
5265382d832SPeter Avalos 	    fkey = FALSE;	/* ...no, we did not transform */
5275382d832SPeter Avalos 	    break;
5285382d832SPeter Avalos 	}
5295382d832SPeter Avalos     }
5305382d832SPeter Avalos 
5315382d832SPeter Avalos     if (fkey) {
5325382d832SPeter Avalos 	switch (key) {
5335382d832SPeter Avalos 	case 0:		/* special case for loop entry */
5345382d832SPeter Avalos 	    edit = force;
5355382d832SPeter Avalos 	    break;
5365382d832SPeter Avalos 	case DLGK_GRID_LEFT:
5375382d832SPeter Avalos 	    if (*chr_offset && offset > 0)
5385382d832SPeter Avalos 		*chr_offset = indx[offset - 1];
5395382d832SPeter Avalos 	    break;
5405382d832SPeter Avalos 	case DLGK_GRID_RIGHT:
5415382d832SPeter Avalos 	    if (offset < limit)
5425382d832SPeter Avalos 		*chr_offset = indx[offset + 1];
5435382d832SPeter Avalos 	    break;
5445382d832SPeter Avalos 	case DLGK_BEGIN:
5455382d832SPeter Avalos 	    if (*chr_offset)
5465382d832SPeter Avalos 		*chr_offset = 0;
5475382d832SPeter Avalos 	    break;
5485382d832SPeter Avalos 	case DLGK_FINAL:
5495382d832SPeter Avalos 	    if (offset < limit)
5505382d832SPeter Avalos 		*chr_offset = indx[limit];
5515382d832SPeter Avalos 	    break;
5525382d832SPeter Avalos 	case DLGK_DELETE_LEFT:
5535382d832SPeter Avalos 	    if (offset) {
5545382d832SPeter Avalos 		int gap = indx[offset] - indx[offset - 1];
5555382d832SPeter Avalos 		*chr_offset = indx[offset - 1];
5565382d832SPeter Avalos 		if (gap > 0) {
5575382d832SPeter Avalos 		    for (i = *chr_offset;
5585382d832SPeter Avalos 			 (string[i] = string[i + gap]) != '\0';
5595382d832SPeter Avalos 			 i++) {
5605382d832SPeter Avalos 			;
5615382d832SPeter Avalos 		    }
5625382d832SPeter Avalos 		}
5635382d832SPeter Avalos 	    }
5645382d832SPeter Avalos 	    break;
5655382d832SPeter Avalos 	case DLGK_DELETE_RIGHT:
5665382d832SPeter Avalos 	    if (limit) {
5675382d832SPeter Avalos 		if (--limit == 0) {
5685382d832SPeter Avalos 		    string[*chr_offset = 0] = '\0';
5695382d832SPeter Avalos 		} else {
5705382d832SPeter Avalos 		    int gap = ((offset <= limit)
5715382d832SPeter Avalos 			       ? (indx[offset + 1] - indx[offset])
5725382d832SPeter Avalos 			       : 0);
5735382d832SPeter Avalos 		    if (gap > 0) {
5745382d832SPeter Avalos 			for (i = indx[offset];
5755382d832SPeter Avalos 			     (string[i] = string[i + gap]) != '\0';
5765382d832SPeter Avalos 			     i++) {
5775382d832SPeter Avalos 			    ;
5785382d832SPeter Avalos 			}
5795382d832SPeter Avalos 		    } else if (offset > 0) {
5805382d832SPeter Avalos 			string[indx[offset - 1]] = '\0';
5815382d832SPeter Avalos 		    }
5825382d832SPeter Avalos 		    if (*chr_offset > indx[limit])
5835382d832SPeter Avalos 			*chr_offset = indx[limit];
5845382d832SPeter Avalos 		}
5855382d832SPeter Avalos 	    }
5865382d832SPeter Avalos 	    break;
5875382d832SPeter Avalos 	case DLGK_DELETE_ALL:
5885382d832SPeter Avalos 	    string[*chr_offset = 0] = '\0';
5895382d832SPeter Avalos 	    break;
5905382d832SPeter Avalos 	case DLGK_ENTER:
5915382d832SPeter Avalos 	    edit = 0;
5925382d832SPeter Avalos 	    break;
5935382d832SPeter Avalos #ifdef KEY_RESIZE
5945382d832SPeter Avalos 	case KEY_RESIZE:
5955382d832SPeter Avalos 	    edit = 0;
5965382d832SPeter Avalos 	    break;
5975382d832SPeter Avalos #endif
5985382d832SPeter Avalos 	case DLGK_GRID_UP:
5995382d832SPeter Avalos 	case DLGK_GRID_DOWN:
6005382d832SPeter Avalos 	case DLGK_FIELD_NEXT:
6015382d832SPeter Avalos 	case DLGK_FIELD_PREV:
6025382d832SPeter Avalos 	    edit = 0;
6035382d832SPeter Avalos 	    break;
6045382d832SPeter Avalos 	case ERR:
6055382d832SPeter Avalos 	    edit = 0;
6065382d832SPeter Avalos 	    break;
6075382d832SPeter Avalos 	default:
6085382d832SPeter Avalos 	    beep();
6095382d832SPeter Avalos 	    break;
6105382d832SPeter Avalos 	}
6115382d832SPeter Avalos     } else {
6125382d832SPeter Avalos 	if (key == ESC || key == ERR) {
6135382d832SPeter Avalos 	    edit = 0;
6145382d832SPeter Avalos 	} else {
615*a8e38dc0SAntonio Huete Jimenez 	    if (len < dlg_max_input(-1)) {
6165382d832SPeter Avalos 		for (i = ++len; i > *chr_offset; i--)
6175382d832SPeter Avalos 		    string[i] = string[i - 1];
6185382d832SPeter Avalos 		string[*chr_offset] = (char) key;
6195382d832SPeter Avalos 		*chr_offset += 1;
6205382d832SPeter Avalos 	    } else {
6215382d832SPeter Avalos 		(void) beep();
6225382d832SPeter Avalos 	    }
6235382d832SPeter Avalos 	}
6245382d832SPeter Avalos     }
6255382d832SPeter Avalos     return edit;
6265382d832SPeter Avalos }
6275382d832SPeter Avalos 
6285382d832SPeter Avalos static void
compute_edit_offset(const char * string,int chr_offset,int x_last,int * p_dpy_column,int * p_scroll_amt)6295382d832SPeter Avalos compute_edit_offset(const char *string,
6305382d832SPeter Avalos 		    int chr_offset,
6315382d832SPeter Avalos 		    int x_last,
6325382d832SPeter Avalos 		    int *p_dpy_column,
6335382d832SPeter Avalos 		    int *p_scroll_amt)
6345382d832SPeter Avalos {
6355382d832SPeter Avalos     const int *cols = dlg_index_columns(string);
6365382d832SPeter Avalos     const int *indx = dlg_index_wchars(string);
6375382d832SPeter Avalos     int limit = dlg_count_wchars(string);
6385382d832SPeter Avalos     int offset = dlg_find_index(indx, limit, chr_offset);
6395382d832SPeter Avalos     int offset2;
6405382d832SPeter Avalos     int dpy_column;
6415382d832SPeter Avalos     int n;
6425382d832SPeter Avalos 
6435382d832SPeter Avalos     for (n = offset2 = 0; n <= offset; ++n) {
6445382d832SPeter Avalos 	if ((cols[offset] - cols[n]) < x_last
6455382d832SPeter Avalos 	    && (offset == limit || (cols[offset + 1] - cols[n]) < x_last)) {
6465382d832SPeter Avalos 	    offset2 = n;
6475382d832SPeter Avalos 	    break;
6485382d832SPeter Avalos 	}
6495382d832SPeter Avalos     }
6505382d832SPeter Avalos 
6515382d832SPeter Avalos     dpy_column = cols[offset] - cols[offset2];
6525382d832SPeter Avalos 
6535382d832SPeter Avalos     if (p_dpy_column != 0)
6545382d832SPeter Avalos 	*p_dpy_column = dpy_column;
6555382d832SPeter Avalos     if (p_scroll_amt != 0)
6565382d832SPeter Avalos 	*p_scroll_amt = offset2;
6575382d832SPeter Avalos }
6585382d832SPeter Avalos 
6595382d832SPeter Avalos /*
6605382d832SPeter Avalos  * Given the character-offset in the string, returns the display-offset where
6615382d832SPeter Avalos  * we will position the cursor.
6625382d832SPeter Avalos  */
6635382d832SPeter Avalos int
dlg_edit_offset(char * string,int chr_offset,int x_last)6645382d832SPeter Avalos dlg_edit_offset(char *string, int chr_offset, int x_last)
6655382d832SPeter Avalos {
6665382d832SPeter Avalos     int result;
6675382d832SPeter Avalos 
6685382d832SPeter Avalos     compute_edit_offset(string, chr_offset, x_last, &result, 0);
6695382d832SPeter Avalos 
6705382d832SPeter Avalos     return result;
6715382d832SPeter Avalos }
6725382d832SPeter Avalos 
6735382d832SPeter Avalos /*
6745382d832SPeter Avalos  * Displays the string, shifted as necessary, to fit within the box and show
6755382d832SPeter Avalos  * the current character-offset.
6765382d832SPeter Avalos  */
6775382d832SPeter Avalos void
dlg_show_string(WINDOW * win,const char * string,int chr_offset,chtype attr,int y_base,int x_base,int x_last,bool hidden,bool force)6785382d832SPeter Avalos dlg_show_string(WINDOW *win,
6795382d832SPeter Avalos 		const char *string,	/* string to display (may be multibyte) */
6805382d832SPeter Avalos 		int chr_offset,	/* character (not bytes) offset */
6815382d832SPeter Avalos 		chtype attr,	/* window-attributes */
6825382d832SPeter Avalos 		int y_base,	/* beginning row on screen */
6835382d832SPeter Avalos 		int x_base,	/* beginning column on screen */
6845382d832SPeter Avalos 		int x_last,	/* number of columns on screen */
6855382d832SPeter Avalos 		bool hidden,	/* if true, do not echo */
6865382d832SPeter Avalos 		bool force)	/* if true, force repaint */
6875382d832SPeter Avalos {
6885382d832SPeter Avalos     x_last = MIN(x_last + x_base, getmaxx(win)) - x_base;
6895382d832SPeter Avalos 
6905382d832SPeter Avalos     if (hidden && !dialog_vars.insecure) {
6915382d832SPeter Avalos 	if (force) {
6925382d832SPeter Avalos 	    (void) wmove(win, y_base, x_base);
6935382d832SPeter Avalos 	    wrefresh(win);
6945382d832SPeter Avalos 	}
6955382d832SPeter Avalos     } else {
6965382d832SPeter Avalos 	const int *cols = dlg_index_columns(string);
6975382d832SPeter Avalos 	const int *indx = dlg_index_wchars(string);
6985382d832SPeter Avalos 	int limit = dlg_count_wchars(string);
6995382d832SPeter Avalos 
7005382d832SPeter Avalos 	int i, j, k;
7015382d832SPeter Avalos 	int input_x;
7025382d832SPeter Avalos 	int scrollamt;
7035382d832SPeter Avalos 
7045382d832SPeter Avalos 	compute_edit_offset(string, chr_offset, x_last, &input_x, &scrollamt);
7055382d832SPeter Avalos 
7065940c9abSDaniel Fojt 	dlg_attrset(win, attr);
7075382d832SPeter Avalos 	(void) wmove(win, y_base, x_base);
7085382d832SPeter Avalos 	for (i = scrollamt, k = 0; i < limit && k < x_last; ++i) {
7095382d832SPeter Avalos 	    int check = cols[i + 1] - cols[scrollamt];
7105382d832SPeter Avalos 	    if (check <= x_last) {
7115382d832SPeter Avalos 		for (j = indx[i]; j < indx[i + 1]; ++j) {
7125382d832SPeter Avalos 		    chtype ch = UCH(string[j]);
7135382d832SPeter Avalos 		    if (hidden && dialog_vars.insecure) {
7145382d832SPeter Avalos 			waddch(win, '*');
7155382d832SPeter Avalos 		    } else if (ch == TAB) {
7165382d832SPeter Avalos 			int count = cols[i + 1] - cols[i];
7175382d832SPeter Avalos 			while (--count >= 0)
7185382d832SPeter Avalos 			    waddch(win, ' ');
7195382d832SPeter Avalos 		    } else {
7205382d832SPeter Avalos 			waddch(win, ch);
7215382d832SPeter Avalos 		    }
7225382d832SPeter Avalos 		}
7235382d832SPeter Avalos 		k = check;
7245382d832SPeter Avalos 	    } else {
7255382d832SPeter Avalos 		break;
7265382d832SPeter Avalos 	    }
7275382d832SPeter Avalos 	}
7285382d832SPeter Avalos 	while (k++ < x_last)
7295382d832SPeter Avalos 	    waddch(win, ' ');
7305382d832SPeter Avalos 	(void) wmove(win, y_base, x_base + input_x);
7315382d832SPeter Avalos 	wrefresh(win);
7325382d832SPeter Avalos     }
7335382d832SPeter Avalos }
7345382d832SPeter Avalos 
7351ef6786aSJohn Marino /*
7361ef6786aSJohn Marino  * Discard cached data for the given string.
7371ef6786aSJohn Marino  */
7381ef6786aSJohn Marino void
dlg_finish_string(const char * string)7391ef6786aSJohn Marino dlg_finish_string(const char *string)
7401ef6786aSJohn Marino {
7411ef6786aSJohn Marino #if USE_CACHING
7421ef6786aSJohn Marino     if ((string != 0) && dialog_state.finish_string) {
7431ef6786aSJohn Marino 	CACHE *p = cache_list;
7441ef6786aSJohn Marino 	CACHE *q = 0;
7451ef6786aSJohn Marino 	CACHE *r;
7461ef6786aSJohn Marino 
7471ef6786aSJohn Marino 	while (p != 0) {
7481ef6786aSJohn Marino 	    if (p->string_at == string) {
7491ef6786aSJohn Marino #ifdef HAVE_TSEARCH
7501ef6786aSJohn Marino 		if (tdelete(p, &sorted_cache, compare_cache) == 0) {
7511ef6786aSJohn Marino 		    continue;
7521ef6786aSJohn Marino 		}
7531ef6786aSJohn Marino 		trace_cache(__FILE__, __LINE__);
7541ef6786aSJohn Marino #endif
7551ef6786aSJohn Marino 		if (p->string != 0)
7561ef6786aSJohn Marino 		    free(p->string);
7571ef6786aSJohn Marino 		if (p->list != 0)
7581ef6786aSJohn Marino 		    free(p->list);
7591ef6786aSJohn Marino 		if (p == cache_list) {
7601ef6786aSJohn Marino 		    cache_list = p->next;
7611ef6786aSJohn Marino 		    r = cache_list;
7621ef6786aSJohn Marino 		} else {
7631ef6786aSJohn Marino 		    q->next = p->next;
7641ef6786aSJohn Marino 		    r = q;
7651ef6786aSJohn Marino 		}
7661ef6786aSJohn Marino 		free(p);
7671ef6786aSJohn Marino 		p = r;
7681ef6786aSJohn Marino 	    } else {
7691ef6786aSJohn Marino 		q = p;
7701ef6786aSJohn Marino 		p = p->next;
7711ef6786aSJohn Marino 	    }
7721ef6786aSJohn Marino 	}
7731ef6786aSJohn Marino     }
7741ef6786aSJohn Marino #else
7751ef6786aSJohn Marino     (void) string;
7761ef6786aSJohn Marino #endif
7771ef6786aSJohn Marino }
7781ef6786aSJohn Marino 
7795382d832SPeter Avalos #ifdef NO_LEAKS
7805382d832SPeter Avalos void
_dlg_inputstr_leaks(void)7815382d832SPeter Avalos _dlg_inputstr_leaks(void)
7825382d832SPeter Avalos {
7835382d832SPeter Avalos #if USE_CACHING
7841ef6786aSJohn Marino     dialog_state.finish_string = TRUE;
7851ef6786aSJohn Marino     trace_cache(__FILE__, __LINE__);
7865382d832SPeter Avalos     while (cache_list != 0) {
7871ef6786aSJohn Marino 	dlg_finish_string(cache_list->string_at);
7885382d832SPeter Avalos     }
7895382d832SPeter Avalos #endif /* USE_CACHING */
7905382d832SPeter Avalos }
7915382d832SPeter Avalos #endif /* NO_LEAKS */
792