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