Add some more ed commands.
This commit is contained in:
@@ -17,12 +17,15 @@ struct Command {
|
|||||||
HelpToggle,
|
HelpToggle,
|
||||||
Help,
|
Help,
|
||||||
Print,
|
Print,
|
||||||
|
List,
|
||||||
Number,
|
Number,
|
||||||
Append,
|
Append,
|
||||||
Insert,
|
Insert,
|
||||||
Change,
|
Change,
|
||||||
Delete,
|
Delete,
|
||||||
Write,
|
Write,
|
||||||
|
Join,
|
||||||
|
Mark,
|
||||||
Edit,
|
Edit,
|
||||||
ForceEdit,
|
ForceEdit,
|
||||||
Filename,
|
Filename,
|
||||||
@@ -85,6 +88,8 @@ struct Ed {
|
|||||||
Command parse(std::string cmd, bool eof);
|
Command parse(std::string cmd, bool eof);
|
||||||
void append(std::string text, uint64_t line);
|
void append(std::string text, uint64_t line);
|
||||||
void remove(uint64_t start_line, uint64_t end_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);
|
bool handle(std::string cmd, bool eof);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -70,6 +70,24 @@ bool Ed::handle(std::string cmd, bool eof) {
|
|||||||
std::cout << it.line << std::endl;
|
std::cout << it.line << std::endl;
|
||||||
line = line_end;
|
line = line_end;
|
||||||
} break;
|
} 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: {
|
case Command::Type::Number: {
|
||||||
uint64_t line_start = line;
|
uint64_t line_start = line;
|
||||||
uint64_t line_end = line;
|
uint64_t line_end = line;
|
||||||
@@ -89,6 +107,12 @@ bool Ed::handle(std::string cmd, bool eof) {
|
|||||||
line = line_end;
|
line = line_end;
|
||||||
} break;
|
} break;
|
||||||
case Command::Type::Append: {
|
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 text;
|
||||||
std::string cline;
|
std::string cline;
|
||||||
uint64_t line_count = 0;
|
uint64_t line_count = 0;
|
||||||
@@ -99,12 +123,6 @@ bool Ed::handle(std::string cmd, bool eof) {
|
|||||||
text.append(cline);
|
text.append(cline);
|
||||||
text.push_back('\n');
|
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 (text.empty()) {
|
||||||
if (line == 0)
|
if (line == 0)
|
||||||
line = 1;
|
line = 1;
|
||||||
@@ -116,6 +134,14 @@ bool Ed::handle(std::string cmd, bool eof) {
|
|||||||
line += line_count;
|
line += line_count;
|
||||||
} break;
|
} break;
|
||||||
case Command::Type::Insert: {
|
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 text;
|
||||||
std::string cline;
|
std::string cline;
|
||||||
uint64_t line_count = 0;
|
uint64_t line_count = 0;
|
||||||
@@ -126,14 +152,6 @@ bool Ed::handle(std::string cmd, bool eof) {
|
|||||||
text.append(cline);
|
text.append(cline);
|
||||||
text.push_back('\n');
|
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())
|
if (text.empty())
|
||||||
break;
|
break;
|
||||||
vase.snapshot();
|
vase.snapshot();
|
||||||
@@ -247,6 +265,39 @@ bool Ed::handle(std::string cmd, bool eof) {
|
|||||||
if (!suppress_mode)
|
if (!suppress_mode)
|
||||||
std::cout << bytes << std::endl;
|
std::cout << bytes << std::endl;
|
||||||
} break;
|
} 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: {
|
case Command::Type::Edit: {
|
||||||
if (modified && !was_editing) {
|
if (modified && !was_editing) {
|
||||||
editing = true;
|
editing = true;
|
||||||
@@ -318,8 +369,7 @@ bool Ed::handle(std::string cmd, bool eof) {
|
|||||||
std::cout << line << "\t" << it.line << std::endl;
|
std::cout << line << "\t" << it.line << std::endl;
|
||||||
break;
|
break;
|
||||||
case 'l':
|
case 'l':
|
||||||
// TODO: escaping.
|
std::cout << list_string(it.line) << std::endl;
|
||||||
std::cout << it.line << std::endl;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -59,6 +59,10 @@ Command Ed::parse(std::string cmd, bool eof) {
|
|||||||
command.type = Command::Type::Print;
|
command.type = Command::Type::Print;
|
||||||
i++;
|
i++;
|
||||||
break;
|
break;
|
||||||
|
case 'l':
|
||||||
|
command.type = Command::Type::List;
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
command.type = Command::Type::Number;
|
command.type = Command::Type::Number;
|
||||||
i++;
|
i++;
|
||||||
@@ -91,6 +95,10 @@ Command Ed::parse(std::string cmd, bool eof) {
|
|||||||
command.type = Command::Type::Delete;
|
command.type = Command::Type::Delete;
|
||||||
i++;
|
i++;
|
||||||
break;
|
break;
|
||||||
|
case 'j':
|
||||||
|
command.type = Command::Type::Join;
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
case 'w':
|
case 'w':
|
||||||
command.type = Command::Type::Write;
|
command.type = Command::Type::Write;
|
||||||
i++;
|
i++;
|
||||||
@@ -135,6 +143,16 @@ Command Ed::parse(std::string cmd, bool eof) {
|
|||||||
throw ed_error("Invalid command.");
|
throw ed_error("Invalid command.");
|
||||||
command.argument = cmd.substr(i);
|
command.argument = cmd.substr(i);
|
||||||
return command;
|
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 '#':
|
case '#':
|
||||||
command.type = Command::Type::Dump;
|
command.type = Command::Type::Dump;
|
||||||
i++;
|
i++;
|
||||||
|
|||||||
@@ -7,6 +7,12 @@ std::vector<Vase::ReplacePart> Vase::parse_replace(std::string_view s) {
|
|||||||
auto flush_constant = [&]() {
|
auto flush_constant = [&]() {
|
||||||
if (!constant.empty()) {
|
if (!constant.empty()) {
|
||||||
uint64_t lines = 0;
|
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());
|
uint64_t pos = append->append(constant.data(), (uint64_t)constant.size());
|
||||||
parts.push_back(
|
parts.push_back(
|
||||||
ReplacePart{
|
ReplacePart{
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ std::vector<Vase::RegexMatch> Vase::_regex_search(
|
|||||||
bool global = false;
|
bool global = false;
|
||||||
uint64_t flags = PCRE2_MULTILINE | PCRE2_UTF;
|
uint64_t flags = PCRE2_MULTILINE | PCRE2_UTF;
|
||||||
|
|
||||||
const char *dot = "(?:(?!\\n)\\X)";
|
|
||||||
|
|
||||||
for (char c : options) {
|
for (char c : options) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'g':
|
case 'g':
|
||||||
@@ -18,7 +16,7 @@ std::vector<Vase::RegexMatch> Vase::_regex_search(
|
|||||||
flags |= PCRE2_CASELESS;
|
flags |= PCRE2_CASELESS;
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
dot = "(?:\\X)";
|
flags |= PCRE2_DOTALL;
|
||||||
break;
|
break;
|
||||||
case 'x':
|
case 'x':
|
||||||
flags |= PCRE2_EXTENDED;
|
flags |= PCRE2_EXTENDED;
|
||||||
@@ -40,57 +38,9 @@ std::vector<Vase::RegexMatch> Vase::_regex_search(
|
|||||||
int errornumber;
|
int errornumber;
|
||||||
PCRE2_SIZE erroroffset;
|
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_code *re = pcre2_compile(
|
||||||
(PCRE2_SPTR)out.data(),
|
(PCRE2_SPTR)pattern.data(),
|
||||||
out.size(),
|
pattern.size(),
|
||||||
flags,
|
flags,
|
||||||
&errornumber,
|
&errornumber,
|
||||||
&erroroffset,
|
&erroroffset,
|
||||||
|
|||||||
Reference in New Issue
Block a user