2 * CMDINPUT.C - handles command input (tab completion, history, etc.).
7 * 01/14/95 (Tim Norman)
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.
15 * 12/12/95 (Tim Norman)
16 * added findxy() function to get max x/y coordinates to display
17 * correctly on larger screens
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
25 * fixed a bug in tab completion that caused filenames at the beginning
26 * of the command-line to have their first letter truncated
29 * fixed a silly bug using printf instead of fputs, where typing "%i"
32 * 6/14/97 (Steffan Kaiser)
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 :)
43 * 7/12/97 (Tim Norman)
44 * Note: above changes preempted Steffan's ctrl-break checking.
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.
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
57 * 7/13/97 (Tim Norman)
58 * added different cursor appearance for insert/overstrike mode
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 :)
64 * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
65 * added config.h include
67 * 28-Jul-1998 (John P Price <linux-guru@gcfl.net>)
68 * put ifdef's around filename completion code.
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
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.
78 * 07-Aug-1998 (John P Price <linux-guru@gcfl.net>)
79 * Fixed carriage return output to better match MSDOS with echo
80 * on or off.(marked with "JPP 19980708")
82 * 13-Dec-1998 (Eric Kohl)
83 * Added insert/overwrite cursor.
85 * 25-Jan-1998 (Eric Kohl)
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!
90 * 04-Feb-1999 (Eric Kohl)
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.
96 * 30-Apr-2004 (Filip Navara <xnavara@volny.cz>)
97 * Fixed problems when the screen was scrolled away.
99 * 28-September-2007 (Hervé Poussineau)
100 * Added history possibilities to right key.
106 * See https://technet.microsoft.com/en-us/library/cc978715.aspx
107 * and https://technet.microsoft.com/en-us/library/cc940805.aspx
108 * to know the differences between those two settings.
109 * Values 0x00, 0x0D (carriage return) and 0x20 (space) disable completion.
111 TCHAR AutoCompletionChar
= _T('\t'); // Default is 0x20
112 TCHAR PathCompletionChar
= _T('\t'); // Default is 0x20
119 * global command line insert/overwrite flag
121 static BOOL bInsert
= TRUE
;
125 ClearCommandLine(LPTSTR str
, INT maxlen
, SHORT orgx
, SHORT orgy
)
129 SetCursorXY (orgx
, orgy
);
130 for (count
= 0; count
< (INT
)_tcslen (str
); count
++)
131 ConOutChar (_T(' '));
132 _tcsnset (str
, _T('\0'), maxlen
);
133 SetCursorXY (orgx
, orgy
);
137 /* read in a command line */
138 BOOL
ReadCommand(LPTSTR str
, INT maxlen
)
140 CONSOLE_SCREEN_BUFFER_INFO csbi
;
141 SHORT orgx
; /* origin x/y */
143 SHORT curx
; /*current x/y cursor position*/
146 INT count
; /*used in some for loops*/
147 INT current
= 0; /*the position of the cursor in the string (str)*/
148 INT charcount
= 0;/*chars in the string (str)*/
150 DWORD dwControlKeyState
;
151 #ifdef FEATURE_UNIX_FILENAME_COMPLETION
155 BOOL bReturn
= FALSE
;
157 #ifdef FEATURE_4NT_FILENAME_COMPLETION
158 TCHAR szPath
[MAX_PATH
];
160 #ifdef FEATURE_HISTORY
161 //BOOL bContinue=FALSE;/*is TRUE the second case will not be executed*/
165 if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE
), &csbi
))
168 HANDLE hStdin
= GetStdHandle(STD_INPUT_HANDLE
);
173 if (!ReadFile(hStdin
, &chr
, 1, &dwRead
, NULL
) || !dwRead
)
176 MultiByteToWideChar(InputCodePage
, 0, &chr
, 1, &str
[charcount
++], 1);
178 } while (chr
!= '\n' && charcount
< maxlen
);
179 str
[charcount
] = _T('\0');
183 /* get screen size */
184 maxx
= csbi
.dwSize
.X
;
185 maxy
= csbi
.dwSize
.Y
;
187 curx
= orgx
= csbi
.dwCursorPosition
.X
;
188 cury
= orgy
= csbi
.dwCursorPosition
.Y
;
190 memset (str
, 0, maxlen
* sizeof (TCHAR
));
192 SetCursorType (bInsert
, TRUE
);
199 dwControlKeyState
= ir
.Event
.KeyEvent
.dwControlKeyState
;
201 if (dwControlKeyState
&
202 (RIGHT_ALT_PRESSED
|LEFT_ALT_PRESSED
|
203 RIGHT_CTRL_PRESSED
|LEFT_CTRL_PRESSED
) )
205 switch (ir
.Event
.KeyEvent
.wVirtualKeyCode
)
207 #ifdef FEATURE_HISTORY
209 /* add the current command line to the history */
210 if (dwControlKeyState
&
211 (LEFT_CTRL_PRESSED
|RIGHT_CTRL_PRESSED
))
216 ClearCommandLine (str
, maxlen
, orgx
, orgy
);
217 current
= charcount
= 0;
225 /* delete current history entry */
226 if (dwControlKeyState
&
227 (LEFT_CTRL_PRESSED
|RIGHT_CTRL_PRESSED
))
229 ClearCommandLine (str
, maxlen
, orgx
, orgy
);
230 History_del_current_entry(str
);
231 current
= charcount
= _tcslen (str
);
232 ConOutPrintf (_T("%s"), str
);
233 GetCursorXY (&curx
, &cury
);
237 #endif /*FEATURE_HISTORY*/
240 /* ^M does the same as return */
241 if (dwControlKeyState
&
242 (LEFT_CTRL_PRESSED
|RIGHT_CTRL_PRESSED
))
244 /* end input, return to main */
245 #ifdef FEATURE_HISTORY
246 /* add to the history */
249 #endif /*FEATURE_HISTORY*/
250 str
[charcount
++] = _T('\n');
251 str
[charcount
] = _T('\0');
252 ConOutChar (_T('\n'));
261 switch (ir
.Event
.KeyEvent
.wVirtualKeyCode
)
264 /* <BACKSPACE> - delete character to left of cursor */
265 if (current
> 0 && charcount
> 0)
267 if (current
== charcount
)
269 /* if at end of line */
270 str
[current
- 1] = _T('\0');
271 if (GetCursorX () != 0)
273 ConOutPrintf (_T("\b \b"));
278 SetCursorXY ((SHORT
)(maxx
- 1), (SHORT
)(GetCursorY () - 1));
279 ConOutChar (_T(' '));
280 SetCursorXY ((SHORT
)(maxx
- 1), (SHORT
)(GetCursorY () - 1));
287 for (count
= current
- 1; count
< charcount
; count
++)
288 str
[count
] = str
[count
+ 1];
289 if (GetCursorX () != 0)
291 SetCursorXY ((SHORT
)(GetCursorX () - 1), GetCursorY ());
296 SetCursorXY ((SHORT
)(maxx
- 1), (SHORT
)(GetCursorY () - 1));
300 GetCursorXY (&curx
, &cury
);
301 ConOutPrintf (_T("%s "), &str
[current
- 1]);
302 SetCursorXY (curx
, cury
);
310 /* toggle insert/overstrike mode */
312 SetCursorType (bInsert
, TRUE
);
316 /* delete character under cursor */
317 if (current
!= charcount
&& charcount
> 0)
319 for (count
= current
; count
< charcount
; count
++)
320 str
[count
] = str
[count
+ 1];
322 GetCursorXY (&curx
, &cury
);
323 ConOutPrintf (_T("%s "), &str
[current
]);
324 SetCursorXY (curx
, cury
);
329 /* goto beginning of string */
332 SetCursorXY (orgx
, orgy
);
340 /* goto end of string */
341 if (current
!= charcount
)
343 SetCursorXY (orgx
, orgy
);
344 ConOutPrintf (_T("%s"), str
);
345 GetCursorXY (&curx
, &cury
);
351 #ifdef FEATURE_UNIX_FILENAME_COMPLETION
352 /* expand current file name */
353 if ((current
== charcount
) ||
354 (current
== charcount
- 1 &&
355 str
[current
] == _T('"'))) /* only works at end of line*/
357 if (wLastKey
!= VK_TAB
)
359 /* if first TAB, complete filename*/
360 tempscreen
= charcount
;
361 CompleteFilename (str
, charcount
);
362 charcount
= _tcslen (str
);
365 SetCursorXY (orgx
, orgy
);
366 ConOutPrintf (_T("%s"), str
);
368 if (tempscreen
> charcount
)
370 GetCursorXY (&curx
, &cury
);
371 for (count
= tempscreen
- charcount
; count
--; )
372 ConOutChar (_T(' '));
373 SetCursorXY (curx
, cury
);
377 if (((charcount
+ orgx
) / maxx
) + orgy
> maxy
- 1)
378 orgy
+= maxy
- ((charcount
+ orgx
) / maxx
+ orgy
+ 1);
381 /* set cursor position */
382 SetCursorXY ((orgx
+ current
) % maxx
,
383 orgy
+ (orgx
+ current
) / maxx
);
384 GetCursorXY (&curx
, &cury
);
388 /*if second TAB, list matches*/
389 if (ShowCompletionMatches (str
, charcount
))
392 GetCursorXY(&orgx
, &orgy
);
393 ConOutPrintf(_T("%s"), str
);
395 /* set cursor position */
396 SetCursorXY((orgx
+ current
) % maxx
,
397 orgy
+ (orgx
+ current
) / maxx
);
398 GetCursorXY(&curx
, &cury
);
408 #ifdef FEATURE_4NT_FILENAME_COMPLETION
409 /* used to later see if we went down to the next line */
410 tempscreen
= charcount
;
413 /* str is the whole things that is on the current line
414 that is and and out. arg 2 is weather it goes back
415 one file or forward one file */
416 CompleteFilename(str
, !(ir
.Event
.KeyEvent
.dwControlKeyState
& SHIFT_PRESSED
), szPath
, current
);
417 /* Attempt to clear the line */
418 ClearCommandLine (str
, maxlen
, orgx
, orgy
);
421 current
= charcount
= 0;
423 /* Everything is deleted, lets add it back in */
426 /* Figure out where cusor is going to be after we print it */
427 charcount
= _tcslen(str
);
430 SetCursorXY(orgx
, orgy
);
431 /* Print out what we have now */
432 ConOutPrintf(_T("%s"), str
);
434 /* Move cursor accordingly */
435 if (tempscreen
> charcount
)
437 GetCursorXY(&curx
, &cury
);
438 for(count
= tempscreen
- charcount
; count
--; )
440 SetCursorXY(curx
, cury
);
444 if (((charcount
+ orgx
) / maxx
) + orgy
> maxy
- 1)
445 orgy
+= maxy
- ((charcount
+ orgx
) / maxx
+ orgy
+ 1);
447 SetCursorXY((short)(((int)orgx
+ current
) % maxx
), (short)((int)orgy
+ ((int)orgx
+ current
) / maxx
));
448 GetCursorXY(&curx
, &cury
);
453 if ((ir
.Event
.KeyEvent
.dwControlKeyState
&
454 (RIGHT_CTRL_PRESSED
|LEFT_CTRL_PRESSED
)))
456 /* Ignore the Ctrl-C key event if it has already been handled */
461 * A Ctrl-C. Do not clear the command line,
462 * but return an empty string in str.
467 current
= charcount
= 0;
472 /* Just a normal 'C' character */
478 /* end input, return to main */
479 #ifdef FEATURE_HISTORY
480 /* add to the history */
484 str
[charcount
++] = _T('\n');
485 str
[charcount
] = _T('\0');
486 ConOutChar(_T('\n'));
491 /* clear str Make this callable! */
492 ClearCommandLine (str
, maxlen
, orgx
, orgy
);
495 current
= charcount
= 0;
498 #ifdef FEATURE_HISTORY
500 History_move_to_bottom();
503 #ifdef FEATURE_HISTORY
504 /* get previous command from buffer */
505 ClearCommandLine (str
, maxlen
, orgx
, orgy
);
507 current
= charcount
= _tcslen (str
);
508 if (((charcount
+ orgx
) / maxx
) + orgy
> maxy
- 1)
509 orgy
+= maxy
- ((charcount
+ orgx
) / maxx
+ orgy
+ 1);
510 ConOutPrintf (_T("%s"), str
);
511 GetCursorXY (&curx
, &cury
);
516 #ifdef FEATURE_HISTORY
517 /* get next command from buffer */
518 ClearCommandLine (str
, maxlen
, orgx
, orgy
);
520 current
= charcount
= _tcslen (str
);
521 if (((charcount
+ orgx
) / maxx
) + orgy
> maxy
- 1)
522 orgy
+= maxy
- ((charcount
+ orgx
) / maxx
+ orgy
+ 1);
523 ConOutPrintf (_T("%s"), str
);
524 GetCursorXY (&curx
, &cury
);
529 if (dwControlKeyState
& (RIGHT_CTRL_PRESSED
| LEFT_CTRL_PRESSED
))
531 /* move cursor to the previous word */
534 while (current
> 0 && str
[current
- 1] == _T(' '))
548 while (current
> 0 && str
[current
-1] != _T(' '))
562 SetCursorXY(curx
, cury
);
567 /* move cursor left */
571 if (GetCursorX () == 0)
573 SetCursorXY ((SHORT
)(maxx
- 1), (SHORT
)(GetCursorY () - 1));
579 SetCursorXY ((SHORT
)(GetCursorX () - 1), GetCursorY ());
591 if (dwControlKeyState
& (RIGHT_CTRL_PRESSED
| LEFT_CTRL_PRESSED
))
593 /* move cursor to the next word */
594 if (current
!= charcount
)
596 while (current
!= charcount
&& str
[current
] != _T(' '))
599 if (curx
== maxx
- 1)
610 while (current
!= charcount
&& str
[current
] == _T(' '))
613 if (curx
== maxx
- 1)
624 SetCursorXY(curx
, cury
);
629 /* move cursor right */
630 if (current
!= charcount
)
633 if (GetCursorX () == maxx
- 1)
635 SetCursorXY (0, (SHORT
)(GetCursorY () + 1));
641 SetCursorXY ((SHORT
)(GetCursorX () + 1), GetCursorY ());
645 #ifdef FEATURE_HISTORY
648 LPCTSTR last
= PeekHistory(-1);
649 if (last
&& charcount
< (INT
)_tcslen (last
))
651 PreviousChar
= last
[current
];
652 ConOutChar(PreviousChar
);
653 GetCursorXY(&curx
, &cury
);
654 str
[current
++] = PreviousChar
;
663 /* This input is just a normal char */
668 ch
= ir
.Event
.KeyEvent
.uChar
.UnicodeChar
;
669 if (ch
>= 32 && (charcount
!= (maxlen
- 2)) && bCharInput
)
671 ch
= ir
.Event
.KeyEvent
.uChar
.AsciiChar
;
672 if ((UCHAR
)ch
>= 32 && (charcount
!= (maxlen
- 2)) && bCharInput
)
673 #endif /* _UNICODE */
675 /* insert character into string... */
676 if (bInsert
&& current
!= charcount
)
678 /* If this character insertion will cause screen scrolling,
679 * adjust the saved origin of the command prompt. */
680 tempscreen
= _tcslen(str
+ current
) + curx
;
681 if ((tempscreen
% maxx
) == (maxx
- 1) &&
682 (tempscreen
/ maxx
) + cury
== (maxy
- 1))
688 for (count
= charcount
; count
> current
; count
--)
689 str
[count
] = str
[count
- 1];
691 if (curx
== maxx
- 1)
695 ConOutPrintf (_T("%s"), &str
[current
- 1]);
696 SetCursorXY (curx
, cury
);
701 if (current
== charcount
)
704 if (GetCursorX () == maxx
- 1 && GetCursorY () == maxy
- 1)
706 if (GetCursorX () == maxx
- 1)
714 //wLastKey = ir.Event.KeyEvent.wVirtualKeyCode;
718 SetCursorType (bInsert
, TRUE
);
720 #ifdef FEATURE_ALIASES
721 /* expand all aliases */
722 ExpandAlias (str
, maxlen
);
723 #endif /* FEATURE_ALIAS */