https://git.reactos.org/?p=reactos.git;a=commitdiff;h=31322f5df910f79804c62…
commit 31322f5df910f79804c62d7b216415449cd5b4fb
Author: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
AuthorDate: Mon Jun 28 20:08:38 2021 +0200
Commit: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
CommitDate: Mon Jun 28 21:26:32 2021 +0200
[CONUTILS:PAGER][MORE] Implement text line caching + fix some bugs.
- Implement caching of individual (newline-separated) text lines; this
behaviour can be enabled with a flag (enabled by MORE):
CON_PAGER_CACHE_INCOMPLETE_LINE.
This feature is necessary when reading a text file, whose text lines
may span across two or more successive temporary read buffers, and is
required for correctly determining whether the lines being read are
blank and may be squeezed.
- When squeezing blank lines, the blank-line check must be done for each
line segment corresponding to the screen line (and following) that
need to be displayed. This matches the behaviour of MS MORE.COM.
- Fix the IsBlankLine() check to not consider FORM-FEEDs as being blank
characters: This is necessary for correctly handling FORM-FEED
expansion. Also note that MS MORE.COM only checks for spaces and TABs,
so we are slightly overdoing these checks (considering other types of
whitespace).
- Get rid of ConCallPagerLine() and the intermediate CON_PAGER_DONT_OUTPUT
state flag that were used repeatedly for each and every small line
chunks. Instead, call directly the user-specified 'PagerLine' callback
when we are about to start treating the next line segment to be
displayed (see comment above).
- Fix the exit return condition of ConPagerWorker(): it should return
TRUE whenever we displayed all the required lines, and FALSE otherwise.
Otherwise, the previous (buggy) condition on the data being read from
the text file, may lead to the prompt not showing when a screenful of
text has been displayed, if it happened that the current text buffer
becomes empty at the same time (even if, overall, the text file hasn't
been fully displayed).
- In MorePagerLine(), when we encounter for the first time a blank line
that will be squeezed with other successive ones, display a single
blank line. But for that, just display one space and a newline: this
single space is especially needed in order to force line wrapping when
the ENABLE_VIRTUAL_TERMINAL_PROCESSING or DISABLE_NEWLINE_AUTO_RETURN
console modes are enabled. Otherwise the cursor remains at the
previous line (without wrapping), and just outputting one newline will
not make it move past 2 lines as one would naively expect.
---
base/applications/cmdutils/more/more.c | 67 ++++--
sdk/lib/conutils/pager.c | 387 ++++++++++++++++++++++++++-------
sdk/lib/conutils/pager.h | 18 +-
3 files changed, 363 insertions(+), 109 deletions(-)
diff --git a/base/applications/cmdutils/more/more.c b/base/applications/cmdutils/more/more.c
index 5b868f0b5dc..afe20b9e803 100644
--- a/base/applications/cmdutils/more/more.c
+++ b/base/applications/cmdutils/more/more.c
@@ -80,6 +80,19 @@ static BOOL IsBlankLine(IN PCWCH line, IN DWORD cch)
WORD wType;
for (ich = 0; ich < cch; ++ich)
{
+ /*
+ * Explicitly exclude FORM-FEED from the check,
+ * so that the pager can handle it.
+ */
+ if (line[ich] == L'\f')
+ return FALSE;
+
+ /*
+ * Otherwise do the extended blanks check.
+ * Note that MS MORE.COM only checks for spaces (\x20) and TABs (\x09).
+ * See http://archives.miloush.net/michkap/archive/2007/06/11/3230072.html
+ * for more information.
+ */
wType = 0;
GetStringTypeW(CT_CTYPE1, &line[ich], 1, &wType);
if (!(wType & (C1_BLANK | C1_SPACE)))
@@ -95,15 +108,12 @@ MorePagerLine(
IN PCWCH line,
IN DWORD cch)
{
- DWORD ich;
-
if (s_dwFlags & FLAG_PLUSn) /* Skip lines */
{
if (Pager->lineno < s_nNextLineNo)
{
- Pager->dwFlags |= CON_PAGER_DONT_OUTPUT;
s_bPrevLineIsBlank = FALSE;
- return TRUE; /* Don't output */
+ return TRUE; /* Handled */
}
s_dwFlags &= ~FLAG_PLUSn;
}
@@ -113,19 +123,26 @@ MorePagerLine(
if (IsBlankLine(line, cch))
{
if (s_bPrevLineIsBlank)
- {
- Pager->dwFlags |= CON_PAGER_DONT_OUTPUT;
- return TRUE; /* Don't output */
- }
+ return TRUE; /* Handled */
- for (ich = 0; ich < cch; ++ich)
- {
- if (line[ich] == L'\n')
- {
- s_bPrevLineIsBlank = TRUE;
- break;
- }
- }
+ /*
+ * Display a single blank line, independently of the actual size
+ * of the current line, by displaying just one space: this is
+ * especially needed in order to force line wrapping when the
+ * ENABLE_VIRTUAL_TERMINAL_PROCESSING or DISABLE_NEWLINE_AUTO_RETURN
+ * console modes are enabled.
+ * Then, reposition the cursor to the next line, first column.
+ */
+ if (Pager->PageColumns > 0)
+ ConStreamWrite(Pager->Screen->Stream, TEXT(" "), 1);
+ ConStreamWrite(Pager->Screen->Stream, TEXT("\n"), 1);
+ Pager->iLine++;
+ Pager->iColumn = 0;
+
+ s_bPrevLineIsBlank = TRUE;
+ s_nNextLineNo = 0;
+
+ return TRUE; /* Handled */
}
else
{
@@ -134,7 +151,8 @@ MorePagerLine(
}
s_nNextLineNo = 0;
- return FALSE; /* Do output */
+ /* Not handled, let the pager do the default action */
+ return FALSE;
}
static BOOL
@@ -998,7 +1016,7 @@ int wmain(int argc, WCHAR* argv[])
}
Pager.PagerLine = MorePagerLine;
- Pager.dwFlags |= CON_PAGER_EXPAND_TABS;
+ Pager.dwFlags |= CON_PAGER_EXPAND_TABS | CON_PAGER_CACHE_INCOMPLETE_LINE;
if (s_dwFlags & FLAG_P)
Pager.dwFlags |= CON_PAGER_EXPAND_FF;
Pager.nTabWidth = s_nTabWidth;
@@ -1023,7 +1041,7 @@ int wmain(int argc, WCHAR* argv[])
Encoding = ENCODING_ANSI; // ENCODING_UTF8;
/* Start paging */
- bContinue = ConPutsPaging(&Pager, PagePrompt, TRUE, L"");
+ bContinue = ConWritePaging(&Pager, PagePrompt, TRUE, NULL, 0);
if (!bContinue)
goto Quit;
@@ -1050,6 +1068,11 @@ int wmain(int argc, WCHAR* argv[])
break;
}
while (bRet && dwReadBytes > 0);
+
+ /* Flush any cached pager buffers */
+ if (bContinue)
+ bContinue = ConWritePaging(&Pager, PagePrompt, FALSE, NULL, 0);
+
goto Quit;
}
@@ -1100,7 +1123,7 @@ int wmain(int argc, WCHAR* argv[])
dwSumReadBytes = dwSumReadChars = 0;
/* Start paging */
- bContinue = ConPutsPaging(&Pager, PagePrompt, TRUE, L"");
+ bContinue = ConWritePaging(&Pager, PagePrompt, TRUE, NULL, 0);
if (!bContinue)
{
/* We stop displaying this file */
@@ -1152,6 +1175,10 @@ int wmain(int argc, WCHAR* argv[])
}
while (bRet && dwReadBytes > 0);
+ /* Flush any cached pager buffers */
+ if (bContinue)
+ bContinue = ConWritePaging(&Pager, PagePrompt, FALSE, NULL, 0);
+
CloseHandle(hFile);
/* Check whether we should stop displaying this file */
diff --git a/sdk/lib/conutils/pager.c b/sdk/lib/conutils/pager.c
index 4005e455410..56b0f3e95e3 100644
--- a/sdk/lib/conutils/pager.c
+++ b/sdk/lib/conutils/pager.c
@@ -57,45 +57,187 @@ GetWidthOfCharCJK(
return ret;
}
-static VOID
-ConCallPagerLine(
+/**
+ * @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 line,
- IN DWORD cch)
+ IN PCTCH TextBuff,
+ IN SIZE_T cch)
{
- Pager->dwFlags &= ~CON_PAGER_DONT_OUTPUT; /* Clear the flag */
+ 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;
+ }
- if (!Pager->PagerLine || !Pager->PagerLine(Pager, line, cch))
- CON_STREAM_WRITE(Pager->Screen->Stream, line, cch);
+ /* 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)
+ConPagerWorker(
+ IN PCON_PAGER Pager,
+ IN PCTCH TextBuff,
+ IN DWORD cch)
{
const DWORD PageColumns = Pager->PageColumns;
const DWORD ScrollRows = Pager->ScrollRows;
- const PCTCH Line = Pager->TextBuff;
- const DWORD cch = Pager->cch;
BOOL bFinitePaging = ((PageColumns > 0) && (Pager->PageRows > 0));
LONG nTabWidth = Pager->nTabWidth;
- DWORD ich = Pager->ich;
+ PCTCH Line;
+ SIZE_T ich;
+ SIZE_T ichStart;
+ SIZE_T iEndLine;
DWORD iColumn = Pager->iColumn;
- DWORD iLine = Pager->iLine;
- DWORD ichStart = ich;
- UINT nCodePage;
- BOOL IsCJK;
+ UINT nCodePage = GetConsoleOutputCP();
+ BOOL IsCJK = IsCJKCodePage(nCodePage);
UINT nWidthOfChar = 1;
BOOL IsDoubleWidthCharTrailing = FALSE;
- if (ich >= cch)
- return FALSE;
-
- nCodePage = GetConsoleOutputCP();
- IsCJK = IsCJKCodePage(nCodePage);
-
/* Normalize the tab width: if negative or too large,
* cap it to the number of columns. */
if (PageColumns > 0) // if (bFinitePaging)
@@ -113,38 +255,88 @@ ConPagerWorker(IN PCON_PAGER Pager)
nTabWidth = 8;
}
- if (Pager->dwFlags & CON_PAGER_EXPAND_TABS)
+
+ /* 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)
{
- /* Stop now if we have displayed more screen lines than requested */
- if (bFinitePaging && (iLine >= ScrollRows))
- break;
-
- ConCallPagerLine(Pager, L" ", 1);
+ /* 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))
{
- if (!(Pager->dwFlags & CON_PAGER_DONT_OUTPUT))
- ++iLine;
+ // 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;
}
}
}
-ProcessLine:
- /* Stop now if we have displayed more screen lines than requested */
- if (bFinitePaging && (iLine >= ScrollRows))
- goto End;
- /* Loop over each character in the buffer */
- for (; ich < cch; ++ich)
+ /* 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;
}
@@ -194,33 +386,34 @@ ProcessLine:
}
}
- /* Output the pending text */
- Pager->dwFlags &= ~CON_PAGER_DONT_OUTPUT;
+ /* Output the pending line segment */
if (ich - ichStart > 0)
- ConCallPagerLine(Pager, &Line[ichStart], ich - ichStart);
+ CON_STREAM_WRITE(Pager->Screen->Stream, &Line[ichStart], ich - ichStart);
- /* Have we finished the buffer? */
- if (ich >= cch)
- goto End;
+ /* 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'))
{
- /* Output the newline */
- if (!(Pager->dwFlags & CON_PAGER_DONT_OUTPUT))
- {
- // ConCallPagerLine(Pager, L"\n", 1);
- CON_STREAM_WRITE(Pager->Screen->Stream, TEXT("\n"), 1);
- ++iLine;
- }
+ // 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.
- Pager->lineno++;
- ichStart = ++ich;
+ ichStart = iEndLine;
goto ProcessLine;
}
@@ -245,28 +438,38 @@ ProcessLine:
if (Line[ich] == TEXT('\f') &&
(Pager->dwFlags & CON_PAGER_EXPAND_FF))
{
- // FIXME: Should we handle CON_PAGER_DONT_OUTPUT ?
if (bFinitePaging)
{
- /* Clear until the end of the screen */
- while (iLine < ScrollRows)
+ /* Clear until the end of the page */
+ while (Pager->iLine < ScrollRows)
{
- ConCallPagerLine(Pager, L"\n", 1);
- // CON_STREAM_WRITE(Pager->Screen->Stream, TEXT("\n"), 1);
- // if (!(Pager->dwFlags & CON_PAGER_DONT_OUTPUT))
- ++iLine;
+ /* 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 character */
- ConCallPagerLine(Pager, L"\f", 1);
- // CON_STREAM_WRITE(Pager->Screen->Stream, L"\f", 1);
+ /* 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;
}
@@ -276,19 +479,18 @@ ProcessLine:
if (IsDoubleWidthCharTrailing)
{
IsDoubleWidthCharTrailing = FALSE; // Reset the flag.
-
- if (!(Pager->dwFlags & CON_PAGER_DONT_OUTPUT))
- CON_STREAM_WRITE(Pager->Screen->Stream, TEXT(" "), 1);
- // ++iLine;
-
+ CON_STREAM_WRITE(Pager->Screen->Stream, TEXT(" "), 1);
/* Fall back below */
}
/* Are we wrapping the line? */
if ((PageColumns > 0) && (iColumn % PageColumns == 0))
{
- if (!(Pager->dwFlags & CON_PAGER_DONT_OUTPUT))
- ++iLine;
+ /* 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 */
@@ -297,14 +499,25 @@ ProcessLine:
End:
- if (iLine >= ScrollRows)
- iLine = 0; /* Reset the count of lines being printed */
+ /*
+ * We are exiting, either because we displayed all the required lines
+ * (iLine >= ScrollRows), or, because we don't have more data to display.
+ */
- Pager->ich = ich;
+ Pager->ichCurr = ichStart;
Pager->iColumn = iColumn;
- Pager->iLine = iLine;
+ // INVESTIGATE: Can we get rid of CurrentLine here? // if (ichStart >= iEndLine) ...
- return (ich < cch);
+ /* 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;
+ }
}
@@ -374,25 +587,34 @@ ConWritePaging(
/* File output, or single line: all lines are displayed at once; reset to a default value */
Pager->ScrollRows = 0;
}
- }
- if (StartPaging)
- {
+ /* 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 = 1;
+ Pager->lineno = 0;
}
- Pager->TextBuff = szStr;
- Pager->cch = len;
+ /* Reset the reading index in the user-provided source buffer */
Pager->ich = 0;
- if (len == 0 || szStr == NULL)
- return TRUE;
+ /* 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))
+ 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
@@ -403,7 +625,8 @@ ConWritePaging(
Pager->ScrollRows = Pager->PageRows - 1;
/* Prompt the user; give him some values for statistics */
- if (!PagePrompt(Pager, Pager->ich, Pager->cch))
+ // FIXME: Doesn't reflect what's currently being displayed.
+ if (!PagePrompt(Pager, Pager->ich, len))
return FALSE;
}
diff --git a/sdk/lib/conutils/pager.h b/sdk/lib/conutils/pager.h
index 8d38f07881f..1ddc5606970 100644
--- a/sdk/lib/conutils/pager.h
+++ b/sdk/lib/conutils/pager.h
@@ -37,9 +37,10 @@ typedef BOOL
IN DWORD cch);
/* Flags for CON_PAGER */
-#define CON_PAGER_DONT_OUTPUT (1 << 0)
-#define CON_PAGER_EXPAND_TABS (1 << 1)
-#define CON_PAGER_EXPAND_FF (1 << 2)
+#define CON_PAGER_EXPAND_TABS (1 << 0)
+#define CON_PAGER_EXPAND_FF (1 << 1)
+// Whether or not the pager will cache the line if it's incomplete (not NEWLINE-terminated).
+#define CON_PAGER_CACHE_INCOMPLETE_LINE (1 << 2)
typedef struct _CON_PAGER
{
@@ -55,12 +56,15 @@ typedef struct _CON_PAGER
DWORD ScrollRows;
/* Data buffer */
- PCTCH TextBuff; /* The text buffer */
- DWORD cch; /* The total number of characters */
+ PCTCH CachedLine; /* Cached line, HeapAlloc'ated */
+ SIZE_T cchCachedLine; /* Its length (number of characters) */
+ SIZE_T ich; /* The current index of character in TextBuff (a user-provided source buffer) */
/* Paging state */
- DWORD ich; /* The current index of character */
- DWORD nSpacePending; /* Pending spaces for TAB expansion */
+ PCTCH CurrentLine; /* Pointer to the current line (either within a user-provided source buffer, or to CachedLine) */
+ SIZE_T ichCurr; /* The current index of character in CurrentLine */
+ SIZE_T iEndLine; /* End (length) of CurrentLine */
+ DWORD nSpacePending; /* Pending spaces for TAB expansion */
DWORD iColumn; /* The current index of column */
DWORD iLine; /* The physical output line count of screen */
DWORD lineno; /* The logical line number */
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=ff445ca184abf213083a4…
commit ff445ca184abf213083a45b2ae49ffa662d8c028
Author: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
AuthorDate: Sun Jun 27 15:58:25 2021 +0200
Commit: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
CommitDate: Mon Jun 28 21:26:31 2021 +0200
[CONUTILS:PAGER][MORE] Fix the default paging region.
- The column extent should cover the whole width of the console screenbuffer.
On the contrary, the line extent covers only the number of lines that
cover the current displayed console window.
- Since the console can be resized while the pager is prompting, we need
to actually recalculate in the prompt routine the default number of
lines for a screen.
- Reset s_nNextLineNo when paging a new file.
---
base/applications/cmdutils/more/more.c | 17 +++++++++++++++++
sdk/lib/conutils/pager.c | 8 ++++----
2 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/base/applications/cmdutils/more/more.c b/base/applications/cmdutils/more/more.c
index f9179138ee1..5b868f0b5dc 100644
--- a/base/applications/cmdutils/more/more.c
+++ b/base/applications/cmdutils/more/more.c
@@ -172,6 +172,14 @@ PagePrompt(PCON_PAGER Pager, DWORD Done, DWORD Total)
if (!*StrLines)
K32LoadStringW(NULL, IDS_CONTINUE_LINES, StrLines, ARRAYSIZE(StrLines));
+ /*
+ * Check whether the pager is prompting, but we have actually finished
+ * to display a given file, or no data is present in STDIN anymore.
+ * In this case, skip the prompt altogether. The only exception is when
+ * we are displaying other files.
+ */
+ // TODO: Implement!
+
Restart:
nLines = 0;
@@ -323,6 +331,11 @@ Restart:
dwMode |= ENABLE_PROCESSED_INPUT;
SetConsoleMode(hInput, dwMode);
+ /* Refresh the screen information, as the console may have been
+ * redimensioned. Update also the default number of lines to scroll. */
+ ConGetScreenInfo(Pager->Screen, &csbi);
+ Pager->ScrollRows = csbi.srWindow.Bottom - csbi.srWindow.Top;
+
/*
* Erase the full line where the cursor is, and move
* the cursor back to the beginning of the line.
@@ -357,6 +370,7 @@ Restart:
{
s_dwFlags |= FLAG_PLUSn;
s_nNextLineNo = Pager->lineno + nLines;
+ /* Use the default Pager->ScrollRows value */
return TRUE;
}
default:
@@ -412,6 +426,7 @@ Restart:
/* Clear the screen */
ConClearScreen(Pager->Screen);
}
+ /* Use the default Pager->ScrollRows value */
return TRUE;
}
@@ -444,6 +459,7 @@ Restart:
else
{
/* Extended features are unavailable: display one page */
+ /* Use the default Pager->ScrollRows value */
return TRUE;
}
}
@@ -1075,6 +1091,7 @@ int wmain(int argc, WCHAR* argv[])
SetFilePointer(hFile, SkipBytes, NULL, FILE_BEGIN);
/* Reset state for paging */
+ s_nNextLineNo = 0;
s_bPrevLineIsBlank = FALSE;
s_fPrompt = PROMPT_PERCENT;
s_bDoNextFile = FALSE;
diff --git a/sdk/lib/conutils/pager.c b/sdk/lib/conutils/pager.c
index ced3bd4e0cb..db7e17eb883 100644
--- a/sdk/lib/conutils/pager.c
+++ b/sdk/lib/conutils/pager.c
@@ -312,7 +312,7 @@ ConWritePaging(
if (bIsConsole)
{
/* Calculate the console screen extent */
- Pager->PageColumns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
+ Pager->PageColumns = csbi.dwSize.X;
Pager->PageRows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
}
else
@@ -324,14 +324,14 @@ ConWritePaging(
if (StartPaging)
{
- if (bIsConsole)
+ if (bIsConsole && (Pager->PageRows >= 2))
{
/* Reset to display one page by default */
Pager->ScrollRows = Pager->PageRows - 1;
}
else
{
- /* File output: all lines are displayed at once; reset to a default value */
+ /* File output, or single line: all lines are displayed at once; reset to a default value */
Pager->ScrollRows = 0;
}
}
@@ -371,7 +371,7 @@ ConWritePaging(
* in case the user has redimensioned it during the prompt. */
if (bIsConsole && ConGetScreenInfo(Pager->Screen, &csbi))
{
- Pager->PageColumns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
+ Pager->PageColumns = csbi.dwSize.X;
Pager->PageRows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
}
}