omegatest: Use test_scriptindex_error in another case
[xapian.git] / xapian-core / examples / xapian-pos.cc
blobf5317bfb124e2611740c1a1b0f9d8d8aa842ff77
1 /** @file
2 * @brief Debug positional data
3 */
4 /* Copyright 2018 Olly Betts
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 * USA
22 #include <config.h>
24 #include <xapian.h>
25 #include <xapian/iterator.h>
27 #include <iostream>
29 #include <algorithm>
30 #include <cerrno>
31 #include <cstdlib>
32 #include <limits>
34 #include "gnu_getopt.h"
35 #include "stringutils.h"
37 using namespace std;
39 #define PROG_NAME "xapian-pos"
40 #define PROG_DESC "Debug positional data in a Xapian database"
42 #define OPT_HELP 1
43 #define OPT_VERSION 2
45 static void
46 show_usage()
48 cout << "Usage: " PROG_NAME " [OPTIONS] DATABASE\n\n"
49 "Options:\n"
50 " -d, --doc=DOCID Show positions for document DOCID\n"
51 " -s, --start=POS Specifies the first position to show\n"
52 " -e, --end=POS Specifies the last position to show\n"
53 " --help display this help and exit\n"
54 " --version output version information and exit" << endl;
57 class Pos {
58 Xapian::termpos pos;
60 Xapian::PositionIterator p;
62 string term;
64 public:
65 Pos(const string& term_, const Xapian::PositionIterator& p_)
66 : p(p_), term(term_) { pos = *p; }
68 Xapian::termpos get_pos() const { return pos; }
70 const string& get_term() const { return term; }
72 bool next() {
73 if (!Xapian::iterator_valid(++p)) {
74 return false;
76 pos = *p;
77 return true;
81 struct PosCmp {
82 bool operator()(const Pos* a, const Pos* b) {
83 if (a->get_pos() != b->get_pos()) {
84 return a->get_pos() > b->get_pos();
86 return a->get_term() > b->get_term();
90 template<typename T>
91 bool to_unsigned_int(const char* s, T& result)
93 errno = 0;
94 char* e;
95 auto v = strtoull(s, &e, 0);
96 if (errno == 0) {
97 if (*e || e == s) {
98 // Junk after or empty input.
99 errno = EINVAL;
100 } else if (v > numeric_limits<T>::max()) {
101 // Exceeds the type.
102 errno = ERANGE;
103 } else {
104 result = T(v);
105 return true;
108 return false;
112 main(int argc, char **argv)
113 try {
114 static const struct option long_opts[] = {
115 {"doc", required_argument, 0, 'd'},
116 {"start", required_argument, 0, 's'},
117 {"end", required_argument, 0, 'e'},
118 {"help", no_argument, 0, OPT_HELP},
119 {"version", no_argument, 0, OPT_VERSION},
120 {NULL, 0, 0, 0}
123 Xapian::docid did = 0;
124 Xapian::termpos startpos = 0;
125 Xapian::termpos endpos = numeric_limits<Xapian::termpos>::max();
126 int c;
127 while ((c = gnu_getopt_long(argc, argv, "d:e:s:", long_opts, 0)) != -1) {
128 switch (c) {
129 case 'd':
130 if (!to_unsigned_int(optarg, did) || did == 0) {
131 if (errno == 0) errno = ERANGE;
132 cerr << "Bad docid value '" << optarg << "': "
133 << strerror(errno) << endl;
134 exit(1);
136 break;
137 case 's':
138 if (!to_unsigned_int(optarg, startpos)) {
139 cerr << "Bad start position '" << optarg << "': "
140 << strerror(errno) << endl;
141 exit(1);
143 break;
144 case 'e':
145 if (!to_unsigned_int(optarg, endpos)) {
146 cerr << "Bad end position '" << optarg << "': "
147 << strerror(errno) << endl;
148 exit(1);
150 break;
151 case OPT_HELP:
152 cout << PROG_NAME " - " PROG_DESC "\n\n";
153 show_usage();
154 exit(0);
155 case OPT_VERSION:
156 cout << PROG_NAME " - " PACKAGE_STRING << endl;
157 exit(0);
158 default:
159 show_usage();
160 exit(1);
164 // We expect one argument - a database path.
165 if (argc - optind != 1) {
166 show_usage();
167 exit(1);
170 if (did == 0) {
171 cerr << "--doc=DOCID option required." << endl;
172 exit(1);
175 vector<Pos*> heap;
177 Xapian::Database db(argv[optind]);
179 for (auto term_it = db.termlist_begin(did);
180 term_it != db.termlist_end(did); ++term_it) {
181 const string& term = *term_it;
182 auto pos_it = db.positionlist_begin(did, term);
183 if (startpos) pos_it.skip_to(startpos);
184 if (pos_it != db.positionlist_end(did, term)) {
185 heap.push_back(new Pos(term, pos_it));
189 make_heap(heap.begin(), heap.end(), PosCmp());
191 Xapian::termpos old_pos = startpos - 1;
192 while (!heap.empty()) {
193 auto tip = heap.front();
194 Xapian::termpos pos = tip->get_pos();
195 if (pos > endpos) break;
197 switch (pos - old_pos) {
198 case 0:
199 // Another term at the same position.
200 cout << ' ';
201 break;
202 case 1:
203 cout << '\n' << pos << '\t';
204 break;
205 default:
206 cout << "\nGap of " << (pos - old_pos - 1)
207 << " unused positions\n" << pos << '\t';
208 break;
210 cout << tip->get_term();
212 old_pos = pos;
214 if (tip->next()) {
215 pop_heap(heap.begin(), heap.end(), PosCmp());
216 push_heap(heap.begin(), heap.end(), PosCmp());
217 } else {
218 pop_heap(heap.begin(), heap.end(), PosCmp());
219 heap.resize(heap.size() - 1);
223 cout << endl;
224 } catch (const Xapian::Error & e) {
225 cerr << '\n' << argv[0] << ": " << e.get_description() << endl;
226 exit(1);