xref: /haiku/src/system/boot/loader/pager.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
1 /*
2  * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "pager.h"
8 
9 #include <ctype.h>
10 #include <string.h>
11 
12 #include <algorithm>
13 
14 #include <boot/platform/generic/text_console.h>
15 
16 
17 // #pragma mark - PagerTextSource
18 
19 
20 PagerTextSource::~PagerTextSource()
21 {
22 }
23 
24 
25 // #pragma mark -
26 
27 
28 static size_t
29 next_line(const PagerTextSource& textSource, size_t width, size_t offset,
30 	char* buffer, size_t bufferSize)
31 {
32 	size_t bytesRead = textSource.Read(offset, buffer, bufferSize - 1);
33 	if (bytesRead == 0)
34 		return 0;
35 
36 	buffer[bytesRead] = '\0';
37 
38 	// replace all '\0's by spaces
39 	for (size_t i = 0; i < bytesRead; i++) {
40 		if (buffer[i] == '\0')
41 			buffer[i] = ' ';
42 	}
43 
44 	if (const char* lineEnd = strchr(buffer, '\n'))
45 		bytesRead = lineEnd - buffer;
46 
47 	if (bytesRead > (size_t)width)
48 		bytesRead = width;
49 
50 	// replace unprintables by '.'
51 	for (size_t i = 0; i < bytesRead; i++) {
52 		if (!isprint(buffer[i]))
53 			buffer[i] = '.';
54 	}
55 
56 	bool lineBreak = buffer[bytesRead] == '\n';
57 
58 	buffer[bytesRead] = '\0';
59 
60 	return bytesRead + (lineBreak ? 1 : 0);
61 }
62 
63 
64 static int32
65 count_lines(const PagerTextSource& textSource, size_t width, char* buffer,
66 	size_t bufferSize)
67 {
68 	int32 lineCount = 0;
69 	size_t offset = 0;
70 
71 	while (true) {
72 		size_t bytesRead = next_line(textSource, width, offset, buffer,
73 			bufferSize);
74 		if (bytesRead == 0)
75 			break;
76 
77 		offset += bytesRead;
78 		lineCount++;
79 	}
80 
81 	return lineCount;
82 }
83 
84 
85 static size_t
86 offset_of_line(const PagerTextSource& textSource, size_t width, char* buffer,
87 	size_t bufferSize, int32 line)
88 {
89 	int32 lineCount = 0;
90 	size_t offset = 0;
91 
92 	while (true) {
93 		if (line == lineCount)
94 			return offset;
95 
96 		size_t bytesRead = next_line(textSource, width, offset, buffer,
97 			bufferSize);
98 		if (bytesRead == 0)
99 			break;
100 
101 		offset += bytesRead;
102 		lineCount++;
103 	}
104 
105 	return offset;
106 }
107 
108 
109 // #pragma mark -
110 
111 
112 void
113 pager(const PagerTextSource& textSource)
114 {
115 	console_set_cursor(0, 0);
116 
117 	int32 width = console_width();
118 	int32 height = console_height();
119 
120 	char lineBuffer[256];
121 
122 	int32 lineCount = count_lines(textSource, width, lineBuffer,
123 		sizeof(lineBuffer));
124 	int32 topLine = 0;
125 
126 	bool quit = false;
127 	while (!quit) {
128 		// get the text offset for the top line
129 		size_t offset = offset_of_line(textSource, width, lineBuffer,
130 			sizeof(lineBuffer), topLine);
131 
132 		// clear the screen and print the lines
133 		console_clear_screen();
134 
135 		int32 screenLine = 0;
136 		while (screenLine + 1 < height) {
137 			size_t bytesRead = next_line(textSource, width, offset, lineBuffer,
138 				sizeof(lineBuffer));
139 			if (bytesRead == 0)
140 				break;
141 
142 			console_set_cursor(0, screenLine);
143 			puts(lineBuffer);
144 
145 			offset += bytesRead;
146 			screenLine++;
147 		}
148 
149 		// print the statistics line at the bottom
150 		console_set_cursor(0, height - 1);
151 		console_set_color(BLACK, GRAY);
152 		int32 bottomLine = std::min(topLine + height - 2, lineCount - 1);
153 		printf("%" B_PRId32 " - %" B_PRId32 "  %" B_PRId32 "%%",
154 			topLine, bottomLine, (int32)((bottomLine + 1) * 100 / lineCount));
155 		console_set_color(WHITE, BLACK);
156 
157 		// wait for a key that changes the position
158 		int32 previousTopLine = topLine;
159 
160 		while (!quit && topLine == previousTopLine) {
161 			switch (console_wait_for_key()) {
162 				case TEXT_CONSOLE_KEY_ESCAPE:
163 				case 'q':
164 				case 'Q':
165 					// quit
166 					quit = true;
167 					break;
168 
169 				case TEXT_CONSOLE_KEY_DOWN:
170 				case TEXT_CONSOLE_KEY_RETURN:
171 				case 'j':
172 				case 'J':
173 					// next line
174 					topLine++;
175 					break;
176 
177 				case TEXT_CONSOLE_KEY_UP:
178 				case 'k':
179 				case 'K':
180 					// previous line
181 					topLine--;
182 					break;
183 
184 				case TEXT_CONSOLE_KEY_PAGE_UP:
185 					// previous page
186 					topLine -= height - 1;
187 					break;
188 
189 				case TEXT_CONSOLE_KEY_PAGE_DOWN:
190 					// next page
191 					topLine += height - 1;
192 					break;
193 
194 				case TEXT_CONSOLE_KEY_HOME:
195 					// beginning of text
196 					topLine = 0;
197 					break;
198 
199 				case TEXT_CONSOLE_KEY_END:
200 					// end of text
201 					topLine = lineCount;
202 					break;
203 			}
204 
205 			if (topLine > lineCount - (height - 1))
206 				topLine = lineCount - (height - 1);
207 			if (topLine < 0)
208 				topLine = 0;
209 		}
210 	}
211 }
212