- Fixed the check for printable ascii characters.
[reactos.git] / reactos / subsys / system / cmd / cmdinput.c
1 /*
2 * CMDINPUT.C - handles command input (tab completion, history, etc.).
3 *
4 *
5 * History:
6 *
7 * 01/14/95 (Tim Norman)
8 * started.
9 *
10 * 08/08/95 (Matt Rains)
11 * i have cleaned up the source code. changes now bring this source
12 * into guidelines for recommended programming practice.
13 * i have added some constants to help making changes easier.
14 *
15 * 12/12/95 (Tim Norman)
16 * added findxy() function to get max x/y coordinates to display
17 * correctly on larger screens
18 *
19 * 12/14/95 (Tim Norman)
20 * fixed the Tab completion code that Matt Rains broke by moving local
21 * variables to a more global scope and forgetting to initialize them
22 * when needed
23 *
24 * 8/1/96 (Tim Norman)
25 * fixed a bug in tab completion that caused filenames at the beginning
26 * of the command-line to have their first letter truncated
27 *
28 * 9/1/96 (Tim Norman)
29 * fixed a silly bug using printf instead of fputs, where typing "%i"
30 * confused printf :)
31 *
32 * 6/14/97 (Steffan Kaiser)
33 * ctrl-break checking
34 *
35 * 6/7/97 (Marc Desrochers)
36 * recoded everything! now properly adjusts when text font is changed.
37 * removed findxy(), reposition(), and reprint(), as these functions
38 * were inefficient. added goxy() function as gotoxy() was buggy when
39 * the screen font was changed. the printf() problem with %i on the
40 * command line was fixed by doing printf("%s",str) instead of
41 * printf(str). Don't ask how I find em just be glad I do :)
42 *
43 * 7/12/97 (Tim Norman)
44 * Note: above changes pre-empted Steffan's ctrl-break checking.
45 *
46 * 7/7/97 (Marc Desrochers)
47 * rewrote a new findxy() because the new dir() used it. This
48 * findxy() simply returns the values of *maxx *maxy. In the
49 * future, please use the pointers, they will always be correct
50 * since they point to BIOS values.
51 *
52 * 7/8/97 (Marc Desrochers)
53 * once again removed findxy(), moved the *maxx, *maxy pointers
54 * global and included them as externs in command.h. Also added
55 * insert/overstrike capability
56 *
57 * 7/13/97 (Tim Norman)
58 * added different cursor appearance for insert/overstrike mode
59 *
60 * 7/13/97 (Tim Norman)
61 * changed my code to use _setcursortype until I can figure out why
62 * my code is crashing on some machines. It doesn't crash on mine :)
63 *
64 * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
65 * added config.h include
66 *
67 * 28-Jul-1998 (John P Price <linux-guru@gcfl.net>)
68 * put ifdef's around filename completion code.
69 *
70 * 30-Jul-1998 (John P Price <linux-guru@gcfl.net>)
71 * moved filename completion code to filecomp.c
72 * made second TAB display list of filename matches
73 *
74 * 31-Jul-1998 (John P Price <linux-guru@gcfl.net>)
75 * Fixed bug where if you typed something, then hit HOME, then tried
76 * to type something else in insert mode, it crashed.
77 *
78 * 07-Aug-1998 (John P Price <linux-guru@gcfl.net>)
79 * Fixed carrage return output to better match MSDOS with echo
80 * on or off.(marked with "JPP 19980708")
81 *
82 * 13-Dec-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
83 * Added insert/overwrite cursor.
84 *
85 * 25-Jan-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
86 * Replaced CRT io functions by Win32 console io functions.
87 * This can handle <Shift>-<Tab> for 4NT filename completion.
88 * Unicode and redirection safe!
89 *
90 * 04-Feb-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
91 * Fixed input bug. A "line feed" character remained in the keyboard
92 * input queue when you pressed <RETURN>. This sometimes caused
93 * some very strange effects.
94 * Fixed some command line editing annoyances.
95 */
96
97 #include "config.h"
98
99 #include <windows.h>
100 #include <tchar.h>
101 #include <string.h>
102
103 #include "cmd.h"
104 #include "batch.h"
105
106
107 SHORT maxx;
108 SHORT maxy;
109
110 /*
111 * global command line insert/overwrite flag
112 */
113 static BOOL bInsert = TRUE;
114
115
116 static VOID
117 ClearCommandLine (LPTSTR str, INT maxlen, SHORT orgx, SHORT orgy)
118 {
119 INT count;
120
121 SetCursorXY (orgx, orgy);
122 for (count = 0; count < (INT)_tcslen (str); count++)
123 ConOutChar (_T(' '));
124 _tcsnset (str, _T('\0'), maxlen);
125 SetCursorXY (orgx, orgy);
126 }
127
128
129 /* read in a command line */
130 VOID ReadCommand (LPTSTR str, INT maxlen)
131 {
132 SHORT orgx; /* origin x/y */
133 SHORT orgy;
134 SHORT curx; /*current x/y cursor position*/
135 SHORT cury;
136 INT count; /*used in some for loops*/
137 INT current = 0; /*the position of the cursor in the string (str)*/
138 INT charcount = 0;/*chars in the string (str)*/
139 INPUT_RECORD ir;
140 WORD wLastKey = 0;
141 TCHAR ch;
142 BOOL bContinue=FALSE;/*is TRUE the second case will not be executed*/
143
144 /* get screen size */
145 GetScreenSize (&maxx, &maxy);
146
147 /* JPP 19980807 - if echo off, don't print prompt */
148 if (bEcho)
149 PrintPrompt();
150
151 GetCursorXY (&orgx, &orgy);
152
153 memset (str, 0, maxlen * sizeof (TCHAR));
154
155 SetCursorType (bInsert, TRUE);
156
157 do
158 {
159 ConInKey (&ir);
160
161 if (ir.Event.KeyEvent.dwControlKeyState &
162 (RIGHT_ALT_PRESSED|RIGHT_ALT_PRESSED|
163 RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED) )
164 {
165
166 switch (ir.Event.KeyEvent.wVirtualKeyCode)
167 {
168
169 #ifdef FEATURE_HISTORY
170
171 case 'K':
172 /*add the current command line to the history*/
173 if (ir.Event.KeyEvent.dwControlKeyState &
174 (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED))
175 {
176
177 if (str[0])
178 History(0,str);
179
180 ClearCommandLine (str, maxlen, orgx, orgy);
181 current = charcount = 0;
182 bContinue=TRUE;
183 break;
184 }
185
186 case 'D':
187 /*delete current history entry*/
188 if (ir.Event.KeyEvent.dwControlKeyState &
189 (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED))
190 {
191 ClearCommandLine (str, maxlen, orgx, orgy);
192 History_del_current_entry(str);
193 current = charcount = _tcslen (str);
194 ConOutPrintf (_T("%s"), str);
195 bContinue=TRUE;
196 break;
197 }
198
199 #endif/*FEATURE_HISTORY*/
200 }
201
202
203
204
205 }
206
207 //if (bContinue)
208 // continue;
209
210
211
212 switch (ir.Event.KeyEvent.wVirtualKeyCode)
213 {
214 case VK_BACK:
215 /* <BACKSPACE> - delete character to left of cursor */
216 if (current > 0 && charcount > 0)
217 {
218 if (current == charcount)
219 {
220 /* if at end of line */
221 str[current - 1] = _T('\0');
222 if (GetCursorX () != 0)
223 {
224 ConOutPrintf (_T("\b \b"));
225 }
226 else
227 {
228 SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1));
229 ConOutChar (_T(' '));
230 SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1));
231 }
232 }
233 else
234 {
235 for (count = current - 1; count < charcount; count++)
236 str[count] = str[count + 1];
237 if (GetCursorX () != 0)
238 SetCursorXY ((SHORT)(GetCursorX () - 1), GetCursorY ());
239 else
240 SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1));
241 GetCursorXY (&curx, &cury);
242 ConOutPrintf (_T("%s "), &str[current - 1]);
243 SetCursorXY (curx, cury);
244 }
245 charcount--;
246 current--;
247 }
248 break;
249
250 case VK_INSERT:
251 /* toggle insert/overstrike mode */
252 bInsert ^= TRUE;
253 SetCursorType (bInsert, TRUE);
254 break;
255
256 case VK_DELETE:
257 /* delete character under cursor */
258 if (current != charcount && charcount > 0)
259 {
260 for (count = current; count < charcount; count++)
261 str[count] = str[count + 1];
262 charcount--;
263 GetCursorXY (&curx, &cury);
264 ConOutPrintf (_T("%s "), &str[current]);
265 SetCursorXY (curx, cury);
266 }
267 break;
268
269 case VK_HOME:
270 /* goto beginning of string */
271 if (current != 0)
272 {
273 SetCursorXY (orgx, orgy);
274 current = 0;
275 }
276 break;
277
278 case VK_END:
279 /* goto end of string */
280 if (current != charcount)
281 {
282 SetCursorXY (orgx, orgy);
283 ConOutPrintf (_T("%s"), str);
284 current = charcount;
285 }
286 break;
287
288 case VK_TAB:
289 #ifdef FEATURE_UNIX_FILENAME_COMPLETION
290 /* expand current file name */
291 if ((current == charcount) ||
292 (current == charcount - 1 &&
293 str[current] == _T('"'))) /* only works at end of line*/
294 {
295 if (wLastKey != VK_TAB)
296 {
297 /* if first TAB, complete filename*/
298 CompleteFilename (str, charcount);
299 charcount = _tcslen (str);
300 current = charcount;
301
302 if (current > 0 &&
303 str[current-1] == _T('"'))
304 current--;
305
306 SetCursorXY (orgx, orgy);
307 ConOutPrintf (_T("%s"), str);
308 if ((charcount > (USHORT)(maxx - orgx)) && (orgy == maxy + 1))
309 orgy--;
310
311 /* set cursor position */
312 SetCursorXY ((orgx + current) % maxx,
313 orgy + (orgx + current) / maxx);
314 }
315 else
316 {
317 /*if second TAB, list matches*/
318 if (ShowCompletionMatches (str, charcount))
319 {
320 PrintPrompt ();
321 GetCursorXY (&orgx, &orgy);
322 ConOutPrintf (_T("%s"), str);
323
324 /* set cursor position */
325 SetCursorXY ((orgx + current) % maxx,
326 orgy + (orgx + current) / maxx);
327 }
328
329 }
330 }
331 else
332 {
333 #ifdef __REACTOS__
334 Beep (440, 50);
335 #else
336 MessageBeep (-1);
337 #endif
338 }
339 #endif
340 #ifdef FEATURE_4NT_FILENAME_COMPLETION
341 /* this is not implemented yet */
342 if (ir.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED)
343 {
344 /* get previous match */
345
346 }
347 else
348 {
349 /* get next match */
350
351 }
352 #endif
353 break;
354
355 case VK_RETURN:
356 /* end input, return to main */
357 #ifdef FEATURE_HISTORY
358 /* add to the history */
359 if (str[0])
360 History (0, str);
361 #endif
362 ConInDummy ();
363 ConOutChar (_T('\n'));
364 break;
365
366 case VK_ESCAPE:
367 /* clear str Make this callable! */
368 ClearCommandLine (str, maxlen, orgx, orgy);
369 current = charcount = 0;
370 break;
371
372 #ifdef FEATURE_HISTORY
373 case VK_F3:
374 History_move_to_bottom();
375 #endif
376 case VK_UP:
377 #ifdef FEATURE_HISTORY
378 /* get previous command from buffer */
379 ClearCommandLine (str, maxlen, orgx, orgy);
380 History (-1, str);
381 current = charcount = _tcslen (str);
382 ConOutPrintf (_T("%s"), str);
383 #endif
384 break;
385
386 case VK_DOWN:
387 #ifdef FEATURE_HISTORY
388 /* get next command from buffer */
389 ClearCommandLine (str, maxlen, orgx, orgy);
390 History (1, str);
391 current = charcount = _tcslen (str);
392 ConOutPrintf (_T("%s"), str);
393 #endif
394 break;
395
396 case VK_LEFT:
397 /* move cursor left */
398 if (current > 0)
399 {
400 current--;
401 if (GetCursorX () == 0)
402 SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1));
403 else
404 SetCursorXY ((SHORT)(GetCursorX () - 1), GetCursorY ());
405 }
406 else
407 {
408 #ifdef __REACTOS__
409 Beep (440, 50);
410 #else
411 MessageBeep (-1);
412 #endif
413 }
414 break;
415
416 case VK_RIGHT:
417 /* move cursor right */
418 if (current != charcount)
419 {
420 current++;
421 if (GetCursorX () == maxx - 1)
422 SetCursorXY (0, (SHORT)(GetCursorY () + 1));
423 else
424 SetCursorXY ((SHORT)(GetCursorX () + 1), GetCursorY ());
425 }
426 break;
427
428 #if 0
429
430 #ifdef FEATURE_HISTORY
431
432
433 /*!!!WARNING!!!*/
434 /*this will only work as long as the two if statement
435 evaluates the same expression and a break is included
436 in each if statement.
437 This can be used for any combination using CTRL.
438 For other combinations is needed another system*/
439
440 case 'K':
441 /*add the current command line to the history*/
442 if (ir.Event.KeyEvent.dwControlKeyState &
443 (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED))
444 {
445
446 if (str[0])
447 History(0,str);
448
449 ClearCommandLine (str, maxlen, orgx, orgy);
450 current = charcount = 0;
451 break;
452 }
453
454 case 'D':
455 if (ir.Event.KeyEvent.dwControlKeyState &
456 (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED))
457 {
458 ClearCommandLine (str, maxlen, orgx, orgy);
459 History_del_current_entry(str);
460 current = charcount = _tcslen (str);
461 ConOutPrintf (_T("%s"), str);
462 break;
463 }
464
465 #endif/*FEATURE_HISTORY*/
466 #endif/*0*/
467
468 default:
469 #ifdef _UNICODE
470 ch = ir.Event.KeyEvent.uChar.UnicodeChar;
471 if ((ch >= 32 && ch <= 255) && (charcount != (maxlen - 2)))
472 #else
473 ch = ir.Event.KeyEvent.uChar.AsciiChar;
474 if ((UCHAR)ch >= 32 && (charcount != (maxlen - 2)))
475 #endif /* _UNICODE */
476 {
477 /* insert character into string... */
478 if (bInsert && current != charcount)
479 {
480 for (count = charcount; count > current; count--)
481 str[count] = str[count - 1];
482 str[current++] = ch;
483 if (GetCursorX () == maxx - 1)
484 {
485 curx = 0;
486 cury = GetCursorY () + 1;
487 }
488 else
489 {
490 GetCursorXY (&curx, &cury);
491 curx++;
492 }
493 ConOutPrintf (_T("%s"), &str[current - 1]);
494 if ((_tcslen (str) > (USHORT)(maxx - orgx)) && (orgy == maxy + 1))
495 cury--;
496 SetCursorXY (curx, cury);
497 charcount++;
498 }
499 else
500 {
501 if (current == charcount)
502 charcount++;
503 str[current++] = ch;
504 ConOutChar (ch);
505 }
506 if ((_tcslen (str) > (USHORT)(maxx - orgx)) && (orgy == maxy + 1))
507 orgy--;
508 }
509 #if 0
510 else
511 {
512 #ifdef __REACTOS__
513 Beep (440, 100);
514 #else
515 MessageBeep (-1);
516 #endif
517 }
518 #endif
519 break;
520
521 }
522 wLastKey = ir.Event.KeyEvent.wVirtualKeyCode;
523 }
524 while (ir.Event.KeyEvent.wVirtualKeyCode != VK_RETURN);
525
526 SetCursorType (bInsert, TRUE);
527 }