Files
bed/src/commands/ed/handle.cc
T
2026-08-10 16:29:33 +01:00

385 lines
12 KiB
C++

#include "commands/ed/ed.h"
namespace crib::commands::ed {
bool Ed::handle(std::string cmd, bool eof) {
using namespace crib::internal::vase;
try {
if (line > vase.lines())
line = vase.lines();
Command command = parse(cmd, eof);
bool was_quitting = quitting;
quitting = false;
bool was_editing = editing;
editing = false;
switch (command.type) {
case Command::Type::Quit:
if (modified && !was_quitting) {
quitting = true;
throw ed_error("Buffer modified.");
} else {
return false;
}
case Command::Type::ForceQuit:
return false;
case Command::Type::None: {
if (command.start.type != Command::Address::Type::None) {
if (command.address_flags & Command::RANGE)
resolve_address(command.end, &line);
else
resolve_address(command.start, &line);
} else {
line++;
}
if (line == 0)
throw ed_error("Line 0 is invalid.");
if (line > vase.lines())
throw ed_error("Line position too high.");
Iterator it = vase.iterate(line - 1, Direction::Forward);
it.next();
std::cout << it.line << std::endl;
} break;
case Command::Type::Invalid:
throw ed_error("Invalid command.");
case Command::Type::HelpToggle:
help_mode = !help_mode;
if (help_mode && last_message != "")
std::cout << last_message << std::endl;
break;
case Command::Type::Help:
if (last_message != "")
std::cout << last_message << std::endl;
break;
case Command::Type::PromptToggle:
prompt_mode = !prompt_mode;
break;
case Command::Type::Print: {
uint64_t line_start = line;
uint64_t line_end = line;
if (command.start.type != Command::Address::Type::None) {
resolve_address(command.start, &line_start);
line_end = line_start;
if (command.address_flags & Command::RANGE)
resolve_address(command.end, &line_end);
}
if (line_start == 0)
throw ed_error("Line 0 is invalid.");
if (line_end < line_start)
throw ed_error("Invalid address range.");
Iterator it = vase.iterate(line_start - 1, Direction::Forward);
while (it.next() && line_start++ <= line_end)
std::cout << it.line << std::endl;
line = line_end;
} break;
case Command::Type::List: {
uint64_t line_start = line;
uint64_t line_end = line;
if (command.start.type != Command::Address::Type::None) {
resolve_address(command.start, &line_start);
line_end = line_start;
if (command.address_flags & Command::RANGE)
resolve_address(command.end, &line_end);
}
if (line_start == 0)
throw ed_error("Line 0 is invalid.");
if (line_end < line_start)
throw ed_error("Invalid address range.");
Iterator it = vase.iterate(line_start - 1, Direction::Forward);
while (it.next() && line_start++ <= line_end)
std::cout << list_string(it.line) << std::endl;
line = line_end;
} break;
case Command::Type::Number: {
uint64_t line_start = line;
uint64_t line_end = line;
if (command.start.type != Command::Address::Type::None) {
resolve_address(command.start, &line_start);
line_end = line_start;
if (command.address_flags & Command::RANGE)
resolve_address(command.end, &line_end);
}
if (line_start == 0)
throw ed_error("Line 0 is invalid.");
if (line_end < line_start)
throw ed_error("Invalid address range.");
Iterator it = vase.iterate(line_start - 1, Direction::Forward);
while (it.next() && line_start <= line_end)
std::cout << line_start++ << "\t" << it.line << std::endl;
line = line_end;
} break;
case Command::Type::Append: {
if (command.start.type != Command::Address::Type::None) {
if (command.address_flags & Command::RANGE)
resolve_address(command.end, &line);
else
resolve_address(command.start, &line);
}
std::string text;
std::string cline;
uint64_t line_count = 0;
while (std::getline(std::cin, cline)) {
if (cline == ".")
break;
line_count++;
text.append(cline);
text.push_back('\n');
}
if (text.empty()) {
if (line == 0)
line = 1;
break;
}
vase.snapshot();
append(text, line);
modified = true;
line += line_count;
} break;
case Command::Type::Insert: {
if (command.start.type != Command::Address::Type::None) {
if (command.address_flags & Command::RANGE)
resolve_address(command.end, &line);
else
resolve_address(command.start, &line);
}
if (line == 0)
line = 1;
std::string text;
std::string cline;
uint64_t line_count = 0;
while (std::getline(std::cin, cline)) {
if (cline == ".")
break;
line_count++;
text.append(cline);
text.push_back('\n');
}
if (text.empty())
break;
vase.snapshot();
append(text, line - 1);
modified = true;
line += line_count - 1;
} break;
case Command::Type::Change: {
uint64_t line_start = line;
uint64_t line_end = line;
if (command.start.type != Command::Address::Type::None) {
resolve_address(command.start, &line_start);
if (line_start == 0)
line_start = 1;
line_end = line_start;
if (command.address_flags & Command::RANGE)
resolve_address(command.end, &line_end);
}
if (line_end < line_start)
throw ed_error("Invalid address range.");
std::string text;
std::string cline;
uint64_t line_count = 0;
while (std::getline(std::cin, cline)) {
if (cline == ".")
break;
line_count++;
text.append(cline);
text.push_back('\n');
}
vase.snapshot();
remove(line_start, line_end);
line = line_start;
if (text.empty())
break;
append(text, line);
modified = true;
line += line_count;
} break;
case Command::Type::Delete: {
uint64_t line_start = line;
uint64_t line_end = line;
if (command.start.type != Command::Address::Type::None) {
resolve_address(command.start, &line_start);
if (line_start == 0)
line_start = 1;
line_end = line_start;
if (command.address_flags & Command::RANGE)
resolve_address(command.end, &line_end);
}
if (line_end < line_start)
throw ed_error("Invalid address range.");
vase.snapshot();
vase.erase({{line_start - 1, 0}, {line_end, 0}});
modified = true;
line = line_start;
if (line > vase.lines())
line = vase.lines();
} break;
case Command::Type::Write: {
std::string path = command.argument;
if (path.empty()) {
path = save_path;
if (path.empty())
throw ed_error("Need file to write to.");
}
uint64_t line_start = 1;
uint64_t line_end = vase.lines();
if (vase.lines() == 0)
line_start = line_end = 0;
if (command.start.type != Command::Address::Type::None) {
resolve_address(command.start, &line_start);
line_end = line_start;
if (command.address_flags & Command::RANGE)
resolve_address(command.end, &line_end);
}
if (vase.lines() && line_start == 0)
throw ed_error("Line 0 is invalid.");
if (line_end < line_start)
throw ed_error("Invalid address range.");
uint64_t bytes = 0;
if (path[0] == '!') {
FILE *pipe = popen(path.c_str() + 1, "w");
if (!pipe)
throw ed_error("Error starting command.");
Iterator it = vase.iterate(line_start - 1, Direction::Forward);
while (it.next() && line_start++ <= line_end) {
it.line += '\n';
bytes += it.line.size();
if (fputs(it.line.c_str(), pipe) == EOF) {
pclose(pipe);
throw ed_error("Error writing to command.");
}
}
if (pclose(pipe) == -1)
throw ed_error("Error closing command.");
} else {
std::ofstream file(path, std::ios::out | std::ios::trunc);
if (!file)
throw ed_error("Error writing to file.");
Iterator it = vase.iterate(line_start - 1, Direction::Forward);
while (it.next() && line_start++ <= line_end) {
file << it.line << '\n';
bytes += it.line.size() + 1;
}
if (!file)
throw ed_error("Error writing to file.");
save_path = path;
modified = false;
}
if (!suppress_mode)
std::cout << bytes << std::endl;
} break;
case Command::Type::Join: {
uint64_t line_start = line;
uint64_t line_end = line + 1;
if (command.start.type != Command::Address::Type::None) {
resolve_address(command.start, &line_start);
line_end = line_start;
if (command.address_flags & Command::RANGE)
resolve_address(command.end, &line_end);
}
if (line_end < line_start)
throw ed_error("Invalid address range.");
if (line_start == 0)
throw ed_error("Invalid line number given.");
if (line_end == line_start)
break;
vase.snapshot();
join(line_start, line_end);
line = line_start;
modified = true;
} break;
case Command::Type::Mark: {
uint64_t mark_line = line;
if (command.start.type != Command::Address::Type::None) {
if (command.address_flags & Command::RANGE)
resolve_address(command.end, &mark_line);
else
resolve_address(command.start, &mark_line);
}
if (mark_line == 0)
throw ed_error("Invalid line number given.");
char mark = command.argument[0];
marks[mark - 'a'] = mark_line;
} break;
case Command::Type::Edit: {
if (modified && !was_editing) {
editing = true;
throw ed_error("Buffer modified.");
}
std::string path = command.argument;
if (path.empty()) {
path = save_path;
if (path.empty())
throw ed_error("Need file to read from.");
}
if (path[0] == '!') {
path.erase(1);
Vase new_vase(std::string(path), "/tmp");
vase = std::move(new_vase);
modified = true;
} else {
Vase new_vase(std::filesystem::path(path), "/tmp");
vase = std::move(new_vase);
save_path = path;
modified = false;
}
line = vase.lines();
if (!suppress_mode)
std::cout << vase.length() << std::endl;
} break;
case Command::Type::ForceEdit: {
std::string path = command.argument;
if (path.empty()) {
path = save_path;
if (path.empty())
throw ed_error("Need file to read from.");
}
if (path[0] == '!') {
path.erase(0, 1);
Vase new_vase(std::string(path), "/tmp");
vase = std::move(new_vase);
modified = true;
} else {
Vase new_vase(std::filesystem::path(path), "/tmp");
vase = std::move(new_vase);
save_path = path;
modified = false;
}
line = vase.lines();
std::fill(std::begin(marks), std::end(marks), '\0');
if (!suppress_mode)
std::cout << vase.length() << std::endl;
} break;
case Command::Type::Filename: {
std::string path = command.argument;
if (path.empty())
throw ed_error("No filename given.");
save_path = path;
std::cout << save_path.string() << std::endl;
} break;
case Command::Type::Dump:
Shard::dump(vase.root);
break;
}
if (command.suffix) {
Iterator it = vase.iterate(line - 1, Direction::Forward);
it.next();
switch (command.suffix) {
case 'p':
std::cout << it.line << std::endl;
break;
case 'n':
std::cout << line << "\t" << it.line << std::endl;
break;
case 'l':
std::cout << list_string(it.line) << std::endl;
break;
}
}
} catch (ed_error &e) {
last_message = e.what();
std::cout << "?" << std::endl;
if (help_mode)
std::cout << e.what() << std::endl;
}
return true;
}
} // namespace crib::commands::ed