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