-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmore.cpp
More file actions
127 lines (107 loc) · 3.93 KB
/
more.cpp
File metadata and controls
127 lines (107 loc) · 3.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <cfbox/applet.hpp>
#include <cfbox/args.hpp>
#include <cfbox/help.hpp>
#include <cfbox/terminal.hpp>
#include <cfbox/tui.hpp>
#include <cfbox/error.hpp>
namespace {
constexpr cfbox::help::HelpEntry HELP = {
.name = "more",
.version = CFBOX_VERSION_STRING,
.one_line = "file perusal filter for crt viewing",
.usage = "more [FILE]",
.options = "",
.extra = "Space=next page Enter=next line q=quit",
};
auto read_lines(std::FILE* f) -> std::vector<std::string> {
std::vector<std::string> lines;
char buf[4096];
while (std::fgets(buf, sizeof(buf), f)) {
auto len = std::strlen(buf);
while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r')) {
buf[--len] = '\0';
}
lines.emplace_back(buf);
}
return lines;
}
} // anonymous namespace
auto more_main(int argc, char* argv[]) -> int {
auto parsed = cfbox::args::parse(argc, argv, {});
if (parsed.has_long("help")) { cfbox::help::print_help(HELP); return 0; }
if (parsed.has_long("version")) { cfbox::help::print_version(HELP); return 0; }
const auto& pos = parsed.positional();
std::string filename = pos.empty() ? "" : std::string(pos[0]);
std::FILE* f = stdin;
if (!filename.empty() && filename != "-") {
f = std::fopen(filename.c_str(), "r");
if (!f) {
CFBOX_ERR("more", "cannot open %s", filename.c_str());
return 1;
}
}
auto lines = read_lines(f);
if (f != stdin) std::fclose(f);
if (lines.empty()) return 0;
// Check if output is a terminal
if (!isatty(STDOUT_FILENO)) {
for (const auto& line : lines) std::printf("%s\n", line.c_str());
return 0;
}
auto [rows, cols] = cfbox::terminal::get_size();
int usable_rows = rows - 1; // Leave room for status line
if (usable_rows < 1) usable_rows = 1;
cfbox::terminal::RawMode raw_mode;
std::size_t top_line = 0;
while (top_line < lines.size()) {
cfbox::terminal::clear_screen();
cfbox::terminal::move_cursor(1, 1);
// Display a page
auto end = std::min(top_line + static_cast<std::size_t>(usable_rows), lines.size());
for (auto i = top_line; i < end; ++i) {
cfbox::terminal::move_cursor(static_cast<int>(i - top_line) + 1, 1);
const auto& line = lines[i];
// Truncate to terminal width
auto print_len = std::min(static_cast<int>(line.size()), cols);
std::printf("%.*s", print_len, line.c_str());
cfbox::terminal::clear_line();
}
// Status line
cfbox::terminal::invert_video(true);
cfbox::terminal::move_cursor(rows, 1);
std::printf("--More--(%zu%%)", std::min(end * 100 / lines.size(), static_cast<std::size_t>(100)));
cfbox::terminal::clear_line();
cfbox::terminal::invert_video(false);
std::fflush(stdout);
// Wait for key
while (true) {
auto key = cfbox::tui::read_key(0, -1);
if (!key) continue;
if (key->is_char() && key->ch == 'q') return 0;
if (key->type == cfbox::tui::KeyType::Escape) return 0;
if (key->type == cfbox::tui::KeyType::Enter) {
top_line += 1;
break;
}
if ((key->is_char() && key->ch == ' ') ||
key->type == cfbox::tui::KeyType::PageDown) {
top_line += static_cast<std::size_t>(usable_rows);
break;
}
if (key->type == cfbox::tui::KeyType::PageUp) {
if (top_line >= static_cast<std::size_t>(usable_rows)) {
top_line -= static_cast<std::size_t>(usable_rows);
} else {
top_line = 0;
}
break;
}
}
}
cfbox::terminal::clear_screen();
return 0;
}