Add some more ed commands.

This commit is contained in:
2026-08-10 16:29:33 +01:00
parent 23b7b8c8a8
commit f6d4987b8c
7 changed files with 181 additions and 90 deletions
+5
View File
@@ -17,12 +17,15 @@ struct Command {
HelpToggle,
Help,
Print,
List,
Number,
Append,
Insert,
Change,
Delete,
Write,
Join,
Mark,
Edit,
ForceEdit,
Filename,
@@ -85,6 +88,8 @@ struct Ed {
Command parse(std::string cmd, bool eof);
void append(std::string text, uint64_t line);
void remove(uint64_t start_line, uint64_t end_line);
std::string list_string(std::string_view s);
void join(uint64_t start_line, uint64_t end_line);
bool handle(std::string cmd, bool eof);
};
-21
View File
@@ -1,21 +0,0 @@
#include "commands/ed/ed.h"
namespace crib::commands::ed {
void Ed::append(std::string text, uint64_t line) {
using namespace crib::internal::vase;
Point p = {line, 0};
if (!vase.lines()) {
text.pop_back();
} else if (line == vase.lines()) {
p.row--;
p.col = UINT64_MAX;
text = "\n" + text;
text.pop_back();
}
vase.insert(&p, text);
}
void Ed::remove(uint64_t start_line, uint64_t end_line) {
vase.erase({{start_line - 1, 0}, {end_line, 0}});
}
} // namespace crib::commands::ed
+66 -16
View File
@@ -70,6 +70,24 @@ bool Ed::handle(std::string cmd, bool eof) {
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;
@@ -89,6 +107,12 @@ bool Ed::handle(std::string cmd, bool eof) {
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;
@@ -99,12 +123,6 @@ bool Ed::handle(std::string cmd, bool eof) {
text.append(cline);
text.push_back('\n');
}
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 (text.empty()) {
if (line == 0)
line = 1;
@@ -116,6 +134,14 @@ bool Ed::handle(std::string cmd, bool eof) {
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;
@@ -126,14 +152,6 @@ bool Ed::handle(std::string cmd, bool eof) {
text.append(cline);
text.push_back('\n');
}
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;
if (text.empty())
break;
vase.snapshot();
@@ -247,6 +265,39 @@ bool Ed::handle(std::string cmd, bool eof) {
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;
@@ -318,8 +369,7 @@ bool Ed::handle(std::string cmd, bool eof) {
std::cout << line << "\t" << it.line << std::endl;
break;
case 'l':
// TODO: escaping.
std::cout << it.line << std::endl;
std::cout << list_string(it.line) << std::endl;
break;
}
}
+83
View File
@@ -0,0 +1,83 @@
#include "commands/ed/ed.h"
namespace crib::commands::ed {
void Ed::append(std::string text, uint64_t line) {
using namespace crib::internal::vase;
Point p = {line, 0};
if (!vase.lines()) {
text.pop_back();
} else if (line == vase.lines()) {
p.row--;
p.col = UINT64_MAX;
text = "\n" + text;
text.pop_back();
}
vase.insert(&p, text);
}
void Ed::remove(uint64_t start_line, uint64_t end_line) {
vase.erase({{start_line - 1, 0}, {end_line, 0}});
}
void Ed::join(uint64_t start_line, uint64_t end_line) {
vase.regex_search_replace("\\n", {{start_line - 1, 0}, {end_line, 0}}, "", "");
}
std::string Ed::list_string(std::string_view s) {
uint32_t width = 80;
winsize ws{};
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_col != 0)
width = ws.ws_col;
std::string out;
out.reserve(s.size());
const uint32_t max_width = width > 1 ? width - 1 : 1;
uint32_t column = 0;
auto append = [&](std::string_view text) {
if (column + text.size() > max_width) {
out += "\\\n";
column = 0;
}
out += text;
column += text.size();
};
for (unsigned char c : s) {
switch (c) {
case '\\':
append("\\\\");
break;
case '$':
append("\\$");
break;
case '\a':
append("\\a");
break;
case '\b':
append("\\b");
break;
case '\f':
append("\\f");
break;
case '\r':
append("\\r");
break;
case '\t':
append("\\t");
break;
case '\v':
append("\\v");
break;
default:
if (!std::isprint(c)) {
char buf[5];
std::snprintf(buf, sizeof(buf), "\\%03o", c);
append(buf);
} else {
append(std::string_view((const char *)&c, 1));
}
break;
}
}
out += '$';
return out;
}
} // namespace crib::commands::ed
+18
View File
@@ -59,6 +59,10 @@ Command Ed::parse(std::string cmd, bool eof) {
command.type = Command::Type::Print;
i++;
break;
case 'l':
command.type = Command::Type::List;
i++;
break;
case 'n':
command.type = Command::Type::Number;
i++;
@@ -91,6 +95,10 @@ Command Ed::parse(std::string cmd, bool eof) {
command.type = Command::Type::Delete;
i++;
break;
case 'j':
command.type = Command::Type::Join;
i++;
break;
case 'w':
command.type = Command::Type::Write;
i++;
@@ -135,6 +143,16 @@ Command Ed::parse(std::string cmd, bool eof) {
throw ed_error("Invalid command.");
command.argument = cmd.substr(i);
return command;
case 'k':
command.type = Command::Type::Mark;
i++;
if (i >= cmd.size())
throw ed_error("Invalid command.");
if ('a' <= cmd[i] && cmd[i] <= 'z')
command.argument = cmd[i];
else
throw ed_error("Invalid mark used.");
break;
case '#':
command.type = Command::Type::Dump;
i++;
+6
View File
@@ -7,6 +7,12 @@ std::vector<Vase::ReplacePart> Vase::parse_replace(std::string_view s) {
auto flush_constant = [&]() {
if (!constant.empty()) {
uint64_t lines = 0;
const char *p = constant.data();
const char *end = p + constant.size();
while ((p = (const char *)memchr(p, '\n', end - p))) {
++lines;
++p;
}
uint64_t pos = append->append(constant.data(), (uint64_t)constant.size());
parts.push_back(
ReplacePart{
+3 -53
View File
@@ -7,8 +7,6 @@ std::vector<Vase::RegexMatch> Vase::_regex_search(
bool global = false;
uint64_t flags = PCRE2_MULTILINE | PCRE2_UTF;
const char *dot = "(?:(?!\\n)\\X)";
for (char c : options) {
switch (c) {
case 'g':
@@ -18,7 +16,7 @@ std::vector<Vase::RegexMatch> Vase::_regex_search(
flags |= PCRE2_CASELESS;
break;
case 's':
dot = "(?:\\X)";
flags |= PCRE2_DOTALL;
break;
case 'x':
flags |= PCRE2_EXTENDED;
@@ -40,57 +38,9 @@ std::vector<Vase::RegexMatch> Vase::_regex_search(
int errornumber;
PCRE2_SIZE erroroffset;
std::string out;
out.reserve(pattern.size() * 2);
bool escaped = false;
bool in_class = false;
bool in_quote = false;
for (size_t i = 0; i < pattern.size(); i++) {
char c = pattern[i];
if (escaped) {
out += c;
escaped = false;
continue;
}
if (c == '\\') {
out += c;
if (i + 1 < pattern.size()) {
char next = pattern[i + 1];
if (next == 'Q')
in_quote = true;
else if (next == 'E')
in_quote = false;
out += next;
i++;
continue;
}
escaped = true;
continue;
}
if (in_quote) {
out += c;
continue;
}
if (c == '[') {
in_class = true;
out += c;
continue;
}
if (c == ']' && in_class) {
in_class = false;
out += c;
continue;
}
if (c == '.' && !in_class) {
out += dot;
continue;
}
out += c;
}
pcre2_code *re = pcre2_compile(
(PCRE2_SPTR)out.data(),
out.size(),
(PCRE2_SPTR)pattern.data(),
pattern.size(),
flags,
&errornumber,
&erroroffset,