diff options
-rw-r--r-- | cmdline.cpp | 4 | ||||
-rw-r--r-- | goto.cpp | 4 | ||||
-rw-r--r-- | goto.h | 4 | ||||
-rw-r--r-- | parser.cpp | 122 | ||||
-rw-r--r-- | parser.h | 83 | ||||
-rw-r--r-- | qbuffers.cpp | 46 | ||||
-rw-r--r-- | qbuffers.h | 24 |
7 files changed, 174 insertions, 113 deletions
diff --git a/cmdline.cpp b/cmdline.cpp index 5c6c14b..e2103ac 100644 --- a/cmdline.cpp +++ b/cmdline.cpp @@ -56,7 +56,9 @@ cmdline_keypress(gchar key) for (const gchar *p = insert; *p; p++) { strcat(cmdline, (gchar[]){*p, '\0'}); - if (!macro_execute(cmdline)) { + try { + macro_execute(cmdline); + } catch (...) { cmdline[old_cmdline_len] = '\0'; break; } @@ -171,7 +171,7 @@ StateLabel::StateLabel() : State() } State * -StateLabel::custom(gchar chr) +StateLabel::custom(gchar chr) throw (Error) { gchar *new_str; @@ -203,7 +203,7 @@ StateLabel::custom(gchar chr) } State * -StateGotoCmd::done(const gchar *str) +StateGotoCmd::done(const gchar *str) throw (Error) { gint64 value; gchar **labels; @@ -15,12 +15,12 @@ public: StateLabel(); private: - State *custom(gchar chr); + State *custom(gchar chr) throw (Error); }; class StateGotoCmd : public StateExpectString { private: - State *done(const gchar *str); + State *done(const gchar *str) throw (Error); }; namespace States { @@ -1,3 +1,4 @@ +#include <stdarg.h> #include <string.h> #include <glib.h> @@ -43,8 +44,8 @@ static gint nest_level = 0; gchar *strings[2] = {NULL, NULL}; gchar escape_char = '\x1B'; -bool -macro_execute(const gchar *macro) +void +macro_execute(const gchar *macro) throw (State::Error) { while (macro[macro_pc]) { #ifdef DEBUG @@ -53,23 +54,15 @@ macro_execute(const gchar *macro) States::current); #endif - if (!State::input(macro[macro_pc])) { - interface.msg(Interface::MSG_ERROR, - "Syntax error \"%c\"", macro[macro_pc]); - return false; - } - + State::input(macro[macro_pc]); macro_pc++; } - - return true; } bool file_execute(const gchar *filename) { gchar *macro, *p = NULL; - bool rc; macro_pc = 0; States::current = &States::start; @@ -80,16 +73,28 @@ file_execute(const gchar *filename) if (macro[0] == '#') p = MAX(strchr(macro, '\r'), strchr(macro, '\n')); - rc = macro_execute(p ? p+1 : macro); - g_free(macro); - if (!rc) + try { + macro_execute(p ? p+1 : macro); + } catch (...) { + g_free(macro); return false; + } + g_free(macro); macro_pc = 0; States::current = &States::start; return true; } +State::Error::Error(const gchar *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + interface.vmsg(Interface::MSG_ERROR, fmt, ap); + va_end(ap); +} + State::State() { for (guint i = 0; i < G_N_ELEMENTS(transitions); i++) @@ -107,18 +112,14 @@ State::eval_colon(void) return true; } -bool -State::input(gchar chr) +void +State::input(gchar chr) throw (Error) { State *state = States::current; for (;;) { State *next = state->get_next_state(chr); - if (!next) - /* Syntax error */ - return false; - if (next == state) break; @@ -130,12 +131,10 @@ State::input(gchar chr) undo.push_var<State *>(States::current); States::current = state; } - - return true; } State * -State::get_next_state(gchar chr) +State::get_next_state(gchar chr) throw (Error) { State *next = NULL; guint upper = g_ascii_toupper(chr); @@ -144,12 +143,14 @@ State::get_next_state(gchar chr) next = transitions[upper]; if (!next) next = custom(chr); + if (!next) + throw SyntaxError(chr); return next; } gchar * -StateExpectString::machine_input(gchar chr) +StateExpectString::machine_input(gchar chr) throw (Error) { switch (machine.mode) { case Machine::MODE_UPPER: @@ -225,7 +226,7 @@ StateExpectString::machine_input(gchar chr) reg = qregisters[g_ascii_toupper(chr)]; if (!reg) - return NULL; + throw InvalidQRegError(chr); return state == Machine::STATE_CTL_EQ ? reg->get_string() @@ -243,7 +244,7 @@ original: } State * -StateExpectString::custom(gchar chr) +StateExpectString::custom(gchar chr) throw (Error) { gchar *insert, *new_str; @@ -311,12 +312,12 @@ StateExpectQReg::StateExpectQReg() : State() } State * -StateExpectQReg::custom(gchar chr) +StateExpectQReg::custom(gchar chr) throw (Error) { QRegister *reg = qregisters[g_ascii_toupper(chr)]; if (!reg) - return NULL; + throw InvalidQRegError(chr); return got_register(reg); } @@ -418,7 +419,7 @@ StateStart::delete_words(gint64 n) } State * -StateStart::custom(gchar chr) +StateStart::custom(gchar chr) throw (Error) { gint64 v; tecoBool rc; @@ -656,7 +657,7 @@ StateStart::custom(gchar chr) } else if (eval_colon()) { expressions.push(FAILURE); } else { - return NULL; /* FIXME */ + throw MoveError("J"); } break; @@ -666,7 +667,7 @@ StateStart::custom(gchar chr) if (eval_colon()) expressions.push(rc); else if (IS_FAILURE(rc)) - return NULL; /* FIXME */ + throw MoveError("C"); break; case 'R': @@ -675,7 +676,7 @@ StateStart::custom(gchar chr) if (eval_colon()) expressions.push(rc); else if (IS_FAILURE(rc)) - return NULL; /* FIXME */ + throw MoveError("R"); break; case 'L': @@ -684,7 +685,7 @@ StateStart::custom(gchar chr) if (eval_colon()) expressions.push(rc); else if (IS_FAILURE(rc)) - return NULL; /* FIXME */ + throw MoveError("L"); break; case 'B': @@ -693,7 +694,7 @@ StateStart::custom(gchar chr) if (eval_colon()) expressions.push(rc); else if (IS_FAILURE(rc)) - return NULL; /* FIXME */ + throw MoveError("B"); break; case 'W': { @@ -728,7 +729,7 @@ StateStart::custom(gchar chr) if (eval_colon()) expressions.push(FAILURE); else - return NULL; /* FIXME */ + throw MoveError("W"); } break; } @@ -739,7 +740,7 @@ StateStart::custom(gchar chr) if (eval_colon()) expressions.push(rc); else if (IS_FAILURE(rc)) - return NULL; /* FIXME */ + throw Error("Not enough words to delete with <V>"); break; case 'Y': @@ -748,7 +749,7 @@ StateStart::custom(gchar chr) if (eval_colon()) expressions.push(rc); else if (IS_FAILURE(rc)) - return NULL; /* FIXME */ + throw Error("Not enough words to delete with <Y>"); break; case '=': @@ -792,7 +793,7 @@ StateStart::custom(gchar chr) if (eval_colon()) expressions.push(rc); else if (IS_FAILURE(rc)) - return NULL; /* FIXME */ + throw RangeError(chr); if (len == 0 || IS_FAILURE(rc)) break; @@ -807,7 +808,7 @@ StateStart::custom(gchar chr) } default: - return NULL; + throw SyntaxError(chr); } return this; @@ -819,7 +820,7 @@ StateFlowCommand::StateFlowCommand() : State() } State * -StateFlowCommand::custom(gchar chr) +StateFlowCommand::custom(gchar chr) throw (Error) { switch (chr) { /* @@ -880,7 +881,7 @@ StateFlowCommand::custom(gchar chr) break; default: - return NULL; + throw SyntaxError(chr); } return &States::start; @@ -892,7 +893,7 @@ StateCondCommand::StateCondCommand() : State() } State * -StateCondCommand::custom(gchar chr) +StateCondCommand::custom(gchar chr) throw (Error) { gint64 value = 0; bool result; @@ -959,7 +960,7 @@ StateCondCommand::custom(gchar chr) result = g_ascii_isupper((gchar)value); break; default: - return NULL; + throw Error("Invalid conditional type \"%c\"", chr); } if (!result) { @@ -978,7 +979,7 @@ StateControl::StateControl() : State() } State * -StateControl::custom(gchar chr) +StateControl::custom(gchar chr) throw (Error) { switch (g_ascii_toupper(chr)) { case 'O': @@ -1036,7 +1037,7 @@ StateControl::custom(gchar chr) break; default: - return NULL; + throw Error("Unsupported command <^%c>", chr); } return &States::start; @@ -1051,17 +1052,18 @@ StateECommand::StateECommand() : State() } State * -StateECommand::custom(gchar chr) +StateECommand::custom(gchar chr) throw (Error) { switch (g_ascii_toupper(chr)) { case 'F': BEGIN_EXEC(&States::start); if (!ring.current) - return NULL; /* FIXME */ + throw Error("No buffer selected"); if (IS_FAILURE(expressions.pop_num_calc()) && ring.current->dirty) - return NULL; /* FIXME */ + throw Error("Buffer \"%s\" is dirty", + ring.current->filename ? : "(Unnamed)"); ring.close(); break; @@ -1071,14 +1073,14 @@ StateECommand::custom(gchar chr) if (IS_FAILURE(expressions.pop_num_calc()) && ring.is_any_dirty()) - return NULL; /* FIXME */ + throw Error("Modified buffers exist"); undo.push_var<bool>(quit_requested); quit_requested = true; break; default: - return NULL; + throw SyntaxError(chr); } return &States::start; @@ -1090,7 +1092,7 @@ StateECommand::custom(gchar chr) * syntactically */ void -StateInsert::initial(void) +StateInsert::initial(void) throw (Error) { int args; @@ -1112,7 +1114,7 @@ StateInsert::initial(void) } void -StateInsert::process(const gchar *str, gint new_chars) +StateInsert::process(const gchar *str, gint new_chars) throw (Error) { interface.ssm(SCI_BEGINUNDOACTION); interface.ssm(SCI_ADDTEXT, new_chars, @@ -1123,14 +1125,14 @@ StateInsert::process(const gchar *str, gint new_chars) } State * -StateInsert::done(const gchar *str __attribute__((unused))) +StateInsert::done(const gchar *str __attribute__((unused))) throw (Error) { /* nothing to be done when done */ return &States::start; } void -StateSearch::initial(void) +StateSearch::initial(void) throw (Error) { gint64 v; @@ -1143,6 +1145,10 @@ StateSearch::initial(void) parameters.count = 1; parameters.from = (gint)v; parameters.to = (gint)expressions.pop_num_calc(); + + if (!Validate::pos(parameters.from) || + !Validate::pos(parameters.to)) + throw RangeError("S"); } else { parameters.count = (gint)v; if (v >= 0) { @@ -1246,7 +1252,8 @@ StateSearch::class2regexp(MatchState &state, const gchar *&pattern, } gchar * -StateSearch::pattern2regexp(const gchar *&pattern, bool single_expr) +StateSearch::pattern2regexp(const gchar *&pattern, + bool single_expr) { MatchState state = STATE_START; gchar *re = NULL; @@ -1357,7 +1364,8 @@ error: } void -StateSearch::process(const gchar *str, gint new_chars __attribute__((unused))) +StateSearch::process(const gchar *str, + gint new_chars __attribute__((unused))) throw (Error) { static const gint flags = G_REGEX_CASELESS | G_REGEX_MULTILINE | G_REGEX_DOTALL | G_REGEX_RAW; @@ -1447,7 +1455,7 @@ StateSearch::process(const gchar *str, gint new_chars __attribute__((unused))) } State * -StateSearch::done(const gchar *str) +StateSearch::done(const gchar *str) throw (Error) { BEGIN_EXEC(&States::start); @@ -10,6 +10,44 @@ #define MAX_TRANSITIONS 127 class State { +public: + class Error { + public: + Error(const gchar *fmt, ...); + }; + + class SyntaxError : public Error { + public: + SyntaxError(gchar chr) + : Error("Syntax error \"%c\" (%d)", chr, chr) {} + }; + + class MoveError : public Error { + public: + MoveError(const gchar *cmd) + : Error("Attempt to move pointer off page with <%s>", + cmd) {} + MoveError(gchar cmd) + : Error("Attempt to move pointer off page with <%c>", + cmd) {} + }; + + class RangeError : public Error { + public: + RangeError(const gchar *cmd) + : Error("Invalid range specified for <%s>", cmd) {} + RangeError(gchar cmd) + : Error("Invalid range specified for <%c>", cmd) {} + }; + + class InvalidQRegError : public Error { + public: + InvalidQRegError(const gchar *name) + : Error("Invalid Q-Register \"%s\"", name) {} + InvalidQRegError(gchar name) + : Error("Invalid Q-Register \"%c\"", name) {} + }; + protected: /* static transitions */ State *transitions[MAX_TRANSITIONS]; @@ -29,15 +67,16 @@ protected: public: State(); - static bool input(gchar chr); - State *get_next_state(gchar chr); + static void input(gchar chr) throw (Error); + State *get_next_state(gchar chr) throw (Error); protected: static bool eval_colon(void); virtual State * - custom(gchar chr) + custom(gchar chr) throw (Error) { + throw SyntaxError(chr); return NULL; } }; @@ -75,13 +114,13 @@ public: StateExpectString() : State() {} private: - gchar *machine_input(gchar key); - State *custom(gchar chr); + gchar *machine_input(gchar key) throw (Error); + State *custom(gchar chr) throw (Error); protected: - virtual void initial(void) {} - virtual void process(const gchar *str, gint new_chars) {} - virtual State *done(const gchar *str) = 0; + virtual void initial(void) throw (Error) {} + virtual void process(const gchar *str, gint new_chars) throw (Error) {} + virtual State *done(const gchar *str) throw (Error) = 0; }; class QRegister; @@ -94,14 +133,14 @@ public: StateExpectQReg(); private: - State *custom(gchar chr); + State *custom(gchar chr) throw (Error); protected: /* * FIXME: would be nice to pass reg as reference, but there are * circular header dependencies... */ - virtual State *got_register(QRegister *reg) = 0; + virtual State *got_register(QRegister *reg) throw (Error) = 0; }; class StateStart : public State { @@ -113,7 +152,7 @@ private: tecoBool move_lines(gint64 n); tecoBool delete_words(gint64 n); - State *custom(gchar chr); + State *custom(gchar chr) throw (Error); }; class StateControl : public State { @@ -121,7 +160,7 @@ public: StateControl(); private: - State *custom(gchar chr); + State *custom(gchar chr) throw (Error); }; class StateFlowCommand : public State { @@ -129,7 +168,7 @@ public: StateFlowCommand(); private: - State *custom(gchar chr); + State *custom(gchar chr) throw (Error); }; class StateCondCommand : public State { @@ -137,7 +176,7 @@ public: StateCondCommand(); private: - State *custom(gchar chr); + State *custom(gchar chr) throw (Error); }; class StateECommand : public State { @@ -145,14 +184,14 @@ public: StateECommand(); private: - State *custom(gchar chr); + State *custom(gchar chr) throw (Error); }; class StateInsert : public StateExpectString { private: - void initial(void); - void process(const gchar *str, gint new_chars); - State *done(const gchar *str); + void initial(void) throw (Error); + void process(const gchar *str, gint new_chars) throw (Error); + State *done(const gchar *str) throw (Error); }; class StateSearch : public StateExpectString { @@ -176,9 +215,9 @@ private: bool escape_default = false); gchar *pattern2regexp(const gchar *&pattern, bool single_expr = false); - void initial(void); - void process(const gchar *str, gint new_chars); - State *done(const gchar *str); + void initial(void) throw (Error); + void process(const gchar *str, gint new_chars) throw (Error); + State *done(const gchar *str) throw (Error); }; extern gint macro_pc; @@ -210,7 +249,7 @@ extern enum Mode { extern gchar *strings[2]; extern gchar escape_char; -bool macro_execute(const gchar *macro); +void macro_execute(const gchar *macro) throw (State::Error); bool file_execute(const gchar *filename); #endif diff --git a/qbuffers.cpp b/qbuffers.cpp index 28b54dc..d8294ed 100644 --- a/qbuffers.cpp +++ b/qbuffers.cpp @@ -495,7 +495,7 @@ StateEditFile::do_edit(const gchar *filename) } void -StateEditFile::initial(void) +StateEditFile::initial(void) throw (Error) { gint64 id = expressions.pop_num_calc(1, -1); @@ -510,7 +510,7 @@ StateEditFile::initial(void) } State * -StateEditFile::done(const gchar *str) +StateEditFile::done(const gchar *str) throw (Error) { BEGIN_EXEC(&States::start); @@ -554,18 +554,18 @@ StateEditFile::done(const gchar *str) } State * -StateSaveFile::done(const gchar *str) +StateSaveFile::done(const gchar *str) throw (Error) { BEGIN_EXEC(&States::start); if (!ring.save(*str ? str : NULL)) - return NULL; + throw Error("Unable to save file"); return &States::start; } State * -StateEQCommand::got_register(QRegister *reg) +StateEQCommand::got_register(QRegister *reg) throw (Error) { BEGIN_EXEC(&States::loadqreg); register_argument = reg; @@ -573,7 +573,7 @@ StateEQCommand::got_register(QRegister *reg) } State * -StateLoadQReg::done(const gchar *str) +StateLoadQReg::done(const gchar *str) throw (Error) { BEGIN_EXEC(&States::start); @@ -593,7 +593,7 @@ StateLoadQReg::done(const gchar *str) } State * -StateCtlUCommand::got_register(QRegister *reg) +StateCtlUCommand::got_register(QRegister *reg) throw (Error) { BEGIN_EXEC(&States::setqregstring); register_argument = reg; @@ -601,7 +601,7 @@ StateCtlUCommand::got_register(QRegister *reg) } State * -StateSetQRegString::done(const gchar *str) +StateSetQRegString::done(const gchar *str) throw (Error) { BEGIN_EXEC(&States::start); @@ -612,7 +612,7 @@ StateSetQRegString::done(const gchar *str) } State * -StateGetQRegInteger::got_register(QRegister *reg) +StateGetQRegInteger::got_register(QRegister *reg) throw (Error) { BEGIN_EXEC(&States::start); @@ -623,7 +623,7 @@ StateGetQRegInteger::got_register(QRegister *reg) } State * -StateSetQRegInteger::got_register(QRegister *reg) +StateSetQRegInteger::got_register(QRegister *reg) throw (Error) { BEGIN_EXEC(&States::start); @@ -634,7 +634,7 @@ StateSetQRegInteger::got_register(QRegister *reg) } State * -StateIncreaseQReg::got_register(QRegister *reg) +StateIncreaseQReg::got_register(QRegister *reg) throw (Error) { BEGIN_EXEC(&States::start); @@ -646,11 +646,10 @@ StateIncreaseQReg::got_register(QRegister *reg) } State * -StateMacro::got_register(QRegister *reg) +StateMacro::got_register(QRegister *reg) throw (Error) { gint pc = macro_pc; gchar *str; - bool rc; BEGIN_EXEC(&States::start); @@ -664,18 +663,23 @@ StateMacro::got_register(QRegister *reg) macro_pc = 0; str = reg->get_string(); - rc = macro_execute(str); + try { + macro_execute(str); + } catch (...) { + g_free(str); + macro_pc = pc; + States::current = this; + throw; /* forward */ + } g_free(str); macro_pc = pc; States::current = this; - if (!rc) - return NULL; return &States::start; } State * -StateCopyToQReg::got_register(QRegister *reg) +StateCopyToQReg::got_register(QRegister *reg) throw (Error) { gint64 from, len; Sci_TextRange tr; @@ -687,6 +691,10 @@ StateCopyToQReg::got_register(QRegister *reg) from = interface.ssm(SCI_GETCURRENTPOS); sptr_t line = interface.ssm(SCI_LINEFROMPOSITION, from) + expressions.pop_num_calc(); + + if (!Validate::line(line)) + throw RangeError("X"); + len = interface.ssm(SCI_POSITIONFROMLINE, line) - from; if (len < 0) { @@ -696,6 +704,10 @@ StateCopyToQReg::got_register(QRegister *reg) } else { gint64 to = expressions.pop_num(); from = expressions.pop_num(); + + if (!Validate::pos(from) || !Validate::pos(to)) + throw RangeError("X"); + len = to - from; } @@ -305,58 +305,58 @@ class StateEditFile : public StateExpectString { private: void do_edit(const gchar *filename); - void initial(void); - State *done(const gchar *str); + void initial(void) throw (Error); + State *done(const gchar *str) throw (Error); }; class StateSaveFile : public StateExpectString { private: - State *done(const gchar *str); + State *done(const gchar *str) throw (Error); }; class StateEQCommand : public StateExpectQReg { private: - State *got_register(QRegister *reg); + State *got_register(QRegister *reg) throw (Error); }; class StateLoadQReg : public StateExpectString { private: - State *done(const gchar *str); + State *done(const gchar *str) throw (Error); }; class StateCtlUCommand : public StateExpectQReg { private: - State *got_register(QRegister *reg); + State *got_register(QRegister *reg) throw (Error); }; class StateSetQRegString : public StateExpectString { private: - State *done(const gchar *str); + State *done(const gchar *str) throw (Error); }; class StateGetQRegInteger : public StateExpectQReg { private: - State *got_register(QRegister *reg); + State *got_register(QRegister *reg) throw (Error); }; class StateSetQRegInteger : public StateExpectQReg { private: - State *got_register(QRegister *reg); + State *got_register(QRegister *reg) throw (Error); }; class StateIncreaseQReg : public StateExpectQReg { private: - State *got_register(QRegister *reg); + State *got_register(QRegister *reg) throw (Error); }; class StateMacro : public StateExpectQReg { private: - State *got_register(QRegister *reg); + State *got_register(QRegister *reg) throw (Error); }; class StateCopyToQReg : public StateExpectQReg { private: - State *got_register(QRegister *reg); + State *got_register(QRegister *reg) throw (Error); }; namespace States { |