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