//-< BROWSER.CXX >---------------------------------------------------*--------* // GOODS Version 1.0 (c) 1997 GARRET * ? * // (Generic Object Oriented Database System) * /\| * // * / \ * // Created: 30-Oct-97 K.A. Knizhnik * / [] \ * // Last update: 30-Oct-97 K.A. Knizhnik * GARRET * //-------------------------------------------------------------------*--------* // Database browser //-------------------------------------------------------------------*--------* #include "goods.h" #include "client.h" #include "locale.h" USE_GOODS_NAMESPACE const char* format_i = "%d"; const char* format_u = "%u"; class database_browser : public dbs_application { protected: dbs_storage** storage; int n_storages; dnm_array class_dict; dnm_buffer obj_buf; dnm_buffer cls_buf; void dump_object(stid_t sid, opid_t opid); virtual void disconnected(stid_t sid); virtual void login_refused(stid_t sid); virtual void invalidate(stid_t sid, opid_t opid); public: boolean open(const char* dbs_name); void close(); void dialogue(); virtual~database_browser() {} }; void database_browser::disconnected(stid_t sid) { console::output("Server %d is disconnected\n", sid); storage[sid]->close(); delete storage[sid]; storage[sid] = NULL; } void database_browser::login_refused(stid_t sid) { console::output("Authorization procedure fails at server %d\n", sid); storage[sid]->close(); delete storage[sid]; storage[sid] = NULL; } void database_browser::invalidate(stid_t sid, opid_t opid) { console::output("Object %x:%x was modified\n", sid, opid); storage[sid]->throw_object(opid); } boolean database_browser::open(const char* dbs_name) { char cfg_file_name[MAX_CFG_FILE_LINE_SIZE]; char cfg_buf[MAX_CFG_FILE_LINE_SIZE]; int len = strlen(dbs_name); if (len < 4 || strcmp(dbs_name+len-4, ".cfg") != 0) { sprintf(cfg_file_name, "%s.cfg", dbs_name); } else { strcpy(cfg_file_name, dbs_name); } FILE* cfg = fopen(cfg_file_name, "r"); if (cfg == NULL) { console::output("Failed to open database configuration file: '%s'\n", cfg_file_name); return False; } if (fgets(cfg_buf, sizeof cfg_buf, cfg) == NULL || sscanf(cfg_buf, "%d", &n_storages) != 1) { console::output("Bad format of configuration file '%s'\n", cfg_file_name); return False; } storage = NEW dbs_storage*[n_storages]; memset(storage, 0, n_storages*sizeof(obj_storage*)); while (fgets(cfg_buf, sizeof cfg_buf, cfg)) { int i; char hostname[MAX_CFG_FILE_LINE_SIZE]; if (sscanf(cfg_buf, "%d:%s", &i, hostname) == 2) { if (storage[i] != NULL) { console::output("Duplicated entry in configuration file: %s", cfg_buf); } storage[i] = new dbs_client_storage(i, this); if (!storage[i]->open(hostname)) { console::output("Failed to establish connection with server" " '%s'\n", hostname); delete storage[i]; storage[i] = NULL; } } } fclose(cfg); return True; } void database_browser::close() { for (int i = 0; i < n_storages; i++) { if (storage[i] != NULL) { storage[i]->close(); delete storage[i]; } } delete[] storage; } inline boolean is_ascii(char* s, int len) { while (--len >= 0) { int ch = *s++; if (ch != 0 && !isprint(ch & 0xFF)) { return False; } } return True; } static char* dump_raw_binary(char* bins) { nat4 len; bins = unpack4((char*)&len, bins); int i, n = len; for (i = 0; i < n && bins[i] > 0 && isprint(bins[i]); i++); if (i == n || (i == n-1 && bins[i] == '\0')) { console::output("\"%.*s\"", n, bins); } else { char sep = '{'; for (i = 0, n = len; i < n; i++) { console::output("%c0x%02x", sep, bins[i] & 0xFF); sep = ','; } if (sep == '{') { console::output("{}"); } else { console::output("}"); } } return bins + n; } static char* dump_imu_string(char* bins) { nat2 len; bins = unpack2((char*)&len, bins); if(len == 0xFFFF) { console::output("null"); } else { console::output("\""); while(len-- > 0) { nat2 ch; bins = unpack2((char*)&ch, bins); if((ch & 0xFF00) || !isprint(ch)) { console::output("\\u%04X", ch); } else if (ch == '\\') { console::output("\\\\"); } else { console::output("%c", (char)ch); } } console::output("\""); } return bins; } static void dump_fields(dbs_class_descriptor* cld, size_t obj_size, int field_no, int n_fields, char* &refs, char* &bins) { nat2 sid; nat4 opid; boolean first = True; console::output("{"); if (n_fields != 0) { int next_field = field_no + n_fields; do { dbs_field_descriptor* field = &cld->fields[field_no]; if (!first) { console::output(", "); } first = False; console::output("%s=", &cld->names[field->name]); int n = field->is_varying() ? cld->get_varying_length(obj_size) : field->n_items; if (field->size == 1 && is_ascii(bins, n)) { if (n == 1) { char ch = *bins++; if (ch == 0) { console::output("0"); } else { console::output("'%c'(%X)", ch, nat1(ch)); } } else { console::output("\"%.*s\"", n, bins); bins += n; } } else { if (n > 1) { console::output("{"); } for (int i = 0; i < n; i++) { switch (field->type) { case fld_structure: dump_fields(cld, obj_size, field_no+1, field->next ? field->next - field_no - 1 : next_field - field_no - 1, refs, bins); break; case fld_reference: refs = unpackref(sid, opid, refs); console::output("%x:%x", sid, opid); break; case fld_signed_integer: if (field->size == 1) { console::output(format_i, *(int1*)bins); bins += 1; } else if (field->size == 2) { int2 val; bins = unpack2((char*)&val, bins); console::output(format_i, val); } else if (field->size == 4) { int4 val; bins = unpack4((char*)&val, bins); console::output(format_i, val); } else { int8 val; bins = unpack8((char*)&val, bins); console::output("%X%08x", int8_high_part(val), int8_low_part(val)); } break; case fld_unsigned_integer: if (field->size == 1) { console::output(format_u, *(nat1*)bins); bins += 1; } else if (field->size == 2) { nat2 val; bins = unpack2((char*)&val, bins); console::output(format_u, val); } else if (field->size == 4) { nat4 val; bins = unpack4((char*)&val, bins); console::output(format_u, val); } else { nat8 val; bins = unpack8((char*)&val, bins); console::output("%X%08x", nat8_high_part(val), nat8_low_part(val)); } break; case fld_real: if (field->size == 4) { real4 val; bins = unpack4((char*)&val, bins); console::output("%f", val); } else { real8 val; bins = unpack8((char*)&val, bins); console::output("%lf", val); } break; case fld_string: bins = dump_imu_string(bins); break; case fld_raw_binary: bins = dump_raw_binary(bins); break; } if (i != n-1) { console::output(", "); } } if (n > 1) { console::output("}"); } } field_no = field->next; } while (field_no != 0); } console::output("}"); } void dump_class(dbs_class_descriptor* cld, int level, int field_no, int n_fields) { const char indent[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; if (n_fields == 0) { console::output("\n"); } else { int next_field = field_no + n_fields; level += 1; do { dbs_field_descriptor* field = &cld->fields[field_no]; console::output("%s", indent + sizeof(indent) - level); switch (field->type) { case fld_structure: console::output("struct {\n"); dump_class(cld, level, field_no+1, field->next ? field->next - field_no - 1 : next_field - field_no - 1); console::output("%s}", indent + sizeof(indent)-level); break; case fld_reference: console::output("ref"); break; case fld_signed_integer: console::output("int%d", field->size); break; case fld_unsigned_integer: console::output("nat%d", field->size); break; case fld_real: console::output("real%d", field->size); break; } console::output(" %s", &cld->names[field->name]); if (field->n_items > 1) { console::output("[%d]", field->n_items); } else if (field->is_varying()) { console::output("[1]"); } console::output(";\n"); field_no = field->next; } while (field_no != 0); } } void database_browser::dump_object(stid_t sid, opid_t opid) { dbs_class_descriptor* cld; if (sid >= n_storages || storage[sid] == NULL) { console::output("Storage %d is not in configuration file or " "not available\n", sid); return; } if (opid == 0) { console::output("NULL\n"); return; } if (opid == RAW_CPID) { console::output("ABSTRACT ROOT CLASS\n"); return; } if (opid <= MAX_CPID) { if ((cld = class_dict[opid]) == NULL) { storage[sid]->get_class(opid, cls_buf); if (cls_buf.size() == 0) { console::output("Class %x:%x is not in the database\n", sid, opid); return; } cld = (dbs_class_descriptor*)&cls_buf; cld->unpack(); class_dict[opid] = cld = cld->clone(); } console::output("class %s {\n", cld->name()); dump_class(cld, 1, 0, cld->n_fields); console::output("}\n"); return; } storage[sid]->load(opid, lof_none, obj_buf); dbs_object_header* hdr = (dbs_object_header*)&obj_buf; cpid_t cpid = hdr->get_cpid(); if (cpid == 0) { console::output("Object %x:%x is not in the database\n", sid, opid); return; } else if (cpid == RAW_CPID) { console::output("ABSTRACT ROOT OBJECT\n"); return; } if ((cld = class_dict[cpid]) == NULL) { storage[sid]->get_class(cpid, cls_buf); assert(cls_buf.size() != 0); cld = (dbs_class_descriptor*)&cls_buf; cld->unpack(); class_dict[cpid] = cld = cld->clone(); } console::output("Object \"%s\" %x:%x\n", cld->name(), sid, opid); size_t obj_size = hdr->get_size(); char* refs = hdr->body(); char* bins = refs + cld->get_number_of_references(obj_size)*sizeof(dbs_reference_t); dump_fields(cld, obj_size, 0, cld->n_fields, refs, bins); console::output("\n"); } void database_browser::dialogue() { char buf[MAX_CFG_FILE_LINE_SIZE]; while (True) { int sid = 0; int oid = ROOT_OPID; console::output(">> "); if (console::input(buf, sizeof buf)) { char* cmd = buf; while (isspace(*(nat1*)cmd)) cmd += 1; if (*cmd == '\0') { continue; } if (strincmp(cmd, ".hex", 4) == 0) { format_i = format_u = "%#x"; } else if (strincmp(cmd, ".oct", 4) == 0) { format_i = format_u = "%#o"; } else if (strincmp(cmd, ".dec", 4) == 0) { format_i = "%d"; format_u = "%u"; } else if (strincmp(cmd, "exit", 4) == 0) { break; } else if ((strchr(cmd, ':') == NULL && sscanf(cmd, "%x", &oid) == 1) || sscanf(cmd, "%x:%x", &sid, &oid) >= 1) { dump_object(sid, oid); } else { console::output( "Commands:\n" " .hex - output integer in hexademical radix\n" " .dec - output integer in decimal radix\n" " .oct - output integer in octal radix\n" " exit - exit browser\n" " : - dump object from specified storage\n" " - dump object from storage 0\n" " : - dump root object of specified storage\n" ); } } else { break; } } } int main(int argc, char* argv[]) { setlocale(LC_ALL, ""); if (argc < 2) { console::output("GOODS database browser\n" "Usage: browser \n"); return EXIT_FAILURE; } task::initialize(task::huge_stack); database_browser db; if (db.open(argv[1])) { db.dialogue(); db.close(); console::output("Browser terminated\n"); return EXIT_SUCCESS; } else { console::output("Database not found\n"); return EXIT_FAILURE; } }