#include #include #include #include #include #if __cplusplus >= 201703L #include using std::optional; #else #include using boost::optional; #endif #include "database.h" #include "db_common.h" #include "io_primitives.h" #include "oldattrb.h" using namespace std::literals::string_literals; class boolexp { public: virtual ~boolexp() {} virtual std::ostream &print(std::ostream &, bool) = 0; }; class not_boolexp : public boolexp { std::unique_ptr be; public: not_boolexp(std::unique_ptr &&b) : be(std::move(b)) {} std::ostream & print(std::ostream &out, bool) override { out << '!'; return be->print(out, true); } }; class simple_boolexp : public boolexp { char flag; dbref obj; public: simple_boolexp(char f, dbref o) : flag(f), obj(o) {} std::ostream & print(std::ostream &out, bool) override { if (flag) { out << flag; } return out << '#' << obj; } }; class ind_boolexp : public boolexp { dbref obj; optional lock; public: ind_boolexp(dbref o) : obj(o) {} ind_boolexp(dbref o, std::string l) : obj(o), lock(std::move(l)) {} std::ostream & print(std::ostream &out, bool) override { out << "@#" << obj; if (lock) { out << '/' << *lock; } return out; } }; class is_boolexp : public simple_boolexp { public: is_boolexp(dbref o) : simple_boolexp('=', o) {} }; class carry_boolexp : public simple_boolexp { public: carry_boolexp(dbref o) : simple_boolexp('+', o) {} }; class owner_boolexp : public simple_boolexp { public: owner_boolexp(dbref o) : simple_boolexp('$', o) {} }; class const_boolexp : public simple_boolexp { public: const_boolexp(dbref o) : simple_boolexp('\0', o) {} }; class bool_boolexp : public boolexp { bool val; public: bool_boolexp(bool v) : val(v) {} std::ostream & print(std::ostream &out, bool) override { if (val) { out << "#true"; } else { out << "#false"; } return out; } }; class pair_boolexp : public boolexp { std::string name; char flag; std::string val; public: pair_boolexp(std::string n, char f, std::string v) : name(std::move(n)), flag(f), val(std::move(v)) { } std::ostream & print(std::ostream &out, bool) override { return out << name << flag << val; } }; class atr_boolexp : public pair_boolexp { public: atr_boolexp(const std::string &n, const std::string &v) : pair_boolexp(n, ':', v) { } }; class eval_boolexp : public pair_boolexp { public: eval_boolexp(const std::string &n, const std::string &v) : pair_boolexp(n, '/', v) { } }; class flag_boolexp : public pair_boolexp { public: flag_boolexp(const std::string &n, const std::string &v) : pair_boolexp(n, '^', v) { } }; class andor_boolexp : public boolexp { std::unique_ptr left; char flag; std::unique_ptr right; public: andor_boolexp(std::unique_ptr &&l, char f, std::unique_ptr &&r) : left(std::move(l)), flag(f), right(std::move(r)) { } std::ostream & print(std::ostream &out, bool pnot) override { bool pand = flag == '&'; if (pnot) { out << '('; } left->print(out, pand); out << flag; right->print(out, pand); if (pnot) { out << ')'; } return out; } }; class and_boolexp : public andor_boolexp { public: and_boolexp(std::unique_ptr &&l, std::unique_ptr &&r) : andor_boolexp(std::move(l), '&', std::move(r)) { } }; class or_boolexp : public andor_boolexp { public: or_boolexp(std::unique_ptr &&l, std::unique_ptr &&r) : andor_boolexp(std::move(l), '|', std::move(r)) { } }; void close_paren(istream &in) { char c; if (!(in.get(c) && c == ')')) { throw db_format_exception{"Expected to read ), got "s + c + istream_line(in)}; } } // Copied from 1.7.3 source const char * convert_atr(int oldatr) { static char result[3]; switch (oldatr) { case A_OSUCC: return "OSUCCESS"; case A_OFAIL: return "OFAILURE"; case A_FAIL: return "FAILURE"; case A_SUCC: return "SUCCESS"; case A_PASS: return "XYXXY"; case A_DESC: return "DESCRIBE"; case A_SEX: return "SEX"; case A_ODROP: return "ODROP"; case A_DROP: return "DROP"; case A_OKILL: return "OKILL"; case A_KILL: return "KILL"; case A_ASUCC: return "ASUCCESS"; case A_AFAIL: return "AFAILURE"; case A_ADROP: return "ADROP"; case A_AKILL: return "AKILL"; case A_USE: return "DOES"; case A_CHARGES: return "CHARGES"; case A_RUNOUT: return "RUNOUT"; case A_STARTUP: return "STARTUP"; case A_ACLONE: return "ACLONE"; case A_APAY: return "APAYMENT"; case A_OPAY: return "OPAYMENT"; case A_PAY: return "PAYMENT"; case A_COST: return "COST"; case A_RAND: return "RAND"; case A_LISTEN: return "LISTEN"; case A_AAHEAR: return "AAHEAR"; case A_AMHEAR: return "AMHEAR"; case A_AHEAR: return "AHEAR"; case A_LAST: return "LAST"; case A_QUEUE: return "QUEUE"; case A_IDESC: return "IDESCRIBE"; case A_ENTER: return "ENTER"; case A_OXENTER: return "OXENTER"; case A_AENTER: return "AENTER"; case A_ADESC: return "ADESCRIBE"; case A_ODESC: return "ODESCRIBE"; case A_RQUOTA: return "RQUOTA"; case A_ACONNECT: return "ACONNECT"; case A_ADISCONNECT: return "ADISCONNECT"; case A_LEAVE: return "LEAVE"; case A_ALEAVE: return "ALEAVE"; case A_OLEAVE: return "OLEAVE"; case A_OENTER: return "OENTER"; case A_OXLEAVE: return "OXLEAVE"; default: if (oldatr >= 100 && oldatr < 178) { result[0] = 'V' + (oldatr - 100) / 26; result[1] = 'A' + (oldatr - 100) % 26; return result; } else { throw db_format_exception{"Invalid attribute number in convert_atr"}; } } /*NOTREACHED */ return ""; } std::unique_ptr parse_boolexp(istream &in) { char c; if (!in.get(c)) { throw db_format_exception{"Unable to read full lock at"s + istream_line(in)}; } if (c == '(') { in.get(c); dbref d; switch (c) { case '=': in >> d; close_paren(in); return std::make_unique(d); case '+': in >> d; close_paren(in); return std::make_unique(d); case '$': in >> d; close_paren(in); return std::make_unique(d); case '@': in >> d; close_paren(in); return std::make_unique(d); case '!': { auto b = parse_boolexp(in); close_paren(in); return std::make_unique(std::move(b)); } default: { in.unget(); auto l = parse_boolexp(in); char c2; in.get(c2); if (c2 == '&') { auto r = parse_boolexp(in); close_paren(in); return std::make_unique(std::move(l), std::move(r)); } else if (c2 == '|') { auto r = parse_boolexp(in); close_paren(in); return std::make_unique(std::move(l), std::move(r)); } else { throw db_format_exception{"Invalid character in lock: "s + c2 + istream_line(in)}; } } } } else if (std::isdigit(c)) { in.unget(); dbref d; in >> d; if (in.peek() == ':') { in.get(); auto key = convert_atr(d); std::string val; char c2; while (in.get(c2)) { if (c2 == '\n' || c2 == ')') { break; } val.push_back(c2); } if (in.eof()) { throw db_format_exception{"Unexpected EOF reading old-style atr lock"s + istream_line(in)}; } return std::make_unique(key, val); } return std::make_unique(d); } else if (c == '"') { in.unget(); std::string key = db_read_str(in); char c2 = in.get(); std::string val = db_read_str(in); switch (c2) { case ':': return std::make_unique(key, val); case '/': return std::make_unique(key, val); case '^': return std::make_unique(key, val); default: throw db_format_exception{"Invalid character in lock: "s + c2 + istream_line(in)}; } } else if (c == '-') { // 1.7.6 source calls this an obsolete NOTHING key and eats it. while (in.peek() != '\n') { in.get(); } } else { // Unquoted attr or eval lock? in.unget(); std::string key; char c2; while (in.get(c2)) { if (c2 == ':' || c2 == '/' || c2 == '\n') { break; } key.push_back(c2); } if (in.eof() || c2 == '\n') { throw db_format_exception{"Invalid lock"s + istream_line(in)}; } char type = c2; std::string val; while (in.get(c2)) { if (c2 == '\n' || c2 == ')' || c2 == '&' || c2 == '|') { break; } val.push_back(c2); } if (in.eof()) { throw db_format_exception{"Unexpected end of file in lock."}; } in.unget(); if (type == ':') { return std::make_unique(key, val); } else { return std::make_unique(key, val); } } // Should never be reached throw db_format_exception{"Unable to read lock!"s + istream_line(in)}; } // Read a DBF_NEW_LOCKS boolexp std::string read_boolexp(istream &in) { auto be = parse_boolexp(in); if (in.peek() == '\n') { in.get(); } else { throw db_format_exception{"Invalid character in lock: "s + static_cast(in.peek()) + istream_line(in)}; } std::ostringstream out; be->print(out, false); // std::cout << "Read lock: " << out.str() << '\n'; return out.str(); }