1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 1995, by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 /*
28  * wgetn_ws.c
29  *
30  * XCurses Library
31  *
32  * Copyright 1990, 1995 by Mortice Kern Systems Inc.  All rights reserved.
33  *
34  */
35 
36 #ifdef M_RCSID
37 #ifndef lint
38 static char rcsID[] = "$Header: /rd/src/libc/xcurses/rcs/wgetn_ws.c 1.3 1995/10/02 15:07:23 ant Exp $";
39 #endif
40 #endif
41 
42 #include <private.h>
43 #include <limits.h>
44 #include <stdlib.h>
45 #include <m_wio.h>
46 
47 static wint_t fld_key;
48 static int fld_echo;		/* Software echo flag. */
49 static int fld_index;		/* Number of characters in fld_buffer. */
50 static int fld_bytes;		/* fld_index expressed in bytes. */
51 static int fld_mb;		/* Field is a multibyte character string. */
52 static wint_t *fld_buffer;	/* Wide character buffer. */
53 static int fld_maxlength;	/* Character length of buffer. */
54 static WINDOW *fld_window;
55 static int fld_row, fld_col;	/* Start of field in fld_window. */
56 
57 STATIC int fld_done(void);
58 STATIC int fld_erase(void);
59 STATIC int fld_kill(void);
60 STATIC int fld_insert(void);
61 
62 typedef struct t_key_entry {
63 	int type;
64 	wint_t code;
65 	int (*func)(void);
66 } t_key_entry;
67 
68 #define ERASE_KEY	0
69 #define KILL_KEY	1
70 #define EOF_KEY		2
71 #define EOL_KEY		3
72 
73 static t_key_entry key_table[] = {
74 	{ OK, 0, fld_erase },
75 	{ OK, 0, fld_kill },
76 	{ OK, 0, fld_done },
77 	{ OK, 0, fld_done },
78 	{ OK, '\n', fld_done },
79 	{ OK, WEOF, fld_done },
80 	{ KEY_CODE_YES, KEY_LEFT, fld_erase },
81 	{ KEY_CODE_YES, KEY_BACKSPACE, fld_erase },
82 	{ ERR, 0, fld_insert }
83 };
84 
85 /*f
86  * The effect of wgetnstr() is as though a series of calls to wgetch()
87  * were made, until a <newline> or <return> are received.  The
88  * resulting value is placed in the area pointed to by the character
89  * pointer 's'.  wgetnstr() reads at most n characters, thus
90  * preventing a possible overflow of the input buffer.  The user's
91  * erase and kill characters are interpreted.
92  *
93  * If n < 0, wgetnstr() will read until a <newline> or <return> is
94  * entered.  To accept functions keys, keypad() must be set for the
95  * window.
96  */
97 int
__m_wgetn_wstr(w,s,n,mb_flag)98 __m_wgetn_wstr(w, s, n, mb_flag)
99 WINDOW *w;
100 void *s;
101 int n, mb_flag;
102 {
103 	int type;
104 	wchar_t wc;
105 	t_key_entry *k;
106 	struct termios saved;
107 
108 #ifdef M_CURSES_TRACE
109 	__m_trace("__m_wgetn_wstr(%p, %p, %d, %d)", w, s, n, mb_flag);
110 #endif
111 
112 	fld_window = w;
113 	fld_index = 0;
114 	fld_bytes = 0;
115 	fld_mb = mb_flag;
116 
117 	/* Read at most N bytes from the field. */
118 	fld_maxlength = n < 0 ? LINE_MAX : n;
119 
120 	/* Make sure there is enough to hold the largest characater. */
121 	if (fld_mb && fld_maxlength < MB_CUR_MAX)
122 		return __m_return_code("wgetn_wstr", ERR);
123 
124 	if (mb_flag) {
125 		/* Create a wint_t buffer, which makes it easier to
126 		 * handle erasing characters from the line.
127 		 */
128 		fld_buffer = (wint_t *) calloc(
129 			fld_maxlength+1, sizeof *fld_buffer
130 		);
131 		if (fld_buffer == (wint_t *) 0)
132 			return __m_return_code("wgetn_wstr", ERR);
133 	} else {
134 		fld_buffer = (wint_t *) s;
135 	}
136 
137 	(void) __m_tty_wc(VEOL, &wc);
138 	key_table[EOL_KEY].code = (wint_t) wc;
139 	(void) __m_tty_wc(VEOF, &wc);
140 	key_table[EOF_KEY].code = (wint_t) wc;
141 	(void) __m_tty_wc(VKILL, &wc);
142 	key_table[KILL_KEY].code = (wint_t) wc;
143 	(void) __m_tty_wc(VERASE, &wc);
144 	key_table[ERASE_KEY].code = (wint_t) wc;
145 
146 	getyx(fld_window, fld_row, fld_col);
147 
148 	/* We remember if the user specified echo on or off, then disable it
149 	 * so that wgetch() doesn't perform any echoing before we've had a
150 	 * chance to process the key.  fld_insert() will handle the necessary
151 	 * echoing of characters.
152 	 */
153         fld_echo = __m_set_echo(0);
154 	saved = cur_term->_prog;
155 	(void) cbreak();
156 
157 	for (;;) {
158 		type = wget_wch(fld_window, &fld_key);
159 
160 		for (k = key_table; k->type != ERR; ++k)
161 			if (k->type == type && k->code == fld_key)
162 				break;
163 
164 		if (k->func != (int (*)(void)) 0 && !(*k->func)()) {
165 			/* If the edit function returned false then quit. */
166 			fld_buffer[fld_index] = '\0';
167 			break;
168 		}
169 	}
170 
171 	/* Restore the I/O settings. */
172 	(void) __m_set_echo(fld_echo);
173 	(void) __m_tty_set(&saved);
174 
175 	if (mb_flag) {
176 		(void) wistombs((char *) s, fld_buffer, fld_maxlength+1);
177 		free(fld_buffer);
178 	}
179 
180 	return __m_return_code("__m_wgetn_wstr", OK);
181 }
182 
183 STATIC int
wint_len(wc)184 wint_len(wc)
185 wint_t wc;
186 {
187 	int len;
188 	char mb[MB_LEN_MAX];
189 
190 	if (wc == WEOF)
191 		return 0;
192 
193 	len = wctomb(mb, (wchar_t) wc);
194 
195 	return len < 0 ? 0 : len;
196 }
197 
198 STATIC int
fld_done()199 fld_done()
200 {
201 	return 0;
202 }
203 
204 STATIC int
fld_erase()205 fld_erase()
206 {
207 	int x, y, width;
208 
209 	if (fld_index <= 0)
210 		return 1;
211 
212 	width = wcwidth(fld_buffer[--fld_index]);
213 	fld_bytes -= wint_len(fld_buffer[fld_index]);
214 	fld_buffer[fld_index] = '\0';
215 	getyx(fld_window, y, x);
216 
217 	if (0 < x) {
218 		/* Rubout previous character. */
219 		x -= width;
220 	} else if (0 < y) {
221 		/* Reverse line wrap. */
222 		--y;
223 		x = fld_window->_maxx - 1;
224 
225 		/* Find end of previous character, skipping any background
226 		 * character that may have been written by auto-wrap.
227 		 */
228 		while (fld_buffer[fld_index] != fld_window->_line[y][x]._wc[0])
229 			--x;
230 
231 		/* Find first column of character. */
232 		x = __m_cc_first(fld_window, y, x);
233 	}
234 
235 	(void) __m_cc_erase(fld_window, y, x, y, x);
236 
237 	fld_window->_cury = y;
238 	fld_window->_curx = x;
239 
240 	return 1;
241 }
242 
243 STATIC int
fld_kill()244 fld_kill()
245 {
246 	int y, x;
247 
248 	getyx(fld_window, y, x);
249 	(void) __m_cc_erase(fld_window, fld_row, fld_col, y, x);
250 
251 	fld_window->_cury = fld_row;
252 	fld_window->_curx = fld_col;
253 	fld_index = fld_bytes = 0;
254 	fld_buffer[0] = '\0';
255 
256 	return 1;
257 }
258 
259 STATIC int
fld_insert()260 fld_insert()
261 {
262 	cchar_t cc;
263 	t_wide_io *wio;
264 
265 	if (fld_maxlength < fld_index)
266 		/* Completely full, terminate input. */
267 		return 0;
268 
269 	wio = (t_wide_io *) __m_screen->_in;
270 
271 	/* Don't exceed the byte length for the field.
272 	 *
273 	 * m_wio_get() called by wget_wch(), records the
274 	 * number of bytes converted, when _next == _size.
275 	 *
276 	 * wget_wch() makes sure that _next == _size by
277 	 * pushing invalid multibyte characters on to an
278 	 * input stack.
279 	 */
280 	if (fld_mb && fld_maxlength < fld_bytes + wio->_size) {
281 		/* Is there still room for single-byte characters? */
282 		if (fld_bytes < fld_maxlength) {
283 			(void) beep();
284 			return 1;
285 		}
286 
287 		/* Completely full, terminate input. */
288 		return 0;
289 	}
290 
291 	if (0 <= fld_key) {
292 		fld_buffer[fld_index++] = fld_key;
293 		fld_bytes += wio->_size;
294 
295 		if (fld_echo) {
296 			(void) __m_wc_cc(fld_key, &cc);
297 			(void) wadd_wch(fld_window, &cc);
298 		}
299 	} else {
300 		(void) beep();
301 	}
302 
303 	return 1;
304 }
305 
306 int
wgetnstr(w,s,n)307 wgetnstr(w, s, n)
308 WINDOW *w;
309 char *s;
310 int n;
311 {
312 	int code;
313 
314 #ifdef M_CURSES_TRACE
315 	__m_trace("wgetnstr(%p, %p, %d)", w, s, n);
316 #endif
317 
318 	code = __m_wgetn_wstr(w, (void *) s, n, 1);
319 
320 	return __m_return_code("wgetnstr", code);
321 }
322 
323 int
wgetn_wstr(w,s,n)324 wgetn_wstr(w, s, n)
325 WINDOW *w;
326 wint_t *s;
327 int n;
328 {
329 	int code;
330 
331 #ifdef M_CURSES_TRACE
332 	__m_trace("wgetn_wstr(%p, %p, %d)", w, s, n);
333 #endif
334 
335 	code = __m_wgetn_wstr(w, (void *) s, n, 0);
336 
337 	return __m_return_code("wgetn_wstr", code);
338 }
339