1217309Snwhitehorn/* 2255852Sdteske * $Id: inputstr.c,v 1.83 2013/09/23 23:19:26 tom Exp $ 3217309Snwhitehorn * 4220749Snwhitehorn * inputstr.c -- functions for input/display of a string 5217309Snwhitehorn * 6255852Sdteske * Copyright 2000-2012,2013 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 54255852Sdteske int cache_num; /* tells what type of data is in list[] */ 55217309Snwhitehorn const char *string_at; /* unique: associate caches by char* */ 56217309Snwhitehorn#endif 57220749Snwhitehorn size_t s_len; /* strlen(string) - we add 1 for EOS */ 58220749Snwhitehorn size_t 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 68255852Sdtesketypedef enum { 69255852Sdteske cInxCols 70255852Sdteske ,cCntWideBytes 71255852Sdteske ,cCntWideChars 72255852Sdteske ,cInxWideChars 73255852Sdteske ,cMAX 74255852Sdteske} CACHE_USED; 75255852Sdteske 76217309Snwhitehorn#ifdef HAVE_TSEARCH 77217309Snwhitehornstatic void *sorted_cache; 78217309Snwhitehorn#endif 79217309Snwhitehorn 80217309Snwhitehorn#ifdef USE_WIDE_CURSES 81217309Snwhitehornstatic int 82217309Snwhitehornhave_locale(void) 83217309Snwhitehorn{ 84217309Snwhitehorn static int result = -1; 85217309Snwhitehorn if (result < 0) { 86217309Snwhitehorn char *test = setlocale(LC_ALL, 0); 87217309Snwhitehorn if (test == 0 || *test == 0) { 88217309Snwhitehorn result = FALSE; 89217309Snwhitehorn } else if (strcmp(test, "C") && strcmp(test, "POSIX")) { 90217309Snwhitehorn result = TRUE; 91217309Snwhitehorn } else { 92217309Snwhitehorn result = FALSE; 93217309Snwhitehorn } 94217309Snwhitehorn } 95217309Snwhitehorn return result; 96217309Snwhitehorn} 97217309Snwhitehorn#endif 98217309Snwhitehorn 99217309Snwhitehorn#ifdef HAVE_TSEARCH 100255852Sdteske 101255852Sdteske#if 0 102255852Sdteskestatic void 103255852Sdteskeshow_tsearch(const void *nodep, const VISIT which, const int depth) 104255852Sdteske{ 105255852Sdteske const CACHE *p = *(CACHE * const *) nodep; 106255852Sdteske (void) depth; 107255852Sdteske if (which == postorder || which == leaf) { 108255852Sdteske dlg_trace_msg("\tcache %p %p:%s\n", p, p->string, p->string); 109255852Sdteske } 110255852Sdteske} 111255852Sdteske 112255852Sdteskestatic void 113255852Sdtesketrace_cache(const char *fn, int ln) 114255852Sdteske{ 115255852Sdteske dlg_trace_msg("trace_cache %s@%d\n", fn, ln); 116255852Sdteske twalk(sorted_cache, show_tsearch); 117255852Sdteske} 118255852Sdteske 119255852Sdteske#else 120255852Sdteske#define trace_cache(fn, ln) /* nothing */ 121255852Sdteske#endif 122255852Sdteske 123217309Snwhitehornstatic int 124217309Snwhitehorncompare_cache(const void *a, const void *b) 125217309Snwhitehorn{ 126217309Snwhitehorn const CACHE *p = (const CACHE *) a; 127217309Snwhitehorn const CACHE *q = (const CACHE *) b; 128255852Sdteske int result = (p->cache_num - q->cache_num); 129217309Snwhitehorn if (result == 0) 130220749Snwhitehorn result = (int) (p->string_at - q->string_at); 131217309Snwhitehorn return result; 132217309Snwhitehorn} 133217309Snwhitehorn#endif 134217309Snwhitehorn 135217309Snwhitehornstatic CACHE * 136255852Sdteskefind_cache(int cache_num, const char *string) 137217309Snwhitehorn{ 138217309Snwhitehorn CACHE *p; 139217309Snwhitehorn 140217309Snwhitehorn#ifdef HAVE_TSEARCH 141217309Snwhitehorn void *pp; 142217309Snwhitehorn CACHE find; 143217309Snwhitehorn 144217309Snwhitehorn memset(&find, 0, sizeof(find)); 145255852Sdteske find.cache_num = cache_num; 146217309Snwhitehorn find.string_at = string; 147217309Snwhitehorn 148217309Snwhitehorn if ((pp = tfind(&find, &sorted_cache, compare_cache)) != 0) { 149217309Snwhitehorn p = *(CACHE **) pp; 150217309Snwhitehorn } else { 151217309Snwhitehorn p = 0; 152217309Snwhitehorn } 153217309Snwhitehorn#else 154217309Snwhitehorn for (p = cache_list; p != 0; p = p->next) { 155255852Sdteske if (p->string_at == string) { 156217309Snwhitehorn break; 157217309Snwhitehorn } 158217309Snwhitehorn } 159217309Snwhitehorn#endif 160217309Snwhitehorn return p; 161217309Snwhitehorn} 162217309Snwhitehorn 163255852Sdteskestatic CACHE * 164255852Sdteskemake_cache(int cache_num, const char *string) 165217309Snwhitehorn{ 166217309Snwhitehorn CACHE *p; 167217309Snwhitehorn 168217309Snwhitehorn p = dlg_calloc(CACHE, 1); 169217309Snwhitehorn assert_ptr(p, "load_cache"); 170217309Snwhitehorn p->next = cache_list; 171217309Snwhitehorn cache_list = p; 172217309Snwhitehorn 173255852Sdteske p->cache_num = cache_num; 174217309Snwhitehorn p->string_at = string; 175217309Snwhitehorn 176217309Snwhitehorn#ifdef HAVE_TSEARCH 177217309Snwhitehorn (void) tsearch(p, &sorted_cache, compare_cache); 178217309Snwhitehorn#endif 179255852Sdteske return p; 180217309Snwhitehorn} 181217309Snwhitehorn 182255852Sdteskestatic CACHE * 183255852Sdteskeload_cache(int cache_num, const char *string) 184217309Snwhitehorn{ 185217309Snwhitehorn CACHE *p; 186217309Snwhitehorn 187255852Sdteske if ((p = find_cache(cache_num, string)) == 0) { 188255852Sdteske p = make_cache(cache_num, string); 189217309Snwhitehorn } 190255852Sdteske return p; 191217309Snwhitehorn} 192217309Snwhitehorn#else 193255852Sdteskestatic CACHE my_cache; 194217309Snwhitehorn#define SAME_CACHE(c,s,l) (c->string != 0) 195255852Sdteske#define load_cache(cache, string) &my_cache 196255852Sdteske#endif /* USE_CACHING */ 197217309Snwhitehorn 198217309Snwhitehorn/* 199217309Snwhitehorn * If the given string has not changed, we do not need to update the index. 200217309Snwhitehorn * If we need to update the index, allocate enough memory for it. 201217309Snwhitehorn */ 202217309Snwhitehornstatic bool 203217309Snwhitehornsame_cache2(CACHE * cache, const char *string, unsigned i_len) 204217309Snwhitehorn{ 205217309Snwhitehorn unsigned need; 206220749Snwhitehorn size_t s_len = strlen(string); 207255852Sdteske bool result = TRUE; 208217309Snwhitehorn 209255852Sdteske if (cache->s_len == 0 210255852Sdteske || cache->s_len < s_len 211255852Sdteske || cache->list == 0 212255852Sdteske || !SAME_CACHE(cache, string, (size_t) s_len)) { 213217309Snwhitehorn 214255852Sdteske need = (i_len + 1); 215255852Sdteske if (cache->list == 0) { 216255852Sdteske cache->list = dlg_malloc(int, need); 217255852Sdteske } else if (cache->i_len < i_len) { 218255852Sdteske cache->list = dlg_realloc(int, need, cache->list); 219255852Sdteske } 220255852Sdteske assert_ptr(cache->list, "load_cache"); 221255852Sdteske cache->i_len = i_len; 222217309Snwhitehorn 223255852Sdteske if (cache->s_len >= s_len && cache->string != 0) { 224255852Sdteske strcpy(cache->string, string); 225255852Sdteske } else { 226255852Sdteske if (cache->string != 0) 227255852Sdteske free(cache->string); 228255852Sdteske cache->string = dlg_strclone(string); 229255852Sdteske } 230255852Sdteske cache->s_len = s_len; 231255852Sdteske 232255852Sdteske result = FALSE; 233217309Snwhitehorn } 234255852Sdteske return result; 235217309Snwhitehorn} 236217309Snwhitehorn 237217309Snwhitehorn#ifdef USE_WIDE_CURSES 238217309Snwhitehorn/* 239217309Snwhitehorn * Like same_cache2(), but we are only concerned about caching a copy of the 240217309Snwhitehorn * string and its associated length. 241217309Snwhitehorn */ 242217309Snwhitehornstatic bool 243220749Snwhitehornsame_cache1(CACHE * cache, const char *string, size_t i_len) 244217309Snwhitehorn{ 245220749Snwhitehorn size_t s_len = strlen(string); 246255852Sdteske bool result = TRUE; 247217309Snwhitehorn 248255852Sdteske if (cache->s_len != s_len 249255852Sdteske || !SAME_CACHE(cache, string, (size_t) s_len)) { 250217309Snwhitehorn 251255852Sdteske if (cache->s_len >= s_len && cache->string != 0) { 252255852Sdteske strcpy(cache->string, string); 253255852Sdteske } else { 254255852Sdteske if (cache->string != 0) 255255852Sdteske free(cache->string); 256255852Sdteske cache->string = dlg_strclone(string); 257255852Sdteske } 258255852Sdteske cache->s_len = s_len; 259255852Sdteske cache->i_len = i_len; 260255852Sdteske 261255852Sdteske result = FALSE; 262217309Snwhitehorn } 263255852Sdteske return result; 264217309Snwhitehorn} 265217309Snwhitehorn#endif /* USE_CACHING */ 266217309Snwhitehorn 267217309Snwhitehorn/* 268217309Snwhitehorn * Counts the number of bytes that make up complete wide-characters, up to byte 269217309Snwhitehorn * 'len'. If there is no locale set, simply return the original length. 270217309Snwhitehorn */ 271217309Snwhitehorn#ifdef USE_WIDE_CURSES 272217309Snwhitehornstatic int 273217309Snwhitehorndlg_count_wcbytes(const char *string, size_t len) 274217309Snwhitehorn{ 275217309Snwhitehorn int result; 276217309Snwhitehorn 277217309Snwhitehorn if (have_locale()) { 278255852Sdteske CACHE *cache = load_cache(cCntWideBytes, string); 279255852Sdteske if (!same_cache1(cache, string, len)) { 280217309Snwhitehorn while (len != 0) { 281217309Snwhitehorn size_t code = 0; 282255852Sdteske const char *src = cache->string; 283217309Snwhitehorn mbstate_t state; 284255852Sdteske char save = cache->string[len]; 285217309Snwhitehorn 286255852Sdteske cache->string[len] = '\0'; 287217309Snwhitehorn memset(&state, 0, sizeof(state)); 288217309Snwhitehorn code = mbsrtowcs((wchar_t *) 0, &src, len, &state); 289255852Sdteske cache->string[len] = save; 290217309Snwhitehorn if ((int) code >= 0) { 291217309Snwhitehorn break; 292217309Snwhitehorn } 293217309Snwhitehorn --len; 294217309Snwhitehorn } 295255852Sdteske cache->i_len = len; 296217309Snwhitehorn } 297255852Sdteske result = (int) cache->i_len; 298217309Snwhitehorn } else { 299217309Snwhitehorn result = (int) len; 300217309Snwhitehorn } 301217309Snwhitehorn return result; 302217309Snwhitehorn} 303217309Snwhitehorn#endif /* USE_WIDE_CURSES */ 304217309Snwhitehorn 305217309Snwhitehorn/* 306217309Snwhitehorn * Counts the number of wide-characters in the string. 307217309Snwhitehorn */ 308217309Snwhitehornint 309217309Snwhitehorndlg_count_wchars(const char *string) 310217309Snwhitehorn{ 311217309Snwhitehorn int result; 312255852Sdteske#ifdef USE_WIDE_CURSES 313217309Snwhitehorn 314217309Snwhitehorn if (have_locale()) { 315217309Snwhitehorn size_t len = strlen(string); 316255852Sdteske CACHE *cache = load_cache(cCntWideChars, string); 317217309Snwhitehorn 318255852Sdteske if (!same_cache1(cache, string, len)) { 319255852Sdteske const char *src = cache->string; 320217309Snwhitehorn mbstate_t state; 321255852Sdteske int part = dlg_count_wcbytes(cache->string, len); 322255852Sdteske char save = cache->string[part]; 323217309Snwhitehorn size_t code; 324217309Snwhitehorn wchar_t *temp = dlg_calloc(wchar_t, len + 1); 325217309Snwhitehorn 326251843Sbapt if (temp != 0) { 327255852Sdteske cache->string[part] = '\0'; 328251843Sbapt memset(&state, 0, sizeof(state)); 329251843Sbapt code = mbsrtowcs(temp, &src, (size_t) part, &state); 330255852Sdteske cache->i_len = ((int) code >= 0) ? wcslen(temp) : 0; 331255852Sdteske cache->string[part] = save; 332251843Sbapt free(temp); 333251843Sbapt } else { 334255852Sdteske cache->i_len = 0; 335251843Sbapt } 336217309Snwhitehorn } 337255852Sdteske result = (int) cache->i_len; 338217309Snwhitehorn } else 339217309Snwhitehorn#endif /* USE_WIDE_CURSES */ 340217309Snwhitehorn { 341217309Snwhitehorn result = (int) strlen(string); 342217309Snwhitehorn } 343217309Snwhitehorn return result; 344217309Snwhitehorn} 345217309Snwhitehorn 346217309Snwhitehorn/* 347217309Snwhitehorn * Build an index of the wide-characters in the string, so we can easily tell 348217309Snwhitehorn * which byte-offset begins a given wide-character. 349217309Snwhitehorn */ 350217309Snwhitehornconst int * 351217309Snwhitehorndlg_index_wchars(const char *string) 352217309Snwhitehorn{ 353217309Snwhitehorn unsigned len = (unsigned) dlg_count_wchars(string); 354217309Snwhitehorn unsigned inx; 355255852Sdteske CACHE *cache = load_cache(cInxWideChars, string); 356217309Snwhitehorn 357255852Sdteske if (!same_cache2(cache, string, len)) { 358217309Snwhitehorn const char *current = string; 359217309Snwhitehorn 360255852Sdteske cache->list[0] = 0; 361217309Snwhitehorn for (inx = 1; inx <= len; ++inx) { 362217309Snwhitehorn#ifdef USE_WIDE_CURSES 363217309Snwhitehorn if (have_locale()) { 364217309Snwhitehorn mbstate_t state; 365217309Snwhitehorn int width; 366217309Snwhitehorn memset(&state, 0, sizeof(state)); 367217309Snwhitehorn width = (int) mbrlen(current, strlen(current), &state); 368217309Snwhitehorn if (width <= 0) 369217309Snwhitehorn width = 1; /* FIXME: what if we have a control-char? */ 370217309Snwhitehorn current += width; 371255852Sdteske cache->list[inx] = cache->list[inx - 1] + width; 372217309Snwhitehorn } else 373217309Snwhitehorn#endif /* USE_WIDE_CURSES */ 374217309Snwhitehorn { 375217309Snwhitehorn (void) current; 376255852Sdteske cache->list[inx] = (int) inx; 377217309Snwhitehorn } 378217309Snwhitehorn } 379217309Snwhitehorn } 380255852Sdteske return cache->list; 381217309Snwhitehorn} 382217309Snwhitehorn 383217309Snwhitehorn/* 384217309Snwhitehorn * Given the character-offset to find in the list, return the corresponding 385217309Snwhitehorn * array index. 386217309Snwhitehorn */ 387217309Snwhitehornint 388217309Snwhitehorndlg_find_index(const int *list, int limit, int to_find) 389217309Snwhitehorn{ 390217309Snwhitehorn int result; 391217309Snwhitehorn for (result = 0; result <= limit; ++result) { 392217309Snwhitehorn if (to_find == list[result] 393217309Snwhitehorn || result == limit 394251843Sbapt || ((result < limit) && (to_find < list[result + 1]))) { 395217309Snwhitehorn break; 396251843Sbapt } 397217309Snwhitehorn } 398217309Snwhitehorn return result; 399217309Snwhitehorn} 400217309Snwhitehorn 401217309Snwhitehorn/* 402217309Snwhitehorn * Build a list of the display-columns for the given string's characters. 403217309Snwhitehorn */ 404217309Snwhitehornconst int * 405217309Snwhitehorndlg_index_columns(const char *string) 406217309Snwhitehorn{ 407217309Snwhitehorn unsigned len = (unsigned) dlg_count_wchars(string); 408217309Snwhitehorn unsigned inx; 409255852Sdteske CACHE *cache = load_cache(cInxCols, string); 410217309Snwhitehorn 411255852Sdteske if (!same_cache2(cache, string, len)) { 412255852Sdteske cache->list[0] = 0; 413217309Snwhitehorn#ifdef USE_WIDE_CURSES 414217309Snwhitehorn if (have_locale()) { 415217309Snwhitehorn size_t num_bytes = strlen(string); 416217309Snwhitehorn const int *inx_wchars = dlg_index_wchars(string); 417217309Snwhitehorn mbstate_t state; 418217309Snwhitehorn 419217309Snwhitehorn for (inx = 0; inx < len; ++inx) { 420217309Snwhitehorn wchar_t temp[2]; 421217309Snwhitehorn size_t check; 422217309Snwhitehorn int result; 423217309Snwhitehorn 424217309Snwhitehorn if (string[inx_wchars[inx]] == TAB) { 425255852Sdteske result = ((cache->list[inx] | 7) + 1) - cache->list[inx]; 426217309Snwhitehorn } else { 427217309Snwhitehorn memset(&state, 0, sizeof(state)); 428217309Snwhitehorn memset(temp, 0, sizeof(temp)); 429217309Snwhitehorn check = mbrtowc(temp, 430217309Snwhitehorn string + inx_wchars[inx], 431217309Snwhitehorn num_bytes - (size_t) inx_wchars[inx], 432217309Snwhitehorn &state); 433217309Snwhitehorn if ((int) check <= 0) { 434217309Snwhitehorn result = 1; 435217309Snwhitehorn } else { 436217309Snwhitehorn result = wcwidth(temp[0]); 437217309Snwhitehorn } 438217309Snwhitehorn if (result < 0) { 439220749Snwhitehorn const wchar_t *printable; 440220749Snwhitehorn cchar_t temp2, *temp2p = &temp2; 441220749Snwhitehorn setcchar(temp2p, temp, 0, 0, 0); 442220749Snwhitehorn printable = wunctrl(temp2p); 443217309Snwhitehorn result = printable ? (int) wcslen(printable) : 1; 444217309Snwhitehorn } 445217309Snwhitehorn } 446255852Sdteske cache->list[inx + 1] = result; 447217309Snwhitehorn if (inx != 0) 448255852Sdteske cache->list[inx + 1] += cache->list[inx]; 449217309Snwhitehorn } 450217309Snwhitehorn } else 451217309Snwhitehorn#endif /* USE_WIDE_CURSES */ 452217309Snwhitehorn { 453217309Snwhitehorn for (inx = 0; inx < len; ++inx) { 454217309Snwhitehorn chtype ch = UCH(string[inx]); 455217309Snwhitehorn 456217309Snwhitehorn if (ch == TAB) 457255852Sdteske cache->list[inx + 1] = 458255852Sdteske ((cache->list[inx] | 7) + 1) - cache->list[inx]; 459217309Snwhitehorn else if (isprint(ch)) 460255852Sdteske cache->list[inx + 1] = 1; 461217309Snwhitehorn else { 462217309Snwhitehorn const char *printable; 463217309Snwhitehorn printable = unctrl(ch); 464255852Sdteske cache->list[inx + 1] = (printable 465255852Sdteske ? (int) strlen(printable) 466255852Sdteske : 1); 467217309Snwhitehorn } 468217309Snwhitehorn if (inx != 0) 469255852Sdteske cache->list[inx + 1] += cache->list[inx]; 470217309Snwhitehorn } 471217309Snwhitehorn } 472217309Snwhitehorn } 473255852Sdteske return cache->list; 474217309Snwhitehorn} 475217309Snwhitehorn 476217309Snwhitehorn/* 477217309Snwhitehorn * Returns the number of columns used for a string. That happens to be the 478217309Snwhitehorn * end-value of the cols[] array. 479217309Snwhitehorn */ 480217309Snwhitehornint 481217309Snwhitehorndlg_count_columns(const char *string) 482217309Snwhitehorn{ 483217309Snwhitehorn int result = 0; 484217309Snwhitehorn int limit = dlg_count_wchars(string); 485217309Snwhitehorn if (limit > 0) { 486217309Snwhitehorn const int *cols = dlg_index_columns(string); 487217309Snwhitehorn result = cols[limit]; 488217309Snwhitehorn } else { 489217309Snwhitehorn result = (int) strlen(string); 490217309Snwhitehorn } 491255852Sdteske dlg_finish_string(string); 492217309Snwhitehorn return result; 493217309Snwhitehorn} 494217309Snwhitehorn 495217309Snwhitehorn/* 496217309Snwhitehorn * Given a column limit, count the number of wide characters that can fit 497217309Snwhitehorn * into that limit. The offset is used to skip over a leading character 498217309Snwhitehorn * that was already written. 499217309Snwhitehorn */ 500217309Snwhitehornint 501217309Snwhitehorndlg_limit_columns(const char *string, int limit, int offset) 502217309Snwhitehorn{ 503217309Snwhitehorn const int *cols = dlg_index_columns(string); 504217309Snwhitehorn int result = dlg_count_wchars(string); 505217309Snwhitehorn 506217309Snwhitehorn while (result > 0 && (cols[result] - cols[offset]) > limit) 507217309Snwhitehorn --result; 508217309Snwhitehorn return result; 509217309Snwhitehorn} 510217309Snwhitehorn 511217309Snwhitehorn/* 512217309Snwhitehorn * Updates the string and character-offset, given various editing characters 513217309Snwhitehorn * or literal characters which are inserted at the character-offset. 514217309Snwhitehorn */ 515217309Snwhitehornbool 516217309Snwhitehorndlg_edit_string(char *string, int *chr_offset, int key, int fkey, bool force) 517217309Snwhitehorn{ 518217309Snwhitehorn int i; 519217309Snwhitehorn int len = (int) strlen(string); 520217309Snwhitehorn int limit = dlg_count_wchars(string); 521217309Snwhitehorn const int *indx = dlg_index_wchars(string); 522217309Snwhitehorn int offset = dlg_find_index(indx, limit, *chr_offset); 523217309Snwhitehorn int max_len = dlg_max_input(MAX_LEN); 524217309Snwhitehorn bool edit = TRUE; 525217309Snwhitehorn 526217309Snwhitehorn /* transform editing characters into equivalent function-keys */ 527217309Snwhitehorn if (!fkey) { 528217309Snwhitehorn fkey = TRUE; /* assume we transform */ 529217309Snwhitehorn switch (key) { 530217309Snwhitehorn case 0: 531217309Snwhitehorn break; 532217309Snwhitehorn case ESC: 533217309Snwhitehorn case TAB: 534217309Snwhitehorn fkey = FALSE; /* this is used for navigation */ 535217309Snwhitehorn break; 536217309Snwhitehorn default: 537217309Snwhitehorn fkey = FALSE; /* ...no, we did not transform */ 538217309Snwhitehorn break; 539217309Snwhitehorn } 540217309Snwhitehorn } 541217309Snwhitehorn 542217309Snwhitehorn if (fkey) { 543217309Snwhitehorn switch (key) { 544217309Snwhitehorn case 0: /* special case for loop entry */ 545217309Snwhitehorn edit = force; 546217309Snwhitehorn break; 547217309Snwhitehorn case DLGK_GRID_LEFT: 548251843Sbapt if (*chr_offset && offset > 0) 549217309Snwhitehorn *chr_offset = indx[offset - 1]; 550217309Snwhitehorn break; 551217309Snwhitehorn case DLGK_GRID_RIGHT: 552217309Snwhitehorn if (offset < limit) 553217309Snwhitehorn *chr_offset = indx[offset + 1]; 554217309Snwhitehorn break; 555217309Snwhitehorn case DLGK_BEGIN: 556217309Snwhitehorn if (*chr_offset) 557217309Snwhitehorn *chr_offset = 0; 558217309Snwhitehorn break; 559217309Snwhitehorn case DLGK_FINAL: 560217309Snwhitehorn if (offset < limit) 561217309Snwhitehorn *chr_offset = indx[limit]; 562217309Snwhitehorn break; 563217309Snwhitehorn case DLGK_DELETE_LEFT: 564217309Snwhitehorn if (offset) { 565217309Snwhitehorn int gap = indx[offset] - indx[offset - 1]; 566217309Snwhitehorn *chr_offset = indx[offset - 1]; 567217309Snwhitehorn if (gap > 0) { 568217309Snwhitehorn for (i = *chr_offset; 569217309Snwhitehorn (string[i] = string[i + gap]) != '\0'; 570217309Snwhitehorn i++) { 571217309Snwhitehorn ; 572217309Snwhitehorn } 573217309Snwhitehorn } 574217309Snwhitehorn } 575217309Snwhitehorn break; 576217309Snwhitehorn case DLGK_DELETE_RIGHT: 577217309Snwhitehorn if (limit) { 578217309Snwhitehorn if (--limit == 0) { 579217309Snwhitehorn string[*chr_offset = 0] = '\0'; 580217309Snwhitehorn } else { 581217309Snwhitehorn int gap = ((offset <= limit) 582217309Snwhitehorn ? (indx[offset + 1] - indx[offset]) 583217309Snwhitehorn : 0); 584217309Snwhitehorn if (gap > 0) { 585217309Snwhitehorn for (i = indx[offset]; 586217309Snwhitehorn (string[i] = string[i + gap]) != '\0'; 587217309Snwhitehorn i++) { 588217309Snwhitehorn ; 589217309Snwhitehorn } 590217309Snwhitehorn } else if (offset > 0) { 591217309Snwhitehorn string[indx[offset - 1]] = '\0'; 592217309Snwhitehorn } 593217309Snwhitehorn if (*chr_offset > indx[limit]) 594217309Snwhitehorn *chr_offset = indx[limit]; 595217309Snwhitehorn } 596217309Snwhitehorn } 597217309Snwhitehorn break; 598217309Snwhitehorn case DLGK_DELETE_ALL: 599217309Snwhitehorn string[*chr_offset = 0] = '\0'; 600217309Snwhitehorn break; 601217309Snwhitehorn case DLGK_ENTER: 602217309Snwhitehorn edit = 0; 603217309Snwhitehorn break; 604217309Snwhitehorn#ifdef KEY_RESIZE 605217309Snwhitehorn case KEY_RESIZE: 606217309Snwhitehorn edit = 0; 607217309Snwhitehorn break; 608217309Snwhitehorn#endif 609217309Snwhitehorn case DLGK_GRID_UP: 610217309Snwhitehorn case DLGK_GRID_DOWN: 611217309Snwhitehorn case DLGK_FIELD_NEXT: 612217309Snwhitehorn case DLGK_FIELD_PREV: 613217309Snwhitehorn edit = 0; 614217309Snwhitehorn break; 615217309Snwhitehorn case ERR: 616217309Snwhitehorn edit = 0; 617217309Snwhitehorn break; 618217309Snwhitehorn default: 619217309Snwhitehorn beep(); 620217309Snwhitehorn break; 621217309Snwhitehorn } 622217309Snwhitehorn } else { 623217309Snwhitehorn if (key == ESC || key == ERR) { 624217309Snwhitehorn edit = 0; 625217309Snwhitehorn } else { 626217309Snwhitehorn if (len < max_len) { 627217309Snwhitehorn for (i = ++len; i > *chr_offset; i--) 628217309Snwhitehorn string[i] = string[i - 1]; 629217309Snwhitehorn string[*chr_offset] = (char) key; 630217309Snwhitehorn *chr_offset += 1; 631217309Snwhitehorn } else { 632217309Snwhitehorn (void) beep(); 633217309Snwhitehorn } 634217309Snwhitehorn } 635217309Snwhitehorn } 636217309Snwhitehorn return edit; 637217309Snwhitehorn} 638217309Snwhitehorn 639217309Snwhitehornstatic void 640217309Snwhitehorncompute_edit_offset(const char *string, 641217309Snwhitehorn int chr_offset, 642217309Snwhitehorn int x_last, 643217309Snwhitehorn int *p_dpy_column, 644217309Snwhitehorn int *p_scroll_amt) 645217309Snwhitehorn{ 646217309Snwhitehorn const int *cols = dlg_index_columns(string); 647217309Snwhitehorn const int *indx = dlg_index_wchars(string); 648217309Snwhitehorn int limit = dlg_count_wchars(string); 649217309Snwhitehorn int offset = dlg_find_index(indx, limit, chr_offset); 650217309Snwhitehorn int offset2; 651217309Snwhitehorn int dpy_column; 652217309Snwhitehorn int n; 653217309Snwhitehorn 654217309Snwhitehorn for (n = offset2 = 0; n <= offset; ++n) { 655217309Snwhitehorn if ((cols[offset] - cols[n]) < x_last 656217309Snwhitehorn && (offset == limit || (cols[offset + 1] - cols[n]) < x_last)) { 657217309Snwhitehorn offset2 = n; 658217309Snwhitehorn break; 659217309Snwhitehorn } 660217309Snwhitehorn } 661217309Snwhitehorn 662217309Snwhitehorn dpy_column = cols[offset] - cols[offset2]; 663217309Snwhitehorn 664217309Snwhitehorn if (p_dpy_column != 0) 665217309Snwhitehorn *p_dpy_column = dpy_column; 666217309Snwhitehorn if (p_scroll_amt != 0) 667217309Snwhitehorn *p_scroll_amt = offset2; 668217309Snwhitehorn} 669217309Snwhitehorn 670217309Snwhitehorn/* 671217309Snwhitehorn * Given the character-offset in the string, returns the display-offset where 672217309Snwhitehorn * we will position the cursor. 673217309Snwhitehorn */ 674217309Snwhitehornint 675217309Snwhitehorndlg_edit_offset(char *string, int chr_offset, int x_last) 676217309Snwhitehorn{ 677217309Snwhitehorn int result; 678217309Snwhitehorn 679217309Snwhitehorn compute_edit_offset(string, chr_offset, x_last, &result, 0); 680217309Snwhitehorn 681217309Snwhitehorn return result; 682217309Snwhitehorn} 683217309Snwhitehorn 684217309Snwhitehorn/* 685217309Snwhitehorn * Displays the string, shifted as necessary, to fit within the box and show 686217309Snwhitehorn * the current character-offset. 687217309Snwhitehorn */ 688217309Snwhitehornvoid 689217309Snwhitehorndlg_show_string(WINDOW *win, 690217309Snwhitehorn const char *string, /* string to display (may be multibyte) */ 691217309Snwhitehorn int chr_offset, /* character (not bytes) offset */ 692217309Snwhitehorn chtype attr, /* window-attributes */ 693217309Snwhitehorn int y_base, /* beginning row on screen */ 694217309Snwhitehorn int x_base, /* beginning column on screen */ 695217309Snwhitehorn int x_last, /* number of columns on screen */ 696217309Snwhitehorn bool hidden, /* if true, do not echo */ 697217309Snwhitehorn bool force) /* if true, force repaint */ 698217309Snwhitehorn{ 699217309Snwhitehorn x_last = MIN(x_last + x_base, getmaxx(win)) - x_base; 700217309Snwhitehorn 701217309Snwhitehorn if (hidden && !dialog_vars.insecure) { 702217309Snwhitehorn if (force) { 703217309Snwhitehorn (void) wmove(win, y_base, x_base); 704217309Snwhitehorn wrefresh(win); 705217309Snwhitehorn } 706217309Snwhitehorn } else { 707217309Snwhitehorn const int *cols = dlg_index_columns(string); 708217309Snwhitehorn const int *indx = dlg_index_wchars(string); 709217309Snwhitehorn int limit = dlg_count_wchars(string); 710217309Snwhitehorn 711217309Snwhitehorn int i, j, k; 712217309Snwhitehorn int input_x; 713217309Snwhitehorn int scrollamt; 714217309Snwhitehorn 715217309Snwhitehorn compute_edit_offset(string, chr_offset, x_last, &input_x, &scrollamt); 716217309Snwhitehorn 717251843Sbapt (void) wattrset(win, attr); 718217309Snwhitehorn (void) wmove(win, y_base, x_base); 719217309Snwhitehorn for (i = scrollamt, k = 0; i < limit && k < x_last; ++i) { 720217309Snwhitehorn int check = cols[i + 1] - cols[scrollamt]; 721217309Snwhitehorn if (check <= x_last) { 722217309Snwhitehorn for (j = indx[i]; j < indx[i + 1]; ++j) { 723217309Snwhitehorn chtype ch = UCH(string[j]); 724217309Snwhitehorn if (hidden && dialog_vars.insecure) { 725217309Snwhitehorn waddch(win, '*'); 726217309Snwhitehorn } else if (ch == TAB) { 727217309Snwhitehorn int count = cols[i + 1] - cols[i]; 728217309Snwhitehorn while (--count >= 0) 729217309Snwhitehorn waddch(win, ' '); 730217309Snwhitehorn } else { 731217309Snwhitehorn waddch(win, ch); 732217309Snwhitehorn } 733217309Snwhitehorn } 734217309Snwhitehorn k = check; 735217309Snwhitehorn } else { 736217309Snwhitehorn break; 737217309Snwhitehorn } 738217309Snwhitehorn } 739217309Snwhitehorn while (k++ < x_last) 740217309Snwhitehorn waddch(win, ' '); 741217309Snwhitehorn (void) wmove(win, y_base, x_base + input_x); 742217309Snwhitehorn wrefresh(win); 743217309Snwhitehorn } 744217309Snwhitehorn} 745217309Snwhitehorn 746255852Sdteske/* 747255852Sdteske * Discard cached data for the given string. 748255852Sdteske */ 749255852Sdteskevoid 750255852Sdteskedlg_finish_string(const char *string) 751255852Sdteske{ 752255852Sdteske#if USE_CACHING 753255852Sdteske if ((string != 0) && dialog_state.finish_string) { 754255852Sdteske CACHE *p = cache_list; 755255852Sdteske CACHE *q = 0; 756255852Sdteske CACHE *r; 757255852Sdteske 758255852Sdteske while (p != 0) { 759255852Sdteske if (p->string_at == string) { 760255852Sdteske#ifdef HAVE_TSEARCH 761255852Sdteske if (tdelete(p, &sorted_cache, compare_cache) == 0) { 762255852Sdteske continue; 763255852Sdteske } 764255852Sdteske trace_cache(__FILE__, __LINE__); 765255852Sdteske#endif 766255852Sdteske if (p->string != 0) 767255852Sdteske free(p->string); 768255852Sdteske if (p->list != 0) 769255852Sdteske free(p->list); 770255852Sdteske if (p == cache_list) { 771255852Sdteske cache_list = p->next; 772255852Sdteske r = cache_list; 773255852Sdteske } else { 774255852Sdteske q->next = p->next; 775255852Sdteske r = q; 776255852Sdteske } 777255852Sdteske free(p); 778255852Sdteske p = r; 779255852Sdteske } else { 780255852Sdteske q = p; 781255852Sdteske p = p->next; 782255852Sdteske } 783255852Sdteske } 784255852Sdteske } 785255852Sdteske#else 786255852Sdteske (void) string; 787255852Sdteske#endif 788255852Sdteske} 789255852Sdteske 790217309Snwhitehorn#ifdef NO_LEAKS 791217309Snwhitehornvoid 792217309Snwhitehorn_dlg_inputstr_leaks(void) 793217309Snwhitehorn{ 794217309Snwhitehorn#if USE_CACHING 795255852Sdteske dialog_state.finish_string = TRUE; 796255852Sdteske trace_cache(__FILE__, __LINE__); 797217309Snwhitehorn while (cache_list != 0) { 798255852Sdteske dlg_finish_string(cache_list->string_at); 799217309Snwhitehorn } 800217309Snwhitehorn#endif /* USE_CACHING */ 801217309Snwhitehorn} 802217309Snwhitehorn#endif /* NO_LEAKS */ 803