Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

730 lines
19 KiB

  1. /*++
  2. Copyright (c) 1988-1999 Microsoft Corporation
  3. Module Name:
  4. readcon.c
  5. Abstract:
  6. Emulate NT console on Win9x
  7. --*/
  8. ///////////////////////////////////////////////////////////////
  9. //
  10. // ReadCon.cpp
  11. //
  12. //
  13. // ReadConsole implementation for Win95 that implements the command line editing
  14. // keys, since Win95 console implementation does not.
  15. //
  16. #include "cmd.h"
  17. #ifdef WIN95_CMD
  18. // adv declarations....
  19. extern BOOLEAN CtrlCSeen;
  20. extern BOOL bInsertDefault;
  21. void ShowBuf( TCHAR* pBuf, int nFromPos );
  22. // some state variables.....
  23. static int nConsoleWidth; // num columns
  24. static int nConsoleHeight; // num rows
  25. static COORD coordStart; // coord of first cmd char....
  26. static COORD coordEnd; // coord of last cmd char....
  27. static int nBufPos = 0; // buffer cursor position
  28. static int nBufLen = 0; // length of current command
  29. static BOOL bInsert; // insert mode
  30. static HANDLE hOut; // output buffer handle
  31. static TCHAR* pPrompt; // allocated buffer to store prompt
  32. static int nPromptSize; // num chars in prompt buffer
  33. static WORD wDefAttr; // default character attribute
  34. static int nState = 0; // input state
  35. static TCHAR history[50][2049]; // history list
  36. static int nFirstCmd = -1; // index of first cmd
  37. static int nLastCmd = -1; // index of last command entered
  38. static int nHistNdx = -1; // index into history list....
  39. static TCHAR* pSearchStr = 0; // search criteria buffer (allocated)
  40. void
  41. IncCoord(
  42. COORD* coord,
  43. int nDelta
  44. )
  45. {
  46. coord->X += (SHORT)nDelta;
  47. if ( coord->X < 0 )
  48. {
  49. coord->Y += coord->X/nConsoleWidth - 1;
  50. coord->X = nConsoleWidth - (-coord->X)%nConsoleWidth;
  51. }
  52. else
  53. if ( coord->X >= nConsoleWidth )
  54. {
  55. coord->Y += coord->X / nConsoleWidth;
  56. coord->X %= nConsoleWidth;
  57. }
  58. }
  59. void
  60. GetOffsetCoord(
  61. TCHAR* pBuf,
  62. int nOffset,
  63. COORD* coord
  64. )
  65. {
  66. int ndx;
  67. // let's walk the buffer to find the correct coord....
  68. *coord = coordStart;
  69. for( ndx=0; ndx<nOffset; ++ndx )
  70. {
  71. if ( pBuf[ndx] == 9 )
  72. {
  73. int nTab;
  74. for ( nTab=1; (nTab+coord->X)%8 ; ++nTab );
  75. coord->X += nTab-1;
  76. }
  77. else
  78. if ( pBuf[ndx] < 32 )
  79. ++coord->X;
  80. IncCoord( coord, +1 );
  81. }
  82. }
  83. int
  84. ScrollTo(
  85. TCHAR* pBuf,
  86. COORD* coord
  87. )
  88. {
  89. int nLines = 0;
  90. SMALL_RECT rectFrom;
  91. COORD coordTo;
  92. CHAR_INFO cBlank;
  93. // get some data....
  94. cBlank.Char.AsciiChar = ' ';
  95. cBlank.Attributes = wDefAttr;
  96. coordTo.X = 0;
  97. rectFrom.Left = 0;
  98. rectFrom.Right = nConsoleWidth - 1;
  99. // scroll it....
  100. if ( coord->Y < 0 )
  101. {
  102. // scroll down....
  103. nLines = coord->Y;
  104. rectFrom.Top = 0;
  105. rectFrom.Bottom = nConsoleHeight + nLines - 1;
  106. coordTo.Y = -nLines;
  107. ScrollConsoleScreenBuffer( hOut, &rectFrom, NULL, coordTo, &cBlank );
  108. }
  109. else
  110. {
  111. // scroll up....
  112. nLines = coord->Y - nConsoleHeight + 1;
  113. rectFrom.Top = (SHORT)nLines;
  114. rectFrom.Bottom = nConsoleHeight - 1;
  115. coordTo.Y = 0;
  116. ScrollConsoleScreenBuffer( hOut, &rectFrom, NULL, coordTo, &cBlank );
  117. }
  118. // adjust start/end coords and orig coord to reflect new scroll....
  119. coordStart.Y -= (SHORT)nLines;
  120. coordEnd.Y -= (SHORT)nLines;
  121. coord->Y -= (SHORT)nLines;
  122. // redraw the whole command AND the prompt.....
  123. ShowBuf( pBuf, -1 );
  124. return nLines;
  125. }
  126. void
  127. AdjCursor(
  128. TCHAR* pBuf
  129. )
  130. {
  131. COORD coordCursor;
  132. GetOffsetCoord( pBuf, nBufPos, &coordCursor );
  133. if ( coordCursor.Y < 0 || coordCursor.Y >= nConsoleHeight )
  134. {
  135. // scroll it....
  136. ScrollTo( pBuf, &coordCursor );
  137. }
  138. SetConsoleCursorPosition( hOut, coordCursor );
  139. }
  140. void
  141. AdjCursorSize( void )
  142. {
  143. CONSOLE_CURSOR_INFO cci;
  144. cci.bVisible = TRUE;
  145. if ( bInsert == bInsertDefault )
  146. cci.dwSize = 13;
  147. else
  148. cci.dwSize = 40;
  149. SetConsoleCursorInfo( hOut, &cci );
  150. }
  151. void
  152. AdvancePos(
  153. TCHAR* pBuf,
  154. int nDelta,
  155. DWORD dwKeyState
  156. )
  157. {
  158. // see if control key down....
  159. if ( (dwKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) != 0 )
  160. {
  161. if ( nDelta > 0 )
  162. { // skip to NEXT word....
  163. while ( nBufPos < nBufLen && !isspace(pBuf[nBufPos]) )
  164. ++nBufPos;
  165. while ( nBufPos < nBufLen && isspace(pBuf[nBufPos]) )
  166. ++nBufPos;
  167. }
  168. else
  169. { // skip to PREVIOUS word....
  170. // if already at beginning of word, back up one....
  171. if ( nBufPos > 0 && isspace(pBuf[nBufPos-1]) )
  172. --nBufPos;
  173. // skip white space....
  174. while ( nBufPos > 0 && isspace(pBuf[nBufPos]) )
  175. --nBufPos;
  176. // skip non-ws....
  177. while ( nBufPos > 0 && !isspace(pBuf[nBufPos-1]) )
  178. --nBufPos;
  179. }
  180. }
  181. else
  182. nBufPos += nDelta;
  183. AdjCursor( pBuf );
  184. }
  185. void
  186. ShowBuf(
  187. TCHAR* pBuf,
  188. int nFromPos
  189. )
  190. {
  191. DWORD cPrint;
  192. COORD coord, cPrompt;
  193. TCHAR temp[8];
  194. int ndx;
  195. // see if we want the prompt, too....
  196. if ( nFromPos < 0 )
  197. {
  198. nFromPos = 0;
  199. GetOffsetCoord( pBuf, 0, &coord );
  200. cPrompt = coord;
  201. cPrompt.X -= (SHORT)nPromptSize;
  202. WriteConsoleOutputCharacter( hOut, pPrompt, nPromptSize, cPrompt, &cPrint );
  203. }
  204. else
  205. GetOffsetCoord( pBuf, nFromPos, &coord );
  206. // walk the rest of the buffer, displaying as we go....
  207. for( ndx=nFromPos;ndx < nBufLen; ++ndx )
  208. {
  209. if ( pBuf[ndx] == 9 )
  210. {
  211. int nTab;
  212. temp[0] = TEXT(' ');
  213. for ( nTab=1; (nTab+coord.X)%8; ++nTab )
  214. {
  215. temp[nTab] = TEXT(' ');
  216. }
  217. WriteConsoleOutputCharacter( hOut, temp, nTab, coord, &cPrint );
  218. coord.X += nTab-1;
  219. }
  220. else if ( pBuf[ndx] < 32 )
  221. {
  222. temp[0] = '^';
  223. temp[1] = pBuf[ndx] + 'A' - 1;
  224. WriteConsoleOutputCharacter( hOut, temp, 2, coord, &cPrint );
  225. ++coord.X;
  226. }
  227. else
  228. WriteConsoleOutputCharacter( hOut, pBuf + ndx, 1, coord, &cPrint );
  229. // advance cursor.....
  230. IncCoord( &coord, +1 );
  231. }
  232. // now blank out the rest.....
  233. temp[0] = TEXT(' ');
  234. while ( coordEnd.Y > coord.Y || (coordEnd.Y == coord.Y && coordEnd.X >= coord.X) )
  235. {
  236. WriteConsoleOutputCharacter( hOut, temp, 1, coordEnd, &cPrint );
  237. IncCoord( &coordEnd, -1 );
  238. }
  239. // save the new ending....
  240. coordEnd = coord;
  241. }
  242. void
  243. ShiftBuffer(
  244. TCHAR* pBuf,
  245. int cch,
  246. int nFrom,
  247. int nDelta
  248. )
  249. {
  250. int ndx;
  251. // which direction?
  252. if ( nDelta > 0 )
  253. {
  254. // move all (including this character) out one place....
  255. for( ndx = nBufLen; ndx > nFrom; --ndx )
  256. pBuf[ndx] = pBuf[ndx-1];
  257. ++nBufLen;
  258. }
  259. else if ( nDelta < 0 )
  260. {
  261. // move all characters in one place (over this character)...
  262. for( ndx = nFrom; ndx < nBufLen; ++ ndx )
  263. pBuf[ndx] = pBuf[ndx+1];
  264. --nBufLen;
  265. }
  266. }
  267. void
  268. LoadHistory(
  269. TCHAR* pBuf,
  270. int cch,
  271. TCHAR* pHist
  272. )
  273. {
  274. // first go to the front of the line....
  275. nBufPos = 0;
  276. AdjCursor( pBuf );
  277. // then blank-out the current buffer....
  278. if ( nBufLen )
  279. {
  280. nBufLen = 0;
  281. ShowBuf( pBuf, 0 );
  282. }
  283. // now copy in the new one (if there is one)....
  284. if ( pHist )
  285. {
  286. _tcsncpy( pBuf, pHist, cch );
  287. nBufLen = _tcslen( pHist );
  288. // move to the end of the line....
  289. nBufPos = nBufLen;
  290. AdjCursor( pBuf );
  291. // show the whole buffer from the start....
  292. ShowBuf( pBuf, 0 );
  293. }
  294. }
  295. void
  296. SearchHist(
  297. TCHAR* pBuf,
  298. int cch
  299. )
  300. {
  301. int ndx, nSearch, nStop;
  302. // if we're in input mode, set ndx to AFTER last command....
  303. if ( nState == 0 )
  304. ndx = (nLastCmd+1)%50;
  305. else
  306. ndx = nHistNdx;
  307. nStop = ndx; // don't search past here
  308. // if not already in search mode, get a copy of the target....
  309. if ( nState != 4 )
  310. {
  311. // if there already is a search string, get rid of it....
  312. if ( pSearchStr )
  313. free( pSearchStr );
  314. // pSearchStr and copy a new search string....
  315. pSearchStr = calloc( nBufLen+1, sizeof(TCHAR) );
  316. if (pSearchStr == NULL) {
  317. return;
  318. }
  319. _tcsncpy( pSearchStr, pBuf, nBufLen );
  320. pSearchStr[nBufLen] = 0;
  321. }
  322. nSearch = _tcslen( pSearchStr );
  323. // enter search mode....
  324. nState = 4;
  325. do
  326. {
  327. // back up one cmd....
  328. ndx = (ndx+49)%50;
  329. // check it....
  330. if ( _tcsncmp( history[ndx], pSearchStr, nSearch ) == 0 )
  331. {
  332. // found a match....
  333. nHistNdx = ndx;
  334. LoadHistory( pBuf, cch, history[ndx] );
  335. break;
  336. }
  337. } while ( ndx != nStop );
  338. }
  339. int touched = 1;
  340. BOOL
  341. ProcessKey(
  342. const KEY_EVENT_RECORD* keyEvent,
  343. TCHAR* pBuf,
  344. int cch,
  345. DWORD dwCtrlWakeupMask,
  346. PBOOL bWakeupKeyHit
  347. )
  348. {
  349. BOOL bDone = FALSE;
  350. TCHAR ch;
  351. *bWakeupKeyHit = FALSE;
  352. ch = keyEvent->uChar.AsciiChar;
  353. if ( keyEvent->wVirtualKeyCode == VK_SPACE )
  354. ch = TEXT(' ');
  355. switch ( keyEvent->wVirtualKeyCode )
  356. {
  357. case VK_RETURN:
  358. bDone = TRUE;
  359. // move cursor to end of cmd, if not already there....
  360. if ( nBufPos != nBufLen )
  361. {
  362. nBufPos = nBufLen;
  363. AdjCursor( pBuf );
  364. }
  365. // add the NLN to end of cmd....
  366. pBuf[nBufLen] = _T('\n');
  367. ++nBufLen;
  368. touched = 1;
  369. break;
  370. case VK_BACK:
  371. if ( nBufPos > 0 )
  372. {
  373. // return to input state....
  374. nState = 0;
  375. // back up the cursor....
  376. AdvancePos( pBuf, -1, 0 );
  377. // shift over this character and print....
  378. ShiftBuffer( pBuf, cch, nBufPos, -1 );
  379. ShowBuf( pBuf, nBufPos );
  380. touched = 1;
  381. }
  382. break;
  383. case VK_END:
  384. if ( nBufPos != nBufLen )
  385. {
  386. // doesn't affect state....
  387. nBufPos = nBufLen;
  388. AdjCursor( pBuf );
  389. }
  390. break;
  391. case VK_HOME:
  392. if ( nBufPos )
  393. {
  394. // doesn't affect state....
  395. nBufPos = 0;
  396. AdjCursor( pBuf );
  397. }
  398. break;
  399. case VK_LEFT:
  400. // doesn't affect state....
  401. if ( nBufPos > 0 )
  402. AdvancePos( pBuf, -1, keyEvent->dwControlKeyState );
  403. break;
  404. case VK_RIGHT:
  405. // doesn't affect state....
  406. if ( nBufPos < nBufLen )
  407. AdvancePos( pBuf, +1, keyEvent->dwControlKeyState );
  408. break;
  409. case VK_INSERT:
  410. // doesn't affect state....
  411. bInsert = !bInsert;
  412. AdjCursorSize();
  413. break;
  414. case VK_DELETE:
  415. if ( nBufPos < nBufLen )
  416. {
  417. // fall back to input state....
  418. nState = 0;
  419. // shift over this character and print....
  420. ShiftBuffer( pBuf, cch, nBufPos, -1 );
  421. ShowBuf( pBuf, nBufPos );
  422. touched = 1;
  423. }
  424. break;
  425. case VK_F8:
  426. // if there's something to match....
  427. if ( nBufLen != 0 )
  428. {
  429. // if we're not already at the top of the list....
  430. //if ( nHistNdx != nFirstCmd )
  431. //{
  432. // search backwards up the list....
  433. SearchHist( pBuf, cch );
  434. //}
  435. touched = 1;
  436. break;
  437. }
  438. // fall through if there's nothing to match with....
  439. // same as pressing up arrow....
  440. case VK_UP:
  441. // if we're not already at the top of the list....
  442. if ( nState == 0 || nHistNdx != nFirstCmd )
  443. {
  444. if ( nState == 0 )
  445. nHistNdx = nLastCmd;
  446. else
  447. nHistNdx = (nHistNdx+49)%50;
  448. LoadHistory( pBuf, cch, history[nHistNdx] );
  449. // scrolling through history....
  450. nState = 2;
  451. touched = 1;
  452. }
  453. break;
  454. case VK_DOWN:
  455. if ( nState == 0 || nHistNdx == nLastCmd )
  456. {
  457. // blank out command buffer...
  458. LoadHistory( pBuf, cch, NULL );
  459. // return to input state....
  460. nState = 0;
  461. }
  462. else
  463. {
  464. // get the next one....
  465. nHistNdx = (nHistNdx+1)%50;
  466. LoadHistory( pBuf, cch, history[nHistNdx] );
  467. // scrolling through history....
  468. nState = 2;
  469. }
  470. touched = 1;
  471. break;
  472. case VK_ESCAPE:
  473. // blank-out the command buffer....
  474. LoadHistory( pBuf, cch, NULL );
  475. // return to input....
  476. nState = 0;
  477. touched = 1;
  478. break;
  479. default:
  480. // if printable character, let's add it in....
  481. if ( ch >= 1 && ch <= 255 )
  482. {
  483. touched = 1;
  484. // fall back to input state....
  485. nState = 0;
  486. // see if there's room....
  487. if ( nBufPos >= cch || (bInsert && nBufLen >= cch) )
  488. MessageBeep( MB_ICONEXCLAMATION );
  489. else
  490. {
  491. if ( bInsert )
  492. {
  493. // shift the buffer out for the insert....
  494. ShiftBuffer( pBuf, cch, nBufPos, +1 );
  495. }
  496. else
  497. if ( nBufPos >= nBufLen )
  498. ++nBufLen;
  499. // place the character in the buffer at the current pos....
  500. pBuf[nBufPos] = ch;
  501. if (ch < TEXT(' ') && (dwCtrlWakeupMask & (1 << ch))) {
  502. *bWakeupKeyHit = TRUE;
  503. AdjCursor(pBuf);
  504. bDone = TRUE;
  505. } else {
  506. // show from this position on....
  507. ShowBuf( pBuf, nBufPos );
  508. // advance position/cursor....
  509. AdvancePos( pBuf, +1, 0 );
  510. }
  511. }
  512. }
  513. }
  514. return bDone;
  515. }
  516. static UINT nOldIndex =0;
  517. static DWORD cRead = 0;
  518. static INPUT_RECORD ir[32];
  519. BOOL bInsertDefault = FALSE;
  520. BOOL
  521. Win95ReadConsoleA(
  522. HANDLE hIn,
  523. LPSTR pBuf,
  524. DWORD cch,
  525. LPDWORD pcch,
  526. LPVOID lpReserved
  527. )
  528. {
  529. PCONSOLE_READCONSOLE_CONTROL pInputControl;
  530. CONSOLE_SCREEN_BUFFER_INFO csbi;
  531. const KEY_EVENT_RECORD* keyEvent;
  532. BOOL bOk = TRUE; // return status value
  533. DWORD dwc, dwOldMode;
  534. DWORD dwCtrlWakeupMask;
  535. BOOL bWakeupKeyHit;
  536. // initialize the state variables....
  537. nState = 0; // input mode
  538. nBufPos = 0; // buffer cursor position
  539. nBufLen = 0; // length of current command
  540. bInsert = bInsertDefault; // insert mode
  541. // set the appropriate console mode....
  542. if (!GetConsoleMode( hIn, &dwOldMode ))
  543. return FALSE;
  544. SetConsoleMode( hIn, ENABLE_PROCESSED_INPUT );
  545. // get the ouput buffer handle....
  546. hOut = GetStdHandle(STD_OUTPUT_HANDLE);
  547. if ( hOut == INVALID_HANDLE_VALUE )
  548. {
  549. DWORD dwErr = GetLastError();
  550. hOut = CRTTONT( STDOUT );
  551. }
  552. // get the output console info....
  553. if ( GetConsoleScreenBufferInfo( hOut, &csbi ) == FALSE )
  554. return FALSE;
  555. // save size and initial cursor pos and console width....
  556. coordStart = coordEnd = csbi.dwCursorPosition;
  557. nConsoleWidth = csbi.dwSize.X;
  558. nConsoleHeight = csbi.dwSize.Y;
  559. wDefAttr = csbi.wAttributes;
  560. // allocate a buffer to hold whatever is before command....
  561. nPromptSize = 0;
  562. if ( coordStart.X > 0 )
  563. {
  564. COORD cPrompt;
  565. DWORD dwRead;
  566. nPromptSize = coordStart.X;
  567. cPrompt = coordStart;
  568. cPrompt.X = 0;
  569. pPrompt = calloc( nPromptSize+1, sizeof(TCHAR) );
  570. if (pPrompt == NULL) {
  571. PutStdErr( MSG_NO_MEMORY, NOARGS );
  572. Abort( );
  573. }
  574. // now copy the data....
  575. ReadConsoleOutputCharacter( hOut, pPrompt, nPromptSize,
  576. cPrompt, &dwRead );
  577. // NULL-terminate it....
  578. pPrompt[dwRead] = 0;
  579. }
  580. AdjCursorSize();
  581. pInputControl = (PCONSOLE_READCONSOLE_CONTROL)lpReserved;
  582. if (pInputControl != NULL && pInputControl->nLength == sizeof(*pInputControl)) {
  583. dwCtrlWakeupMask = pInputControl->dwCtrlWakeupMask;
  584. nBufLen = pInputControl->nInitialChars;
  585. nBufPos = pInputControl->nInitialChars;
  586. } else {
  587. pInputControl = NULL;
  588. dwCtrlWakeupMask = 0;
  589. }
  590. while ( !CtrlCSeen )
  591. {
  592. // get the next input record....
  593. UINT ndx;
  594. if( nOldIndex == 0 )
  595. ReadConsoleInput( hIn, ir, sizeof(ir)/sizeof(INPUT_RECORD), &cRead );
  596. // process all the records we just received....
  597. for( ndx=nOldIndex; ndx < cRead; ++ndx )
  598. {
  599. // process only key down events....
  600. keyEvent = &(ir[ndx].Event.KeyEvent);
  601. if( ir[ndx].EventType == KEY_EVENT &&
  602. keyEvent->bKeyDown &&
  603. ProcessKey( keyEvent, pBuf, cch, dwCtrlWakeupMask, &bWakeupKeyHit )
  604. )
  605. {
  606. if (pInputControl != NULL)
  607. pInputControl->dwControlKeyState = keyEvent->dwControlKeyState;
  608. goto donereading;
  609. }
  610. }
  611. if( cRead == ndx ) {
  612. nOldIndex = 0;
  613. } else {
  614. nOldIndex = ndx + 1;
  615. }
  616. }
  617. donereading:
  618. // clean-up....
  619. if ( pPrompt ) {
  620. free( pPrompt );
  621. pPrompt = NULL;
  622. }
  623. if ( pSearchStr ) {
  624. free( pSearchStr );
  625. pSearchStr = NULL;
  626. }
  627. if (!bWakeupKeyHit) {
  628. // not okay if we Ctrl-C'ed out....
  629. if ( CtrlCSeen )
  630. {
  631. bOk = FALSE;
  632. nBufPos = nBufLen;
  633. AdjCursor( pBuf );
  634. *pcch = 0;
  635. }
  636. else
  637. {
  638. // save to history (less the NLN) if something entered....
  639. if ( nBufLen > 1 )
  640. {
  641. nLastCmd = (nLastCmd+1)%50;
  642. // adjust the first for wrap-around....
  643. if ( nLastCmd == nFirstCmd || nFirstCmd == -1 )
  644. nFirstCmd = (nFirstCmd+1)%50;
  645. _tcsncpy( history[nLastCmd], pBuf, nBufLen-1 );
  646. // null-terminate....
  647. history[nLastCmd][nBufLen-1] = 0;
  648. }
  649. *pcch = nBufLen;
  650. }
  651. // go to next line....
  652. WriteConsole( hOut, _T("\n\r"), 2, &dwc, NULL );
  653. FlushConsoleInputBuffer( hIn );
  654. } else {
  655. *pcch = nBufLen;
  656. }
  657. // restore the console mode....
  658. SetConsoleMode( hIn, dwOldMode );
  659. return bOk;
  660. }
  661. #endif // ifdef WIN95_CMD