Leaked source code of windows server 2003
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.

911 lines
21 KiB

  1. /*++
  2. Copyright (c) 1990-2000 Microsoft Corporation
  3. Module Name:
  4. More.cxx
  5. Abstract:
  6. "More" pager
  7. Author:
  8. Ramon Juan San Andres (ramonsa) 11-Apr-1990
  9. Revision History:
  10. --*/
  11. #include "ulib.hxx"
  12. #include "arg.hxx"
  13. #include "arrayit.hxx"
  14. #include "file.hxx"
  15. #include "filestrm.hxx"
  16. #include "keyboard.hxx"
  17. #include "rtmsg.h"
  18. #include "pager.hxx"
  19. #include "path.hxx"
  20. #include "smsg.hxx"
  21. #include "system.hxx"
  22. #include "more.hxx"
  23. #define DEFAULT_TABEXP 8
  24. #define NULL_CHARACTER ((CHAR)'\0')
  25. #define CTRLC_CHARACTER ((CHAR)0x03)
  26. VOID __cdecl
  27. main (
  28. )
  29. /*++
  30. Routine Description:
  31. Main function of the more pager.
  32. Arguments:
  33. None.
  34. Return Value:
  35. None.
  36. Notes:
  37. --*/
  38. {
  39. // Initialize stuff
  40. //
  41. DEFINE_CLASS_DESCRIPTOR( MORE );
  42. //
  43. // Now do the paging
  44. //
  45. {
  46. MORE More;
  47. //
  48. // Initialize the MORE object.
  49. //
  50. if( More.Initialize() ) {
  51. //
  52. // Do the paging
  53. //
  54. More.DoPaging();
  55. }
  56. }
  57. }
  58. DEFINE_CONSTRUCTOR( MORE, PROGRAM );
  59. BOOLEAN
  60. MORE::Initialize (
  61. )
  62. /*++
  63. Routine Description:
  64. Initializes the MORE object
  65. Arguments:
  66. None.
  67. Return Value:
  68. None.
  69. Notes:
  70. --*/
  71. {
  72. //
  73. // Initialize program object
  74. //
  75. if( !PROGRAM::Initialize( MORE_MESSAGE_USAGE, MORE_ERROR_NO_MEMORY, EXIT_ERROR ) ) {
  76. return FALSE;
  77. }
  78. //
  79. // Initialize whatever needs initialization
  80. //
  81. InitializeThings();
  82. //
  83. // Do the argument parsing
  84. //
  85. SetArguments();
  86. return TRUE;
  87. }
  88. VOID
  89. MORE::Construct (
  90. )
  91. /*++
  92. Routine Description:
  93. Construct a MORE object
  94. Arguments:
  95. None.
  96. Return Value:
  97. None.
  98. Notes:
  99. --*/
  100. {
  101. _Keyboard = NULL;
  102. _FilesArgument = NULL;
  103. _LineDelimiters = NULL;
  104. _Percent = NULL;
  105. _Line = NULL;
  106. _Help = NULL;
  107. _DisplayLinesOption = NULL;
  108. _SkipLinesOption = NULL;
  109. _NextFileOption = NULL;
  110. _ShowLineNumberOption = NULL;
  111. _QuitOption = NULL;
  112. _Help1Option = NULL;
  113. _Help2Option = NULL;
  114. }
  115. MORE::~MORE (
  116. )
  117. /*++
  118. Routine Description:
  119. Destructs a MORE object
  120. Arguments:
  121. None.
  122. Return Value:
  123. None.
  124. Notes:
  125. --*/
  126. {
  127. //
  128. // Deallocate the global structures previously allocated
  129. //
  130. DeallocateThings();
  131. //
  132. // Exit without error
  133. //
  134. exit( EXIT_NORMAL );
  135. }
  136. VOID
  137. MORE::InitializeThings (
  138. )
  139. /*++
  140. Routine Description:
  141. Initializes the global variables that need initialization
  142. Arguments:
  143. None.
  144. Return Value:
  145. None.
  146. Notes:
  147. --*/
  148. {
  149. if ( //
  150. // Initialize the library
  151. //
  152. !(_Keyboard = NEW KEYBOARD)
  153. ) {
  154. exit( EXIT_ERROR );
  155. }
  156. // MORE translates from MBCS to Unicode according to the
  157. // current console codepage.
  158. //
  159. WSTRING::SetConsoleConversions();
  160. if ( //
  161. // Pager stuff
  162. //
  163. !DEFINE_CLASS_DESCRIPTOR( PAGER ) ||
  164. //
  165. // Misc. Strings
  166. //
  167. ((_LineDelimiters = NEW DSTRING) == NULL ) ||
  168. !_LineDelimiters->Initialize( "\r\n" ) ||
  169. ((_Percent = NEW DSTRING) == NULL ) ||
  170. ((_Line = NEW DSTRING) == NULL ) ||
  171. ((_OtherPrompt = NEW DSTRING) == NULL )
  172. ) {
  173. Fatal();
  174. }
  175. //
  176. // Get the strings containing valid user options
  177. //
  178. if ( (( _Help = QueryMessageString( MORE_HELP )) == NULL ) ||
  179. (( _DisplayLinesOption = QueryMessageString( MORE_OPTION_DISPLAYLINES )) == NULL ) ||
  180. (( _SkipLinesOption = QueryMessageString( MORE_OPTION_SKIPLINES )) == NULL ) ||
  181. (( _NextFileOption = QueryMessageString( MORE_OPTION_NEXTFILE )) == NULL ) ||
  182. (( _ShowLineNumberOption = QueryMessageString( MORE_OPTION_SHOWLINENUMBER )) == NULL ) ||
  183. (( _QuitOption = QueryMessageString( MORE_OPTION_QUIT )) == NULL ) ||
  184. (( _Help1Option = QueryMessageString( MORE_OPTION_HELP1 )) == NULL ) ||
  185. (( _Help2Option = QueryMessageString( MORE_OPTION_HELP2 )) == NULL ) ) {
  186. Fatal();
  187. }
  188. _Keyboard->Initialize();
  189. _Quit = FALSE;
  190. _ExtendedModeSwitch = FALSE;
  191. _ClearScreenSwitch = FALSE;
  192. _ExpandFormFeedSwitch = FALSE;
  193. _SqueezeBlanksSwitch = FALSE;
  194. _HelpSwitch = FALSE;
  195. _StartAtLine = 0;
  196. _TabExp = DEFAULT_TABEXP;
  197. _FilesArgument = NULL;
  198. }
  199. VOID
  200. MORE::DeallocateThings (
  201. )
  202. /*++
  203. Routine Description:
  204. Deallocates the global variables that need deallocation
  205. Arguments:
  206. None.
  207. Return Value:
  208. None.
  209. Notes:
  210. --*/
  211. {
  212. DELETE( _Keyboard );
  213. DELETE( _FilesArgument );
  214. DELETE( _LineDelimiters );
  215. DELETE( _Percent );
  216. DELETE( _Line );
  217. DELETE( _Help );
  218. //
  219. // Delete the strings containing valid user options
  220. //
  221. DELETE( _DisplayLinesOption );
  222. DELETE( _SkipLinesOption );
  223. DELETE( _NextFileOption );
  224. DELETE( _ShowLineNumberOption );
  225. DELETE( _QuitOption );
  226. DELETE( _Help1Option );
  227. DELETE( _Help2Option );
  228. }
  229. VOID
  230. MORE::DoPaging (
  231. )
  232. /*++
  233. Routine Description:
  234. Does the paging.
  235. Arguments:
  236. None.
  237. Return Value:
  238. None.
  239. Notes:
  240. --*/
  241. {
  242. PPATH Path;
  243. PITERATOR Iterator;
  244. BOOLEAN IsFirstFile = TRUE;
  245. ULONG FilesLeft;
  246. PFSN_FILE FsnFile;
  247. PFILE_STREAM FileStream;
  248. FilesLeft = _FilesArgument->QueryPathCount();
  249. if ( FilesLeft > 0 ) {
  250. //
  251. // We have a list of files, we will page each one in turn
  252. //
  253. // Get an iterator for going thru the file list
  254. //
  255. if ((Iterator = _FilesArgument->GetPathArray()->QueryIterator()) == NULL ) {
  256. Fatal();
  257. }
  258. Path = (PPATH)Iterator->GetNext();
  259. //
  260. // Iterate thru all the files in the array
  261. //
  262. while ( Path && !_Quit) {
  263. //
  264. // Get a new stream out of the file name
  265. //
  266. if ((FsnFile = SYSTEM::QueryFile( Path )) == NULL ||
  267. (FileStream = FsnFile->QueryStream( READ_ACCESS )) == NULL ) {
  268. Fatal( EXIT_ERROR, MORE_ERROR_CANNOT_ACCESS, "%W", Path->GetPathString() );
  269. }
  270. PageStream( FileStream,
  271. FsnFile,
  272. IsFirstFile ? _StartAtLine : 0, --FilesLeft );
  273. DELETE( FileStream );
  274. DELETE( FsnFile );
  275. Path = (PPATH)Iterator->GetNext();
  276. IsFirstFile = FALSE;
  277. }
  278. DELETE( Iterator );
  279. } else {
  280. //
  281. // The user did'nt specify a file list, so we will page
  282. // standard input.
  283. //
  284. PageStream( GetStandardInput(),
  285. NULL,
  286. _StartAtLine,
  287. 0 );
  288. }
  289. }
  290. VOID
  291. MORE::PageStream (
  292. IN PSTREAM Stream,
  293. IN PFSN_FILE FsnFile,
  294. IN ULONG FirstLineToDisplay,
  295. IN ULONG FilesLeft
  296. )
  297. /*++
  298. Routine Description:
  299. Pages a stream
  300. Arguments:
  301. Stream - Supplies pointer to stream
  302. FsnFile - Supplies pointer to file object
  303. FirstLineToDisplay - Supplies first line to display
  304. FilesLeft - Files remaining to be displayed
  305. Return Value:
  306. None.
  307. Notes:
  308. --*/
  309. {
  310. PAGER Pager;
  311. ULONG LinesToDisplay;
  312. BOOLEAN ClearScreen;
  313. BOOLEAN StayInFile;
  314. //
  315. // Initialize the pager
  316. //
  317. if (!Pager.Initialize( Stream, this)) {
  318. Fatal();
  319. }
  320. //
  321. // Skip to the first line to be displayed
  322. //
  323. if ( FirstLineToDisplay > 0 ) {
  324. Pager.SkipLines( FirstLineToDisplay, _TabExp );
  325. }
  326. LinesToDisplay = Pager.QueryLinesPerPage() - 1;
  327. ClearScreen = _ClearScreenSwitch;
  328. StayInFile = TRUE;
  329. while (StayInFile && Pager.ThereIsMoreToPage() && !_Quit) {
  330. // If QueryLinesPerPage() returns 0 then undo the -1 operation.
  331. if (LinesToDisplay == (ULONG) -1) {
  332. LinesToDisplay = 0;
  333. }
  334. //
  335. // Display a group of lines
  336. //
  337. Pager.DisplayPage( LinesToDisplay,
  338. ClearScreen,
  339. _SqueezeBlanksSwitch,
  340. _ExpandFormFeedSwitch,
  341. _TabExp );
  342. //
  343. // If not at end of stream, we wait for an option
  344. //
  345. if (Pager.ThereIsMoreToPage() || (FilesLeft > 0)) {
  346. StayInFile = DoOption( FsnFile, &Pager, &LinesToDisplay, &ClearScreen );
  347. }
  348. }
  349. }
  350. BOOLEAN
  351. MORE::DoOption (
  352. IN PFSN_FILE FsnFile,
  353. IN PPAGER Pager,
  354. OUT PULONG LinesInPage,
  355. OUT PBOOLEAN ClearScreen
  356. )
  357. /*++
  358. Routine Description:
  359. Gets an option from the user
  360. Arguments:
  361. FsnFile - Supplies pointer to file object
  362. Pager - Supplies pointer to pager
  363. LinesInpage - Supplies pointer to lines to display in next page
  364. ClearScreen - Supplies pointer to Clearscreen flag.
  365. Return Value:
  366. TRUE if paging should continue for this file,
  367. FALSE otherwise
  368. --*/
  369. {
  370. WCHAR Char;
  371. DSTRING String;
  372. BOOLEAN ShowLineNumber = FALSE;
  373. BOOLEAN ShowHelp = FALSE;
  374. LONG Number;
  375. String.Initialize( " " );
  376. while ( TRUE ) {
  377. //
  378. // Display prompt
  379. //
  380. Prompt( FsnFile, Pager, ShowLineNumber, ShowHelp, 0 );
  381. ShowHelp = FALSE;
  382. ShowLineNumber = FALSE;
  383. //
  384. // Get option from the user
  385. //
  386. _Keyboard->DisableLineMode();
  387. _Keyboard->ReadChar( &Char );
  388. _Keyboard->EnableLineMode();
  389. String.SetChAt(Char, 0);
  390. String.Strupr();
  391. Pager->ClearLine();
  392. //
  393. // If Ctl-C, get out
  394. //
  395. if ( Char == CTRLC_CHARACTER ) {
  396. _Keyboard->EnableLineMode();
  397. GenerateConsoleCtrlEvent( CTRL_C_EVENT, 0 );
  398. _Quit = TRUE;
  399. return FALSE;
  400. }
  401. //
  402. // If not in extended mode, any key just advances one page
  403. //
  404. if ( !_ExtendedModeSwitch ) {
  405. *LinesInPage = Pager->QueryLinesPerPage() - 1;
  406. return TRUE;
  407. }
  408. //
  409. // Now take the proper action
  410. //
  411. if ( String.QueryChAt(0) == (WCHAR)CARRIAGERETURN ) {
  412. //
  413. // Display next line of the file
  414. //
  415. *LinesInPage = 1;
  416. *ClearScreen = FALSE;
  417. return TRUE;
  418. } else if ( String.QueryChAt(0) == (WCHAR)' ' ) {
  419. //
  420. // Display next page
  421. //
  422. *ClearScreen = _ClearScreenSwitch;
  423. *LinesInPage = Pager->QueryLinesPerPage() - 1;
  424. return TRUE;
  425. } else if ( String.Stricmp(_DisplayLinesOption) == 0 ) {
  426. //
  427. // Display a certain number of lines. Get the number of lines
  428. // to display
  429. //
  430. Prompt( FsnFile, Pager, ShowLineNumber, ShowHelp, MORE_LINEPROMPT );
  431. *LinesInPage = ReadNumber();
  432. //if ( ReadLine( _Keyboard, &String ) &&
  433. // String.QueryNumber((PLONG)LinesInPage) ) {
  434. //
  435. // (*LinesInPage)--;
  436. //
  437. //} else {
  438. // *LinesInPage = 0;
  439. //}
  440. Pager->ClearLine();
  441. *ClearScreen = FALSE;
  442. return TRUE;
  443. } else if ( String.Stricmp(_SkipLinesOption) == 0 ) {
  444. //
  445. // Skip a certain number of lines and then display a page.
  446. //
  447. Prompt( FsnFile, Pager, ShowLineNumber, ShowHelp, MORE_LINEPROMPT );
  448. Number = ReadNumber( );
  449. if ( Number ) {
  450. Pager->SkipLines( Number, _TabExp );
  451. }
  452. Pager->ClearLine();
  453. *LinesInPage = Pager->QueryLinesPerPage() - 1;
  454. return TRUE;
  455. } else if ( String.Stricmp(_NextFileOption) == 0 ) {
  456. //
  457. // Stop paging this file
  458. //
  459. return FALSE;
  460. } else if ( String.Stricmp(_QuitOption) == 0 ) {
  461. //
  462. // Quit the program
  463. //
  464. _Quit = TRUE;
  465. return FALSE;
  466. } else if ( String.Stricmp(_ShowLineNumberOption) == 0) {
  467. //
  468. // Prompt again, showing the line number within the file
  469. //
  470. ShowLineNumber = TRUE;
  471. } else if ( ( String.Stricmp(_Help1Option) == 0) ||
  472. ( String.Stricmp(_Help2Option) == 0)) {
  473. //
  474. // Prompt again, showing a message line
  475. //
  476. ShowHelp = TRUE;
  477. }
  478. }
  479. }
  480. VOID
  481. MORE::Prompt (
  482. IN PFSN_FILE FsnFile,
  483. IN PPAGER Pager,
  484. IN BOOLEAN ShowLineNumber,
  485. IN BOOLEAN ShowHelp,
  486. IN MSGID OtherMsgId
  487. )
  488. /*++
  489. Routine Description:
  490. Displays prompt. The prompt consists of a "base" prompt (e.g.
  491. "-- More --" plus various optional strings:
  492. - Percentage of the file displayed so far.
  493. - Line number within the file
  494. - Help
  495. - Other (e.g prompt for a number )
  496. Arguments:
  497. FsnFile - Supplies pointer to file object
  498. Pager - Supplies pointer to pager
  499. ShowLineNumber - Supplies flag which if TRUE causes the current
  500. line numnber to be displayed
  501. HelpMsg - Supplies flag which if TRUE causes a brief help
  502. to be displayed
  503. OtherMsg - Supplies MsgId of any other string to be displayed
  504. Return Value:
  505. none
  506. --*/
  507. {
  508. CHAR NullBuffer = NULL_CHARACTER;
  509. PVOID PercentMsg;
  510. PVOID LineMsg;
  511. PVOID HelpMsg;
  512. PVOID OtherMsg;
  513. //
  514. // Obtain all the strings that form part of the prompt
  515. //
  516. if ( FsnFile != NULL ) {
  517. SYSTEM::QueryResourceString( _Percent, MORE_PERCENT, "%d", (Pager->QueryCurrentByte() * 100) / FsnFile->QuerySize());
  518. _Percent->QuerySTR( 0, TO_END, (PSTR)_StringBuffer0, STRING_BUFFER_SIZE);
  519. PercentMsg = (PVOID)_StringBuffer0;
  520. } else {
  521. PercentMsg = (PVOID)&NullBuffer;
  522. }
  523. if (ShowLineNumber) {
  524. SYSTEM::QueryResourceString( _Line, MORE_LINE, "%d", Pager->QueryCurrentLine());
  525. _Line->QuerySTR( 0, TO_END, (PSTR)_StringBuffer1, STRING_BUFFER_SIZE);
  526. LineMsg = (PVOID)_StringBuffer1;
  527. } else {
  528. LineMsg = (PVOID)&NullBuffer;
  529. }
  530. if (ShowHelp) {
  531. _Help->QuerySTR(0, TO_END, (PSTR)_StringBuffer2, STRING_BUFFER_SIZE);
  532. HelpMsg = (PVOID)_StringBuffer2;
  533. } else {
  534. HelpMsg = (PVOID)&NullBuffer;
  535. }
  536. if (OtherMsgId != 0) {
  537. SYSTEM::QueryResourceString( _OtherPrompt, OtherMsgId, "" );
  538. _OtherPrompt->QuerySTR(0, TO_END, (PSTR)_StringBuffer3, STRING_BUFFER_SIZE);
  539. OtherMsg = (PVOID)_StringBuffer3;
  540. } else {
  541. OtherMsg = (PVOID)&NullBuffer;
  542. }
  543. //
  544. // Now display the prompt
  545. //
  546. DisplayMessage( MORE_PROMPT, NORMAL_MESSAGE, "%s%s%s%s", PercentMsg, LineMsg, HelpMsg, OtherMsg );
  547. }
  548. PWSTRING
  549. MORE::QueryMessageString (
  550. IN MSGID MsgId
  551. )
  552. /*++
  553. Routine Description:
  554. Obtains a string object initialized to the contents of some message
  555. Arguments:
  556. MsgId - Supplies ID of the message
  557. Return Value:
  558. Pointer to initialized string object
  559. Notes:
  560. --*/
  561. {
  562. PWSTRING String;
  563. if ( ((String = NEW DSTRING) == NULL ) ||
  564. !(SYSTEM::QueryResourceString( String, MsgId, "" )) ) {
  565. DELETE( String );
  566. String = NULL;
  567. }
  568. return String;
  569. }
  570. ULONG
  571. MORE::ReadNumber (
  572. )
  573. /*++
  574. Routine Description:
  575. Reads a number from the keyboard.
  576. Arguments:
  577. None
  578. Return Value:
  579. Number read
  580. Notes:
  581. --*/
  582. {
  583. DSTRING NumberString;
  584. DSTRING CharString;
  585. PSTREAM StandardOut;
  586. ULONG Number = 0;
  587. LONG LongNumber;
  588. WCHAR Char;
  589. BOOLEAN Done = FALSE;
  590. ULONG DigitCount = 0;
  591. StandardOut = GetStandardOutput();
  592. NumberString.Initialize( "" );
  593. CharString.Initialize( " " );
  594. while ( !Done ) {
  595. _Keyboard->DisableLineMode();
  596. _Keyboard->ReadChar( &Char );
  597. _Keyboard->EnableLineMode();
  598. switch ( Char ) {
  599. case '0':
  600. case '1':
  601. case '2':
  602. case '3':
  603. case '4':
  604. case '5':
  605. case '6':
  606. case '7':
  607. case '8':
  608. case '9':
  609. CharString.SetChAt( Char, 0 );
  610. NumberString.Strcat( &CharString );
  611. StandardOut->WriteChar( Char );
  612. DigitCount++;
  613. break;
  614. case '\b':
  615. if ( DigitCount > 0 ) {
  616. NumberString.Truncate( NumberString.QueryChCount() - 1 );
  617. StandardOut->WriteChar( Char );
  618. StandardOut->WriteChar( ' ' );
  619. StandardOut->WriteChar( Char );
  620. DigitCount--;
  621. }
  622. break;
  623. case '\r':
  624. case '\n':
  625. Done = TRUE;
  626. break;
  627. case CTRLC_CHARACTER:
  628. _Quit = TRUE;
  629. Done = TRUE;
  630. break;
  631. default:
  632. break;
  633. }
  634. }
  635. if ( NumberString.QueryChCount() > 0 ) {
  636. if ( NumberString.QueryNumber( &LongNumber ) ) {
  637. Number = (ULONG)LongNumber;
  638. }
  639. }
  640. return Number;
  641. }