/*
 * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */
 
 
#include "pager.h"
 
#include <ctype.h>
#include <string.h>
 
#include <algorithm>
 
#include <boot/platform/generic/text_console.h>
 
 
// #pragma mark - PagerTextSource
 
 
PagerTextSource::~PagerTextSource()
{
}
 
 
// #pragma mark -
 
 
static size_t
next_line(const PagerTextSource& textSource, size_t width, size_t offset,
	char* buffer, size_t bufferSize)
{
	size_t bytesRead = textSource.Read(offset, buffer, bufferSize - 1);
	if (bytesRead == 0)
		return 0;
 
	buffer[bytesRead] = '\0';
 
	// replace all '\0's by spaces
	for (size_t i = 0; i < bytesRead; i++) {
		if (buffer[i] == '\0')
			buffer[i] = ' ';
	}
 
	if (const char* lineEnd = strchr(buffer, '\n'))
		bytesRead = lineEnd - buffer;
 
	if (bytesRead > (size_t)width)
		bytesRead = width;
 
	// replace unprintables by '.'
	for (size_t i = 0; i < bytesRead; i++) {
		if (!isprint(buffer[i]))
			buffer[i] = '.';
	}
 
	bool lineBreak = buffer[bytesRead] == '\n';
 
	buffer[bytesRead] = '\0';
 
	return bytesRead + (lineBreak ? 1 : 0);
}
 
 
static int32
count_lines(const PagerTextSource& textSource, size_t width, char* buffer,
	size_t bufferSize)
{
	int32 lineCount = 0;
	size_t offset = 0;
 
	while (true) {
		size_t bytesRead = next_line(textSource, width, offset, buffer,
			bufferSize);
		if (bytesRead == 0)
			break;
 
		offset += bytesRead;
		lineCount++;
	}
 
	return lineCount;
}
 
 
static size_t
offset_of_line(const PagerTextSource& textSource, size_t width, char* buffer,
	size_t bufferSize, int32 line)
{
	int32 lineCount = 0;
	size_t offset = 0;
 
	while (true) {
		if (line == lineCount)
			return offset;
 
		size_t bytesRead = next_line(textSource, width, offset, buffer,
			bufferSize);
		if (bytesRead == 0)
			break;
 
		offset += bytesRead;
		lineCount++;
	}
 
	return offset;
}
 
 
// #pragma mark -
 
 
void
pager(const PagerTextSource& textSource)
{
	console_set_cursor(0, 0);
 
	int32 width = console_width();
	int32 height = console_height();
 
	char lineBuffer[256];
 
	int32 lineCount = count_lines(textSource, width, lineBuffer,
		sizeof(lineBuffer));
	int32 topLine = 0;
 
	bool quit = false;
	while (!quit) {
		// get the text offset for the top line
		size_t offset = offset_of_line(textSource, width, lineBuffer,
			sizeof(lineBuffer), topLine);
 
		// clear the screen and print the lines
		console_clear_screen();
 
		int32 screenLine = 0;
		while (screenLine + 1 < height) {
			size_t bytesRead = next_line(textSource, width, offset, lineBuffer,
				sizeof(lineBuffer));
			if (bytesRead == 0)
				break;
 
			console_set_cursor(0, screenLine);
			puts(lineBuffer);
 
			offset += bytesRead;
			screenLine++;
		}
 
		// print the statistics line at the bottom
		console_set_cursor(0, height - 1);
		console_set_color(BLACK, GRAY);
		int32 bottomLine = std::min(topLine + height - 2, lineCount - 1);
		printf("%" B_PRIuSIZE " - %" B_PRIuSIZE "  %" B_PRIuSIZE "%%",
			topLine, bottomLine, (bottomLine + 1) * 100 / lineCount);
		console_set_color(WHITE, BLACK);
 
		// wait for a key that changes the position
		int32 previousTopLine = topLine;
 
		while (!quit && topLine == previousTopLine) {
			switch (console_wait_for_key()) {
				case TEXT_CONSOLE_KEY_ESCAPE:
				case 'q':
				case 'Q':
					// quit
					quit = true;
					break;
 
				case TEXT_CONSOLE_KEY_DOWN:
				case TEXT_CONSOLE_KEY_RETURN:
					// next line
					topLine++;
					break;
 
				case TEXT_CONSOLE_KEY_UP:
					// previous line
					topLine--;
					break;
 
				case TEXT_CONSOLE_KEY_PAGE_UP:
					// previous page
					topLine -= height - 1;
					break;
 
				case TEXT_CONSOLE_KEY_PAGE_DOWN:
					// next page
					topLine += height - 1;
					break;
 
				case TEXT_CONSOLE_KEY_HOME:
					// beginning of text
					topLine = 0;
					break;
 
				case TEXT_CONSOLE_KEY_END:
					// end of text
					topLine = lineCount;
					break;
			}
 
			if (topLine > lineCount - (height - 1))
				topLine = lineCount - (height - 1);
			if (topLine < 0)
				topLine = 0;
		}
	}
}

V576 Incorrect format. Consider checking the fourth actual argument of the 'printf' function. The memsize type argument is expected.

V576 Incorrect format. Consider checking the third actual argument of the 'printf' function. The memsize type argument is expected.

V576 Incorrect format. Consider checking the second actual argument of the 'printf' function. The memsize type argument is expected.