Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742/* * CMDINPUT.C - handles command input (tab completion, history, etc.). * * * History: * * 01/14/95 (Tim Norman) * started. * * 08/08/95 (Matt Rains) * i have cleaned up the source code. changes now bring this source * into guidelines for recommended programming practice. * i have added some constants to help making changes easier. * * 12/12/95 (Tim Norman) * added findxy() function to get max x/y coordinates to display * correctly on larger screens * * 12/14/95 (Tim Norman) * fixed the Tab completion code that Matt Rains broke by moving local * variables to a more global scope and forgetting to initialize them * when needed * * 8/1/96 (Tim Norman) * fixed a bug in tab completion that caused filenames at the beginning * of the command-line to have their first letter truncated * * 9/1/96 (Tim Norman) * fixed a silly bug using printf instead of fputs, where typing "%i" * confused printf :) * * 6/14/97 (Steffan Kaiser) * ctrl-break checking * * 6/7/97 (Marc Desrochers) * recoded everything! now properly adjusts when text font is changed. * removed findxy(), reposition(), and reprint(), as these functions * were inefficient. added goxy() function as gotoxy() was buggy when * the screen font was changed. the printf() problem with %i on the * command line was fixed by doing printf("%s",str) instead of * printf(str). Don't ask how I find em just be glad I do :) * * 7/12/97 (Tim Norman) * Note: above changes preempted Steffan's ctrl-break checking. * * 7/7/97 (Marc Desrochers) * rewrote a new findxy() because the new dir() used it. This * findxy() simply returns the values of *maxx *maxy. In the * future, please use the pointers, they will always be correct * since they point to BIOS values. * * 7/8/97 (Marc Desrochers) * once again removed findxy(), moved the *maxx, *maxy pointers * global and included them as externs in command.h. Also added * insert/overstrike capability * * 7/13/97 (Tim Norman) * added different cursor appearance for insert/overstrike mode * * 7/13/97 (Tim Norman) * changed my code to use _setcursortype until I can figure out why * my code is crashing on some machines. It doesn't crash on mine :) * * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>) * added config.h include * * 28-Jul-1998 (John P Price <linux-guru@gcfl.net>) * put ifdef's around filename completion code. * * 30-Jul-1998 (John P Price <linux-guru@gcfl.net>) * moved filename completion code to filecomp.c * made second TAB display list of filename matches * * 31-Jul-1998 (John P Price <linux-guru@gcfl.net>) * Fixed bug where if you typed something, then hit HOME, then tried * to type something else in insert mode, it crashed. * * 07-Aug-1998 (John P Price <linux-guru@gcfl.net>) * Fixed carriage return output to better match MSDOS with echo * on or off.(marked with "JPP 19980708") * * 13-Dec-1998 (Eric Kohl) * Added insert/overwrite cursor. * * 25-Jan-1998 (Eric Kohl) * Replaced CRT io functions by Win32 console io functions. * This can handle <Shift>-<Tab> for 4NT filename completion. * Unicode and redirection safe! * * 04-Feb-1999 (Eric Kohl) * Fixed input bug. A "line feed" character remained in the keyboard * input queue when you pressed <RETURN>. This sometimes caused * some very strange effects. * Fixed some command line editing annoyances. * * 30-Apr-2004 (Filip Navara <xnavara@volny.cz>) * Fixed problems when the screen was scrolled away. * * 28-September-2007 (Hervé Poussineau) * Added history possibilities to right key. */
#include "precomp.h"
/* * See https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/cc978715(v=technet.10) * and https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/cc940805(v=technet.10) * to know the differences between those two settings. * Values 0x00, 0x0D (carriage return) and >= 0x20 (space) disable completion. */TCHAR AutoCompletionChar = 0x20; // Disabled by defaultTCHAR PathCompletionChar = 0x20; // Disabled by default
SHORT maxx;SHORT maxy;
/* * global command line insert/overwrite flag */static BOOL bInsert = TRUE;
static VOIDClearCommandLine(LPTSTR str, INT maxlen, SHORT orgx, SHORT orgy){ INT count;
SetCursorXY (orgx, orgy); for (count = 0; count < (INT)_tcslen (str); count++) ConOutChar (_T(' ')); _tcsnset (str, _T('\0'), maxlen); SetCursorXY (orgx, orgy);}
/* read in a command line */BOOL ReadCommand(LPTSTR str, INT maxlen){ CONSOLE_SCREEN_BUFFER_INFO csbi; SHORT orgx; /* origin x/y */ SHORT orgy; SHORT curx; /*current x/y cursor position*/ SHORT cury; SIZE_T tempscreen; INT count; /*used in some for loops*/ INT current = 0; /*the position of the cursor in the string (str)*/ INT charcount = 0;/*chars in the string (str)*/ INPUT_RECORD ir; DWORD dwControlKeyState;#ifdef FEATURE_UNIX_FILENAME_COMPLETION WORD wLastKey = 0;#endif TCHAR ch; BOOL bReturn = FALSE; BOOL bCharInput;#ifdef FEATURE_4NT_FILENAME_COMPLETION TCHAR szPath[MAX_PATH];#endif#ifdef FEATURE_HISTORY //BOOL bContinue=FALSE;/*is TRUE the second case will not be executed*/ TCHAR PreviousChar;#endif
if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) { /* No console */ HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); DWORD dwRead; CHAR chr; do { if (!ReadFile(hStdin, &chr, 1, &dwRead, NULL) || !dwRead) return FALSE;#ifdef _UNICODE MultiByteToWideChar(InputCodePage, 0, &chr, 1, &str[charcount++], 1);#endif } while (chr != '\n' && charcount < maxlen); str[charcount] = _T('\0'); return TRUE; }
/* get screen size */ maxx = csbi.dwSize.X; maxy = csbi.dwSize.Y;
curx = orgx = csbi.dwCursorPosition.X; cury = orgy = csbi.dwCursorPosition.Y;
memset (str, 0, maxlen * sizeof (TCHAR));
SetCursorType (bInsert, TRUE);
do { bReturn = FALSE; ConInKey (&ir);
dwControlKeyState = ir.Event.KeyEvent.dwControlKeyState;
if (dwControlKeyState & (RIGHT_ALT_PRESSED |LEFT_ALT_PRESSED| RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED) ) { switch (ir.Event.KeyEvent.wVirtualKeyCode) {#ifdef FEATURE_HISTORY case _T('K'): /* add the current command line to the history */ if (dwControlKeyState & (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED)) { if (str[0]) History(0,str);
ClearCommandLine (str, maxlen, orgx, orgy); current = charcount = 0; curx = orgx; cury = orgy; //bContinue=TRUE; } break;
case _T('D'): /* delete current history entry */ if (dwControlKeyState & (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED)) { ClearCommandLine (str, maxlen, orgx, orgy); History_del_current_entry(str); current = charcount = _tcslen (str); ConOutPrintf (_T("%s"), str); GetCursorXY (&curx, &cury); //bContinue=TRUE; } break;#endif /*FEATURE_HISTORY*/
case _T('M'): /* ^M does the same as return */ if (dwControlKeyState & (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED)) { /* end input, return to main */#ifdef FEATURE_HISTORY /* add to the history */ if (str[0]) History(0, str);#endif /*FEATURE_HISTORY*/ str[charcount++] = _T('\n'); str[charcount] = _T('\0'); ConOutChar (_T('\n')); bReturn = TRUE; } break;
case _T('H'): /* ^H does the same as VK_BACK */ if (dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) { bCharInput = FALSE; goto DoBackSpace; } break; } }
bCharInput = FALSE;
switch (ir.Event.KeyEvent.wVirtualKeyCode) { case VK_BACK: DoBackSpace: /* <BACKSPACE> - delete character to left of cursor */ if (current > 0 && charcount > 0) { if (current == charcount) { /* if at end of line */ str[current - 1] = _T('\0'); if (GetCursorX () != 0) { ConOutPrintf (_T("\b \b")); curx--; } else { SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1)); ConOutChar (_T(' ')); SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1)); cury--; curx = maxx - 1; } } else { for (count = current - 1; count < charcount; count++) str[count] = str[count + 1]; if (GetCursorX () != 0) { SetCursorXY ((SHORT)(GetCursorX () - 1), GetCursorY ()); curx--; } else { SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1)); cury--; curx = maxx - 1; } GetCursorXY (&curx, &cury); ConOutPrintf (_T("%s "), &str[current - 1]); SetCursorXY (curx, cury); } charcount--; current--; } break;
case VK_INSERT: /* toggle insert/overstrike mode */ bInsert ^= TRUE; SetCursorType (bInsert, TRUE); break;
case VK_DELETE: /* delete character under cursor */ if (current != charcount && charcount > 0) { for (count = current; count < charcount; count++) str[count] = str[count + 1]; charcount--; GetCursorXY (&curx, &cury); ConOutPrintf (_T("%s "), &str[current]); SetCursorXY (curx, cury); } break;
case VK_HOME: /* goto beginning of string */ if (current != 0) { SetCursorXY (orgx, orgy); curx = orgx; cury = orgy; current = 0; } break;
case VK_END: /* goto end of string */ if (current != charcount) { SetCursorXY (orgx, orgy); ConOutPrintf (_T("%s"), str); GetCursorXY (&curx, &cury); current = charcount; } break;
case VK_TAB:#ifdef FEATURE_UNIX_FILENAME_COMPLETION /* expand current file name */ if ((current == charcount) || (current == charcount - 1 && str[current] == _T('"'))) /* only works at end of line*/ { if (wLastKey != VK_TAB) { /* if first TAB, complete filename*/ tempscreen = charcount; CompleteFilename (str, charcount); charcount = _tcslen (str); current = charcount;
SetCursorXY (orgx, orgy); ConOutPrintf (_T("%s"), str);
if (tempscreen > charcount) { GetCursorXY (&curx, &cury); for (count = tempscreen - charcount; count--; ) ConOutChar (_T(' ')); SetCursorXY (curx, cury); } else { if (((charcount + orgx) / maxx) + orgy > maxy - 1) orgy += maxy - ((charcount + orgx) / maxx + orgy + 1); }
/* set cursor position */ SetCursorXY ((orgx + current) % maxx, orgy + (orgx + current) / maxx); GetCursorXY (&curx, &cury); } else { /*if second TAB, list matches*/ if (ShowCompletionMatches (str, charcount)) { PrintPrompt(); GetCursorXY(&orgx, &orgy); ConOutPrintf(_T("%s"), str);
/* set cursor position */ SetCursorXY((orgx + current) % maxx, orgy + (orgx + current) / maxx); GetCursorXY(&curx, &cury); }
} } else { MessageBeep(-1); }#endif#ifdef FEATURE_4NT_FILENAME_COMPLETION /* used to later see if we went down to the next line */ tempscreen = charcount; szPath[0]=_T('\0');
/* str is the whole things that is on the current line that is and and out. arg 2 is weather it goes back one file or forward one file */ CompleteFilename(str, !(ir.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED), szPath, current); /* Attempt to clear the line */ ClearCommandLine (str, maxlen, orgx, orgy); curx = orgx; cury = orgy; current = charcount = 0;
/* Everything is deleted, lets add it back in */ _tcscpy(str,szPath);
/* Figure out where cusor is going to be after we print it */ charcount = _tcslen(str); current = charcount;
SetCursorXY(orgx, orgy); /* Print out what we have now */ ConOutPrintf(_T("%s"), str);
/* Move cursor accordingly */ if (tempscreen > charcount) { GetCursorXY(&curx, &cury); for(count = tempscreen - charcount; count--; ) ConOutChar(_T(' ')); SetCursorXY(curx, cury); } else { if (((charcount + orgx) / maxx) + orgy > maxy - 1) orgy += maxy - ((charcount + orgx) / maxx + orgy + 1); } SetCursorXY((short)(((int)orgx + current) % maxx), (short)((int)orgy + ((int)orgx + current) / maxx)); GetCursorXY(&curx, &cury);#endif break;
case _T('C'): if ((ir.Event.KeyEvent.dwControlKeyState & (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED))) { /* Ignore the Ctrl-C key event if it has already been handled */ if (!bCtrlBreak) break;
/* * Fully print the entered string * so the command prompt would not overwrite it. */ SetCursorXY(orgx, orgy); ConOutPrintf(_T("%s"), str);
/* * A Ctrl-C. Do not clear the command line, * but return an empty string in str. */ str[0] = _T('\0'); curx = orgx; cury = orgy; current = charcount = 0; bReturn = TRUE; } else { /* Just a normal 'C' character */ bCharInput = TRUE; } break;
case VK_RETURN: /* end input, return to main */#ifdef FEATURE_HISTORY /* add to the history */ if (str[0]) History(0, str);#endif str[charcount++] = _T('\n'); str[charcount] = _T('\0'); ConOutChar(_T('\n')); bReturn = TRUE; break;
case VK_ESCAPE: /* clear str Make this callable! */ ClearCommandLine (str, maxlen, orgx, orgy); curx = orgx; cury = orgy; current = charcount = 0; break;
#ifdef FEATURE_HISTORY case VK_F3: History_move_to_bottom();#endif case VK_UP:#ifdef FEATURE_HISTORY /* get previous command from buffer */ ClearCommandLine (str, maxlen, orgx, orgy); History(-1, str); current = charcount = _tcslen (str); if (((charcount + orgx) / maxx) + orgy > maxy - 1) orgy += maxy - ((charcount + orgx) / maxx + orgy + 1); ConOutPrintf (_T("%s"), str); GetCursorXY (&curx, &cury);#endif break;
case VK_DOWN:#ifdef FEATURE_HISTORY /* get next command from buffer */ ClearCommandLine (str, maxlen, orgx, orgy); History(1, str); current = charcount = _tcslen (str); if (((charcount + orgx) / maxx) + orgy > maxy - 1) orgy += maxy - ((charcount + orgx) / maxx + orgy + 1); ConOutPrintf (_T("%s"), str); GetCursorXY (&curx, &cury);#endif break;
case VK_LEFT: if (dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) { /* move cursor to the previous word */ if (current > 0) { while (current > 0 && str[current - 1] == _T(' ')) { current--; if (curx == 0) { cury--; curx = maxx -1; } else { curx--; } }
while (current > 0 && str[current -1] != _T(' ')) { current--; if (curx == 0) { cury--; curx = maxx -1; } else { curx--; } }
SetCursorXY(curx, cury); } } else { /* move cursor left */ if (current > 0) { current--; if (GetCursorX () == 0) { SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1)); curx = maxx - 1; cury--; } else { SetCursorXY ((SHORT)(GetCursorX () - 1), GetCursorY ()); curx--; } } else { MessageBeep (-1); } } break;
case VK_RIGHT: if (dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) { /* move cursor to the next word */ if (current != charcount) { while (current != charcount && str[current] != _T(' ')) { current++; if (curx == maxx - 1) { cury++; curx = 0; } else { curx++; } }
while (current != charcount && str[current] == _T(' ')) { current++; if (curx == maxx - 1) { cury++; curx = 0; } else { curx++; } }
SetCursorXY(curx, cury); } } else { /* move cursor right */ if (current != charcount) { current++; if (GetCursorX () == maxx - 1) { SetCursorXY (0, (SHORT)(GetCursorY () + 1)); curx = 0; cury++; } else { SetCursorXY ((SHORT)(GetCursorX () + 1), GetCursorY ()); curx++; } }#ifdef FEATURE_HISTORY else { LPCTSTR last = PeekHistory(-1); if (last && charcount < (INT)_tcslen (last)) { PreviousChar = last[current]; ConOutChar(PreviousChar); GetCursorXY(&curx, &cury); str[current++] = PreviousChar; charcount++; } }#endif } break;
default: /* This input is just a normal char */ bCharInput = TRUE;
}#ifdef _UNICODE ch = ir.Event.KeyEvent.uChar.UnicodeChar; if (ch >= 32 && (charcount != (maxlen - 2)) && bCharInput)#else ch = ir.Event.KeyEvent.uChar.AsciiChar; if ((UCHAR)ch >= 32 && (charcount != (maxlen - 2)) && bCharInput)#endif /* _UNICODE */ { /* insert character into string... */ if (bInsert && current != charcount) { /* If this character insertion will cause screen scrolling, * adjust the saved origin of the command prompt. */ tempscreen = _tcslen(str + current) + curx; if ((tempscreen % maxx) == (maxx - 1) && (tempscreen / maxx) + cury == (maxy - 1)) { orgy--; cury--; }
for (count = charcount; count > current; count--) str[count] = str[count - 1]; str[current++] = ch; if (curx == maxx - 1) curx = 0, cury++; else curx++; ConOutPrintf (_T("%s"), &str[current - 1]); SetCursorXY (curx, cury); charcount++; } else { if (current == charcount) charcount++; str[current++] = ch; if (GetCursorX () == maxx - 1 && GetCursorY () == maxy - 1) orgy--, cury--; if (GetCursorX () == maxx - 1) curx = 0, cury++; else curx++; ConOutChar (ch); } }
//wLastKey = ir.Event.KeyEvent.wVirtualKeyCode; } while (!bReturn);
SetCursorType (bInsert, TRUE);
#ifdef FEATURE_ALIASES /* expand all aliases */ ExpandAlias (str, maxlen);#endif /* FEATURE_ALIAS */ return TRUE;}