mirror of
https://github.com/reactos/reactos.git
synced 2026-05-08 07:57:48 +08:00
conutils\pager.c(658): warning C4267: '=': conversion from 'size_t' to 'DWORD', possible loss of data conutils\outstream.c(179),(263): warning C4267: '=': conversion from 'size_t' to 'DWORD', possible loss of data conutils\outstream.c(433): warning C4267: '=': conversion from 'size_t' to 'INT', possible loss of data
689 lines
20 KiB
C
689 lines
20 KiB
C
/*
|
|
* PROJECT: ReactOS Console Utilities Library
|
|
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
|
|
* PURPOSE: Console/terminal paging functionality.
|
|
* COPYRIGHT: Copyright 2017-2021 Hermes Belusca-Maito
|
|
* Copyright 2021 Katayama Hirofumi MZ
|
|
*/
|
|
|
|
/**
|
|
* @file pager.c
|
|
* @ingroup ConUtils
|
|
*
|
|
* @brief Console/terminal paging functionality.
|
|
**/
|
|
|
|
/* FIXME: Temporary HACK before we cleanly support UNICODE functions */
|
|
#define UNICODE
|
|
#define _UNICODE
|
|
|
|
#include <windef.h>
|
|
#include <winbase.h>
|
|
#include <wincon.h> // Console APIs (only if kernel32 support included)
|
|
#include <winnls.h> // For WideCharToMultiByte
|
|
#include <strsafe.h>
|
|
|
|
#include "conutils.h"
|
|
#include "stream.h"
|
|
#include "screen.h"
|
|
#include "pager.h"
|
|
|
|
// Temporary HACK
|
|
#define CON_STREAM_WRITE ConStreamWrite
|
|
|
|
#define CP_SHIFTJIS 932 // Japanese Shift-JIS
|
|
#define CP_HANGUL 949 // Korean Hangul/Wansung
|
|
#define CP_JOHAB 1361 // Korean Johab
|
|
#define CP_GB2312 936 // Chinese Simplified (GB2312)
|
|
#define CP_BIG5 950 // Chinese Traditional (Big5)
|
|
|
|
/* IsFarEastCP(CodePage) */
|
|
#define IsCJKCodePage(CodePage) \
|
|
((CodePage) == CP_SHIFTJIS || (CodePage) == CP_HANGUL || \
|
|
/* (CodePage) == CP_JOHAB || */ \
|
|
(CodePage) == CP_BIG5 || (CodePage) == CP_GB2312)
|
|
|
|
static inline INT
|
|
GetWidthOfCharCJK(
|
|
IN UINT nCodePage,
|
|
IN WCHAR ch)
|
|
{
|
|
INT ret = WideCharToMultiByte(nCodePage, 0, &ch, 1, NULL, 0, NULL, NULL);
|
|
if (ret == 0)
|
|
ret = 1;
|
|
else if (ret > 2)
|
|
ret = 2;
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Retrieves a new text line, or continue fetching the current one.
|
|
*
|
|
* @remark Manages setting Pager's CurrentLine, ichCurr, iEndLine, and the
|
|
* line cache (CachedLine, cchCachedLine). Other functions must not
|
|
* modify these values.
|
|
**/
|
|
static BOOL
|
|
GetNextLine(
|
|
IN OUT PCON_PAGER Pager,
|
|
IN PCTCH TextBuff,
|
|
IN SIZE_T cch)
|
|
{
|
|
SIZE_T ich = Pager->ich;
|
|
SIZE_T ichStart;
|
|
SIZE_T cchLine;
|
|
BOOL bCacheLine;
|
|
|
|
Pager->ichCurr = 0;
|
|
Pager->iEndLine = 0;
|
|
|
|
/*
|
|
* If we already had an existing line, then we can safely start a new one
|
|
* and getting rid of any current cached line. Otherwise, we don't have
|
|
* a current line and we may be caching a new one, in which case, continue
|
|
* caching it until it becomes complete.
|
|
*/
|
|
// INVESTIGATE: Do that only if (ichStart >= iEndLine) ??
|
|
if (Pager->CurrentLine)
|
|
{
|
|
// ASSERT(Pager->CurrentLine == Pager->CachedLine);
|
|
if (Pager->CachedLine)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, (PVOID)Pager->CachedLine);
|
|
Pager->CachedLine = NULL;
|
|
Pager->cchCachedLine = 0;
|
|
}
|
|
|
|
Pager->CurrentLine = NULL;
|
|
}
|
|
|
|
/* Nothing else to read if we are past the end of the buffer */
|
|
if (ich >= cch)
|
|
{
|
|
/* If we have a pending cached line, terminate it now */
|
|
if (Pager->CachedLine)
|
|
goto TerminateLine;
|
|
|
|
/* Otherwise, bail out */
|
|
return FALSE;
|
|
}
|
|
|
|
/* Start a new line, or continue an existing one */
|
|
ichStart = ich;
|
|
|
|
/* Find where this line ends, looking for a NEWLINE character.
|
|
* (NOTE: We cannot use strchr because the buffer is not NULL-terminated) */
|
|
for (; ich < cch; ++ich)
|
|
{
|
|
if (TextBuff[ich] == TEXT('\n'))
|
|
{
|
|
++ich;
|
|
break;
|
|
}
|
|
}
|
|
Pager->ich = ich;
|
|
|
|
cchLine = (ich - ichStart);
|
|
|
|
//
|
|
// FIXME: Impose a maximum string limit when the line is cached, in order
|
|
// not to potentially grow memory indefinitely. When the limit is reached,
|
|
// terminate the line.
|
|
//
|
|
|
|
/*
|
|
* If we have stopped because we have exhausted the text buffer
|
|
* and we have not found an end-of-line character, this may mean
|
|
* that the text line spans across different text buffers. If we
|
|
* have been told so, cache this line: we will complete it during
|
|
* the next call(s) and only then, display it.
|
|
* Otherwise, consider the line to be terminated now.
|
|
*/
|
|
bCacheLine = ((Pager->dwFlags & CON_PAGER_CACHE_INCOMPLETE_LINE) &&
|
|
(ich >= cch) && (TextBuff[ich - 1] != TEXT('\n')));
|
|
|
|
/* Allocate, or re-allocate, the cached line buffer */
|
|
if (bCacheLine && !Pager->CachedLine)
|
|
{
|
|
/* We start caching, allocate the cached line buffer */
|
|
Pager->CachedLine = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
|
|
cchLine * sizeof(TCHAR));
|
|
Pager->cchCachedLine = 0;
|
|
|
|
if (!Pager->CachedLine)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (Pager->CachedLine)
|
|
{
|
|
/* We continue caching, re-allocate the cached line buffer */
|
|
PVOID ptr = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
|
|
(PVOID)Pager->CachedLine,
|
|
(Pager->cchCachedLine + cchLine) * sizeof(TCHAR));
|
|
if (!ptr)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, (PVOID)Pager->CachedLine);
|
|
Pager->CachedLine = NULL;
|
|
Pager->cchCachedLine = 0;
|
|
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
Pager->CachedLine = ptr;
|
|
}
|
|
if (Pager->CachedLine)
|
|
{
|
|
/* Copy/append the text to the cached line buffer */
|
|
RtlCopyMemory((PVOID)&Pager->CachedLine[Pager->cchCachedLine],
|
|
&TextBuff[ichStart],
|
|
cchLine * sizeof(TCHAR));
|
|
Pager->cchCachedLine += cchLine;
|
|
}
|
|
if (bCacheLine)
|
|
{
|
|
/* The line is currently incomplete, don't proceed further for now */
|
|
return FALSE;
|
|
}
|
|
|
|
TerminateLine:
|
|
/* The line should be complete now. If we have an existing cached line,
|
|
* it has been completed by appending the remaining text to it. */
|
|
|
|
/* We are starting a new line */
|
|
Pager->ichCurr = 0;
|
|
if (Pager->CachedLine)
|
|
{
|
|
Pager->iEndLine = Pager->cchCachedLine;
|
|
Pager->CurrentLine = Pager->CachedLine;
|
|
}
|
|
else
|
|
{
|
|
Pager->iEndLine = cchLine;
|
|
Pager->CurrentLine = &TextBuff[ichStart];
|
|
}
|
|
|
|
/* Increase only when we have got a NEWLINE */
|
|
if ((Pager->iEndLine > 0) && (Pager->CurrentLine[Pager->iEndLine - 1] == TEXT('\n')))
|
|
Pager->lineno++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Does the main paging work: fetching text lines and displaying them.
|
|
**/
|
|
static BOOL
|
|
ConPagerWorker(
|
|
IN PCON_PAGER Pager,
|
|
IN PCTCH TextBuff,
|
|
IN SIZE_T cch)
|
|
{
|
|
const DWORD PageColumns = Pager->PageColumns;
|
|
const DWORD ScrollRows = Pager->ScrollRows;
|
|
|
|
BOOL bFinitePaging = ((PageColumns > 0) && (Pager->PageRows > 0));
|
|
LONG nTabWidth = Pager->nTabWidth;
|
|
|
|
PCTCH Line;
|
|
SIZE_T ich;
|
|
SIZE_T ichStart;
|
|
SIZE_T iEndLine;
|
|
DWORD iColumn = Pager->iColumn;
|
|
|
|
UINT nCodePage = GetConsoleOutputCP();
|
|
BOOL IsCJK = IsCJKCodePage(nCodePage);
|
|
UINT nWidthOfChar = 1;
|
|
BOOL IsDoubleWidthCharTrailing = FALSE;
|
|
|
|
/* Normalize the tab width: if negative or too large,
|
|
* cap it to the number of columns. */
|
|
if (PageColumns > 0) // if (bFinitePaging)
|
|
{
|
|
if (nTabWidth < 0)
|
|
nTabWidth = PageColumns - 1;
|
|
else
|
|
nTabWidth = min(nTabWidth, PageColumns - 1);
|
|
}
|
|
else
|
|
{
|
|
/* If no column width is known, default to 8 spaces if the
|
|
* original value is negative; otherwise keep the current one. */
|
|
if (nTabWidth < 0)
|
|
nTabWidth = 8;
|
|
}
|
|
|
|
|
|
/* Continue displaying the previous line, if any, or start a new one */
|
|
Line = Pager->CurrentLine;
|
|
ichStart = Pager->ichCurr;
|
|
iEndLine = Pager->iEndLine;
|
|
|
|
ProcessLine:
|
|
|
|
/* Stop now if we have displayed more page lines than requested */
|
|
if (bFinitePaging && (Pager->iLine >= ScrollRows))
|
|
goto End;
|
|
|
|
if (!Line || (ichStart >= iEndLine))
|
|
{
|
|
/* Start a new line */
|
|
if (!GetNextLine(Pager, TextBuff, cch))
|
|
goto End;
|
|
|
|
Line = Pager->CurrentLine;
|
|
ichStart = Pager->ichCurr;
|
|
iEndLine = Pager->iEndLine;
|
|
}
|
|
else
|
|
{
|
|
/* Continue displaying the current line */
|
|
}
|
|
|
|
// ASSERT(Line && ((ichStart < iEndLine) || (ichStart == iEndLine && iEndLine == 0)));
|
|
|
|
/* Determine whether this line segment (from the current position till the end) should be displayed */
|
|
Pager->iColumn = iColumn;
|
|
if (Pager->PagerLine && Pager->PagerLine(Pager, &Line[ichStart], iEndLine - ichStart))
|
|
{
|
|
iColumn = Pager->iColumn;
|
|
|
|
/* Done with this line; start a new one */
|
|
Pager->nSpacePending = 0; // And reset any pending space.
|
|
ichStart = iEndLine;
|
|
goto ProcessLine;
|
|
}
|
|
// else: Continue displaying the line.
|
|
|
|
|
|
/* Print out any pending TAB expansion */
|
|
if (Pager->nSpacePending > 0)
|
|
{
|
|
ExpandTab:
|
|
while (Pager->nSpacePending > 0)
|
|
{
|
|
/* Print filling spaces */
|
|
CON_STREAM_WRITE(Pager->Screen->Stream, TEXT(" "), 1);
|
|
--(Pager->nSpacePending);
|
|
++iColumn;
|
|
|
|
/* Check whether we are going across the column */
|
|
if ((PageColumns > 0) && (iColumn % PageColumns == 0))
|
|
{
|
|
// Pager->nSpacePending = 0; // <-- This is the mode of most text editors...
|
|
|
|
/* Reposition the cursor to the next line, first column */
|
|
if (!bFinitePaging || (PageColumns < Pager->Screen->csbi.dwSize.X))
|
|
CON_STREAM_WRITE(Pager->Screen->Stream, TEXT("\n"), 1);
|
|
|
|
Pager->iLine++;
|
|
|
|
/* Restart at the character */
|
|
// ASSERT(ichStart == ich);
|
|
goto ProcessLine;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Find, within this line segment (starting from its
|
|
* beginning), until where we can print to the page. */
|
|
for (ich = ichStart; ich < iEndLine; ++ich)
|
|
{
|
|
/* NEWLINE character */
|
|
if (Line[ich] == TEXT('\n'))
|
|
{
|
|
/* We should stop now */
|
|
// ASSERT(ich == iEndLine - 1);
|
|
break;
|
|
}
|
|
|
|
/* TAB character */
|
|
if (Line[ich] == TEXT('\t') &&
|
|
(Pager->dwFlags & CON_PAGER_EXPAND_TABS))
|
|
{
|
|
/* We should stop now */
|
|
break;
|
|
}
|
|
|
|
/* FORM-FEED character */
|
|
if (Line[ich] == TEXT('\f') &&
|
|
(Pager->dwFlags & CON_PAGER_EXPAND_FF))
|
|
{
|
|
/* We should stop now */
|
|
break;
|
|
}
|
|
|
|
/* Other character - Handle double-width for CJK */
|
|
|
|
if (IsCJK)
|
|
nWidthOfChar = GetWidthOfCharCJK(nCodePage, Line[ich]);
|
|
|
|
/* Care about CJK character presentation only when outputting
|
|
* to a device where the number of columns is known. */
|
|
if ((PageColumns > 0) && IsCJK)
|
|
{
|
|
IsDoubleWidthCharTrailing = (nWidthOfChar == 2) &&
|
|
((iColumn + 1) % PageColumns == 0);
|
|
if (IsDoubleWidthCharTrailing)
|
|
{
|
|
/* Reserve this character for the next line */
|
|
++iColumn; // Count a blank instead.
|
|
/* We should stop now */
|
|
break;
|
|
}
|
|
}
|
|
|
|
iColumn += nWidthOfChar;
|
|
|
|
/* Check whether we are going across the column */
|
|
if ((PageColumns > 0) && (iColumn % PageColumns == 0))
|
|
{
|
|
++ich;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Output the pending line segment */
|
|
if (ich - ichStart > 0)
|
|
CON_STREAM_WRITE(Pager->Screen->Stream, &Line[ichStart], ich - ichStart);
|
|
|
|
/* Have we finished the line segment? */
|
|
if (ich >= iEndLine)
|
|
{
|
|
/* Restart at the character */
|
|
ichStart = ich;
|
|
goto ProcessLine;
|
|
}
|
|
|
|
/* Handle special characters */
|
|
|
|
/* NEWLINE character */
|
|
if (Line[ich] == TEXT('\n'))
|
|
{
|
|
// ASSERT(ich == iEndLine - 1);
|
|
|
|
/* Reposition the cursor to the next line, first column */
|
|
CON_STREAM_WRITE(Pager->Screen->Stream, TEXT("\n"), 1);
|
|
|
|
Pager->iLine++;
|
|
iColumn = 0;
|
|
|
|
/* Done with this line; start a new one */
|
|
Pager->nSpacePending = 0; // And reset any pending space.
|
|
ichStart = iEndLine;
|
|
goto ProcessLine;
|
|
}
|
|
|
|
/* TAB character */
|
|
if (Line[ich] == TEXT('\t') &&
|
|
(Pager->dwFlags & CON_PAGER_EXPAND_TABS))
|
|
{
|
|
/* Perform TAB expansion, unless the tab width is zero */
|
|
if (nTabWidth == 0)
|
|
{
|
|
ichStart = ++ich;
|
|
goto ProcessLine;
|
|
}
|
|
|
|
ichStart = ++ich;
|
|
/* Reset the number of spaces needed to develop this TAB character */
|
|
Pager->nSpacePending = nTabWidth - (iColumn % nTabWidth);
|
|
goto ExpandTab;
|
|
}
|
|
|
|
/* FORM-FEED character */
|
|
if (Line[ich] == TEXT('\f') &&
|
|
(Pager->dwFlags & CON_PAGER_EXPAND_FF))
|
|
{
|
|
if (bFinitePaging)
|
|
{
|
|
/* Clear until the end of the page */
|
|
while (Pager->iLine < ScrollRows)
|
|
{
|
|
/* Call the user paging function in order to know
|
|
* whether we need to output the blank lines. */
|
|
Pager->iColumn = iColumn;
|
|
if (Pager->PagerLine && Pager->PagerLine(Pager, TEXT("\n"), 1))
|
|
{
|
|
/* Only one blank line displayed, that counts in the line count */
|
|
Pager->iLine++;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
CON_STREAM_WRITE(Pager->Screen->Stream, TEXT("\n"), 1);
|
|
Pager->iLine++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Just output a FORM-FEED and a NEWLINE */
|
|
CON_STREAM_WRITE(Pager->Screen->Stream, TEXT("\f\n"), 2);
|
|
Pager->iLine++;
|
|
}
|
|
|
|
iColumn = 0;
|
|
Pager->nSpacePending = 0; // And reset any pending space.
|
|
|
|
/* Skip and restart past the character */
|
|
ichStart = ++ich;
|
|
goto ProcessLine;
|
|
}
|
|
|
|
/* If we output a double-width character that goes across the column,
|
|
* fill with blank and display the character on the next line. */
|
|
if (IsDoubleWidthCharTrailing)
|
|
{
|
|
IsDoubleWidthCharTrailing = FALSE; // Reset the flag.
|
|
CON_STREAM_WRITE(Pager->Screen->Stream, TEXT(" "), 1);
|
|
/* Fall back below */
|
|
}
|
|
|
|
/* Are we wrapping the line? */
|
|
if ((PageColumns > 0) && (iColumn % PageColumns == 0))
|
|
{
|
|
/* Reposition the cursor to the next line, first column */
|
|
if (!bFinitePaging || (PageColumns < Pager->Screen->csbi.dwSize.X))
|
|
CON_STREAM_WRITE(Pager->Screen->Stream, TEXT("\n"), 1);
|
|
|
|
Pager->iLine++;
|
|
}
|
|
|
|
/* Restart at the character */
|
|
ichStart = ich;
|
|
goto ProcessLine;
|
|
|
|
|
|
End:
|
|
/*
|
|
* We are exiting, either because we displayed all the required lines
|
|
* (iLine >= ScrollRows), or, because we don't have more data to display.
|
|
*/
|
|
|
|
Pager->ichCurr = ichStart;
|
|
Pager->iColumn = iColumn;
|
|
// INVESTIGATE: Can we get rid of CurrentLine here? // if (ichStart >= iEndLine) ...
|
|
|
|
/* Return TRUE if we displayed all the required lines; FALSE otherwise */
|
|
if (bFinitePaging && (Pager->iLine >= ScrollRows))
|
|
{
|
|
Pager->iLine = 0; /* Reset the count of lines being printed */
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @name ConWritePaging
|
|
* Pages the contents of a user-specified character buffer on the screen.
|
|
*
|
|
* @param[in] Pager
|
|
* Pager object that describes where the paged output is issued.
|
|
*
|
|
* @param[in] PagePrompt
|
|
* A user-specific callback, called when a page has been displayed.
|
|
*
|
|
* @param[in] StartPaging
|
|
* Set to TRUE for initializing the paging operation; FALSE during paging.
|
|
*
|
|
* @param[in] szStr
|
|
* Pointer to the character buffer whose contents are to be paged.
|
|
*
|
|
* @param[in] len
|
|
* Length of the character buffer pointed by @p szStr, specified
|
|
* in number of characters.
|
|
*
|
|
* @return
|
|
* TRUE when all the contents of the character buffer has been displayed;
|
|
* FALSE if the paging operation has been stopped (controlled via @p PagePrompt).
|
|
**/
|
|
BOOL
|
|
ConWritePaging(
|
|
IN PCON_PAGER Pager,
|
|
IN PAGE_PROMPT PagePrompt,
|
|
IN BOOL StartPaging,
|
|
IN PCTCH szStr,
|
|
IN SIZE_T len)
|
|
{
|
|
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
|
BOOL bIsConsole;
|
|
|
|
/* Parameters validation */
|
|
if (!Pager)
|
|
return FALSE;
|
|
|
|
/* Get the size of the visual screen that can be printed to */
|
|
bIsConsole = ConGetScreenInfo(Pager->Screen, &csbi);
|
|
if (bIsConsole)
|
|
{
|
|
/* Calculate the console screen extent */
|
|
Pager->PageColumns = csbi.dwSize.X;
|
|
Pager->PageRows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
|
|
}
|
|
else
|
|
{
|
|
/* We assume it's a file handle */
|
|
Pager->PageColumns = 0;
|
|
Pager->PageRows = 0;
|
|
}
|
|
|
|
if (StartPaging)
|
|
{
|
|
if (bIsConsole && (Pager->PageRows >= 2))
|
|
{
|
|
/* Reset to display one page by default */
|
|
Pager->ScrollRows = Pager->PageRows - 1;
|
|
}
|
|
else
|
|
{
|
|
/* File output, or single line: all lines are displayed at once; reset to a default value */
|
|
Pager->ScrollRows = 0;
|
|
}
|
|
|
|
/* Reset the internal data buffer */
|
|
Pager->CachedLine = NULL;
|
|
Pager->cchCachedLine = 0;
|
|
|
|
/* Reset the paging state */
|
|
Pager->CurrentLine = NULL;
|
|
Pager->ichCurr = 0;
|
|
Pager->iEndLine = 0;
|
|
Pager->nSpacePending = 0;
|
|
Pager->iColumn = 0;
|
|
Pager->iLine = 0;
|
|
Pager->lineno = 0;
|
|
}
|
|
|
|
/* Reset the reading index in the user-provided source buffer */
|
|
Pager->ich = 0;
|
|
|
|
/* Run the pager even when the user-provided source buffer is
|
|
* empty, in case we need to flush any remaining cached line. */
|
|
if (!Pager->CachedLine)
|
|
{
|
|
/* No cached line, bail out now */
|
|
if (len == 0 || szStr == NULL)
|
|
return TRUE;
|
|
}
|
|
|
|
while (ConPagerWorker(Pager, szStr, len))
|
|
{
|
|
/* Prompt the user only when we display to a console and the screen
|
|
* is not too small: at least one line for the actual paged text and
|
|
* one line for the prompt. */
|
|
if (bIsConsole && (Pager->PageRows >= 2))
|
|
{
|
|
/* Reset to display one page by default */
|
|
Pager->ScrollRows = Pager->PageRows - 1;
|
|
|
|
/* Prompt the user; give him some values for statistics */
|
|
// FIXME: Doesn't reflect what's currently being displayed.
|
|
if (!PagePrompt(Pager, Pager->ich, len))
|
|
return FALSE;
|
|
}
|
|
|
|
/* If we display to a console, recalculate its screen extent
|
|
* in case the user has redimensioned it during the prompt. */
|
|
if (bIsConsole && ConGetScreenInfo(Pager->Screen, &csbi))
|
|
{
|
|
Pager->PageColumns = csbi.dwSize.X;
|
|
Pager->PageRows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
ConPutsPaging(
|
|
IN PCON_PAGER Pager,
|
|
IN PAGE_PROMPT PagePrompt,
|
|
IN BOOL StartPaging,
|
|
IN PCTSTR szStr)
|
|
{
|
|
SIZE_T len;
|
|
|
|
/* Return if no string has been given */
|
|
if (szStr == NULL)
|
|
return TRUE;
|
|
|
|
len = wcslen(szStr);
|
|
return ConWritePaging(Pager, PagePrompt, StartPaging, szStr, len);
|
|
}
|
|
|
|
BOOL
|
|
ConResPagingEx(
|
|
IN PCON_PAGER Pager,
|
|
IN PAGE_PROMPT PagePrompt,
|
|
IN BOOL StartPaging,
|
|
IN HINSTANCE hInstance OPTIONAL,
|
|
IN UINT uID)
|
|
{
|
|
INT Len;
|
|
PCWSTR szStr = NULL;
|
|
|
|
Len = K32LoadStringW(hInstance, uID, (PWSTR)&szStr, 0);
|
|
if (szStr && Len)
|
|
return ConWritePaging(Pager, PagePrompt, StartPaging, szStr, Len);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
ConResPaging(
|
|
IN PCON_PAGER Pager,
|
|
IN PAGE_PROMPT PagePrompt,
|
|
IN BOOL StartPaging,
|
|
IN UINT uID)
|
|
{
|
|
return ConResPagingEx(Pager, PagePrompt, StartPaging, NULL, uID);
|
|
}
|
|
|
|
/* EOF */
|