00001
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <config.h>
00022
00023 #include <iomanip>
00024 #include <iostream>
00025 #include <string>
00026 #include <cstdio>
00027
00028 #include "flint_table.h"
00029 #include "flint_cursor.h"
00030 #include "stringutils.h"
00031 #include "utils.h"
00032
00033 #include <xapian.h>
00034
00035 #include "gnu_getopt.h"
00036
00037 using namespace std;
00038
00039 #define PROG_NAME "xapian-inspect"
00040 #define PROG_DESC "Inspect the contents of a flint table for development or debugging"
00041
00042 #define OPT_HELP 1
00043 #define OPT_VERSION 2
00044
00045 static void show_usage() {
00046 cout << "Usage: "PROG_NAME" [OPTIONS] TABLE\n\n"
00047 "Options:\n"
00048 " --help display this help and exit\n"
00049 " --version output version information and exit" << endl;
00050 }
00051
00052 static void
00053 display_nicely(const string & data) {
00054 string::const_iterator i;
00055 for (i = data.begin(); i != data.end(); ++i) {
00056 unsigned char ch = *i;
00057 if (ch < 32 || ch >= 127) {
00058 switch (ch) {
00059 case '\n': cout << "\\n"; break;
00060 case '\r': cout << "\\r"; break;
00061 case '\t': cout << "\\t"; break;
00062 default: {
00063 char buf[20];
00064 sprintf(buf, "\\x%02x", (int)ch);
00065 cout << buf;
00066 }
00067 }
00068 } else if (ch == '\\') {
00069 cout << "\\\\";
00070 } else {
00071 cout << ch;
00072 }
00073 }
00074 }
00075
00076 static void
00077 show_help()
00078 {
00079 cout << "Commands:\n"
00080 "next : Next entry (alias 'n' or '')\n"
00081 "prev : Previous entry (alias 'p')\n"
00082 "goto X : Goto entry X (alias 'g')\n"
00083 "until X: Display entries until X (alias 'u')\n"
00084 "open X : Open table X instead (alias 'o') - e.g. open postlist\n"
00085 "help : Show this (alias 'h' or '?')\n"
00086 "quit : Quit this utility (alias 'q')" << endl;
00087 }
00088
00089 static void
00090 do_until(FlintCursor & cursor, const string & target)
00091 {
00092 if (cursor.after_end()) {
00093 cout << "At end already." << endl;
00094 return;
00095 }
00096
00097 if (!target.empty()) {
00098 int cmp = target.compare(cursor.current_key);
00099 if (cmp <= 0) {
00100 if (cmp)
00101 cout << "Already after specified key." << endl;
00102 else
00103 cout << "Already at specified key." << endl;
00104 return;
00105 }
00106 }
00107
00108 while (cursor.next()) {
00109 int cmp = 1;
00110 if (!target.empty()) {
00111 cmp = target.compare(cursor.current_key);
00112 if (cmp < 0) {
00113 cout << "No exact match, stopping at entry before." << endl;
00114 cursor.prev();
00115 return;
00116 }
00117 }
00118 cout << "Key: ";
00119 display_nicely(cursor.current_key);
00120 cout << "\nTag: ";
00121 cursor.read_tag();
00122 display_nicely(cursor.current_tag);
00123 cout << "\n";
00124 if (cmp == 0) {
00125 return;
00126 }
00127 }
00128
00129 cout << "Reached end." << endl;
00130 }
00131
00132 int
00133 main(int argc, char **argv)
00134 {
00135 const struct option long_opts[] = {
00136 {"help", no_argument, 0, OPT_HELP},
00137 {"version", no_argument, 0, OPT_VERSION},
00138 {NULL, 0, 0, 0}
00139 };
00140
00141 int c;
00142 while ((c = gnu_getopt_long(argc, argv, "", long_opts, 0)) != -1) {
00143 switch (c) {
00144 case OPT_HELP:
00145 cout << PROG_NAME" - "PROG_DESC"\n\n";
00146 show_usage();
00147 exit(0);
00148 case OPT_VERSION:
00149 cout << PROG_NAME" - "PACKAGE_STRING << endl;
00150 exit(0);
00151 default:
00152 show_usage();
00153 exit(1);
00154 }
00155 }
00156
00157 if (argc - optind != 1) {
00158 show_usage();
00159 exit(1);
00160 }
00161
00162
00163 string table_name(argv[optind]);
00164 bool arg_is_directory = dir_exists(table_name);
00165 if (endswith(table_name, ".DB"))
00166 table_name.resize(table_name.size() - 2);
00167 else if (!endswith(table_name, '.'))
00168 table_name += '.';
00169 if (arg_is_directory && !file_exists(table_name + "DB")) {
00170 cerr << argv[0] << ": You need to specify a single Btree table, not a database directory." << endl;
00171 exit(1);
00172 }
00173
00174 show_help();
00175 cout << endl;
00176
00177 open_different_table:
00178 try {
00179 FlintTable table("", table_name, true);
00180 table.open();
00181 if (table.empty()) {
00182 cout << "No entries!" << endl;
00183 exit(0);
00184 }
00185
00186 FlintCursor cursor(&table);
00187 cursor.find_entry(string());
00188 cursor.next();
00189
00190 while (!cin.eof()) {
00191 cout << "Key: ";
00192 display_nicely(cursor.current_key);
00193 cout << "\nTag: ";
00194 cursor.read_tag();
00195 display_nicely(cursor.current_tag);
00196 cout << "\n";
00197 wait_for_input:
00198 cout << "? " << flush;
00199
00200 string input;
00201 getline(cin, input);
00202 if (cin.eof()) break;
00203
00204 if (endswith(input, '\r'))
00205 input.resize(input.size() - 1);
00206
00207 if (input.empty() || input == "n" || input == "next") {
00208 if (cursor.after_end() || !cursor.next()) {
00209 cout << "At end already." << endl;
00210 goto wait_for_input;
00211 }
00212 continue;
00213 } else if (input == "p" || input == "prev") {
00214
00215
00216 if (cursor.after_end()) cursor.find_entry(cursor.current_key);
00217 if (!cursor.prev()) {
00218 cout << "At start already." << endl;
00219 goto wait_for_input;
00220 }
00221 continue;
00222 } else if (startswith(input, "u ")) {
00223 do_until(cursor, input.substr(2));
00224 goto wait_for_input;
00225 } else if (startswith(input, "until ")) {
00226 do_until(cursor, input.substr(6));
00227 goto wait_for_input;
00228 } else if (input == "u" || input == "until") {
00229 do_until(cursor, string());
00230 goto wait_for_input;
00231 } else if (startswith(input, "g ")) {
00232 if (!cursor.find_entry(input.substr(2))) {
00233 cout << "No exact match, going to entry before." << endl;
00234 }
00235 continue;
00236 } else if (startswith(input, "goto ")) {
00237 if (!cursor.find_entry(input.substr(5))) {
00238 cout << "No exact match, going to entry before." << endl;
00239 }
00240 continue;
00241 } else if (startswith(input, "o ")) {
00242 size_t slash = table_name.find_last_of('/');
00243 if (slash == string::npos)
00244 table_name.resize(0);
00245 else
00246 table_name.resize(slash + 1);
00247 table_name += input.substr(2);
00248 if (endswith(table_name, ".DB"))
00249 table_name.resize(table_name.size() - 2);
00250 else if (!endswith(table_name, '.'))
00251 table_name += '.';
00252 goto open_different_table;
00253 } else if (startswith(input, "open ")) {
00254 size_t slash = table_name.find_last_of('/');
00255 if (slash == string::npos)
00256 table_name.resize(0);
00257 else
00258 table_name.resize(slash + 1);
00259 table_name += input.substr(5);
00260 if (endswith(table_name, ".DB"))
00261 table_name.resize(table_name.size() - 2);
00262 else if (!endswith(table_name, '.'))
00263 table_name += '.';
00264 goto open_different_table;
00265 } else if (input == "q" || input == "quit") {
00266 break;
00267 } else if (input == "h" || input == "help" || input == "?") {
00268 show_help();
00269 goto wait_for_input;
00270 } else {
00271 cout << "Unknown command." << endl;
00272 goto wait_for_input;
00273 }
00274 }
00275 } catch (const Xapian::Error &error) {
00276 cerr << argv[0] << ": " << error.get_description() << endl;
00277 exit(1);
00278 }
00279 }