inputstr.c revision 217309
1217309Snwhitehorn/*
2217309Snwhitehorn * $Id: inputstr.c,v 1.66 2010/01/15 23:13:36 tom Exp $
3217309Snwhitehorn *
4217309Snwhitehorn * inputstr.c -- functions for input/display of a string
5217309Snwhitehorn *
6217309Snwhitehorn * Copyright 2000-2009,2010 Thomas E. Dickey
7217309Snwhitehorn *
8217309Snwhitehorn *  This program is free software; you can redistribute it and/or modify
9217309Snwhitehorn *  it under the terms of the GNU Lesser General Public License, version 2.1
10217309Snwhitehorn *  as published by the Free Software Foundation.
11217309Snwhitehorn *
12217309Snwhitehorn *  This program is distributed in the hope that it will be useful, but
13217309Snwhitehorn *  WITHOUT ANY WARRANTY; without even the implied warranty of
14217309Snwhitehorn *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15217309Snwhitehorn *  Lesser General Public License for more details.
16217309Snwhitehorn *
17217309Snwhitehorn *  You should have received a copy of the GNU Lesser General Public
18217309Snwhitehorn *  License along with this program; if not, write to
19217309Snwhitehorn *	Free Software Foundation, Inc.
20217309Snwhitehorn *	51 Franklin St., Fifth Floor
21217309Snwhitehorn *	Boston, MA 02110, USA.
22217309Snwhitehorn */
23217309Snwhitehorn
24217309Snwhitehorn#include <dialog.h>
25217309Snwhitehorn#include <dlg_keys.h>
26217309Snwhitehorn
27217309Snwhitehorn#include <errno.h>
28217309Snwhitehorn
29217309Snwhitehorn#ifdef HAVE_SETLOCALE
30217309Snwhitehorn#include <locale.h>
31217309Snwhitehorn#endif
32217309Snwhitehorn
33217309Snwhitehorn#if defined(HAVE_SEARCH_H) && defined(HAVE_TSEARCH)
34217309Snwhitehorn#include <search.h>
35217309Snwhitehorn#else
36217309Snwhitehorn#undef HAVE_TSEARCH
37217309Snwhitehorn#endif
38217309Snwhitehorn
39217309Snwhitehorn#ifdef NEED_WCHAR_H
40217309Snwhitehorn#include <wchar.h>
41217309Snwhitehorn#endif
42217309Snwhitehorn
43217309Snwhitehorn#if defined(USE_WIDE_CURSES)
44217309Snwhitehorn#define USE_CACHING 1
45217309Snwhitehorn#elif defined(HAVE_XDIALOG)
46217309Snwhitehorn#define USE_CACHING 1		/* editbox really needs caching! */
47217309Snwhitehorn#else
48217309Snwhitehorn#define USE_CACHING 0
49217309Snwhitehorn#endif
50217309Snwhitehorn
51217309Snwhitehorntypedef struct _cache {
52217309Snwhitehorn    struct _cache *next;
53217309Snwhitehorn#if USE_CACHING
54217309Snwhitehorn    struct _cache *cache_at;	/* unique: associate caches by CACHE */
55217309Snwhitehorn    const char *string_at;	/* unique: associate caches by char* */
56217309Snwhitehorn#endif
57217309Snwhitehorn    unsigned s_len;		/* strlen(string) - we add 1 for EOS */
58217309Snwhitehorn    unsigned i_len;		/* length(list) - we add 1 for EOS */
59217309Snwhitehorn    char *string;		/* a copy of the last-processed string */
60217309Snwhitehorn    int *list;			/* indices into the string */
61217309Snwhitehorn} CACHE;
62217309Snwhitehorn
63217309Snwhitehorn#if USE_CACHING
64217309Snwhitehorn#define SAME_CACHE(c,s,l) (c->string != 0 && memcmp(c->string,s,l) == 0)
65217309Snwhitehorn
66217309Snwhitehornstatic CACHE *cache_list;
67217309Snwhitehorn
68217309Snwhitehorn#ifdef HAVE_TSEARCH
69217309Snwhitehornstatic void *sorted_cache;
70217309Snwhitehorn#endif
71217309Snwhitehorn
72217309Snwhitehorn#ifdef USE_WIDE_CURSES
73217309Snwhitehornstatic int
74217309Snwhitehornhave_locale(void)
75217309Snwhitehorn{
76217309Snwhitehorn    static int result = -1;
77217309Snwhitehorn    if (result < 0) {
78217309Snwhitehorn	char *test = setlocale(LC_ALL, 0);
79217309Snwhitehorn	if (test == 0 || *test == 0) {
80217309Snwhitehorn	    result = FALSE;
81217309Snwhitehorn	} else if (strcmp(test, "C") && strcmp(test, "POSIX")) {
82217309Snwhitehorn	    result = TRUE;
83217309Snwhitehorn	} else {
84217309Snwhitehorn	    result = FALSE;
85217309Snwhitehorn	}
86217309Snwhitehorn    }
87217309Snwhitehorn    return result;
88217309Snwhitehorn}
89217309Snwhitehorn#endif
90217309Snwhitehorn
91217309Snwhitehorn#ifdef HAVE_TSEARCH
92217309Snwhitehornstatic int
93217309Snwhitehorncompare_cache(const void *a, const void *b)
94217309Snwhitehorn{
95217309Snwhitehorn    const CACHE *p = (const CACHE *) a;
96217309Snwhitehorn    const CACHE *q = (const CACHE *) b;
97217309Snwhitehorn    int result = 0;
98217309Snwhitehorn    result = p->cache_at - q->cache_at;
99217309Snwhitehorn    if (result == 0)
100217309Snwhitehorn	result = p->string_at - q->string_at;
101217309Snwhitehorn    return result;
102217309Snwhitehorn}
103217309Snwhitehorn#endif
104217309Snwhitehorn
105217309Snwhitehornstatic CACHE *
106217309Snwhitehornfind_cache(CACHE * cache, const char *string)
107217309Snwhitehorn{
108217309Snwhitehorn    CACHE *p;
109217309Snwhitehorn
110217309Snwhitehorn#ifdef HAVE_TSEARCH
111217309Snwhitehorn    void *pp;
112217309Snwhitehorn    CACHE find;
113217309Snwhitehorn
114217309Snwhitehorn    memset(&find, 0, sizeof(find));
115217309Snwhitehorn    find.cache_at = cache;
116217309Snwhitehorn    find.string_at = string;
117217309Snwhitehorn
118217309Snwhitehorn    if ((pp = tfind(&find, &sorted_cache, compare_cache)) != 0) {
119217309Snwhitehorn	p = *(CACHE **) pp;
120217309Snwhitehorn    } else {
121217309Snwhitehorn	p = 0;
122217309Snwhitehorn    }
123217309Snwhitehorn#else
124217309Snwhitehorn    for (p = cache_list; p != 0; p = p->next) {
125217309Snwhitehorn	if (p->cache_at == cache
126217309Snwhitehorn	    && p->string_at == string) {
127217309Snwhitehorn	    break;
128217309Snwhitehorn	}
129217309Snwhitehorn    }
130217309Snwhitehorn#endif
131217309Snwhitehorn    return p;
132217309Snwhitehorn}
133217309Snwhitehorn
134217309Snwhitehornstatic void
135217309Snwhitehornmake_cache(CACHE * cache, const char *string)
136217309Snwhitehorn{
137217309Snwhitehorn    CACHE *p;
138217309Snwhitehorn
139217309Snwhitehorn    p = dlg_calloc(CACHE, 1);
140217309Snwhitehorn    assert_ptr(p, "load_cache");
141217309Snwhitehorn    p->next = cache_list;
142217309Snwhitehorn    cache_list = p;
143217309Snwhitehorn
144217309Snwhitehorn    p->cache_at = cache;
145217309Snwhitehorn    p->string_at = string;
146217309Snwhitehorn
147217309Snwhitehorn    *cache = *p;
148217309Snwhitehorn#ifdef HAVE_TSEARCH
149217309Snwhitehorn    (void) tsearch(p, &sorted_cache, compare_cache);
150217309Snwhitehorn#endif
151217309Snwhitehorn}
152217309Snwhitehorn
153217309Snwhitehornstatic void
154217309Snwhitehornload_cache(CACHE * cache, const char *string)
155217309Snwhitehorn{
156217309Snwhitehorn    CACHE *p;
157217309Snwhitehorn
158217309Snwhitehorn    if ((p = find_cache(cache, string)) != 0) {
159217309Snwhitehorn	*cache = *p;
160217309Snwhitehorn    } else {
161217309Snwhitehorn	make_cache(cache, string);
162217309Snwhitehorn    }
163217309Snwhitehorn}
164217309Snwhitehorn
165217309Snwhitehornstatic void
166217309Snwhitehornsave_cache(CACHE * cache, const char *string)
167217309Snwhitehorn{
168217309Snwhitehorn    CACHE *p;
169217309Snwhitehorn
170217309Snwhitehorn    if ((p = find_cache(cache, string)) != 0) {
171217309Snwhitehorn	CACHE *q = p->next;
172217309Snwhitehorn	*p = *cache;
173217309Snwhitehorn	p->next = q;
174217309Snwhitehorn    }
175217309Snwhitehorn}
176217309Snwhitehorn#else
177217309Snwhitehorn#define SAME_CACHE(c,s,l) (c->string != 0)
178217309Snwhitehorn#define load_cache(cache, string)	/* nothing */
179217309Snwhitehorn#define save_cache(cache, string)	/* nothing */
180217309Snwhitehorn#endif /* USE_WIDE_CURSES */
181217309Snwhitehorn
182217309Snwhitehorn/*
183217309Snwhitehorn * If the given string has not changed, we do not need to update the index.
184217309Snwhitehorn * If we need to update the index, allocate enough memory for it.
185217309Snwhitehorn */
186217309Snwhitehornstatic bool
187217309Snwhitehornsame_cache2(CACHE * cache, const char *string, unsigned i_len)
188217309Snwhitehorn{
189217309Snwhitehorn    unsigned need;
190217309Snwhitehorn    unsigned s_len = strlen(string);
191217309Snwhitehorn
192217309Snwhitehorn    if (cache->s_len != 0
193217309Snwhitehorn	&& cache->s_len >= s_len
194217309Snwhitehorn	&& cache->list != 0
195217309Snwhitehorn	&& SAME_CACHE(cache, string, s_len)) {
196217309Snwhitehorn	return TRUE;
197217309Snwhitehorn    }
198217309Snwhitehorn
199217309Snwhitehorn    need = (i_len + 1);
200217309Snwhitehorn    if (cache->list == 0) {
201217309Snwhitehorn	cache->list = dlg_malloc(int, need);
202217309Snwhitehorn    } else if (cache->i_len < i_len) {
203217309Snwhitehorn	cache->list = dlg_realloc(int, need, cache->list);
204217309Snwhitehorn    }
205217309Snwhitehorn    cache->i_len = i_len;
206217309Snwhitehorn
207217309Snwhitehorn    if (cache->s_len >= s_len && cache->string != 0) {
208217309Snwhitehorn	strcpy(cache->string, string);
209217309Snwhitehorn    } else {
210217309Snwhitehorn	if (cache->string != 0)
211217309Snwhitehorn	    free(cache->string);
212217309Snwhitehorn	cache->string = dlg_strclone(string);
213217309Snwhitehorn    }
214217309Snwhitehorn    cache->s_len = s_len;
215217309Snwhitehorn
216217309Snwhitehorn    return FALSE;
217217309Snwhitehorn}
218217309Snwhitehorn
219217309Snwhitehorn#ifdef USE_WIDE_CURSES
220217309Snwhitehorn/*
221217309Snwhitehorn * Like same_cache2(), but we are only concerned about caching a copy of the
222217309Snwhitehorn * string and its associated length.
223217309Snwhitehorn */
224217309Snwhitehornstatic bool
225217309Snwhitehornsame_cache1(CACHE * cache, const char *string, unsigned i_len)
226217309Snwhitehorn{
227217309Snwhitehorn    unsigned s_len = strlen(string);
228217309Snwhitehorn
229217309Snwhitehorn    if (cache->s_len == s_len
230217309Snwhitehorn	&& SAME_CACHE(cache, string, s_len)) {
231217309Snwhitehorn	return TRUE;
232217309Snwhitehorn    }
233217309Snwhitehorn
234217309Snwhitehorn    if (cache->s_len >= s_len && cache->string != 0) {
235217309Snwhitehorn	strcpy(cache->string, string);
236217309Snwhitehorn    } else {
237217309Snwhitehorn	if (cache->string != 0)
238217309Snwhitehorn	    free(cache->string);
239217309Snwhitehorn	cache->string = dlg_strclone(string);
240217309Snwhitehorn    }
241217309Snwhitehorn    cache->s_len = s_len;
242217309Snwhitehorn    cache->i_len = i_len;
243217309Snwhitehorn
244217309Snwhitehorn    return FALSE;
245217309Snwhitehorn}
246217309Snwhitehorn#endif /* USE_CACHING */
247217309Snwhitehorn
248217309Snwhitehorn/*
249217309Snwhitehorn * Counts the number of bytes that make up complete wide-characters, up to byte
250217309Snwhitehorn * 'len'.  If there is no locale set, simply return the original length.
251217309Snwhitehorn */
252217309Snwhitehorn#ifdef USE_WIDE_CURSES
253217309Snwhitehornstatic int
254217309Snwhitehorndlg_count_wcbytes(const char *string, size_t len)
255217309Snwhitehorn{
256217309Snwhitehorn    int result;
257217309Snwhitehorn
258217309Snwhitehorn    if (have_locale()) {
259217309Snwhitehorn	static CACHE cache;
260217309Snwhitehorn
261217309Snwhitehorn	load_cache(&cache, string);
262217309Snwhitehorn	if (!same_cache1(&cache, string, len)) {
263217309Snwhitehorn	    while (len != 0) {
264217309Snwhitehorn		int part = 0;
265217309Snwhitehorn		size_t code = 0;
266217309Snwhitehorn		const char *src = cache.string;
267217309Snwhitehorn		mbstate_t state;
268217309Snwhitehorn		char save = cache.string[len];
269217309Snwhitehorn
270217309Snwhitehorn		cache.string[len] = '\0';
271217309Snwhitehorn		memset(&state, 0, sizeof(state));
272217309Snwhitehorn		code = mbsrtowcs((wchar_t *) 0, &src, len, &state);
273217309Snwhitehorn		cache.string[len] = save;
274217309Snwhitehorn		if ((int) code >= 0) {
275217309Snwhitehorn		    break;
276217309Snwhitehorn		}
277217309Snwhitehorn		++part;
278217309Snwhitehorn		--len;
279217309Snwhitehorn	    }
280217309Snwhitehorn	    cache.i_len = len;
281217309Snwhitehorn	    save_cache(&cache, string);
282217309Snwhitehorn	}
283217309Snwhitehorn	result = (int) cache.i_len;
284217309Snwhitehorn    } else {
285217309Snwhitehorn	result = (int) len;
286217309Snwhitehorn    }
287217309Snwhitehorn    return result;
288217309Snwhitehorn}
289217309Snwhitehorn#endif /* USE_WIDE_CURSES */
290217309Snwhitehorn
291217309Snwhitehorn/*
292217309Snwhitehorn * Counts the number of wide-characters in the string.
293217309Snwhitehorn */
294217309Snwhitehornint
295217309Snwhitehorndlg_count_wchars(const char *string)
296217309Snwhitehorn{
297217309Snwhitehorn    int result;
298217309Snwhitehorn
299217309Snwhitehorn#ifdef USE_WIDE_CURSES
300217309Snwhitehorn    if (have_locale()) {
301217309Snwhitehorn	static CACHE cache;
302217309Snwhitehorn	size_t len = strlen(string);
303217309Snwhitehorn
304217309Snwhitehorn	load_cache(&cache, string);
305217309Snwhitehorn	if (!same_cache1(&cache, string, len)) {
306217309Snwhitehorn	    const char *src = cache.string;
307217309Snwhitehorn	    mbstate_t state;
308217309Snwhitehorn	    int part = dlg_count_wcbytes(cache.string, len);
309217309Snwhitehorn	    char save = cache.string[part];
310217309Snwhitehorn	    size_t code;
311217309Snwhitehorn	    wchar_t *temp = dlg_calloc(wchar_t, len + 1);
312217309Snwhitehorn
313217309Snwhitehorn	    cache.string[part] = '\0';
314217309Snwhitehorn	    memset(&state, 0, sizeof(state));
315217309Snwhitehorn	    code = mbsrtowcs(temp, &src, (size_t) part, &state);
316217309Snwhitehorn	    cache.i_len = ((int) code >= 0) ? wcslen(temp) : 0;
317217309Snwhitehorn	    cache.string[part] = save;
318217309Snwhitehorn	    free(temp);
319217309Snwhitehorn	    save_cache(&cache, string);
320217309Snwhitehorn	}
321217309Snwhitehorn	result = (int) cache.i_len;
322217309Snwhitehorn    } else
323217309Snwhitehorn#endif /* USE_WIDE_CURSES */
324217309Snwhitehorn    {
325217309Snwhitehorn	result = (int) strlen(string);
326217309Snwhitehorn    }
327217309Snwhitehorn    return result;
328217309Snwhitehorn}
329217309Snwhitehorn
330217309Snwhitehorn/*
331217309Snwhitehorn * Build an index of the wide-characters in the string, so we can easily tell
332217309Snwhitehorn * which byte-offset begins a given wide-character.
333217309Snwhitehorn */
334217309Snwhitehornconst int *
335217309Snwhitehorndlg_index_wchars(const char *string)
336217309Snwhitehorn{
337217309Snwhitehorn    static CACHE cache;
338217309Snwhitehorn    unsigned len = (unsigned) dlg_count_wchars(string);
339217309Snwhitehorn    unsigned inx;
340217309Snwhitehorn
341217309Snwhitehorn    load_cache(&cache, string);
342217309Snwhitehorn    if (!same_cache2(&cache, string, len)) {
343217309Snwhitehorn	const char *current = string;
344217309Snwhitehorn
345217309Snwhitehorn	cache.list[0] = 0;
346217309Snwhitehorn	for (inx = 1; inx <= len; ++inx) {
347217309Snwhitehorn#ifdef USE_WIDE_CURSES
348217309Snwhitehorn	    if (have_locale()) {
349217309Snwhitehorn		mbstate_t state;
350217309Snwhitehorn		int width;
351217309Snwhitehorn		memset(&state, 0, sizeof(state));
352217309Snwhitehorn		width = (int) mbrlen(current, strlen(current), &state);
353217309Snwhitehorn		if (width <= 0)
354217309Snwhitehorn		    width = 1;	/* FIXME: what if we have a control-char? */
355217309Snwhitehorn		current += width;
356217309Snwhitehorn		cache.list[inx] = cache.list[inx - 1] + width;
357217309Snwhitehorn	    } else
358217309Snwhitehorn#endif /* USE_WIDE_CURSES */
359217309Snwhitehorn	    {
360217309Snwhitehorn		(void) current;
361217309Snwhitehorn		cache.list[inx] = (int) inx;
362217309Snwhitehorn	    }
363217309Snwhitehorn	}
364217309Snwhitehorn	save_cache(&cache, string);
365217309Snwhitehorn    }
366217309Snwhitehorn    return cache.list;
367217309Snwhitehorn}
368217309Snwhitehorn
369217309Snwhitehorn/*
370217309Snwhitehorn * Given the character-offset to find in the list, return the corresponding
371217309Snwhitehorn * array index.
372217309Snwhitehorn */
373217309Snwhitehornint
374217309Snwhitehorndlg_find_index(const int *list, int limit, int to_find)
375217309Snwhitehorn{
376217309Snwhitehorn    int result;
377217309Snwhitehorn    for (result = 0; result <= limit; ++result) {
378217309Snwhitehorn	if (to_find == list[result]
379217309Snwhitehorn	    || result == limit
380217309Snwhitehorn	    || to_find < list[result + 1])
381217309Snwhitehorn	    break;
382217309Snwhitehorn    }
383217309Snwhitehorn    return result;
384217309Snwhitehorn}
385217309Snwhitehorn
386217309Snwhitehorn/*
387217309Snwhitehorn * Build a list of the display-columns for the given string's characters.
388217309Snwhitehorn */
389217309Snwhitehornconst int *
390217309Snwhitehorndlg_index_columns(const char *string)
391217309Snwhitehorn{
392217309Snwhitehorn    static CACHE cache;
393217309Snwhitehorn    unsigned len = (unsigned) dlg_count_wchars(string);
394217309Snwhitehorn    unsigned inx;
395217309Snwhitehorn
396217309Snwhitehorn    load_cache(&cache, string);
397217309Snwhitehorn    if (!same_cache2(&cache, string, len)) {
398217309Snwhitehorn	cache.list[0] = 0;
399217309Snwhitehorn#ifdef USE_WIDE_CURSES
400217309Snwhitehorn	if (have_locale()) {
401217309Snwhitehorn	    size_t num_bytes = strlen(string);
402217309Snwhitehorn	    const int *inx_wchars = dlg_index_wchars(string);
403217309Snwhitehorn	    mbstate_t state;
404217309Snwhitehorn
405217309Snwhitehorn	    for (inx = 0; inx < len; ++inx) {
406217309Snwhitehorn		wchar_t temp[2];
407217309Snwhitehorn		size_t check;
408217309Snwhitehorn		int result;
409217309Snwhitehorn
410217309Snwhitehorn		if (string[inx_wchars[inx]] == TAB) {
411217309Snwhitehorn		    result = ((cache.list[inx] | 7) + 1) - cache.list[inx];
412217309Snwhitehorn		} else {
413217309Snwhitehorn		    memset(&state, 0, sizeof(state));
414217309Snwhitehorn		    memset(temp, 0, sizeof(temp));
415217309Snwhitehorn		    check = mbrtowc(temp,
416217309Snwhitehorn				    string + inx_wchars[inx],
417217309Snwhitehorn				    num_bytes - (size_t) inx_wchars[inx],
418217309Snwhitehorn				    &state);
419217309Snwhitehorn		    if ((int) check <= 0) {
420217309Snwhitehorn			result = 1;
421217309Snwhitehorn		    } else {
422217309Snwhitehorn			result = wcwidth(temp[0]);
423217309Snwhitehorn		    }
424217309Snwhitehorn		    if (result < 0) {
425217309Snwhitehorn			wchar_t *printable;
426217309Snwhitehorn			cchar_t temp2;
427217309Snwhitehorn			setcchar(&temp2, temp, 0, 0, 0);
428217309Snwhitehorn			printable = wunctrl(&temp2);
429217309Snwhitehorn			result = printable ? (int) wcslen(printable) : 1;
430217309Snwhitehorn		    }
431217309Snwhitehorn		}
432217309Snwhitehorn		cache.list[inx + 1] = result;
433217309Snwhitehorn		if (inx != 0)
434217309Snwhitehorn		    cache.list[inx + 1] += cache.list[inx];
435217309Snwhitehorn	    }
436217309Snwhitehorn	} else
437217309Snwhitehorn#endif /* USE_WIDE_CURSES */
438217309Snwhitehorn	{
439217309Snwhitehorn	    for (inx = 0; inx < len; ++inx) {
440217309Snwhitehorn		chtype ch = UCH(string[inx]);
441217309Snwhitehorn
442217309Snwhitehorn		if (ch == TAB)
443217309Snwhitehorn		    cache.list[inx + 1] =
444217309Snwhitehorn			((cache.list[inx] | 7) + 1) - cache.list[inx];
445217309Snwhitehorn		else if (isprint(ch))
446217309Snwhitehorn		    cache.list[inx + 1] = 1;
447217309Snwhitehorn		else {
448217309Snwhitehorn		    const char *printable;
449217309Snwhitehorn		    printable = unctrl(ch);
450217309Snwhitehorn		    cache.list[inx + 1] = (printable
451217309Snwhitehorn					   ? (int) strlen(printable)
452217309Snwhitehorn					   : 1);
453217309Snwhitehorn		}
454217309Snwhitehorn		if (inx != 0)
455217309Snwhitehorn		    cache.list[inx + 1] += cache.list[inx];
456217309Snwhitehorn	    }
457217309Snwhitehorn	}
458217309Snwhitehorn	save_cache(&cache, string);
459217309Snwhitehorn    }
460217309Snwhitehorn    return cache.list;
461217309Snwhitehorn}
462217309Snwhitehorn
463217309Snwhitehorn/*
464217309Snwhitehorn * Returns the number of columns used for a string.  That happens to be the
465217309Snwhitehorn * end-value of the cols[] array.
466217309Snwhitehorn */
467217309Snwhitehornint
468217309Snwhitehorndlg_count_columns(const char *string)
469217309Snwhitehorn{
470217309Snwhitehorn    int result = 0;
471217309Snwhitehorn    int limit = dlg_count_wchars(string);
472217309Snwhitehorn    if (limit > 0) {
473217309Snwhitehorn	const int *cols = dlg_index_columns(string);
474217309Snwhitehorn	result = cols[limit];
475217309Snwhitehorn    } else {
476217309Snwhitehorn	result = (int) strlen(string);
477217309Snwhitehorn    }
478217309Snwhitehorn    return result;
479217309Snwhitehorn}
480217309Snwhitehorn
481217309Snwhitehorn/*
482217309Snwhitehorn * Given a column limit, count the number of wide characters that can fit
483217309Snwhitehorn * into that limit.  The offset is used to skip over a leading character
484217309Snwhitehorn * that was already written.
485217309Snwhitehorn */
486217309Snwhitehornint
487217309Snwhitehorndlg_limit_columns(const char *string, int limit, int offset)
488217309Snwhitehorn{
489217309Snwhitehorn    const int *cols = dlg_index_columns(string);
490217309Snwhitehorn    int result = dlg_count_wchars(string);
491217309Snwhitehorn
492217309Snwhitehorn    while (result > 0 && (cols[result] - cols[offset]) > limit)
493217309Snwhitehorn	--result;
494217309Snwhitehorn    return result;
495217309Snwhitehorn}
496217309Snwhitehorn
497217309Snwhitehorn/*
498217309Snwhitehorn * Updates the string and character-offset, given various editing characters
499217309Snwhitehorn * or literal characters which are inserted at the character-offset.
500217309Snwhitehorn */
501217309Snwhitehornbool
502217309Snwhitehorndlg_edit_string(char *string, int *chr_offset, int key, int fkey, bool force)
503217309Snwhitehorn{
504217309Snwhitehorn    int i;
505217309Snwhitehorn    int len = (int) strlen(string);
506217309Snwhitehorn    int limit = dlg_count_wchars(string);
507217309Snwhitehorn    const int *indx = dlg_index_wchars(string);
508217309Snwhitehorn    int offset = dlg_find_index(indx, limit, *chr_offset);
509217309Snwhitehorn    int max_len = dlg_max_input(MAX_LEN);
510217309Snwhitehorn    bool edit = TRUE;
511217309Snwhitehorn
512217309Snwhitehorn    /* transform editing characters into equivalent function-keys */
513217309Snwhitehorn    if (!fkey) {
514217309Snwhitehorn	fkey = TRUE;		/* assume we transform */
515217309Snwhitehorn	switch (key) {
516217309Snwhitehorn	case 0:
517217309Snwhitehorn	    break;
518217309Snwhitehorn	case ESC:
519217309Snwhitehorn	case TAB:
520217309Snwhitehorn	    fkey = FALSE;	/* this is used for navigation */
521217309Snwhitehorn	    break;
522217309Snwhitehorn	default:
523217309Snwhitehorn	    fkey = FALSE;	/* ...no, we did not transform */
524217309Snwhitehorn	    break;
525217309Snwhitehorn	}
526217309Snwhitehorn    }
527217309Snwhitehorn
528217309Snwhitehorn    if (fkey) {
529217309Snwhitehorn	switch (key) {
530217309Snwhitehorn	case 0:		/* special case for loop entry */
531217309Snwhitehorn	    edit = force;
532217309Snwhitehorn	    break;
533217309Snwhitehorn	case DLGK_GRID_LEFT:
534217309Snwhitehorn	    if (*chr_offset)
535217309Snwhitehorn		*chr_offset = indx[offset - 1];
536217309Snwhitehorn	    break;
537217309Snwhitehorn	case DLGK_GRID_RIGHT:
538217309Snwhitehorn	    if (offset < limit)
539217309Snwhitehorn		*chr_offset = indx[offset + 1];
540217309Snwhitehorn	    break;
541217309Snwhitehorn	case DLGK_BEGIN:
542217309Snwhitehorn	    if (*chr_offset)
543217309Snwhitehorn		*chr_offset = 0;
544217309Snwhitehorn	    break;
545217309Snwhitehorn	case DLGK_FINAL:
546217309Snwhitehorn	    if (offset < limit)
547217309Snwhitehorn		*chr_offset = indx[limit];
548217309Snwhitehorn	    break;
549217309Snwhitehorn	case DLGK_DELETE_LEFT:
550217309Snwhitehorn	    if (offset) {
551217309Snwhitehorn		int gap = indx[offset] - indx[offset - 1];
552217309Snwhitehorn		*chr_offset = indx[offset - 1];
553217309Snwhitehorn		if (gap > 0) {
554217309Snwhitehorn		    for (i = *chr_offset;
555217309Snwhitehorn			 (string[i] = string[i + gap]) != '\0';
556217309Snwhitehorn			 i++) {
557217309Snwhitehorn			;
558217309Snwhitehorn		    }
559217309Snwhitehorn		}
560217309Snwhitehorn	    }
561217309Snwhitehorn	    break;
562217309Snwhitehorn	case DLGK_DELETE_RIGHT:
563217309Snwhitehorn	    if (limit) {
564217309Snwhitehorn		if (--limit == 0) {
565217309Snwhitehorn		    string[*chr_offset = 0] = '\0';
566217309Snwhitehorn		} else {
567217309Snwhitehorn		    int gap = ((offset <= limit)
568217309Snwhitehorn			       ? (indx[offset + 1] - indx[offset])
569217309Snwhitehorn			       : 0);
570217309Snwhitehorn		    if (gap > 0) {
571217309Snwhitehorn			for (i = indx[offset];
572217309Snwhitehorn			     (string[i] = string[i + gap]) != '\0';
573217309Snwhitehorn			     i++) {
574217309Snwhitehorn			    ;
575217309Snwhitehorn			}
576217309Snwhitehorn		    } else if (offset > 0) {
577217309Snwhitehorn			string[indx[offset - 1]] = '\0';
578217309Snwhitehorn		    }
579217309Snwhitehorn		    if (*chr_offset > indx[limit])
580217309Snwhitehorn			*chr_offset = indx[limit];
581217309Snwhitehorn		}
582217309Snwhitehorn	    }
583217309Snwhitehorn	    break;
584217309Snwhitehorn	case DLGK_DELETE_ALL:
585217309Snwhitehorn	    string[*chr_offset = 0] = '\0';
586217309Snwhitehorn	    break;
587217309Snwhitehorn	case DLGK_ENTER:
588217309Snwhitehorn	    edit = 0;
589217309Snwhitehorn	    break;
590217309Snwhitehorn#ifdef KEY_RESIZE
591217309Snwhitehorn	case KEY_RESIZE:
592217309Snwhitehorn	    edit = 0;
593217309Snwhitehorn	    break;
594217309Snwhitehorn#endif
595217309Snwhitehorn	case DLGK_GRID_UP:
596217309Snwhitehorn	case DLGK_GRID_DOWN:
597217309Snwhitehorn	case DLGK_FIELD_NEXT:
598217309Snwhitehorn	case DLGK_FIELD_PREV:
599217309Snwhitehorn	    edit = 0;
600217309Snwhitehorn	    break;
601217309Snwhitehorn	case ERR:
602217309Snwhitehorn	    edit = 0;
603217309Snwhitehorn	    break;
604217309Snwhitehorn	default:
605217309Snwhitehorn	    beep();
606217309Snwhitehorn	    break;
607217309Snwhitehorn	}
608217309Snwhitehorn    } else {
609217309Snwhitehorn	if (key == ESC || key == ERR) {
610217309Snwhitehorn	    edit = 0;
611217309Snwhitehorn	} else {
612217309Snwhitehorn	    if (len < max_len) {
613217309Snwhitehorn		for (i = ++len; i > *chr_offset; i--)
614217309Snwhitehorn		    string[i] = string[i - 1];
615217309Snwhitehorn		string[*chr_offset] = (char) key;
616217309Snwhitehorn		*chr_offset += 1;
617217309Snwhitehorn	    } else {
618217309Snwhitehorn		(void) beep();
619217309Snwhitehorn	    }
620217309Snwhitehorn	}
621217309Snwhitehorn    }
622217309Snwhitehorn    return edit;
623217309Snwhitehorn}
624217309Snwhitehorn
625217309Snwhitehornstatic void
626217309Snwhitehorncompute_edit_offset(const char *string,
627217309Snwhitehorn		    int chr_offset,
628217309Snwhitehorn		    int x_last,
629217309Snwhitehorn		    int *p_dpy_column,
630217309Snwhitehorn		    int *p_scroll_amt)
631217309Snwhitehorn{
632217309Snwhitehorn    const int *cols = dlg_index_columns(string);
633217309Snwhitehorn    const int *indx = dlg_index_wchars(string);
634217309Snwhitehorn    int limit = dlg_count_wchars(string);
635217309Snwhitehorn    int offset = dlg_find_index(indx, limit, chr_offset);
636217309Snwhitehorn    int offset2;
637217309Snwhitehorn    int dpy_column;
638217309Snwhitehorn    int n;
639217309Snwhitehorn
640217309Snwhitehorn    for (n = offset2 = 0; n <= offset; ++n) {
641217309Snwhitehorn	if ((cols[offset] - cols[n]) < x_last
642217309Snwhitehorn	    && (offset == limit || (cols[offset + 1] - cols[n]) < x_last)) {
643217309Snwhitehorn	    offset2 = n;
644217309Snwhitehorn	    break;
645217309Snwhitehorn	}
646217309Snwhitehorn    }
647217309Snwhitehorn
648217309Snwhitehorn    dpy_column = cols[offset] - cols[offset2];
649217309Snwhitehorn
650217309Snwhitehorn    if (p_dpy_column != 0)
651217309Snwhitehorn	*p_dpy_column = dpy_column;
652217309Snwhitehorn    if (p_scroll_amt != 0)
653217309Snwhitehorn	*p_scroll_amt = offset2;
654217309Snwhitehorn}
655217309Snwhitehorn
656217309Snwhitehorn/*
657217309Snwhitehorn * Given the character-offset in the string, returns the display-offset where
658217309Snwhitehorn * we will position the cursor.
659217309Snwhitehorn */
660217309Snwhitehornint
661217309Snwhitehorndlg_edit_offset(char *string, int chr_offset, int x_last)
662217309Snwhitehorn{
663217309Snwhitehorn    int result;
664217309Snwhitehorn
665217309Snwhitehorn    compute_edit_offset(string, chr_offset, x_last, &result, 0);
666217309Snwhitehorn
667217309Snwhitehorn    return result;
668217309Snwhitehorn}
669217309Snwhitehorn
670217309Snwhitehorn/*
671217309Snwhitehorn * Displays the string, shifted as necessary, to fit within the box and show
672217309Snwhitehorn * the current character-offset.
673217309Snwhitehorn */
674217309Snwhitehornvoid
675217309Snwhitehorndlg_show_string(WINDOW *win,
676217309Snwhitehorn		const char *string,	/* string to display (may be multibyte) */
677217309Snwhitehorn		int chr_offset,	/* character (not bytes) offset */
678217309Snwhitehorn		chtype attr,	/* window-attributes */
679217309Snwhitehorn		int y_base,	/* beginning row on screen */
680217309Snwhitehorn		int x_base,	/* beginning column on screen */
681217309Snwhitehorn		int x_last,	/* number of columns on screen */
682217309Snwhitehorn		bool hidden,	/* if true, do not echo */
683217309Snwhitehorn		bool force)	/* if true, force repaint */
684217309Snwhitehorn{
685217309Snwhitehorn    x_last = MIN(x_last + x_base, getmaxx(win)) - x_base;
686217309Snwhitehorn
687217309Snwhitehorn    if (hidden && !dialog_vars.insecure) {
688217309Snwhitehorn	if (force) {
689217309Snwhitehorn	    (void) wmove(win, y_base, x_base);
690217309Snwhitehorn	    wrefresh(win);
691217309Snwhitehorn	}
692217309Snwhitehorn    } else {
693217309Snwhitehorn	const int *cols = dlg_index_columns(string);
694217309Snwhitehorn	const int *indx = dlg_index_wchars(string);
695217309Snwhitehorn	int limit = dlg_count_wchars(string);
696217309Snwhitehorn
697217309Snwhitehorn	int i, j, k;
698217309Snwhitehorn	int input_x;
699217309Snwhitehorn	int scrollamt;
700217309Snwhitehorn
701217309Snwhitehorn	compute_edit_offset(string, chr_offset, x_last, &input_x, &scrollamt);
702217309Snwhitehorn
703217309Snwhitehorn	wattrset(win, attr);
704217309Snwhitehorn	(void) wmove(win, y_base, x_base);
705217309Snwhitehorn	for (i = scrollamt, k = 0; i < limit && k < x_last; ++i) {
706217309Snwhitehorn	    int check = cols[i + 1] - cols[scrollamt];
707217309Snwhitehorn	    if (check <= x_last) {
708217309Snwhitehorn		for (j = indx[i]; j < indx[i + 1]; ++j) {
709217309Snwhitehorn		    chtype ch = UCH(string[j]);
710217309Snwhitehorn		    if (hidden && dialog_vars.insecure) {
711217309Snwhitehorn			waddch(win, '*');
712217309Snwhitehorn		    } else if (ch == TAB) {
713217309Snwhitehorn			int count = cols[i + 1] - cols[i];
714217309Snwhitehorn			while (--count >= 0)
715217309Snwhitehorn			    waddch(win, ' ');
716217309Snwhitehorn		    } else {
717217309Snwhitehorn			waddch(win, ch);
718217309Snwhitehorn		    }
719217309Snwhitehorn		}
720217309Snwhitehorn		k = check;
721217309Snwhitehorn	    } else {
722217309Snwhitehorn		break;
723217309Snwhitehorn	    }
724217309Snwhitehorn	}
725217309Snwhitehorn	while (k++ < x_last)
726217309Snwhitehorn	    waddch(win, ' ');
727217309Snwhitehorn	(void) wmove(win, y_base, x_base + input_x);
728217309Snwhitehorn	wrefresh(win);
729217309Snwhitehorn    }
730217309Snwhitehorn}
731217309Snwhitehorn
732217309Snwhitehorn#ifdef NO_LEAKS
733217309Snwhitehornvoid
734217309Snwhitehorn_dlg_inputstr_leaks(void)
735217309Snwhitehorn{
736217309Snwhitehorn#if USE_CACHING
737217309Snwhitehorn    while (cache_list != 0) {
738217309Snwhitehorn	CACHE *next = cache_list->next;
739217309Snwhitehorn#ifdef HAVE_TSEARCH
740217309Snwhitehorn	tdelete(cache_list, &sorted_cache, compare_cache);
741217309Snwhitehorn#endif
742217309Snwhitehorn	if (cache_list->string != 0)
743217309Snwhitehorn	    free(cache_list->string);
744217309Snwhitehorn	if (cache_list->list != 0)
745217309Snwhitehorn	    free(cache_list->list);
746217309Snwhitehorn	free(cache_list);
747217309Snwhitehorn	cache_list = next;
748217309Snwhitehorn    }
749217309Snwhitehorn#endif /* USE_CACHING */
750217309Snwhitehorn}
751217309Snwhitehorn#endif /* NO_LEAKS */
752