1/*	$NetBSD: eln.c,v 1.28 2016/02/28 23:02:24 christos Exp $	*/
2
3/*-
4 * Copyright (c) 2009 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28#include "config.h"
29#if !defined(lint) && !defined(SCCSID)
30__RCSID("$NetBSD: eln.c,v 1.28 2016/02/28 23:02:24 christos Exp $");
31#endif /* not lint && not SCCSID */
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD: stable/11/lib/libedit/eln.c 313981 2017-02-20 03:33:59Z pfg $");
34
35#include <errno.h>
36#include <stdarg.h>
37#include <stdio.h>
38#include <stdlib.h>
39
40#include "el.h"
41
42public int
43el_getc(EditLine *el, char *cp)
44{
45	int num_read;
46	wchar_t wc = 0;
47
48	num_read = el_wgetc(el, &wc);
49	*cp = '\0';
50	if (num_read <= 0)
51		return num_read;
52	num_read = ct_wctob(wc);
53	if (num_read == EOF) {
54		errno = ERANGE;
55		return -1;
56	} else {
57		*cp = (char)num_read;
58		return 1;
59	}
60}
61
62
63#ifdef WIDECHAR
64public void
65el_push(EditLine *el, const char *str)
66{
67	/* Using multibyte->wide string decoding works fine under single-byte
68	 * character sets too, and Does The Right Thing. */
69	el_wpush(el, ct_decode_string(str, &el->el_lgcyconv));
70}
71
72
73public const char *
74el_gets(EditLine *el, int *nread)
75{
76	const wchar_t *tmp;
77
78	tmp = el_wgets(el, nread);
79	if (tmp != NULL) {
80	    int i;
81	    size_t nwread = 0;
82
83	    for (i = 0; i < *nread; i++)
84		nwread += ct_enc_width(tmp[i]);
85	    *nread = (int)nwread;
86	}
87	return ct_encode_string(tmp, &el->el_lgcyconv);
88}
89
90
91public int
92el_parse(EditLine *el, int argc, const char *argv[])
93{
94	int ret;
95	const wchar_t **wargv;
96
97	wargv = (const wchar_t **)
98	    ct_decode_argv(argc, argv, &el->el_lgcyconv);
99	if (!wargv)
100		return -1;
101	ret = el_wparse(el, argc, wargv);
102	ct_free_argv(wargv);
103
104	return ret;
105}
106
107
108public int
109el_set(EditLine *el, int op, ...)
110{
111	va_list ap;
112	int ret;
113
114	if (!el)
115		return -1;
116	va_start(ap, op);
117
118	switch (op) {
119	case EL_PROMPT:         /* el_pfunc_t */
120	case EL_RPROMPT: {
121		el_pfunc_t p = va_arg(ap, el_pfunc_t);
122		ret = prompt_set(el, p, 0, op, 0);
123		break;
124	}
125
126	case EL_RESIZE: {
127		el_zfunc_t p = va_arg(ap, el_zfunc_t);
128		void *arg = va_arg(ap, void *);
129		ret = ch_resizefun(el, p, arg);
130		break;
131	}
132
133	case EL_ALIAS_TEXT: {
134		el_afunc_t p = va_arg(ap, el_afunc_t);
135		void *arg = va_arg(ap, void *);
136		ret = ch_aliasfun(el, p, arg);
137		break;
138	}
139
140	case EL_PROMPT_ESC:
141	case EL_RPROMPT_ESC: {
142		el_pfunc_t p = va_arg(ap, el_pfunc_t);
143		int c = va_arg(ap, int);
144
145		ret = prompt_set(el, p, c, op, 0);
146		break;
147	}
148
149	case EL_TERMINAL:       /* const char * */
150		ret = el_wset(el, op, va_arg(ap, char *));
151		break;
152
153	case EL_EDITOR:		/* const wchar_t * */
154		ret = el_wset(el, op, ct_decode_string(va_arg(ap, char *),
155		    &el->el_lgcyconv));
156		break;
157
158	case EL_SIGNAL:         /* int */
159	case EL_EDITMODE:
160	case EL_UNBUFFERED:
161	case EL_PREP_TERM:
162		ret = el_wset(el, op, va_arg(ap, int));
163		break;
164
165	case EL_BIND:   /* const char * list -> const wchar_t * list */
166	case EL_TELLTC:
167	case EL_SETTC:
168	case EL_ECHOTC:
169	case EL_SETTY: {
170		const char *argv[20];
171		int i;
172		const wchar_t **wargv;
173		for (i = 1; i < (int)__arraycount(argv) - 1; ++i)
174			if ((argv[i] = va_arg(ap, const char *)) == NULL)
175			    break;
176		argv[0] = argv[i] = NULL;
177		wargv = (const wchar_t **)
178		    ct_decode_argv(i + 1, argv, &el->el_lgcyconv);
179		if (!wargv) {
180		    ret = -1;
181		    goto out;
182		}
183		/*
184		 * AFAIK we can't portably pass through our new wargv to
185		 * el_wset(), so we have to reimplement the body of
186		 * el_wset() for these ops.
187		 */
188		switch (op) {
189		case EL_BIND:
190			wargv[0] = STR("bind");
191			ret = map_bind(el, i, wargv);
192			break;
193		case EL_TELLTC:
194			wargv[0] = STR("telltc");
195			ret = terminal_telltc(el, i, wargv);
196			break;
197		case EL_SETTC:
198			wargv[0] = STR("settc");
199			ret = terminal_settc(el, i, wargv);
200			break;
201		case EL_ECHOTC:
202			wargv[0] = STR("echotc");
203			ret = terminal_echotc(el, i, wargv);
204			break;
205		case EL_SETTY:
206			wargv[0] = STR("setty");
207			ret = tty_stty(el, i, wargv);
208			break;
209		default:
210			ret = -1;
211		}
212		ct_free_argv(wargv);
213		break;
214	}
215
216	/* XXX: do we need to change el_func_t too? */
217	case EL_ADDFN: {          /* const char *, const char *, el_func_t */
218		const char *args[2];
219		el_func_t func;
220		wchar_t **wargv;
221
222		args[0] = va_arg(ap, const char *);
223		args[1] = va_arg(ap, const char *);
224		func = va_arg(ap, el_func_t);
225
226		wargv = ct_decode_argv(2, args, &el->el_lgcyconv);
227		if (!wargv) {
228		    ret = -1;
229		    goto out;
230		}
231		/* XXX: The two strdup's leak */
232		ret = map_addfunc(el, Strdup(wargv[0]), Strdup(wargv[1]),
233		    func);
234		ct_free_argv(wargv);
235		break;
236	}
237	case EL_HIST: {           /* hist_fun_t, const char * */
238		hist_fun_t fun = va_arg(ap, hist_fun_t);
239		void *ptr = va_arg(ap, void *);
240		ret = hist_set(el, fun, ptr);
241		el->el_flags |= NARROW_HISTORY;
242		break;
243	}
244
245	case EL_GETCFN:         /* el_rfunc_t */
246		ret = el_wset(el, op, va_arg(ap, el_rfunc_t));
247		break;
248
249	case EL_CLIENTDATA:     /* void * */
250		ret = el_wset(el, op, va_arg(ap, void *));
251		break;
252
253	case EL_SETFP: {          /* int, FILE * */
254		int what = va_arg(ap, int);
255		FILE *fp = va_arg(ap, FILE *);
256		ret = el_wset(el, op, what, fp);
257		break;
258	}
259
260	case EL_REFRESH:
261		re_clear_display(el);
262		re_refresh(el);
263		terminal__flush(el);
264		ret = 0;
265		break;
266
267	default:
268		ret = -1;
269		break;
270	}
271
272out:
273	va_end(ap);
274	return ret;
275}
276
277
278public int
279el_get(EditLine *el, int op, ...)
280{
281	va_list ap;
282	int ret;
283
284	if (!el)
285		return -1;
286
287	va_start(ap, op);
288
289	switch (op) {
290	case EL_PROMPT:         /* el_pfunc_t * */
291	case EL_RPROMPT: {
292		el_pfunc_t *p = va_arg(ap, el_pfunc_t *);
293		ret = prompt_get(el, p, 0, op);
294		break;
295	}
296
297	case EL_PROMPT_ESC: /* el_pfunc_t *, char **/
298	case EL_RPROMPT_ESC: {
299		el_pfunc_t *p = va_arg(ap, el_pfunc_t *);
300		char *c = va_arg(ap, char *);
301		wchar_t wc = 0;
302		ret = prompt_get(el, p, &wc, op);
303		*c = (char)wc;
304		break;
305	}
306
307	case EL_EDITOR: {
308		const char **p = va_arg(ap, const char **);
309		const wchar_t *pw;
310		ret = el_wget(el, op, &pw);
311		*p = ct_encode_string(pw, &el->el_lgcyconv);
312		if (!el->el_lgcyconv.csize)
313			ret = -1;
314		break;
315	}
316
317	case EL_TERMINAL:       /* const char ** */
318		ret = el_wget(el, op, va_arg(ap, const char **));
319		break;
320
321	case EL_SIGNAL:         /* int * */
322	case EL_EDITMODE:
323	case EL_UNBUFFERED:
324	case EL_PREP_TERM:
325		ret = el_wget(el, op, va_arg(ap, int *));
326		break;
327
328	case EL_GETTC: {
329		char *argv[20];
330		static char gettc[] = "gettc";
331		int i;
332		for (i = 1; i < (int)__arraycount(argv); ++i)
333			if ((argv[i] = va_arg(ap, char *)) == NULL)
334				break;
335		argv[0] = gettc;
336		ret = terminal_gettc(el, i, argv);
337		break;
338	}
339
340	case EL_GETCFN:         /* el_rfunc_t */
341		ret = el_wget(el, op, va_arg(ap, el_rfunc_t *));
342		break;
343
344	case EL_CLIENTDATA:     /* void ** */
345		ret = el_wget(el, op, va_arg(ap, void **));
346		break;
347
348	case EL_GETFP: {          /* int, FILE ** */
349		int what = va_arg(ap, int);
350		FILE **fpp = va_arg(ap, FILE **);
351		ret = el_wget(el, op, what, fpp);
352		break;
353	}
354
355	default:
356		ret = -1;
357		break;
358	}
359
360	va_end(ap);
361	return ret;
362}
363
364
365const LineInfo *
366el_line(EditLine *el)
367{
368	const LineInfoW *winfo = el_wline(el);
369	LineInfo *info = &el->el_lgcylinfo;
370	size_t offset;
371	const Char *p;
372
373	info->buffer   = ct_encode_string(winfo->buffer, &el->el_lgcyconv);
374
375	offset = 0;
376	for (p = winfo->buffer; p < winfo->cursor; p++)
377		offset += ct_enc_width(*p);
378	info->cursor = info->buffer + offset;
379
380	offset = 0;
381	for (p = winfo->buffer; p < winfo->lastchar; p++)
382		offset += ct_enc_width(*p);
383	info->lastchar = info->buffer + offset;
384
385	return info;
386}
387
388
389int
390el_insertstr(EditLine *el, const char *str)
391{
392	return el_winsertstr(el, ct_decode_string(str, &el->el_lgcyconv));
393}
394#endif /* WIDECHAR */
395