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