1 //===-- EditLineWin.cpp ---------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 // this file is only relevant for Visual C++
10 #if defined(_WIN32)
11
12 #include "lldb/Host/windows/windows.h"
13
14 #include "lldb/Host/windows/editlinewin.h"
15 #include "llvm/Support/ErrorHandling.h"
16 #include <assert.h>
17 #include <vector>
18
19 // edit line EL_ADDFN function pointer type
20 typedef unsigned char (*el_addfn_func)(EditLine *e, int ch);
21 typedef const char *(*el_prompt_func)(EditLine *);
22
23 // edit line wrapper binding container
24 struct el_binding {
25 //
26 const char *name;
27 const char *help;
28 // function pointer to callback routine
29 el_addfn_func func;
30 // ascii key this function is bound to
31 const char *key;
32 };
33
34 // stored key bindings
35 static std::vector<el_binding *> _bindings;
36
37 // TODO: this should in fact be related to the exact edit line context we create
38 static void *clientData = NULL;
39
40 // store the current prompt string
41 // default to what we expect to receive anyway
42 static const char *_prompt = "(lldb) ";
43
44 #if !defined(_WIP_INPUT_METHOD)
45
el_get_s(char * buffer,int chars)46 static char *el_get_s(char *buffer, int chars) { return gets_s(buffer, chars); }
47 #else
48
con_output(char _in)49 static void con_output(char _in) {
50 HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);
51 DWORD written = 0;
52 // get the cursor position
53 CONSOLE_SCREEN_BUFFER_INFO info;
54 GetConsoleScreenBufferInfo(hout, &info);
55 // output this char
56 WriteConsoleOutputCharacterA(hout, &_in, 1, info.dwCursorPosition, &written);
57 // advance cursor position
58 info.dwCursorPosition.X++;
59 SetConsoleCursorPosition(hout, info.dwCursorPosition);
60 }
61
con_backspace(void)62 static void con_backspace(void) {
63 HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);
64 DWORD written = 0;
65 // get cursor position
66 CONSOLE_SCREEN_BUFFER_INFO info;
67 GetConsoleScreenBufferInfo(hout, &info);
68 // nudge cursor backwards
69 info.dwCursorPosition.X--;
70 SetConsoleCursorPosition(hout, info.dwCursorPosition);
71 // blank out the last character
72 WriteConsoleOutputCharacterA(hout, " ", 1, info.dwCursorPosition, &written);
73 }
74
con_return(void)75 static void con_return(void) {
76 HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);
77 DWORD written = 0;
78 // get cursor position
79 CONSOLE_SCREEN_BUFFER_INFO info;
80 GetConsoleScreenBufferInfo(hout, &info);
81 // move onto the new line
82 info.dwCursorPosition.X = 0;
83 info.dwCursorPosition.Y++;
84 SetConsoleCursorPosition(hout, info.dwCursorPosition);
85 }
86
runBind(char _key)87 static bool runBind(char _key) {
88 for (int i = 0; i < _bindings.size(); i++) {
89 el_binding *bind = _bindings[i];
90 if (bind->key[0] == _key) {
91 bind->func((EditLine *)-1, _key);
92 return true;
93 }
94 }
95 return false;
96 }
97
98 // replacement get_s which is EL_BIND aware
el_get_s(char * buffer,int chars)99 static char *el_get_s(char *buffer, int chars) {
100 //
101 char *head = buffer;
102 //
103 for (;; Sleep(10)) {
104 //
105 INPUT_RECORD _record;
106 //
107 DWORD _read = 0;
108 if (ReadConsoleInputA(GetStdHandle(STD_INPUT_HANDLE), &_record, 1,
109 &_read) == FALSE)
110 break;
111 // if we didn't read a key
112 if (_read == 0)
113 continue;
114 // only interested in key events
115 if (_record.EventType != KEY_EVENT)
116 continue;
117 // is the key down
118 if (!_record.Event.KeyEvent.bKeyDown)
119 continue;
120 // read the ascii key character
121 char _key = _record.Event.KeyEvent.uChar.AsciiChar;
122 // non ascii conformant key press
123 if (_key == 0) {
124 // check the scan code
125 // if VK_UP scroll back through history
126 // if VK_DOWN scroll forward through history
127 continue;
128 }
129 // try to execute any bind this key may have
130 if (runBind(_key))
131 continue;
132 // if we read a return key
133 if (_key == '\n' || _key == '\r') {
134 con_return();
135 break;
136 }
137 // key is backspace
138 if (_key == 0x8) {
139 // avoid deleting past beginning
140 if (head > buffer) {
141 con_backspace();
142 head--;
143 }
144 continue;
145 }
146
147 // add this key to the input buffer
148 if ((head - buffer) < (chars - 1)) {
149 con_output(_key);
150 *(head++) = _key;
151 }
152 }
153 // insert end of line character
154 *head = '\0';
155
156 return buffer;
157 }
158 #endif
159
160 // edit line initialize
el_init(const char *,FILE *,FILE *,FILE *)161 EditLine *el_init(const char *, FILE *, FILE *, FILE *) {
162 //
163 SetConsoleTitleA("lldb");
164 // return dummy handle
165 return (EditLine *)-1;
166 }
167
el_gets(EditLine * el,int * length)168 const char *el_gets(EditLine *el, int *length) {
169 // print the prompt if we have one
170 if (_prompt != NULL)
171 printf("%s", _prompt);
172 // create a buffer for the user input
173 char *buffer = new char[MAX_PATH];
174 // try to get user input string
175 if (el_get_s(buffer, MAX_PATH)) {
176 // get the string length in 'length'
177 while (buffer[*length] != '\0')
178 (*length)++;
179 // return the input buffer
180 // remember that this memory has the be free'd somewhere
181 return buffer;
182 } else {
183 // on error
184 delete[] buffer;
185 return NULL;
186 }
187 }
188
el_set(EditLine * el,int code,...)189 int el_set(EditLine *el, int code, ...) {
190 va_list vl;
191 va_start(vl, code);
192 //
193 switch (code) {
194 // edit line set prompt message
195 case (EL_PROMPT): {
196 // EL_PROMPT, char *(*f)( EditLine *)
197 // define a prompt printing function as 'f', which is to return a
198 // string that
199 // contains the prompt.
200
201 // get the function pointer from the arg list
202 void *func_vp = (void *)va_arg(vl, el_prompt_func);
203 // cast to suitable prototype
204 el_prompt_func func_fp = (el_prompt_func)func_vp;
205 // call to get the prompt as a string
206 _prompt = func_fp(el);
207 } break;
208
209 case (EL_PROMPT_ESC): {
210 // EL_PROMPT, char *(*f)( EditLine *)
211 // define a prompt printing function as 'f', which is to return a
212 // string that
213 // contains the prompt.
214
215 // get the function pointer from the arg list
216 void *func_vp = (void *)va_arg(vl, el_prompt_func);
217 va_arg(vl, int);
218 // call to get the prompt as a string
219 el_prompt_func func_fp = (el_prompt_func)func_vp;
220 _prompt = func_fp(el);
221 } break;
222
223 case (EL_EDITOR): {
224 // EL_EDITOR, const char *mode
225 // set editing mode to "emacs" or "vi"
226 } break;
227 case (EL_HIST): {
228 // EL_HIST, History *(*fun)(History *, int op, ... ), const char *ptr
229 // defines which history function to use, which is usually history().
230 // Ptr should be the
231 // value returned by history_init().
232 } break;
233 case (EL_ADDFN): {
234 // EL_ADDFN, const char *name, const char *help, unsigned char
235 // (*func)(EditLine *e, int ch)
236 // add a user defined function, func), referred to as 'name' which is
237 // invoked when a key which is bound to 'name' is
238 // entered. 'help' is a description of 'name'. at invocation time, 'ch'
239 // is the key which caused the invocation. the
240 // return value of 'func()' should be one of:
241 // CC_NORM add a normal character
242 // CC_NEWLINE end of line was entered
243 // CC_EOF EOF was entered
244 // CC_ARGHACK expecting further command input as arguments, do
245 // nothing visually.
246 // CC_REFRESH refresh display.
247 // CC_REFRESH_BEEP refresh display and beep.
248 // CC_CURSOR cursor moved so update and perform CC_REFRESH
249 // CC_REDISPLAY redisplay entire input line. this is useful
250 // if a key binding outputs extra information.
251 // CC_ERROR an error occurred. beep and flush tty.
252 // CC_FATAL fatal error, reset tty to known state.
253
254 el_binding *binding = new el_binding;
255 binding->name = va_arg(vl, const char *);
256 binding->help = va_arg(vl, const char *);
257 binding->func = va_arg(vl, el_addfn_func);
258 binding->key = 0;
259 // add this to the bindings list
260 _bindings.push_back(binding);
261 } break;
262 case (EL_BIND): {
263 // EL_BIND, const char *, ..., NULL
264 // perform the BIND built-in command. Refer to editrc(5) for more
265 // information.
266
267 const char *name = va_arg(vl, const char *);
268
269 for (auto bind : _bindings) {
270 if (strcmp(bind->name, name) == 0) {
271 bind->key = va_arg(vl, const char *);
272 break;
273 }
274 }
275
276 } break;
277 case (EL_CLIENTDATA): {
278 clientData = va_arg(vl, void *);
279 } break;
280 }
281 return 0;
282 }
283
el_end(EditLine * el)284 void el_end(EditLine *el) {
285 // assert( !"Not implemented!" );
286 }
287
el_reset(EditLine *)288 void el_reset(EditLine *) { llvm_unreachable("Not implemented!"); }
289
el_getc(EditLine *,char *)290 int el_getc(EditLine *, char *) {
291 llvm_unreachable("Not implemented!");
292 }
293
el_push(EditLine *,const char *)294 void el_push(EditLine *, const char *) {}
295
el_beep(EditLine *)296 void el_beep(EditLine *) { Beep(1000, 500); }
297
el_parse(EditLine *,int,const char **)298 int el_parse(EditLine *, int, const char **) {
299 llvm_unreachable("Not implemented!");
300 }
301
el_get(EditLine * el,int code,...)302 int el_get(EditLine *el, int code, ...) {
303 va_list vl;
304 va_start(vl, code);
305
306 switch (code) {
307 case (EL_CLIENTDATA): {
308 void **dout = va_arg(vl, void **);
309 *dout = clientData;
310 } break;
311 default:
312 llvm_unreachable("Not implemented!");
313 }
314 return 0;
315 }
316
el_source(EditLine * el,const char * file)317 int el_source(EditLine *el, const char *file) {
318 // init edit line by reading the contents of 'file' nothing to do here on
319 // windows...
320 return 0;
321 }
322
el_resize(EditLine *)323 void el_resize(EditLine *) { llvm_unreachable("Not implemented!"); }
324
el_line(EditLine * el)325 const LineInfo *el_line(EditLine *el) { return 0; }
326
el_insertstr(EditLine *,const char *)327 int el_insertstr(EditLine *, const char *) {
328 // assert( !"Not implemented!" );
329 return 0;
330 }
331
el_deletestr(EditLine *,int)332 void el_deletestr(EditLine *, int) { llvm_unreachable("Not implemented!"); }
333
history_init(void)334 History *history_init(void) {
335 // return dummy handle
336 return (History *)-1;
337 }
338
history_end(History *)339 void history_end(History *) {
340 // assert( !"Not implemented!" );
341 }
342
history(History *,HistEvent *,int op,...)343 int history(History *, HistEvent *, int op, ...) {
344 // perform operation 'op' on the history list with optional arguments as
345 // needed by the operation.
346 return 0;
347 }
348
349 #endif
350