xref: /freebsd/contrib/ntp/libntp/ntp_lineedit.c (revision d6b92ffa)
1 /*
2  * ntp_lineedit.c - generic interface to various line editing libs
3  */
4 #ifdef HAVE_CONFIG_H
5 # include <config.h>
6 #endif
7 
8 #include <errno.h>
9 #include <string.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 
13 #if defined(HAVE_READLINE_HISTORY) &&		\
14     (!defined(HAVE_READLINE_HISTORY_H) ||	\
15      !defined(HAVE_READLINE_READLINE_H))
16 # undef HAVE_READLINE_HISTORY
17 #endif
18 #if defined(HAVE_READLINE_HISTORY)
19 # include <readline/readline.h>
20 # include <readline/history.h>
21 # define LE_READLINE
22 #elif defined(HAVE_HISTEDIT_H)
23 # include <histedit.h>
24 # define LE_EDITLINE
25 #else
26 # define LE_NONE
27 #endif
28 
29 #include "ntp.h"
30 #include "ntp_stdlib.h"
31 #include "ntp_lineedit.h"
32 #include "safecast.h"
33 
34 #define MAXEDITLINE	512
35 
36 /*
37  * external references
38  */
39 
40 extern char const *	progname;
41 
42 /*
43  * globals, private prototypes
44  */
45 
46 static int	ntp_readline_initted;
47 static char *	lineedit_prompt;
48 
49 
50 #ifdef LE_EDITLINE
51 # ifndef H_SETSIZE
52 #  define H_SETSIZE H_EVENT
53 # endif
54 static EditLine *	ntp_el;
55 static History *	ntp_hist;
56 static HistEvent	hev;
57 
58 char *	ntp_prompt_callback(EditLine *);
59 #endif	/* LE_EDITLINE */
60 
61 
62 /*
63  * ntp_readline_init - setup, set or reset prompt string
64  */
65 int
66 ntp_readline_init(
67 	const char *	prompt
68 	)
69 {
70 	int	success;
71 
72 	success = 1;
73 
74 	if (prompt) {
75 		if (lineedit_prompt)
76 			free(lineedit_prompt);
77 		lineedit_prompt = estrdup(prompt);
78 	}
79 
80 #ifdef LE_EDITLINE
81 	if (NULL == ntp_el) {
82 
83 # if 4 == EL_INIT_ARGS
84 		ntp_el = el_init(progname, stdin, stdout, stderr);
85 # else
86 		ntp_el = el_init(progname, stdin, stdout);
87 # endif
88 		if (ntp_el) {
89 
90 			el_set(ntp_el, EL_PROMPT, ntp_prompt_callback);
91 			el_set(ntp_el, EL_EDITOR, "emacs");
92 
93 			ntp_hist = history_init();
94 
95 			if (NULL == ntp_hist) {
96 
97 				mfprintf(stderr, "history_init(): %m\n");
98 				fflush(stderr);
99 
100 				el_end(ntp_el);
101 				ntp_el = NULL;
102 
103 				success = 0;
104 
105 			} else {
106 				ZERO(hev);
107 #ifdef H_SETSIZE
108 				history(ntp_hist, &hev, H_SETSIZE, 128);
109 #endif
110 				el_set(ntp_el, EL_HIST, history,
111 				       ntp_hist);
112 				/* use any .editrc */
113 				el_source(ntp_el, NULL);
114 			}
115 		} else
116 			success = 0;
117 	}
118 #endif	/* LE_EDITLINE */
119 
120 	ntp_readline_initted = success;
121 
122 	return success;
123 }
124 
125 
126 /*
127  * ntp_readline_uninit - release resources
128  */
129 void
130 ntp_readline_uninit(
131 	void
132 	)
133 {
134 #ifdef LE_EDITLINE
135 	if (ntp_el) {
136 		el_end(ntp_el);
137 		ntp_el = NULL;
138 
139 		history_end(ntp_hist);
140 		ntp_hist = NULL;
141 	}
142 #endif	/* LE_EDITLINE */
143 
144 	if (lineedit_prompt) {
145 		free(lineedit_prompt);
146 		lineedit_prompt = NULL;
147 	}
148 
149 	ntp_readline_initted = 0;
150 }
151 
152 
153 /*
154  * ntp_readline - read a line with the line editor available
155  *
156  * The string returned must be released with free()
157  */
158 
159 char *
160 ntp_readline(
161 	int *	pcount
162 	)
163 {
164 	char *		line;
165 #ifdef LE_NONE
166 	char		line_buf[MAXEDITLINE];
167 #endif
168 #ifdef LE_EDITLINE
169 	const char *	cline;
170 #endif
171 
172 	if (!ntp_readline_initted)
173 		return NULL;
174 
175 	*pcount = 0;
176 
177 #ifdef LE_READLINE
178 	line = readline(lineedit_prompt ? lineedit_prompt : "");
179 	if (NULL != line) {
180 		if (*line) {
181 			add_history(line);
182 		}
183 		*pcount = strlen(line);
184 	}
185 #endif	/* LE_READLINE */
186 
187 #ifdef LE_EDITLINE
188 	cline = el_gets(ntp_el, pcount);
189 
190 	if (NULL != cline) {
191 		history(ntp_hist, &hev, H_ENTER, cline);
192 		line = estrdup(cline);
193 	} else if (*pcount == -1) {
194 		line = NULL;
195 	} else {
196 		line = estrdup("");
197 	}
198 #endif	/* LE_EDITLINE */
199 
200 #ifdef LE_NONE
201 					/* stone hammers */
202 	if (lineedit_prompt) {
203 # ifdef VMS
204 			/*
205 			 * work around problem mixing
206 			 * stdout & stderr
207 			 */
208 			fputs("", stdout);
209 # endif	/* VMS */
210 
211 		fputs(lineedit_prompt, stderr);
212 		fflush(stderr);
213 	}
214 
215 	line = fgets(line_buf, sizeof(line_buf), stdin);
216 	if (NULL != line && *line) {
217 		*pcount = (int)strlen(line); /* cannot overflow here */
218 		line = estrdup(line);
219 	} else
220 		line = NULL;
221 
222 #endif	/* LE_NONE */
223 
224 
225 	if (!line)			/* EOF */
226 		fputs("\n", stderr);
227 
228 	return line;
229 }
230 
231 
232 #ifdef LE_EDITLINE
233 /*
234  * ntp_prompt_callback - return prompt string to el_gets()
235  */
236 char *
237 ntp_prompt_callback(
238 	EditLine *el
239 	)
240 {
241 	UNUSED_ARG(el);
242 
243 	return lineedit_prompt;
244 }
245 #endif /* LE_EDITLINE */
246 
247