Kelson has submitted this change and it was merged. (
https://gerrit.wikimedia.org/r/296913 )
Change subject: Add xapian indexer.
......................................................................
Add xapian indexer.
Xapian is optional.
Build your index inside zim by adding "-i" or "--createFullTextIndex"
to zimwriterfs' command line.
Change-Id: I52c255e8335d0b6763c1c59eeb1549300d5f6f81
---
M zimwriterfs/Makefile.am
M zimwriterfs/configure.ac
M zimwriterfs/tools.cpp
M zimwriterfs/tools.h
A zimwriterfs/xapian/htmlparse.cc
A zimwriterfs/xapian/htmlparse.h
A zimwriterfs/xapian/myhtmlparse.cc
A zimwriterfs/xapian/myhtmlparse.h
A zimwriterfs/xapian/namedentities.h
A zimwriterfs/xapianIndexer.cpp
A zimwriterfs/xapianIndexer.h
M zimwriterfs/zimwriterfs.cpp
12 files changed, 1,490 insertions(+), 0 deletions(-)
Approvals:
Kelson: Verified; Looks good to me, approved
diff --git a/zimwriterfs/Makefile.am b/zimwriterfs/Makefile.am
index 628b74c..1d40174 100644
--- a/zimwriterfs/Makefile.am
+++ b/zimwriterfs/Makefile.am
@@ -10,3 +10,15 @@
resourceTools.cpp \
pathTools.cpp \
mimetypecounter.cpp
+
+zimwriterfs_CXXFLAGS = $(ICU_CFLAGS)
+zimwriterfs_LDFLAGS = $(ICU_LDFLAGS)
+
+if HAVE_XAPIAN
+zimwriterfs_CXXFLAGS += $(XAPIAN_CFLAGS)
+zimwriterfs_LDFLAGS += $(XAPIAN_LDFLAGS)
+zimwriterfs_SOURCES += \
+ xapianIndexer.cpp \
+ xapian/myhtmlparse.cc \
+ xapian/htmlparse.cc
+endif
diff --git a/zimwriterfs/configure.ac b/zimwriterfs/configure.ac
index fb12c8f..795d3b1 100644
--- a/zimwriterfs/configure.ac
+++ b/zimwriterfs/configure.ac
@@ -71,6 +71,121 @@
AC_DEFINE_UNQUOTED(LZMA_MEMORY_SIZE, 128, [set lzma uncompress memory size to
number of MB])
AC_DEFINE(ENABLE_LZMA, [1], [defined if lzma compression is enabled])
+
+function findLibrary {
+ found=0
+ for f in $(echo $LIBS_ROOT|tr ":" "\n") ; do
+ sf=`find $f -name $1 | grep $ARCH | head -1 2> /dev/null`
+ if [[ -f "$sf" -a $found -eq 0 ]]
+ then
+ found=1
+ echo $sf
+ fi
+ done
+ if [[ $found -eq 0 ]]
+ then
+ for f in $(echo $LIBS_ROOT|tr ":" "\n") ; do
+ sf=`find $f -name $1 | head -1 2> /dev/null`
+ if [[ -f "$sf" -a $found -eq 0 ]]
+ then
+ found=1
+ echo $sf
+ fi
+ done
+ fi
+ if [[ $found -eq 0 ]]
+ then
+ echo "no"
+ fi
+}
+
+
+####################################################
+############ ICU
+####################################################
+
+
+ICU_CFLAGS=""
+ICU_LDFLAGS="-licui18n -licuuc -licudata" # replaced by icu-config
+ICU_STATIC_LDFLAGS=""
+
+# if --with-x, add path to LIBRARY_PATH
+AC_ARG_WITH(icu,
+ AC_HELP_STRING([--with-icu=DIR], [alternate location for
icu-config]),
+ export
LIBRARY_PATH="${withval}:${LIBRARY_PATH}";ICU_PATH=${withval}
+ )
+
+# look for shared library.
+# AC_CHECK_HEADER([zlib.h],, [AC_MSG_ERROR([[cannot find zlib header]])])
+# AC_CHECK_LIB([z], [zlibVersion],, [AC_MSG_ERROR([[cannot find
zlib]]);COMPILE_ICU=1])
+# ICU_FILES=`findLibrary "libicuuc.${SHARED_EXT}"`
+
+AC_CHECK_TOOL(HAVE_ICU_CONFIG, icu-config,, "${ICU_PATH}:${PATH}")
+if test [ ! "$HAVE_ICU_CONFIG" ]
+then
+ AC_MSG_ERROR([[cannot find icu-config]])
+else
+ OLDPATH=$PATH
+ PATH="${ICU_PATH}:${PATH}"
+ ICU_CFLAGS=`icu-config --cxxflags`;
+ ICU_LDFLAGS=`icu-config --ldflags`;
+ ICU_VER=`icu-config --version`;
+ ICU_FILES="`findLibrary "libicuuc.${SHARED_EXT}"` `findLibrary
"libicudata.${SHARED_EXT}"` `findLibrary "libicui18n.${SHARED_EXT}"`"
+ PATH=$OLDPATH
+ if [[ $ICU_VER \< "4.2" ]]
+ then
+ AC_MSG_ERROR([[You need a version of libicu >= 4.2]])
+ fi
+fi
+
+
+AC_SUBST(ICU_CFLAGS)
+AC_SUBST(ICU_LDFLAGS)
+AC_SUBST(ICU_STATIC_LDFLAGS)
+AC_SUBST(ICU_FILES)
+AC_SUBST(COMPILED_ICUDATA_DAT)
+
+####################################################
+############ XAPIAN
+####################################################
+
+XAPIAN_CFLAGS=""
+XAPIAN_LDFLAGS=""
+XAPIAN_STATIC_LDFLAGS=""
+XAPIAN_ENABLE=0
+
+# if --with-x, add path to LIBRARY_PATH
+AC_ARG_WITH([xapian],
+ [AS_HELP_STRING([--with-xapian=DIR], [alternat location for
xapian-config] @@)],
+ [xapian_dir=$withval],
+ [with_xapian=yes])
+
+
+AS_IF([test "x$with_xapian" == xno],
+ [AM_CONDITIONAL(HAVE_XAPIAN, false)],
+ [OLDPATH=$PATH
+ AS_IF([test "x$with_xapian" != xyes],
+ PATH="$with_xapian:$PATH")
+ AC_CHECK_TOOLS(XAPIAN_CONFIG, xapian-config-1.3,
xapian-config,[],$PATH)
+ AS_IF([test "x$XAPIAN_CONFIG" == x ],
+ AC_MSG_ERROR([[cannot find xapian-config file]])
+ )
+ XAPIAN_VERSION=`$XAPIAN_CONFIG --version`
+ good_version=yes
+ AS_VERSION_COMPARE($XAPIAN_VERSION, "xapian-config - xapian-core
1.3.4", [good_version=no], [], [])
+ AS_IF([test "x$good_version" == xno],
+ AC_MSG_ERROR([[xapian version must be >= 1.3.4]])
+ )
+ AM_CONDITIONAL(HAVE_XAPIAN, true)
+ AC_DEFINE(HAVE_XAPIAN)
+ XAPIAN_CFLAGS=`$XAPIAN_CONFIG --cxxflags`;
+ XAPIAN_LDFLAGS=`$XAPIAN_CONFIG --ltlibs`;
+ PATH=$OLDPATH
+ ])
+
+AC_SUBST(XAPIAN_CFLAGS)
+AC_SUBST(XAPIAN_LDFLAGS)
+
# Configure the output files
AC_CONFIG_FILES([
Makefile
diff --git a/zimwriterfs/tools.cpp b/zimwriterfs/tools.cpp
index 019b22c..868f32c 100644
--- a/zimwriterfs/tools.cpp
+++ b/zimwriterfs/tools.cpp
@@ -32,6 +32,10 @@
#include <sys/stat.h>
#include <magic.h>
+#include <unicode/translit.h>
+#include <unicode/ucnv.h>
+
+
#ifdef _WIN32
#define SEPARATOR "\\"
#else
@@ -523,3 +527,14 @@
return computeRelativePath(baseUrl, newUrl);
}
+std::string removeAccents(const std::string &text) {
+ ucnv_setDefaultName("UTF-8");
+ UErrorCode status = U_ZERO_ERROR;
+ Transliterator *removeAccentsTrans = Transliterator::createInstance("Lower;
NFD; [:M:] remove; NFC", UTRANS_FORWARD, status);
+ UnicodeString ustring = UnicodeString(text.c_str());
+ removeAccentsTrans->transliterate(ustring);
+ delete removeAccentsTrans;
+ std::string unaccentedText;
+ ustring.toUTF8String(unaccentedText);
+ return unaccentedText;
+}
diff --git a/zimwriterfs/tools.h b/zimwriterfs/tools.h
index 8b43da4..d85b292 100644
--- a/zimwriterfs/tools.h
+++ b/zimwriterfs/tools.h
@@ -45,4 +45,6 @@
std::string extractRedirectUrlFromHtml(const GumboVector* head_children);
void getLinks(GumboNode* node, std::map<std::string, bool> &links);
+std::string removeAccents(const std::string &text);
+
#endif // OPENZIM_ZIMWRITERFS_TOOLS_H
diff --git a/zimwriterfs/xapian/htmlparse.cc b/zimwriterfs/xapian/htmlparse.cc
new file mode 100644
index 0000000..39b49ae
--- /dev/null
+++ b/zimwriterfs/xapian/htmlparse.cc
@@ -0,0 +1,373 @@
+/* htmlparse.cc: simple HTML parser for omega indexer
+ *
+ * Copyright 1999,2000,2001 BrightStation PLC
+ * Copyright 2001 Ananova Ltd
+ * Copyright 2002,2006,2007,2008 Olly Betts
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+// #include <config.h>
+
+#include "htmlparse.h"
+
+#include <xapian.h>
+
+// #include "utf8convert.h"
+
+#include <algorithm>
+
+#include <ctype.h>
+#include <cstring>
+#include <stdio.h>
+#include <stdlib.h>
+
+using namespace std;
+
+inline void
+lowercase_string(string &str)
+{
+ for (string::iterator i = str.begin(); i != str.end(); ++i) {
+ *i = tolower(static_cast<unsigned char>(*i));
+ }
+}
+
+map<string, unsigned int> HtmlParser::named_ents;
+
+inline static bool
+p_notdigit(char c)
+{
+ return !isdigit(static_cast<unsigned char>(c));
+}
+
+inline static bool
+p_notxdigit(char c)
+{
+ return !isxdigit(static_cast<unsigned char>(c));
+}
+
+inline static bool
+p_notalnum(char c)
+{
+ return !isalnum(static_cast<unsigned char>(c));
+}
+
+inline static bool
+p_notwhitespace(char c)
+{
+ return !isspace(static_cast<unsigned char>(c));
+}
+
+inline static bool
+p_nottag(char c)
+{
+ return !isalnum(static_cast<unsigned char>(c)) &&
+ c != '.' && c != '-' && c != ':'; // ':' for XML namespaces.
+}
+
+inline static bool
+p_whitespacegt(char c)
+{
+ return isspace(static_cast<unsigned char>(c)) || c == '>';
+}
+
+inline static bool
+p_whitespaceeqgt(char c)
+{
+ return isspace(static_cast<unsigned char>(c)) || c == '=' || c == '>';
+}
+
+bool
+HtmlParser::get_parameter(const string & param, string & value)
+{
+ map<string, string>::const_iterator i = parameters.find(param);
+ if (i == parameters.end()) return false;
+ value = i->second;
+ return true;
+}
+
+HtmlParser::HtmlParser()
+{
+ static const struct ent { const char *n; unsigned int v; } ents[] = {
+#include "namedentities.h"
+ { NULL, 0 }
+ };
+ if (named_ents.empty()) {
+ const struct ent *i = ents;
+ while (i->n) {
+ named_ents[string(i->n)] = i->v;
+ ++i;
+ }
+ }
+}
+
+void
+HtmlParser::decode_entities(string &s)
+{
+ // We need a const_iterator version of s.end() - otherwise the
+ // find() and find_if() templates don't work...
+ string::const_iterator amp = s.begin(), s_end = s.end();
+ while ((amp = find(amp, s_end, '&')) != s_end) {
+ unsigned int val = 0;
+ string::const_iterator end, p = amp + 1;
+ if (p != s_end && *p == '#') {
+ p++;
+ if (p != s_end && (*p == 'x' || *p == 'X')) {
+ // hex
+ p++;
+ end = find_if(p, s_end, p_notxdigit);
+ sscanf(s.substr(p - s.begin(), end - p).c_str(), "%x", &val);
+ } else {
+ // number
+ end = find_if(p, s_end, p_notdigit);
+ val = atoi(s.substr(p - s.begin(), end - p).c_str());
+ }
+ } else {
+ end = find_if(p, s_end, p_notalnum);
+ string code = s.substr(p - s.begin(), end - p);
+ map<string, unsigned int>::const_iterator i;
+ i = named_ents.find(code);
+ if (i != named_ents.end()) val = i->second;
+ }
+ if (end < s_end && *end == ';') end++;
+ if (val) {
+ string::size_type amp_pos = amp - s.begin();
+ if (val < 0x80) {
+ s.replace(amp_pos, end - amp, 1u, char(val));
+ } else {
+ // Convert unicode value val to UTF-8.
+ char seq[4];
+ unsigned len = Xapian::Unicode::nonascii_to_utf8(val, seq);
+ s.replace(amp_pos, end - amp, seq, len);
+ }
+ s_end = s.end();
+ // We've modified the string, so the iterators are no longer
+ // valid...
+ amp = s.begin() + amp_pos + 1;
+ } else {
+ amp = end;
+ }
+ }
+}
+
+void
+HtmlParser::parse_html(const string &body)
+{
+ in_script = false;
+
+ parameters.clear();
+ string::const_iterator start = body.begin();
+
+ while (true) {
+ // Skip through until we find an HTML tag, a comment, or the end of
+ // document. Ignore isolated occurrences of `<' which don't start
+ // a tag or comment.
+ string::const_iterator p = start;
+ while (true) {
+ p = find(p, body.end(), '<');
+ if (p == body.end()) break;
+ unsigned char ch = *(p + 1);
+
+ // Tag, closing tag, or comment (or SGML declaration).
+ if ((!in_script && isalpha(ch)) || ch == '/' || ch == '!') break;
+
+ if (ch == '?') {
+ // PHP code or XML declaration.
+ // XML declaration is only valid at the start of the first line.
+ // FIXME: need to deal with BOMs...
+ if (p != body.begin() || body.size() < 20) break;
+
+ // XML declaration looks something like this:
+ // <?xml version="1.0" encoding="UTF-8"?>
+ if (p[2] != 'x' || p[3] != 'm' || p[4] != 'l') break;
+ if (strchr(" \t\r\n", p[5]) == NULL) break;
+
+ string::const_iterator decl_end = find(p + 6, body.end(), '?');
+ if (decl_end == body.end()) break;
+
+ // Default charset for XML is UTF-8.
+ charset = "UTF-8";
+
+ string decl(p + 6, decl_end);
+ size_t enc = decl.find("encoding");
+ if (enc == string::npos) break;
+
+ enc = decl.find_first_not_of(" \t\r\n", enc + 8);
+ if (enc == string::npos || enc == decl.size()) break;
+
+ if (decl[enc] != '=') break;
+
+ enc = decl.find_first_not_of(" \t\r\n", enc + 1);
+ if (enc == string::npos || enc == decl.size()) break;
+
+ if (decl[enc] != '"' && decl[enc] != '\'') break;
+
+ char quote = decl[enc++];
+ size_t enc_end = decl.find(quote, enc);
+
+ if (enc != string::npos)
+ charset = decl.substr(enc, enc_end - enc);
+
+ break;
+ }
+ p++;
+ }
+
+ // Process text up to start of tag.
+ if (p > start) {
+ string text = body.substr(start - body.begin(), p - start);
+ // convert_to_utf8(text, charset);
+ decode_entities(text);
+ process_text(text);
+ }
+
+ if (p == body.end()) break;
+
+ start = p + 1;
+
+ if (start == body.end()) break;
+
+ if (*start == '!') {
+ if (++start == body.end()) break;
+ if (++start == body.end()) break;
+ // comment or SGML declaration
+ if (*(start - 1) == '-' && *start == '-') {
+ ++start;
+ string::const_iterator close = find(start, body.end(), '>');
+ // An unterminated comment swallows rest of document
+ // (like Netscape, but unlike MSIE IIRC)
+ if (close == body.end()) break;
+
+ p = close;
+ // look for -->
+ while (p != body.end() && (*(p - 1) != '-' || *(p - 2) != '-'))
+ p = find(p + 1, body.end(), '>');
+
+ if (p != body.end()) {
+ // Check for htdig's "ignore this bit" comments.
+ if (p - start == 15 && string(start, p - 2) ==
"htdig_noindex") {
+ string::size_type i;
+ i = body.find("<!--/htdig_noindex-->", p + 1 -
body.begin());
+ if (i == string::npos) break;
+ start = body.begin() + i + 21;
+ continue;
+ }
+ // If we found --> skip to there.
+ start = p;
+ } else {
+ // Otherwise skip to the first > we found (as Netscape
does).
+ start = close;
+ }
+ } else {
+ // just an SGML declaration, perhaps giving the DTD - ignore it
+ start = find(start - 1, body.end(), '>');
+ if (start == body.end()) break;
+ }
+ ++start;
+ } else if (*start == '?') {
+ if (++start == body.end()) break;
+ // PHP - swallow until ?> or EOF
+ start = find(start + 1, body.end(), '>');
+
+ // look for ?>
+ while (start != body.end() && *(start - 1) != '?')
+ start = find(start + 1, body.end(), '>');
+
+ // unterminated PHP swallows rest of document (rather arbitrarily
+ // but it avoids polluting the database when things go wrong)
+ if (start != body.end()) ++start;
+ } else {
+ // opening or closing tag
+ int closing = 0;
+
+ if (*start == '/') {
+ closing = 1;
+ start = find_if(start + 1, body.end(), p_notwhitespace);
+ }
+
+ p = start;
+ start = find_if(start, body.end(), p_nottag);
+ string tag = body.substr(p - body.begin(), start - p);
+ // convert tagname to lowercase
+ lowercase_string(tag);
+
+ if (closing) {
+ closing_tag(tag);
+ if (in_script && tag == "script") in_script = false;
+
+ /* ignore any bogus parameters on closing tags */
+ p = find(start, body.end(), '>');
+ if (p == body.end()) break;
+ start = p + 1;
+ } else {
+ // FIXME: parse parameters lazily.
+ while (start < body.end() && *start != '>') {
+ string name, value;
+
+ p = find_if(start, body.end(), p_whitespaceeqgt);
+
+ name.assign(body, start - body.begin(), p - start);
+
+ p = find_if(p, body.end(), p_notwhitespace);
+
+ start = p;
+ if (start != body.end() && *start == '=') {
+ start = find_if(start + 1, body.end(), p_notwhitespace);
+
+ p = body.end();
+
+ int quote = *start;
+ if (quote == '"' || quote == '\'') {
+ start++;
+ p = find(start, body.end(), quote);
+ }
+
+ if (p == body.end()) {
+ // unquoted or no closing quote
+ p = find_if(start, body.end(), p_whitespacegt);
+ }
+ value.assign(body, start - body.begin(), p - start);
+ start = find_if(p, body.end(), p_notwhitespace);
+
+ if (!name.empty()) {
+ // convert parameter name to lowercase
+ lowercase_string(name);
+ // in case of multiple entries, use the first
+ // (as Netscape does)
+ parameters.insert(make_pair(name, value));
+ }
+ }
+ }
+#if 0
+ cout << "<" << tag;
+ map<string, string>::const_iterator x;
+ for (x = parameters.begin(); x != parameters.end(); x++) {
+ cout << " " << x->first << "=\"" << x->second << "\"";
+ }
+ cout << ">\n";
+#endif
+ opening_tag(tag);
+ parameters.clear();
+
+ // In <script> tags we ignore opening tags to avoid problems
+ // with "a<b".
+ if (tag == "script") in_script = true;
+
+ if (start != body.end() && *start == '>') ++start;
+ }
+ }
+ }
+}
diff --git a/zimwriterfs/xapian/htmlparse.h b/zimwriterfs/xapian/htmlparse.h
new file mode 100644
index 0000000..79e96ec
--- /dev/null
+++ b/zimwriterfs/xapian/htmlparse.h
@@ -0,0 +1,49 @@
+/* htmlparse.h: simple HTML parser for omega indexer
+ *
+ * Copyright 1999,2000,2001 BrightStation PLC
+ * Copyright 2002,2006,2008 Olly Betts
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+#ifndef OMEGA_INCLUDED_HTMLPARSE_H
+#define OMEGA_INCLUDED_HTMLPARSE_H
+
+#include <string>
+#include <map>
+
+using std::string;
+using std::map;
+
+class HtmlParser {
+ map<string, string> parameters;
+ protected:
+ void decode_entities(string &s);
+ bool in_script;
+ string charset;
+ static map<string, unsigned int> named_ents;
+
+ bool get_parameter(const string & param, string & value);
+ public:
+ virtual void process_text(const string &/*text*/) { }
+ virtual void opening_tag(const string &/*tag*/) { }
+ virtual void closing_tag(const string &/*tag*/) { }
+ virtual void parse_html(const string &text);
+ HtmlParser();
+ virtual ~HtmlParser() { }
+};
+
+#endif // OMEGA_INCLUDED_HTMLPARSE_H
diff --git a/zimwriterfs/xapian/myhtmlparse.cc
b/zimwriterfs/xapian/myhtmlparse.cc
new file mode 100644
index 0000000..e1098af
--- /dev/null
+++ b/zimwriterfs/xapian/myhtmlparse.cc
@@ -0,0 +1,302 @@
+/* myhtmlparse.cc: subclass of HtmlParser for extracting text.
+ *
+ * Copyright 1999,2000,2001 BrightStation PLC
+ * Copyright 2002,2003,2004,2006,2007,2008 Olly Betts
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+// #include <config.h>
+
+#include "myhtmlparse.h"
+
+// #include "utf8convert.h"
+
+#include <ctype.h>
+#include <string.h>
+
+inline void
+lowercase_string(string &str)
+{
+ for (string::iterator i = str.begin(); i != str.end(); ++i) {
+ *i = tolower(static_cast<unsigned char>(*i));
+ }
+}
+
+void
+MyHtmlParser::parse_html(const string &text, const string &charset_,
+ bool charset_from_meta_)
+{
+ charset = charset_;
+ charset_from_meta = charset_from_meta_;
+ HtmlParser::parse_html(text);
+}
+
+void
+MyHtmlParser::process_text(const string &text)
+{
+ if (!text.empty() && !in_script_tag && !in_style_tag) {
+ string::size_type b = text.find_first_not_of(WHITESPACE);
+ if (b) pending_space = true;
+ while (b != string::npos) {
+ if (pending_space && !dump.empty()) dump += ' ';
+ string::size_type e = text.find_first_of(WHITESPACE, b);
+ pending_space = (e != string::npos);
+ if (!pending_space) {
+ dump.append(text.data() + b, text.size() - b);
+ return;
+ }
+ dump.append(text.data() + b, e - b);
+ b = text.find_first_not_of(WHITESPACE, e + 1);
+ }
+ }
+}
+
+void
+MyHtmlParser::opening_tag(const string &tag)
+{
+ if (tag.empty()) return;
+ switch (tag[0]) {
+ case 'a':
+ if (tag == "address") pending_space = true;
+ break;
+ case 'b':
+ if (tag == "body") {
+ dump.resize(0);
+ break;
+ }
+ if (tag == "blockquote" || tag == "br") pending_space = true;
+ break;
+ case 'c':
+ if (tag == "center") pending_space = true;
+ break;
+ case 'd':
+ if (tag == "dd" || tag == "dir" || tag == "div" || tag == "dl" ||
+ tag == "dt") pending_space = true;
+ break;
+ case 'e':
+ if (tag == "embed") pending_space = true;
+ break;
+ case 'f':
+ if (tag == "fieldset" || tag == "form") pending_space = true;
+ break;
+ case 'h':
+ // hr, and h1, ..., h6
+ if (tag.length() == 2 && strchr("r123456", tag[1]))
+ pending_space = true;
+ break;
+ case 'i':
+ if (tag == "iframe" || tag == "img" || tag == "isindex" ||
+ tag == "input") pending_space = true;
+ break;
+ case 'k':
+ if (tag == "keygen") pending_space = true;
+ break;
+ case 'l':
+ if (tag == "legend" || tag == "li" || tag == "listing")
+ pending_space = true;
+ break;
+ case 'm':
+ if (tag == "meta") {
+ string content;
+ if (get_parameter("content", content)) {
+ string name;
+ if (get_parameter("name", name)) {
+ lowercase_string(name);
+ if (name == "description") {
+ if (sample.empty()) {
+ swap(sample, content);
+ // convert_to_utf8(sample, charset);
+ decode_entities(sample);
+ }
+ } else if (name == "keywords") {
+ if (!keywords.empty()) keywords += ' ';
+ // convert_to_utf8(content, charset);
+ decode_entities(content);
+ keywords += content;
+ } else if (name == "robots") {
+ decode_entities(content);
+ lowercase_string(content);
+ if (content.find("none") != string::npos ||
+ content.find("noindex") != string::npos) {
+ indexing_allowed = false;
+ throw true;
+ }
+ }
+ break;
+ }
+ // If the current charset came from a meta tag, don't
+ // force reparsing again!
+ if (charset_from_meta) break;
+ string hdr;
+ if (get_parameter("http-equiv", hdr)) {
+ lowercase_string(hdr);
+ if (hdr == "content-type") {
+ lowercase_string(content);
+ size_t start = content.find("charset=");
+ if (start == string::npos) break;
+ start += 8;
+ if (start == content.size()) break;
+ size_t end = start;
+ if (content[start] != '"') {
+ while (end < content.size()) {
+ unsigned char ch = content[end];
+ if (ch <= 32 || ch >= 127 ||
+ strchr(";()<>@,:\\\"/[]?={}", ch))
+ break;
+ ++end;
+ }
+ } else {
+ ++start;
+ ++end;
+ while (end < content.size()) {
+ unsigned char ch = content[end];
+ if (ch == '"') break;
+ if (ch == '\\') content.erase(end, 1);
+ ++end;
+ }
+ }
+ string newcharset(content, start, end - start);
+ if (charset != newcharset) {
+ throw newcharset;
+ }
+ }
+ }
+ break;
+ }
+ if (charset_from_meta) break;
+ string newcharset;
+ if (get_parameter("charset", newcharset)) {
+ // HTML5 added: <meta charset="...">
+ lowercase_string(newcharset);
+ if (charset != newcharset) {
+ throw newcharset;
+ }
+ }
+ break;
+ }
+ if (tag == "marquee" || tag == "menu" || tag == "multicol")
+ pending_space = true;
+ break;
+ case 'o':
+ if (tag == "ol" || tag == "option") pending_space = true;
+ break;
+ case 'p':
+ if (tag == "p" || tag == "pre" || tag == "plaintext")
+ pending_space = true;
+ break;
+ case 'q':
+ if (tag == "q") pending_space = true;
+ break;
+ case 's':
+ if (tag == "style") {
+ in_style_tag = true;
+ break;
+ }
+ if (tag == "script") {
+ in_script_tag = true;
+ break;
+ }
+ if (tag == "select") pending_space = true;
+ break;
+ case 't':
+ if (tag == "table" || tag == "td" || tag == "textarea" ||
+ tag == "th") pending_space = true;
+ break;
+ case 'u':
+ if (tag == "ul") pending_space = true;
+ break;
+ case 'x':
+ if (tag == "xmp") pending_space = true;
+ break;
+ }
+}
+
+void
+MyHtmlParser::closing_tag(const string &tag)
+{
+ if (tag.empty()) return;
+ switch (tag[0]) {
+ case 'a':
+ if (tag == "address") pending_space = true;
+ break;
+ case 'b':
+ if (tag == "body") {
+ throw true;
+ }
+ if (tag == "blockquote" || tag == "br") pending_space = true;
+ break;
+ case 'c':
+ if (tag == "center") pending_space = true;
+ break;
+ case 'd':
+ if (tag == "dd" || tag == "dir" || tag == "div" || tag == "dl" ||
+ tag == "dt") pending_space = true;
+ break;
+ case 'f':
+ if (tag == "fieldset" || tag == "form") pending_space = true;
+ break;
+ case 'h':
+ // hr, and h1, ..., h6
+ if (tag.length() == 2 && strchr("r123456", tag[1]))
+ pending_space = true;
+ break;
+ case 'i':
+ if (tag == "iframe") pending_space = true;
+ break;
+ case 'l':
+ if (tag == "legend" || tag == "li" || tag == "listing")
+ pending_space = true;
+ break;
+ case 'm':
+ if (tag == "marquee" || tag == "menu") pending_space = true;
+ break;
+ case 'o':
+ if (tag == "ol" || tag == "option") pending_space = true;
+ break;
+ case 'p':
+ if (tag == "p" || tag == "pre") pending_space = true;
+ break;
+ case 'q':
+ if (tag == "q") pending_space = true;
+ break;
+ case 's':
+ if (tag == "style") {
+ in_style_tag = false;
+ break;
+ }
+ if (tag == "script") {
+ in_script_tag = false;
+ break;
+ }
+ if (tag == "select") pending_space = true;
+ break;
+ case 't':
+ if (tag == "title") {
+ if (title.empty()) swap(title, dump);
+ break;
+ }
+ if (tag == "table" || tag == "td" || tag == "textarea" ||
+ tag == "th") pending_space = true;
+ break;
+ case 'u':
+ if (tag == "ul") pending_space = true;
+ break;
+ case 'x':
+ if (tag == "xmp") pending_space = true;
+ break;
+ }
+}
diff --git a/zimwriterfs/xapian/myhtmlparse.h b/zimwriterfs/xapian/myhtmlparse.h
new file mode 100644
index 0000000..f221cb5
--- /dev/null
+++ b/zimwriterfs/xapian/myhtmlparse.h
@@ -0,0 +1,65 @@
+/* myhtmlparse.h: subclass of HtmlParser for extracting text
+ *
+ * Copyright 1999,2000,2001 BrightStation PLC
+ * Copyright 2002,2003,2004,2006,2008 Olly Betts
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+#ifndef OMEGA_INCLUDED_MYHTMLPARSE_H
+#define OMEGA_INCLUDED_MYHTMLPARSE_H
+
+#include "htmlparse.h"
+
+// FIXME: Should we include \xa0 which is non-breaking space in iso-8859-1, but
+// not in all charsets and perhaps spans of all \xa0 should become a single
+// \xa0?
+#define WHITESPACE " \t\n\r"
+
+class MyHtmlParser : public HtmlParser {
+ public:
+ bool in_script_tag;
+ bool in_style_tag;
+ bool pending_space;
+ bool indexing_allowed;
+ bool charset_from_meta;
+ string title, sample, keywords, dump;
+ void process_text(const string &text);
+ void opening_tag(const string &tag);
+ void closing_tag(const string &tag);
+ void parse_html(const string &text, const string &charset_,
+ bool charset_from_meta_);
+ MyHtmlParser() :
+ in_script_tag(false),
+ in_style_tag(false),
+ pending_space(false),
+ indexing_allowed(true),
+ charset_from_meta(false) { }
+
+ void reset() {
+ in_script_tag = false;
+ in_style_tag = false;
+ pending_space = false;
+ indexing_allowed = true;
+ charset_from_meta = false;
+ title.resize(0);
+ sample.resize(0);
+ keywords.resize(0);
+ dump.resize(0);
+ }
+};
+
+#endif // OMEGA_INCLUDED_MYHTMLPARSE_H
diff --git a/zimwriterfs/xapian/namedentities.h
b/zimwriterfs/xapian/namedentities.h
new file mode 100644
index 0000000..8b7f03e
--- /dev/null
+++ b/zimwriterfs/xapian/namedentities.h
@@ -0,0 +1,279 @@
+/* namedentities.h: named HTML entities.
+ *
+ * Copyright (C) 2006,2007 Olly Betts
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef OMEGA_INCLUDED_NAMEDENTITIES_H
+#define OMEGA_INCLUDED_NAMEDENTITIES_H
+
+// Names and values from: "Character entity references in HTML 4"
+// http://www.w3.org/TR/html4/sgml/entities.html
+{ "quot", 34 },
+{ "amp", 38 },
+{ "apos", 39 }, // Not in HTML 4 list but used in OpenOffice XML.
+{ "lt", 60 },
+{ "gt", 62 },
+{ "nbsp", 160 },
+{ "iexcl", 161 },
+{ "cent", 162 },
+{ "pound", 163 },
+{ "curren", 164 },
+{ "yen", 165 },
+{ "brvbar", 166 },
+{ "sect", 167 },
+{ "uml", 168 },
+{ "copy", 169 },
+{ "ordf", 170 },
+{ "laquo", 171 },
+{ "not", 172 },
+{ "shy", 173 },
+{ "reg", 174 },
+{ "macr", 175 },
+{ "deg", 176 },
+{ "plusmn", 177 },
+{ "sup2", 178 },
+{ "sup3", 179 },
+{ "acute", 180 },
+{ "micro", 181 },
+{ "para", 182 },
+{ "middot", 183 },
+{ "cedil", 184 },
+{ "sup1", 185 },
+{ "ordm", 186 },
+{ "raquo", 187 },
+{ "frac14", 188 },
+{ "frac12", 189 },
+{ "frac34", 190 },
+{ "iquest", 191 },
+{ "Agrave", 192 },
+{ "Aacute", 193 },
+{ "Acirc", 194 },
+{ "Atilde", 195 },
+{ "Auml", 196 },
+{ "Aring", 197 },
+{ "AElig", 198 },
+{ "Ccedil", 199 },
+{ "Egrave", 200 },
+{ "Eacute", 201 },
+{ "Ecirc", 202 },
+{ "Euml", 203 },
+{ "Igrave", 204 },
+{ "Iacute", 205 },
+{ "Icirc", 206 },
+{ "Iuml", 207 },
+{ "ETH", 208 },
+{ "Ntilde", 209 },
+{ "Ograve", 210 },
+{ "Oacute", 211 },
+{ "Ocirc", 212 },
+{ "Otilde", 213 },
+{ "Ouml", 214 },
+{ "times", 215 },
+{ "Oslash", 216 },
+{ "Ugrave", 217 },
+{ "Uacute", 218 },
+{ "Ucirc", 219 },
+{ "Uuml", 220 },
+{ "Yacute", 221 },
+{ "THORN", 222 },
+{ "szlig", 223 },
+{ "agrave", 224 },
+{ "aacute", 225 },
+{ "acirc", 226 },
+{ "atilde", 227 },
+{ "auml", 228 },
+{ "aring", 229 },
+{ "aelig", 230 },
+{ "ccedil", 231 },
+{ "egrave", 232 },
+{ "eacute", 233 },
+{ "ecirc", 234 },
+{ "euml", 235 },
+{ "igrave", 236 },
+{ "iacute", 237 },
+{ "icirc", 238 },
+{ "iuml", 239 },
+{ "eth", 240 },
+{ "ntilde", 241 },
+{ "ograve", 242 },
+{ "oacute", 243 },
+{ "ocirc", 244 },
+{ "otilde", 245 },
+{ "ouml", 246 },
+{ "divide", 247 },
+{ "oslash", 248 },
+{ "ugrave", 249 },
+{ "uacute", 250 },
+{ "ucirc", 251 },
+{ "uuml", 252 },
+{ "yacute", 253 },
+{ "thorn", 254 },
+{ "yuml", 255 },
+{ "OElig", 338 },
+{ "oelig", 339 },
+{ "Scaron", 352 },
+{ "scaron", 353 },
+{ "Yuml", 376 },
+{ "fnof", 402 },
+{ "circ", 710 },
+{ "tilde", 732 },
+{ "Alpha", 913 },
+{ "Beta", 914 },
+{ "Gamma", 915 },
+{ "Delta", 916 },
+{ "Epsilon", 917 },
+{ "Zeta", 918 },
+{ "Eta", 919 },
+{ "Theta", 920 },
+{ "Iota", 921 },
+{ "Kappa", 922 },
+{ "Lambda", 923 },
+{ "Mu", 924 },
+{ "Nu", 925 },
+{ "Xi", 926 },
+{ "Omicron", 927 },
+{ "Pi", 928 },
+{ "Rho", 929 },
+{ "Sigma", 931 },
+{ "Tau", 932 },
+{ "Upsilon", 933 },
+{ "Phi", 934 },
+{ "Chi", 935 },
+{ "Psi", 936 },
+{ "Omega", 937 },
+{ "alpha", 945 },
+{ "beta", 946 },
+{ "gamma", 947 },
+{ "delta", 948 },
+{ "epsilon", 949 },
+{ "zeta", 950 },
+{ "eta", 951 },
+{ "theta", 952 },
+{ "iota", 953 },
+{ "kappa", 954 },
+{ "lambda", 955 },
+{ "mu", 956 },
+{ "nu", 957 },
+{ "xi", 958 },
+{ "omicron", 959 },
+{ "pi", 960 },
+{ "rho", 961 },
+{ "sigmaf", 962 },
+{ "sigma", 963 },
+{ "tau", 964 },
+{ "upsilon", 965 },
+{ "phi", 966 },
+{ "chi", 967 },
+{ "psi", 968 },
+{ "omega", 969 },
+{ "thetasym", 977 },
+{ "upsih", 978 },
+{ "piv", 982 },
+{ "ensp", 8194 },
+{ "emsp", 8195 },
+{ "thinsp", 8201 },
+{ "zwnj", 8204 },
+{ "zwj", 8205 },
+{ "lrm", 8206 },
+{ "rlm", 8207 },
+{ "ndash", 8211 },
+{ "mdash", 8212 },
+{ "lsquo", 8216 },
+{ "rsquo", 8217 },
+{ "sbquo", 8218 },
+{ "ldquo", 8220 },
+{ "rdquo", 8221 },
+{ "bdquo", 8222 },
+{ "dagger", 8224 },
+{ "Dagger", 8225 },
+{ "bull", 8226 },
+{ "hellip", 8230 },
+{ "permil", 8240 },
+{ "prime", 8242 },
+{ "Prime", 8243 },
+{ "lsaquo", 8249 },
+{ "rsaquo", 8250 },
+{ "oline", 8254 },
+{ "frasl", 8260 },
+{ "euro", 8364 },
+{ "image", 8465 },
+{ "weierp", 8472 },
+{ "real", 8476 },
+{ "trade", 8482 },
+{ "alefsym", 8501 },
+{ "larr", 8592 },
+{ "uarr", 8593 },
+{ "rarr", 8594 },
+{ "darr", 8595 },
+{ "harr", 8596 },
+{ "crarr", 8629 },
+{ "lArr", 8656 },
+{ "uArr", 8657 },
+{ "rArr", 8658 },
+{ "dArr", 8659 },
+{ "hArr", 8660 },
+{ "forall", 8704 },
+{ "part", 8706 },
+{ "exist", 8707 },
+{ "empty", 8709 },
+{ "nabla", 8711 },
+{ "isin", 8712 },
+{ "notin", 8713 },
+{ "ni", 8715 },
+{ "prod", 8719 },
+{ "sum", 8721 },
+{ "minus", 8722 },
+{ "lowast", 8727 },
+{ "radic", 8730 },
+{ "prop", 8733 },
+{ "infin", 8734 },
+{ "ang", 8736 },
+{ "and", 8743 },
+{ "or", 8744 },
+{ "cap", 8745 },
+{ "cup", 8746 },
+{ "int", 8747 },
+{ "there4", 8756 },
+{ "sim", 8764 },
+{ "cong", 8773 },
+{ "asymp", 8776 },
+{ "ne", 8800 },
+{ "equiv", 8801 },
+{ "le", 8804 },
+{ "ge", 8805 },
+{ "sub", 8834 },
+{ "sup", 8835 },
+{ "nsub", 8836 },
+{ "sube", 8838 },
+{ "supe", 8839 },
+{ "oplus", 8853 },
+{ "otimes", 8855 },
+{ "perp", 8869 },
+{ "sdot", 8901 },
+{ "lceil", 8968 },
+{ "rceil", 8969 },
+{ "lfloor", 8970 },
+{ "rfloor", 8971 },
+{ "lang", 9001 },
+{ "rang", 9002 },
+{ "loz", 9674 },
+{ "spades", 9824 },
+{ "clubs", 9827 },
+{ "hearts", 9829 },
+{ "diams", 9830 },
+
+#endif // OMEGA_INCLUDED_NAMEDENTITIES_H
diff --git a/zimwriterfs/xapianIndexer.cpp b/zimwriterfs/xapianIndexer.cpp
new file mode 100644
index 0000000..33666c7
--- /dev/null
+++ b/zimwriterfs/xapianIndexer.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2011 Emmanuel Engelhart <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include "xapianIndexer.h"
+#include "tools.h"
+
+#include <unistd.h>
+
+/* Constructor */
+XapianIndexer::XapianIndexer(const std::string& language, const bool verbose) {
+ setVerboseFlag(verbose);
+ readStopWords(language);
+ /*
+ stemmer(Xapian::Stem("french")) {
+ this->indexer.set_stemmer(this->stemmer);
+ */
+}
+
+void XapianIndexer::indexingPrelude(const string indexPath_) {
+ indexPath = indexPath_;
+ this->writableDatabase = Xapian::WritableDatabase(indexPath + ".tmp",
Xapian::DB_CREATE_OR_OVERWRITE);
+ this->writableDatabase.begin_transaction(true);
+
+ /* Insert the stopwords */
+ if (!this->stopWords.empty()) {
+ std::vector<std::string>::iterator it = this->stopWords.begin();
+ for( ; it != this->stopWords.end(); ++it) {
+ this->stopper.add(*it);
+ }
+
+ this->indexer.set_stopper(&(this->stopper));
+ }
+}
+
+void XapianIndexer::index(const string &url,
+ const string &title,
+ const string &unaccentedTitle,
+ const string &keywords,
+ const string &content,
+ const string &snippet,
+ const string &size,
+ const string &wordCount) {
+
+ /* Put the data in the document */
+ Xapian::Document currentDocument;
+ currentDocument.clear_values();
+ currentDocument.add_value(0, title);
+ currentDocument.add_value(1, snippet);
+ currentDocument.add_value(2, size);
+ currentDocument.add_value(3, wordCount);
+ currentDocument.set_data(url);
+ indexer.set_document(currentDocument);
+
+ /* Index the title */
+ if (!unaccentedTitle.empty()) {
+ this->indexer.index_text_without_positions(unaccentedTitle,
this->getTitleBoostFactor(content.size()));
+ }
+
+ /* Index the keywords */
+ if (!keywords.empty()) {
+ this->indexer.index_text_without_positions(keywords,
keywordsBoostFactor);
+ }
+
+ /* Index the content */
+ if (!content.empty()) {
+ this->indexer.index_text_without_positions(content);
+ }
+
+ /* add to the database */
+ this->writableDatabase.add_document(currentDocument);
+}
+
+void XapianIndexer::flush() {
+ this->writableDatabase.commit_transaction();
+ this->writableDatabase.begin_transaction(true);
+}
+
+void XapianIndexer::indexingPostlude() {
+ this->flush();
+ this->writableDatabase.commit_transaction();
+ this->writableDatabase.commit();
+ this->writableDatabase.compact(indexPath, Xapian::DBCOMPACT_SINGLE_FILE);
+
+ // commit is not available is old version of xapian and seems not
mandatory there
+ // this->writableDatabase.commit();
+}
+
+void XapianIndexer::handleArticle(Article* article)
+{
+ indexerToken token;
+ size_t found;
+ MyHtmlParser htmlParser;
+
+ if ( article->isRedirect() || article->getMimeType().find("text/html") !=
0 )
+ return;
+
+ token.title = article->getTitle();
+ token.url = article->getUrl();
+ zim::Blob article_content = article->getData();
+ token.content = std::string(article_content.data(),
article_content.size());
+
+ /* The parser generate a lot of exceptions which should be avoided */
+ try {
+ htmlParser.parse_html(token.content, "UTF-8", true);
+ } catch (...) {
+ }
+
+ /* If content does not have the noindex meta tag */
+ /* Seems that the parser generates an exception in such case */
+ found = htmlParser.dump.find("NOINDEX");
+
+ if (found == string::npos) {
+ /* Get the accented title */
+ token.accentedTitle = (htmlParser.title.empty() ? token.title :
htmlParser.title);
+
+ /* count words */
+ stringstream countWordStringStream;
+ countWordStringStream << countWords(htmlParser.dump);
+ token.wordCount = countWordStringStream.str();
+
+ /* snippet */
+ std::string snippet = std::string(htmlParser.dump, 0, 300);
+ std::string::size_type last = snippet.find_last_of('.');
+ if (last == snippet.npos)
+ last = snippet.find_last_of(' ');
+ if (last != snippet.npos)
+ snippet = snippet.substr(0, last);
+ token.snippet = snippet;
+
+ /* size */
+ stringstream sizeStringStream;
+ sizeStringStream << token.content.size() / 1024;
+ token.size = sizeStringStream.str();
+
+ /* Remove accent */
+ token.title = removeAccents(token.accentedTitle);
+ token.keywords = removeAccents(htmlParser.keywords);
+ token.content = removeAccents(htmlParser.dump);
+ pushToIndexQueue(token);
+ }
+}
+
+XapianMetaArticle* XapianIndexer::getMetaArticle()
+{
+ return new XapianMetaArticle(this);
+}
+
+zim::Blob XapianMetaArticle::getData() const
+{
+ if ( data.size() == 0 )
+ {
+ indexerToken token;
+ indexer->pushToIndexQueue(token);
+ /* Wait it index everything */
+ int wait = 500;
+ while ( indexer->isRunning() )
+ {
+ usleep(wait);
+ }
+ data = getFileContent(indexer->getIndexPath());
+ }
+ return zim::Blob(data.data(), data.size());
+}
+
diff --git a/zimwriterfs/xapianIndexer.h b/zimwriterfs/xapianIndexer.h
new file mode 100644
index 0000000..71dfe64
--- /dev/null
+++ b/zimwriterfs/xapianIndexer.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2011 Emmanuel Engelhart <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#ifndef OPENZIM_ZIMWRITERFS_XAPIANINDEXER_H
+#define OPENZIM_ZIMWRITERFS_XAPIANINDEXER_H
+
+
+#include "indexer.h"
+#include "articlesource.h"
+#include "article.h"
+
+#include <xapian.h>
+#include "xapian/myhtmlparse.h"
+#include <zim/blob.h>
+
+class XapianIndexer;
+
+class XapianMetaArticle : public Article {
+ private:
+ XapianIndexer* indexer;
+ mutable std::string data;
+ public:
+ XapianMetaArticle(XapianIndexer* indexer):
+ indexer(indexer)
+ {
+ ns = 'Z';
+ aid = url = "/Z/fulltextIndex/xapian";
+ title = "Xapian Fulltext Index";
+ mimeType = "application/octet-stream+xapian";
+ };
+ virtual zim::Blob getData() const;
+};
+
+class XapianIndexer : public Indexer, public IHandler {
+ public:
+ XapianIndexer(const std::string& language, bool verbose);
+ std::string getIndexPath() { return indexPath; }
+
+ protected:
+ void indexingPrelude(const string indexPath);
+ void index(const string &url,
+ const string &title,
+ const string &unaccentedTitle,
+ const string &keywords,
+ const string &content,
+ const string &snippet,
+ const string &size,
+ const string &wordCount);
+ void flush();
+ void indexingPostlude();
+ void handleArticle(Article* article);
+ XapianMetaArticle* getMetaArticle();
+ zim::Blob getData();
+
+ Xapian::WritableDatabase writableDatabase;
+ Xapian::Stem stemmer;
+ Xapian::SimpleStopper stopper;
+ Xapian::TermGenerator indexer;
+ std::string indexPath;
+};
+
+#endif // OPENZIM_ZIMWRITERFS_XAPIANINDEXER_H
diff --git a/zimwriterfs/zimwriterfs.cpp b/zimwriterfs/zimwriterfs.cpp
index 52ed7ea..b48a1ad 100644
--- a/zimwriterfs/zimwriterfs.cpp
+++ b/zimwriterfs/zimwriterfs.cpp
@@ -38,6 +38,9 @@
#include "queue.h"
#include "mimetypecounter.h"
+#if HAVE_XAPIAN
+#include "xapianIndexer.h"
+#endif
std::string language;
std::string creator;
@@ -229,6 +232,9 @@
int main(int argc, char** argv) {
ArticleSource source(filenameQueue);
+#if HAVE_XAPIAN
+ XapianIndexer* xapianIndexer = NULL;
+#endif
int minChunkSize = 2048;
@@ -368,6 +374,17 @@
pthread_create(&(directoryVisitor), NULL, visitDirectoryPath, (void*)NULL);
pthread_detach(directoryVisitor);
+ /* Indexor */
+ if (createFullTextIndex)
+ {
+#if HAVE_XAPIAN
+ xapianIndexer = new XapianIndexer(language, isVerbose());
+ xapianIndexer->start(zimPath + ".indexdb");
+ source.add_customHandler(xapianIndexer);
+#else
+ std::cerr << "Zimwriterfs is compiled without xapian. Indexing is not
available" << std::endl;
+#endif
+ }
MimetypeCounter mimetypeCounter;
source.add_customHandler(&mimetypeCounter);
@@ -381,6 +398,9 @@
std::cerr << e.what() << std::endl;
}
+#if HAVE_XAPIAN
+ delete xapianIndexer;
+#endif
/* Destroy mutex */
pthread_mutex_destroy(&directoryVisitorRunningMutex);
pthread_mutex_destroy(&verboseMutex);
--
To view, visit https://gerrit.wikimedia.org/r/296913
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I52c255e8335d0b6763c1c59eeb1549300d5f6f81
Gerrit-PatchSet: 1
Gerrit-Project: openzim
Gerrit-Branch: master
Gerrit-Owner: Mgautierfr <[email protected]>
Gerrit-Reviewer: Kelson <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits