kernel - Improve basic entropy collector
[dragonfly.git] / contrib / dialog / inputstr.c
bloba9e90c59da5fdcae7cc76431061a7c82c53da70b
1 /*
2 * $Id: inputstr.c,v 1.72 2012/12/30 22:11:37 tom Exp $
4 * inputstr.c -- functions for input/display of a string
6 * Copyright 2000-2011,2012 Thomas E. Dickey
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License, version 2.1
10 * as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program; if not, write to
19 * Free Software Foundation, Inc.
20 * 51 Franklin St., Fifth Floor
21 * Boston, MA 02110, USA.
24 #include <dialog.h>
25 #include <dlg_keys.h>
27 #include <errno.h>
29 #ifdef HAVE_SETLOCALE
30 #include <locale.h>
31 #endif
33 #if defined(HAVE_SEARCH_H) && defined(HAVE_TSEARCH)
34 #include <search.h>
35 #else
36 #undef HAVE_TSEARCH
37 #endif
39 #ifdef NEED_WCHAR_H
40 #include <wchar.h>
41 #endif
43 #if defined(USE_WIDE_CURSES)
44 #define USE_CACHING 1
45 #elif defined(HAVE_XDIALOG)
46 #define USE_CACHING 1 /* editbox really needs caching! */
47 #else
48 #define USE_CACHING 0
49 #endif
51 typedef struct _cache {
52 struct _cache *next;
53 #if USE_CACHING
54 struct _cache *cache_at; /* unique: associate caches by CACHE */
55 const char *string_at; /* unique: associate caches by char* */
56 #endif
57 size_t s_len; /* strlen(string) - we add 1 for EOS */
58 size_t i_len; /* length(list) - we add 1 for EOS */
59 char *string; /* a copy of the last-processed string */
60 int *list; /* indices into the string */
61 } CACHE;
63 #if USE_CACHING
64 #define SAME_CACHE(c,s,l) (c->string != 0 && memcmp(c->string,s,l) == 0)
66 static CACHE *cache_list;
68 #ifdef HAVE_TSEARCH
69 static void *sorted_cache;
70 #endif
72 #ifdef USE_WIDE_CURSES
73 static int
74 have_locale(void)
76 static int result = -1;
77 if (result < 0) {
78 char *test = setlocale(LC_ALL, 0);
79 if (test == 0 || *test == 0) {
80 result = FALSE;
81 } else if (strcmp(test, "C") && strcmp(test, "POSIX")) {
82 result = TRUE;
83 } else {
84 result = FALSE;
87 return result;
89 #endif
91 #ifdef HAVE_TSEARCH
92 static int
93 compare_cache(const void *a, const void *b)
95 const CACHE *p = (const CACHE *) a;
96 const CACHE *q = (const CACHE *) b;
97 int result = 0;
98 result = (int) (p->cache_at - q->cache_at);
99 if (result == 0)
100 result = (int) (p->string_at - q->string_at);
101 return result;
103 #endif
105 static CACHE *
106 find_cache(CACHE * cache, const char *string)
108 CACHE *p;
110 #ifdef HAVE_TSEARCH
111 void *pp;
112 CACHE find;
114 memset(&find, 0, sizeof(find));
115 find.cache_at = cache;
116 find.string_at = string;
118 if ((pp = tfind(&find, &sorted_cache, compare_cache)) != 0) {
119 p = *(CACHE **) pp;
120 } else {
121 p = 0;
123 #else
124 for (p = cache_list; p != 0; p = p->next) {
125 if (p->cache_at == cache
126 && p->string_at == string) {
127 break;
130 #endif
131 return p;
134 static void
135 make_cache(CACHE * cache, const char *string)
137 CACHE *p;
139 p = dlg_calloc(CACHE, 1);
140 assert_ptr(p, "load_cache");
141 p->next = cache_list;
142 cache_list = p;
144 p->cache_at = cache;
145 p->string_at = string;
147 *cache = *p;
148 #ifdef HAVE_TSEARCH
149 (void) tsearch(p, &sorted_cache, compare_cache);
150 #endif
153 static void
154 load_cache(CACHE * cache, const char *string)
156 CACHE *p;
158 if ((p = find_cache(cache, string)) != 0) {
159 *cache = *p;
160 } else {
161 make_cache(cache, string);
165 static void
166 save_cache(CACHE * cache, const char *string)
168 CACHE *p;
170 if ((p = find_cache(cache, string)) != 0) {
171 CACHE *q = p->next;
172 *p = *cache;
173 p->next = q;
176 #else
177 #define SAME_CACHE(c,s,l) (c->string != 0)
178 #define load_cache(cache, string) /* nothing */
179 #define save_cache(cache, string) /* nothing */
180 #endif /* USE_WIDE_CURSES */
183 * If the given string has not changed, we do not need to update the index.
184 * If we need to update the index, allocate enough memory for it.
186 static bool
187 same_cache2(CACHE * cache, const char *string, unsigned i_len)
189 unsigned need;
190 size_t s_len = strlen(string);
192 if (cache->s_len != 0
193 && cache->s_len >= s_len
194 && cache->list != 0
195 && SAME_CACHE(cache, string, (size_t) s_len)) {
196 return TRUE;
199 need = (i_len + 1);
200 if (cache->list == 0) {
201 cache->list = dlg_malloc(int, need);
202 } else if (cache->i_len < i_len) {
203 cache->list = dlg_realloc(int, need, cache->list);
205 cache->i_len = i_len;
207 if (cache->s_len >= s_len && cache->string != 0) {
208 strcpy(cache->string, string);
209 } else {
210 if (cache->string != 0)
211 free(cache->string);
212 cache->string = dlg_strclone(string);
214 cache->s_len = s_len;
216 return FALSE;
219 #ifdef USE_WIDE_CURSES
221 * Like same_cache2(), but we are only concerned about caching a copy of the
222 * string and its associated length.
224 static bool
225 same_cache1(CACHE * cache, const char *string, size_t i_len)
227 size_t s_len = strlen(string);
229 if (cache->s_len == s_len
230 && SAME_CACHE(cache, string, (size_t) s_len)) {
231 return TRUE;
234 if (cache->s_len >= s_len && cache->string != 0) {
235 strcpy(cache->string, string);
236 } else {
237 if (cache->string != 0)
238 free(cache->string);
239 cache->string = dlg_strclone(string);
241 cache->s_len = s_len;
242 cache->i_len = i_len;
244 return FALSE;
246 #endif /* USE_CACHING */
249 * Counts the number of bytes that make up complete wide-characters, up to byte
250 * 'len'. If there is no locale set, simply return the original length.
252 #ifdef USE_WIDE_CURSES
253 static int
254 dlg_count_wcbytes(const char *string, size_t len)
256 int result;
258 if (have_locale()) {
259 static CACHE cache;
261 load_cache(&cache, string);
262 if (!same_cache1(&cache, string, len)) {
263 while (len != 0) {
264 size_t code = 0;
265 const char *src = cache.string;
266 mbstate_t state;
267 char save = cache.string[len];
269 cache.string[len] = '\0';
270 memset(&state, 0, sizeof(state));
271 code = mbsrtowcs((wchar_t *) 0, &src, len, &state);
272 cache.string[len] = save;
273 if ((int) code >= 0) {
274 break;
276 --len;
278 cache.i_len = len;
279 save_cache(&cache, string);
281 result = (int) cache.i_len;
282 } else {
283 result = (int) len;
285 return result;
287 #endif /* USE_WIDE_CURSES */
290 * Counts the number of wide-characters in the string.
293 dlg_count_wchars(const char *string)
295 int result;
297 #ifdef USE_WIDE_CURSES
298 if (have_locale()) {
299 static CACHE cache;
300 size_t len = strlen(string);
302 load_cache(&cache, string);
303 if (!same_cache1(&cache, string, len)) {
304 const char *src = cache.string;
305 mbstate_t state;
306 int part = dlg_count_wcbytes(cache.string, len);
307 char save = cache.string[part];
308 size_t code;
309 wchar_t *temp = dlg_calloc(wchar_t, len + 1);
311 if (temp != 0) {
312 cache.string[part] = '\0';
313 memset(&state, 0, sizeof(state));
314 code = mbsrtowcs(temp, &src, (size_t) part, &state);
315 cache.i_len = ((int) code >= 0) ? wcslen(temp) : 0;
316 cache.string[part] = save;
317 free(temp);
318 save_cache(&cache, string);
319 } else {
320 cache.i_len = 0;
323 result = (int) cache.i_len;
324 } else
325 #endif /* USE_WIDE_CURSES */
327 result = (int) strlen(string);
329 return result;
333 * Build an index of the wide-characters in the string, so we can easily tell
334 * which byte-offset begins a given wide-character.
336 const int *
337 dlg_index_wchars(const char *string)
339 static CACHE cache;
340 unsigned len = (unsigned) dlg_count_wchars(string);
341 unsigned inx;
343 load_cache(&cache, string);
344 if (!same_cache2(&cache, string, len)) {
345 const char *current = string;
347 cache.list[0] = 0;
348 for (inx = 1; inx <= len; ++inx) {
349 #ifdef USE_WIDE_CURSES
350 if (have_locale()) {
351 mbstate_t state;
352 int width;
353 memset(&state, 0, sizeof(state));
354 width = (int) mbrlen(current, strlen(current), &state);
355 if (width <= 0)
356 width = 1; /* FIXME: what if we have a control-char? */
357 current += width;
358 cache.list[inx] = cache.list[inx - 1] + width;
359 } else
360 #endif /* USE_WIDE_CURSES */
362 (void) current;
363 cache.list[inx] = (int) inx;
366 save_cache(&cache, string);
368 return cache.list;
372 * Given the character-offset to find in the list, return the corresponding
373 * array index.
376 dlg_find_index(const int *list, int limit, int to_find)
378 int result;
379 for (result = 0; result <= limit; ++result) {
380 if (to_find == list[result]
381 || result == limit
382 || ((result < limit) && (to_find < list[result + 1]))) {
383 break;
386 return result;
390 * Build a list of the display-columns for the given string's characters.
392 const int *
393 dlg_index_columns(const char *string)
395 static CACHE cache;
396 unsigned len = (unsigned) dlg_count_wchars(string);
397 unsigned inx;
399 load_cache(&cache, string);
400 if (!same_cache2(&cache, string, len)) {
401 cache.list[0] = 0;
402 #ifdef USE_WIDE_CURSES
403 if (have_locale()) {
404 size_t num_bytes = strlen(string);
405 const int *inx_wchars = dlg_index_wchars(string);
406 mbstate_t state;
408 for (inx = 0; inx < len; ++inx) {
409 wchar_t temp[2];
410 size_t check;
411 int result;
413 if (string[inx_wchars[inx]] == TAB) {
414 result = ((cache.list[inx] | 7) + 1) - cache.list[inx];
415 } else {
416 memset(&state, 0, sizeof(state));
417 memset(temp, 0, sizeof(temp));
418 check = mbrtowc(temp,
419 string + inx_wchars[inx],
420 num_bytes - (size_t) inx_wchars[inx],
421 &state);
422 if ((int) check <= 0) {
423 result = 1;
424 } else {
425 result = wcwidth(temp[0]);
427 if (result < 0) {
428 const wchar_t *printable;
429 cchar_t temp2, *temp2p = &temp2;
430 setcchar(temp2p, temp, 0, 0, 0);
431 printable = wunctrl(temp2p);
432 result = printable ? (int) wcslen(printable) : 1;
435 cache.list[inx + 1] = result;
436 if (inx != 0)
437 cache.list[inx + 1] += cache.list[inx];
439 } else
440 #endif /* USE_WIDE_CURSES */
442 for (inx = 0; inx < len; ++inx) {
443 chtype ch = UCH(string[inx]);
445 if (ch == TAB)
446 cache.list[inx + 1] =
447 ((cache.list[inx] | 7) + 1) - cache.list[inx];
448 else if (isprint(ch))
449 cache.list[inx + 1] = 1;
450 else {
451 const char *printable;
452 printable = unctrl(ch);
453 cache.list[inx + 1] = (printable
454 ? (int) strlen(printable)
455 : 1);
457 if (inx != 0)
458 cache.list[inx + 1] += cache.list[inx];
461 save_cache(&cache, string);
463 return cache.list;
467 * Returns the number of columns used for a string. That happens to be the
468 * end-value of the cols[] array.
471 dlg_count_columns(const char *string)
473 int result = 0;
474 int limit = dlg_count_wchars(string);
475 if (limit > 0) {
476 const int *cols = dlg_index_columns(string);
477 result = cols[limit];
478 } else {
479 result = (int) strlen(string);
481 return result;
485 * Given a column limit, count the number of wide characters that can fit
486 * into that limit. The offset is used to skip over a leading character
487 * that was already written.
490 dlg_limit_columns(const char *string, int limit, int offset)
492 const int *cols = dlg_index_columns(string);
493 int result = dlg_count_wchars(string);
495 while (result > 0 && (cols[result] - cols[offset]) > limit)
496 --result;
497 return result;
501 * Updates the string and character-offset, given various editing characters
502 * or literal characters which are inserted at the character-offset.
504 bool
505 dlg_edit_string(char *string, int *chr_offset, int key, int fkey, bool force)
507 int i;
508 int len = (int) strlen(string);
509 int limit = dlg_count_wchars(string);
510 const int *indx = dlg_index_wchars(string);
511 int offset = dlg_find_index(indx, limit, *chr_offset);
512 int max_len = dlg_max_input(MAX_LEN);
513 bool edit = TRUE;
515 /* transform editing characters into equivalent function-keys */
516 if (!fkey) {
517 fkey = TRUE; /* assume we transform */
518 switch (key) {
519 case 0:
520 break;
521 case ESC:
522 case TAB:
523 fkey = FALSE; /* this is used for navigation */
524 break;
525 default:
526 fkey = FALSE; /* ...no, we did not transform */
527 break;
531 if (fkey) {
532 switch (key) {
533 case 0: /* special case for loop entry */
534 edit = force;
535 break;
536 case DLGK_GRID_LEFT:
537 if (*chr_offset && offset > 0)
538 *chr_offset = indx[offset - 1];
539 break;
540 case DLGK_GRID_RIGHT:
541 if (offset < limit)
542 *chr_offset = indx[offset + 1];
543 break;
544 case DLGK_BEGIN:
545 if (*chr_offset)
546 *chr_offset = 0;
547 break;
548 case DLGK_FINAL:
549 if (offset < limit)
550 *chr_offset = indx[limit];
551 break;
552 case DLGK_DELETE_LEFT:
553 if (offset) {
554 int gap = indx[offset] - indx[offset - 1];
555 *chr_offset = indx[offset - 1];
556 if (gap > 0) {
557 for (i = *chr_offset;
558 (string[i] = string[i + gap]) != '\0';
559 i++) {
564 break;
565 case DLGK_DELETE_RIGHT:
566 if (limit) {
567 if (--limit == 0) {
568 string[*chr_offset = 0] = '\0';
569 } else {
570 int gap = ((offset <= limit)
571 ? (indx[offset + 1] - indx[offset])
572 : 0);
573 if (gap > 0) {
574 for (i = indx[offset];
575 (string[i] = string[i + gap]) != '\0';
576 i++) {
579 } else if (offset > 0) {
580 string[indx[offset - 1]] = '\0';
582 if (*chr_offset > indx[limit])
583 *chr_offset = indx[limit];
586 break;
587 case DLGK_DELETE_ALL:
588 string[*chr_offset = 0] = '\0';
589 break;
590 case DLGK_ENTER:
591 edit = 0;
592 break;
593 #ifdef KEY_RESIZE
594 case KEY_RESIZE:
595 edit = 0;
596 break;
597 #endif
598 case DLGK_GRID_UP:
599 case DLGK_GRID_DOWN:
600 case DLGK_FIELD_NEXT:
601 case DLGK_FIELD_PREV:
602 edit = 0;
603 break;
604 case ERR:
605 edit = 0;
606 break;
607 default:
608 beep();
609 break;
611 } else {
612 if (key == ESC || key == ERR) {
613 edit = 0;
614 } else {
615 if (len < max_len) {
616 for (i = ++len; i > *chr_offset; i--)
617 string[i] = string[i - 1];
618 string[*chr_offset] = (char) key;
619 *chr_offset += 1;
620 } else {
621 (void) beep();
625 return edit;
628 static void
629 compute_edit_offset(const char *string,
630 int chr_offset,
631 int x_last,
632 int *p_dpy_column,
633 int *p_scroll_amt)
635 const int *cols = dlg_index_columns(string);
636 const int *indx = dlg_index_wchars(string);
637 int limit = dlg_count_wchars(string);
638 int offset = dlg_find_index(indx, limit, chr_offset);
639 int offset2;
640 int dpy_column;
641 int n;
643 for (n = offset2 = 0; n <= offset; ++n) {
644 if ((cols[offset] - cols[n]) < x_last
645 && (offset == limit || (cols[offset + 1] - cols[n]) < x_last)) {
646 offset2 = n;
647 break;
651 dpy_column = cols[offset] - cols[offset2];
653 if (p_dpy_column != 0)
654 *p_dpy_column = dpy_column;
655 if (p_scroll_amt != 0)
656 *p_scroll_amt = offset2;
660 * Given the character-offset in the string, returns the display-offset where
661 * we will position the cursor.
664 dlg_edit_offset(char *string, int chr_offset, int x_last)
666 int result;
668 compute_edit_offset(string, chr_offset, x_last, &result, 0);
670 return result;
674 * Displays the string, shifted as necessary, to fit within the box and show
675 * the current character-offset.
677 void
678 dlg_show_string(WINDOW *win,
679 const char *string, /* string to display (may be multibyte) */
680 int chr_offset, /* character (not bytes) offset */
681 chtype attr, /* window-attributes */
682 int y_base, /* beginning row on screen */
683 int x_base, /* beginning column on screen */
684 int x_last, /* number of columns on screen */
685 bool hidden, /* if true, do not echo */
686 bool force) /* if true, force repaint */
688 x_last = MIN(x_last + x_base, getmaxx(win)) - x_base;
690 if (hidden && !dialog_vars.insecure) {
691 if (force) {
692 (void) wmove(win, y_base, x_base);
693 wrefresh(win);
695 } else {
696 const int *cols = dlg_index_columns(string);
697 const int *indx = dlg_index_wchars(string);
698 int limit = dlg_count_wchars(string);
700 int i, j, k;
701 int input_x;
702 int scrollamt;
704 compute_edit_offset(string, chr_offset, x_last, &input_x, &scrollamt);
706 (void) wattrset(win, attr);
707 (void) wmove(win, y_base, x_base);
708 for (i = scrollamt, k = 0; i < limit && k < x_last; ++i) {
709 int check = cols[i + 1] - cols[scrollamt];
710 if (check <= x_last) {
711 for (j = indx[i]; j < indx[i + 1]; ++j) {
712 chtype ch = UCH(string[j]);
713 if (hidden && dialog_vars.insecure) {
714 waddch(win, '*');
715 } else if (ch == TAB) {
716 int count = cols[i + 1] - cols[i];
717 while (--count >= 0)
718 waddch(win, ' ');
719 } else {
720 waddch(win, ch);
723 k = check;
724 } else {
725 break;
728 while (k++ < x_last)
729 waddch(win, ' ');
730 (void) wmove(win, y_base, x_base + input_x);
731 wrefresh(win);
735 #ifdef NO_LEAKS
736 void
737 _dlg_inputstr_leaks(void)
739 #if USE_CACHING
740 while (cache_list != 0) {
741 CACHE *next = cache_list->next;
742 #ifdef HAVE_TSEARCH
743 tdelete(cache_list, &sorted_cache, compare_cache);
744 #endif
745 if (cache_list->string != 0)
746 free(cache_list->string);
747 if (cache_list->list != 0)
748 free(cache_list->list);
749 free(cache_list);
750 cache_list = next;
752 #endif /* USE_CACHING */
754 #endif /* NO_LEAKS */