diff options
author | Dimitri van Heesch <dimitri@stack.nl> | 2012-12-26 15:59:17 (GMT) |
---|---|---|
committer | Dimitri van Heesch <dimitri@stack.nl> | 2012-12-26 15:59:17 (GMT) |
commit | 48f4de5c47d55b6622b6fdc9b5c288e19d5692f9 (patch) | |
tree | 629c4681a5158d26512b815623754b33165d8d23 /addon | |
parent | fee4053bd3dd075a2dd2cba4da8166ec5307eadd (diff) | |
download | Doxygen-48f4de5c47d55b6622b6fdc9b5c288e19d5692f9.zip Doxygen-48f4de5c47d55b6622b6fdc9b5c288e19d5692f9.tar.gz Doxygen-48f4de5c47d55b6622b6fdc9b5c288e19d5692f9.tar.bz2 |
Release-1.8.3
Diffstat (limited to 'addon')
-rw-r--r-- | addon/doxyapp/doxyapp.cpp | 13 | ||||
-rw-r--r-- | addon/doxyapp/doxyapp.pro.in | 2 | ||||
-rw-r--r-- | addon/doxysearch/Makefile.in | 34 | ||||
-rw-r--r-- | addon/doxysearch/doxyindexer.cpp | 377 | ||||
-rw-r--r-- | addon/doxysearch/doxyindexer.pro.in | 12 | ||||
-rw-r--r-- | addon/doxysearch/doxysearch.cpp | 434 | ||||
-rw-r--r-- | addon/doxysearch/doxysearch.pro.in | 12 |
7 files changed, 881 insertions, 3 deletions
diff --git a/addon/doxyapp/doxyapp.cpp b/addon/doxyapp/doxyapp.cpp index ffee3c7..521d1cc 100644 --- a/addon/doxyapp/doxyapp.cpp +++ b/addon/doxyapp/doxyapp.cpp @@ -30,6 +30,13 @@ #include "doxygen.h" #include "outputgen.h" #include "parserintf.h" +#include "classdef.h" +#include "namespacedef.h" +#include "filedef.h" +#include "util.h" +#include "classlist.h" +#include "config.h" +#include "filename.h" class XRefDummyCodeGenerator : public CodeOutputInterface { @@ -41,14 +48,16 @@ class XRefDummyCodeGenerator : public CodeOutputInterface // and cross-linked version of the source code, but who needs that anyway ;-) void codify(const char *) {} void writeCodeLink(const char *,const char *,const char *,const char *,const char *) {} - void startCodeLine() {} + void writeLineNumber(const char *,const char *,const char *,int) {} + void startCodeLine(bool) {} void endCodeLine() {} void startCodeAnchor(const char *) {} void endCodeAnchor() {} void startFontClass(const char *) {} void endFontClass() {} void writeCodeAnchor(const char *) {} - void writeLineNumber(const char *,const char *,const char *,int) {} + void setCurrentDoc(Definition *,const char *,bool) {} + void addWord(const char *,bool) {} // here we are presented with the symbols found by the code parser void linkableSymbol(int l, const char *sym,Definition *symDef,Definition *context) diff --git a/addon/doxyapp/doxyapp.pro.in b/addon/doxyapp/doxyapp.pro.in index 68fea3d..f83fe30 100644 --- a/addon/doxyapp/doxyapp.pro.in +++ b/addon/doxyapp/doxyapp.pro.in @@ -2,7 +2,7 @@ TEMPLATE = app.t CONFIG = console warn_on debug HEADERS = SOURCES = doxyapp.cpp -LIBS += -L../../lib -L../../lib -ldoxygen -lqtools -lmd5 -ldoxycfg -lpng +LIBS += -L../../lib -L../../lib -ldoxygen -lqtools -lmd5 -ldoxycfg DESTDIR = OBJECTS_DIR = ../../objects TARGET = ../../bin/doxyapp diff --git a/addon/doxysearch/Makefile.in b/addon/doxysearch/Makefile.in new file mode 100644 index 0000000..3159e22 --- /dev/null +++ b/addon/doxysearch/Makefile.in @@ -0,0 +1,34 @@ + +all clean depend: Makefile.doxysearch Makefile.doxyindexer + $(MAKE) -f Makefile.doxysearch $@ + $(MAKE) -f Makefile.doxyindexer $@ + +distclean: clean + $(RM) -rf Makefile doxysearch.pro Makefile.doxysearch + $(RM) -rf Makefile doxyindexer.pro Makefile.doxyindexer + +tmake: + $(ENV) $(PERL) $(TMAKE) doxysearch.pro >Makefile.doxysearch + $(ENV) $(PERL) $(TMAKE) doxyindexer.pro >Makefile.doxyindexer + +strip: + strip doxysearch + +Makefile.doxysearch: doxysearch.pro + $(ENV) $(PERL) $(TMAKE) doxysearch.pro >Makefile.doxysearch + +Makefile.doxyindexer: doxyindexer.pro + $(ENV) $(PERL) $(TMAKE) doxyindexer.pro >Makefile.doxyindexer + +install: + $(INSTTOOL) -d $(INSTALL)/bin + $(INSTTOOL) -m 755 ../../bin/doxysearch.cgi $(INSTALL)/bin + $(INSTTOOL) -m 755 ../../bin/doxyindexer $(INSTALL)/bin + $(INSTTOOL) -d $(INSTALL)/$(MAN1DIR) + cat ../../doc/doxyindexer.1 | sed -e "s/DATE/$(DATE)/g" -e "s/VERSION/$(VERSION)/g" > doxyindexer.1 + $(INSTTOOL) -m 644 doxyindexer.1 $(INSTALL)/$(MAN1DIR)/doxyindexer.1 + rm doxyindexer.1 + cat ../../doc/doxysearch.1 | sed -e "s/DATE/$(DATE)/g" -e "s/VERSION/$(VERSION)/g" > doxysearch.1 + $(INSTTOOL) -m 644 doxysearch.1 $(INSTALL)/$(MAN1DIR)/doxysearch.1 + rm doxysearch.1 + diff --git a/addon/doxysearch/doxyindexer.cpp b/addon/doxysearch/doxyindexer.cpp new file mode 100644 index 0000000..c809e0e --- /dev/null +++ b/addon/doxysearch/doxyindexer.cpp @@ -0,0 +1,377 @@ +/****************************************************************************** + * + * Copyright (C) 1997-2012 by Dimitri van Heesch. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation under the terms of the GNU General Public License is hereby + * granted. No representations are made about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * See the GNU General Public License for more details. + * + * Documents produced by Doxygen are derivative works derived from the + * input used in their production; they are not affected by this license. + * + */ + +// STL includes +#include <cstdio> +#include <cstdlib> +#include <iostream> +#include <string> +#include <algorithm> +#include <sstream> + +// Qtools includes +#include <qregexp.h> +#include <qxml.h> +#include <qfile.h> +#include <qfileinfo.h> + +// Xapian include +#include <xapian.h> + +#if defined(_WIN32) && !defined(__CYGWIN__) +static char pathSep = '\\'; +#else +static char pathSep = '/'; +#endif + +/** trims \a whitespace characters from the start and end of string \a str. */ +static std::string trim(const std::string& str, + const std::string& whitespace = " \t") +{ + size_t strBegin = str.find_first_not_of(whitespace); + if (strBegin == std::string::npos) + return ""; // no content + + size_t strEnd = str.find_last_not_of(whitespace); + int strRange = strEnd - strBegin + 1; + + return str.substr(strBegin, strRange); +} + +/** trims \a whitespace from start and end and replace occurrences of + * \a whitespace with \a fill. + */ +static std::string reduce(const std::string& str, + const std::string& fill = " ", + const std::string& whitespace = " \t") +{ + // trim first + std::string result = trim(str, whitespace); + + // replace sub ranges + size_t beginSpace = result.find_first_of(whitespace); + while (beginSpace != std::string::npos) + { + size_t endSpace = result.find_first_not_of(whitespace, beginSpace); + int range = endSpace - beginSpace; + + result.replace(beginSpace, range, fill); + + size_t newStart = beginSpace + fill.length(); + beginSpace = result.find_first_of(whitespace, newStart); + } + + return result; +} + +/** Adds all words in \a s to document \a doc with weight \a wfd */ +static void addWords(const std::string &s,Xapian::Document &doc,int wfd) +{ + std::istringstream iss(s); + std::istream_iterator<std::string> begin(iss),end,it; + for (it=begin;it!=end;++it) + { + std::string word = *it; + std::string lword = word; + std::transform(lword.begin(), lword.end(), lword.begin(), ::tolower); + doc.add_term(word,wfd); + if (lword!=word) + { + doc.add_term(lword,wfd); + } + } +} + +/** Adds all identifiers in \a s to document \a doc with weight \a wfd */ +static void addIdentifiers(const std::string &s,Xapian::Document &doc,int wfd) +{ + QRegExp re("[A-Z_a-z][A-Z_a-z0-9]*"); + int i,l,p=0; + QCString qs = s.c_str(); + while ((i=re.match(qs,p,&l))!=-1) + { + doc.add_term(qs.mid(p,i-p).data(),wfd); + p=i+l; + } +} + +/** Replaces all occurrences of \a old with \a repl in string \a str */ +static void replace_all(std::string& str, const std::string& old, const std::string& repl) +{ + size_t pos = 0; + while ((pos = str.find(old, pos)) != std::string::npos) + { + str.replace(pos, old.length(), repl); + pos += repl.length(); + } +} + +/** Replaces all XML entities in \a s with their unescaped representation */ +static std::string unescapeXmlEntities(const std::string &s) +{ + std::string result=s; + replace_all(result,">",">"); + replace_all(result,"<","<"); + replace_all(result,"'","'"); + replace_all(result,""","\""); + replace_all(result,"&","&"); + return result; +} + +/** This class is a wrapper around SAX style XML parser, which + * parses the file without first building a DOM tree in memory. + */ +class XMLContentHandler : public QXmlDefaultHandler +{ + public: + /** Handler for parsing XML data */ + XMLContentHandler(const QString &path) + : m_db((path+"doxysearch.db").utf8().data(),Xapian::DB_CREATE_OR_OVERWRITE), + m_stemmer("english") + { + m_curFieldName = UnknownField; + m_indexer.set_stemmer(m_stemmer); + m_indexer.set_document(m_doc); + } + + /** Free data handler */ + ~XMLContentHandler() + { + m_db.commit(); + } + + private: + enum FieldNames + { + UnknownField = 0, + TypeField = 1, + NameField = 2, + ArgsField = 3, + TagField = 4, + UrlField = 5, + KeywordField = 6, + TextField = 7 + }; + + /** Handler for a start tag. Called for <doc> and <field> tags */ + bool startElement(const QString &, const QString &, + const QString &name, const QXmlAttributes &attrib) + { + m_data=""; + if (name=="field") + { + QString fieldName = attrib.value("name"); + if (fieldName=="type") m_curFieldName=TypeField; + else if (fieldName=="name") m_curFieldName=NameField; + else if (fieldName=="args") m_curFieldName=ArgsField; + else if (fieldName=="tag") m_curFieldName=TagField; + else if (fieldName=="url") m_curFieldName=UrlField; + else if (fieldName=="keywords") m_curFieldName=KeywordField; + else if (fieldName=="text") m_curFieldName=TextField; + else m_curFieldName=UnknownField; + } + return TRUE; + } + + /** Handler for an end tag. Called for </doc> and </field> tags */ + bool endElement(const QString &, const QString &, const QString &name) + { + if (name=="doc") // </doc> + { + std::string term = m_doc.get_value(NameField); + std::string partTerm; + size_t pos = term.rfind("::"); + if (pos!=std::string::npos) + { + partTerm = term.substr(pos+2); + } + if (m_doc.get_value(TypeField)=="class" || + m_doc.get_value(TypeField)=="file" || + m_doc.get_value(TypeField)=="namespace") // containers get highest prio + { + m_doc.add_term(term,1000); + if (!partTerm.empty()) + { + m_doc.add_term(partTerm,500); + } + } + else // members and others get lower prio + { + m_doc.add_term(m_doc.get_value(NameField),100); + if (!partTerm.empty()) + { + m_doc.add_term(partTerm,50); + } + } + m_db.add_document(m_doc); + m_doc.clear_values(); + m_doc.clear_terms(); + } + else if (name=="field" && m_curFieldName!=UnknownField) // </field> + { + // strip whitespace from m_data + m_data = reduce(m_data); + // replace XML entities + m_data = unescapeXmlEntities(m_data); + // add data to the document + m_doc.add_value(m_curFieldName,m_data); + switch (m_curFieldName) + { + case TypeField: + case NameField: + case TagField: + case UrlField: + // meta data that is not searchable + break; + case KeywordField: + addWords(m_data,m_doc,50); + break; + case ArgsField: + addIdentifiers(m_data,m_doc,10); + break; + case TextField: + addWords(m_data,m_doc,2); + break; + default: + break; + } + m_data=""; + m_curFieldName=UnknownField; + } + // reset m_data + return TRUE; + } + + /** Handler for inline text */ + bool characters(const QString& ch) + { + m_data += ch.utf8(); + return TRUE; + } + + // internal state + Xapian::WritableDatabase m_db; + Xapian::Document m_doc; + Xapian::TermGenerator m_indexer; + Xapian::Stem m_stemmer; + std::string m_data; + FieldNames m_curFieldName; +}; + +/** Class for handling error during XML parsing */ +class XMLErrorHandler : public QXmlErrorHandler +{ + public: + virtual ~XMLErrorHandler() {} + bool warning( const QXmlParseException & ) + { + return FALSE; + } + bool error( const QXmlParseException & ) + { + return FALSE; + } + bool fatalError( const QXmlParseException &exception ) + { + std::cerr << "Fatal error at line " << exception.lineNumber() + << " column " << exception.columnNumber() << ": " + << exception.message().utf8() << std::endl; + return FALSE; + } + QString errorString() { return ""; } + + private: + QString errorMsg; +}; + +static void usage(const char *name) +{ + std::cerr << "Usage: " << name << " [-o output_dir] searchdata.xml [searchdata2.xml ...]" << std::endl; + exit(1); +} + +/** main function to index data */ +int main(int argc,const char **argv) +{ + if (argc<2) + { + usage(argv[0]); + } + QString outputDir; + for (int i=1;i<argc;i++) + { + if (std::string(argv[i])=="-o") + { + if (i>=argc-1) + { + std::cerr << "Error: missing parameter for -o option" << std::endl; + usage(argv[0]); + } + else + { + i++; + outputDir=argv[i]; + QFileInfo fi(outputDir); + if (!fi.exists() || !fi.isDir()) + { + std::cerr << "Error: specified output directory does not exist!" << std::endl; + usage(argv[0]); + } + } + } + else if (std::string(argv[i])=="-h" || std::string(argv[i])=="--help") + { + usage(argv[0]); + } + } + + try + { + if (!outputDir.isEmpty() && outputDir.at(outputDir.length()-1)!=pathSep) + { + outputDir+=pathSep; + } + XMLContentHandler handler(outputDir); + XMLErrorHandler errorHandler; + for (int i=1;i<argc;i++) + { + if (std::string(argv[i])=="-o") + { + i++; + } + else + { + QString xmlFileName = argv[i]; + std::cout << "Processing " << xmlFileName.utf8() << "..." << std::endl; + QFile xmlFile(xmlFileName); + QXmlInputSource source(xmlFile); + QXmlSimpleReader reader; + reader.setContentHandler(&handler); + reader.setErrorHandler(&errorHandler); + reader.parse(source); + } + } + } + catch(const Xapian::Error &e) + { + std::cerr << "Caught exception: " << e.get_description() << std::endl; + } + catch(...) + { + std::cerr << "Caught an unknown exception" << std::endl; + } + + return 0; +} diff --git a/addon/doxysearch/doxyindexer.pro.in b/addon/doxysearch/doxyindexer.pro.in new file mode 100644 index 0000000..7286df8 --- /dev/null +++ b/addon/doxysearch/doxyindexer.pro.in @@ -0,0 +1,12 @@ +TEMPLATE = app.t +CONFIG = console warn_on static release +HEADERS = +SOURCES = doxyindexer.cpp +LIBS += -lxapian -lqtools -L../../lib +DESTDIR = +OBJECTS_DIR = ../../objects +TARGET = ../../bin/doxyindexer +INCLUDEPATH += ../../qtools +DEPENDPATH += +TARGETDEPS = + diff --git a/addon/doxysearch/doxysearch.cpp b/addon/doxysearch/doxysearch.cpp new file mode 100644 index 0000000..7b90c82 --- /dev/null +++ b/addon/doxysearch/doxysearch.cpp @@ -0,0 +1,434 @@ +/****************************************************************************** + * + * Copyright (C) 1997-2012 by Dimitri van Heesch. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation under the terms of the GNU General Public License is hereby + * granted. No representations are made about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * See the GNU General Public License for more details. + * + * Documents produced by Doxygen are derivative works derived from the + * input used in their production; they are not affected by this license. + * + */ + +// STL includes +#include <cstdio> +#include <cstdlib> +#include <string> +#include <vector> +#include <sstream> +#include <iostream> +#include <fstream> +#include <string> +#include <algorithm> + +// Xapian includes +#include <xapian.h> + +#ifdef _WIN32 +#include <windows.h> +#else +#include <sys/stat.h> +#endif + +#define FIELD_TYPE 1 +#define FIELD_NAME 2 +#define FIELD_ARGS 3 +#define FIELD_TAG 4 +#define FIELD_URL 5 +#define FIELD_KEYW 6 +#define FIELD_DOC 7 + +#define HEX2DEC(x) (((x)>='0' && (x)<='9')?((x)-'0'):\ + ((x)>='a' && (x)<='f')?((x)-'a'+10):\ + ((x)>='A' && (x)<='F')?((x)-'A'+10):-1) + + +bool dirExists(const std::string& dirName) +{ +#ifdef _WIN32 + DWORD ftyp = GetFileAttributesA(dirName.c_str()); + if (ftyp == INVALID_FILE_ATTRIBUTES) + return false; //something is wrong with your path! + + if (ftyp & FILE_ATTRIBUTE_DIRECTORY) + return true; // this is a directory! +#else + struct stat sb; + + if (stat(dirName.c_str(), &sb)==0 && S_ISDIR(sb.st_mode)) + { + return true; + } +#endif + + return false; +} + + +/** decodes a URI encoded string into a normal string. */ +static std::string uriDecode(const std::string & sSrc) +{ + // Note from RFC1630: "Sequences which start with a percent + // sign but are not followed by two hexadecimal characters + // (0-9, A-F) are reserved for future extension" + + const unsigned char * pSrc = (const unsigned char *)sSrc.c_str(); + const int SRC_LEN = sSrc.length(); + const unsigned char * const SRC_END = pSrc + SRC_LEN; + // last decodable '%' + const unsigned char * const SRC_LAST_DEC = SRC_END - 2; + + char * const pStart = new char[SRC_LEN]; + char * pEnd = pStart; + + while (pSrc < SRC_LAST_DEC) + { + if (*pSrc == '%') // replace %2A with corresponding ASCII character + { + char dec1, dec2; + unsigned char c1=*(pSrc+1); + unsigned char c2=*(pSrc+2); + if (-1 != (dec1 = HEX2DEC(c1)) + && -1 != (dec2 = HEX2DEC(c2))) + { + *pEnd++ = (dec1 << 4) + dec2; + pSrc += 3; + continue; + } + } + else if (*pSrc == '+') // replace '+' with space + { + *pEnd++ = ' '; pSrc++; + continue; + } + *pEnd++ = *pSrc++; + } + + // the last 2- chars + while (pSrc < SRC_END) *pEnd++ = *pSrc++; + + std::string sResult(pStart, pEnd); + delete [] pStart; + return sResult; +} + +/** return list of strings that result when splitting \a s using + * delimeter \a delim + */ +static std::vector<std::string> split(const std::string &s, char delim) +{ + std::vector<std::string> elems; + std::stringstream ss(s); + std::string item; + while (getline(ss, item, delim)) elems.push_back(item); + return elems; +} + +/** Read type T from string \a s */ +template<class T> +T fromString(const std::string& s) +{ + std::istringstream stream (s); + T t; + stream >> t; + return t; +} + +/** Class that holds the startin position of a word */ +struct WordPosition +{ + WordPosition(int s,int i) : start(s), index(i) {} + int start; + int index; +}; + +/** Class representing the '<' operator for WordPosition objects based on position. */ +struct WordPosition_less +{ + bool operator()(const WordPosition &p1,const WordPosition &p2) + { + return p1.start<p2.start; + } +}; + +/** Class that holds a text fragment */ +struct Fragment +{ + Fragment(const std::string &t,int occ) : text(t), occurrences(occ) {} + std::string text; + int occurrences; +}; + +/** Class representing the '>' operator for Fragment objects based on occurrence. */ +struct Fragment_greater +{ + bool operator()(const Fragment &p1,const Fragment &p2) + { + return p1.occurrences>p2.occurrences; + } +}; + +/** Class representing a range within a string */ +struct Range +{ + Range(int s,int e) : start(s), end(e) {} + int start; + int end; +}; + +/** Returns true if [start..start+len] is inside one of the \a ranges. */ +static bool insideRange(const std::vector<Range> &ranges,int start,int len) +{ + for (std::vector<Range>::const_iterator it = ranges.begin(); + it!=ranges.end(); ++it + ) + { + Range r = *it; + if (start>=r.start && start+len<r.end) + { + return true; + } + } + return false; +} + +/** Returns a list of text \a fragments from \a s containing one or + * more \a words. The list is sorted occording to the + * number of occurrences of words within the fragment. + */ +static void highlighter(const std::string &s, + const std::vector<std::string> &words, + std::vector<Fragment> &fragments) +{ + const std::string spanStart="<span class=\"hl\">"; + const std::string spanEnd="</span>"; + const std::string dots="..."; + const int fragLen = 60; + int sl=s.length(); + + // find positions of words in s + size_t j=0; + std::vector<WordPosition> positions; + for (std::vector<std::string>::const_iterator it=words.begin(); + it!=words.end(); + ++it,++j + ) + { + int pos=0; + size_t i; + std::string word = *it; + while ((i=s.find(word,pos))!=std::string::npos) + { + positions.push_back(WordPosition(i,j)); + pos=i+word.length(); + } + } + // sort on position + std::sort(positions.begin(),positions.end(),WordPosition_less()); + // get fragments around words + std::vector<Range> ranges; + for (std::vector<WordPosition>::const_iterator it=positions.begin(); + it!=positions.end(); + ++it) + { + WordPosition wp = *it; + std::string w = words[wp.index]; + int i=wp.start; + int wl=w.length(); + if (!insideRange(ranges,i,wl)) + { + if (wl>fragLen) + { + fragments.push_back(Fragment(spanStart+w+spanEnd,1)); + ranges.push_back(Range(i,i+wl)); + } + else + { + std::string startFragment,endFragment; + int bi=i-(fragLen-wl)/2; + int ei=i+wl+(fragLen-wl)/2; + int occ=0; + if (bi<0) { ei-=bi; bi=0; } else startFragment=dots; + if (ei>sl) { ei=sl; } else endFragment=dots; + while (bi>0 && !isspace(s[bi])) bi--; // round to start of the word + while (ei<sl && !isspace(s[ei])) ei++; // round to end of the word + // highlight any word in s between indexes bi and ei + std::string fragment=startFragment; + int pos=bi; + for (std::vector<WordPosition>::const_iterator it2=positions.begin(); + it2!=positions.end(); + ++it2) + { + WordPosition wp2 = *it2; + std::string w2 = words[wp2.index]; + int wl2 = w2.length(); + if (wp2.start>=bi && wp2.start+wl2<=ei) // word is inside the range! + { + fragment+=s.substr(pos,wp2.start-pos)+ + spanStart+ + s.substr(wp2.start,wl2)+ + spanEnd; + pos=wp2.start+wl2; + occ++; + } + } + fragment+=s.substr(pos,ei-pos)+endFragment; + fragments.push_back(Fragment(fragment,occ)); + ranges.push_back(Range(bi,ei)); + } + } + } + std::sort(fragments.begin(),fragments.end(),Fragment_greater()); +} + +/** Escapes a string such that is can be included in a JSON structure */ +static std::string escapeString(const std::string &s) +{ + std::stringstream dst; + for (unsigned int i=0;i<s.length();i++) + { + char ch = s[i]; + switch (ch) + { + case '\"': dst << "\\\""; break; + default: dst << ch; break; + } + } + return dst.str(); +} + +static void showError(const std::string &callback,const std::string &error) +{ + std::cout << callback << "({\"error\":\"" << error << "\"})"; + exit(0); +} + +/** Main routine */ +int main(int argc,char **argv) +{ + // process inputs that were passed to us via QUERY_STRING + std::cout << "Content-Type:application/javascript;charset=utf-8\r\n\n"; + std::string callback; + try + { + // get input parameters + const char *queryEnv = getenv("QUERY_STRING"); + std::string queryString; + if (queryEnv) + { + queryString = queryEnv; + } + else if (argc>=2) + { + queryString = argv[1]; + } + else + { + std::cout << "No input!\n"; + exit(1); + } + + // parse query string + std::vector<std::string> parts = split(queryString,'&'); + std::string searchFor,callback; + int num=1,page=0; + for (std::vector<std::string>::const_iterator it=parts.begin();it!=parts.end();++it) + { + std::vector<std::string> kv = split(*it,'='); + if (kv.size()==2) + { + std::string val = uriDecode(kv[1]); + if (kv[0]=="q") searchFor = val; + else if (kv[0]=="n") num = fromString<int>(val); + else if (kv[0]=="p") page = fromString<int>(val); + else if (kv[0]=="cb") callback = val; + } + } + + std::string indexDir = "doxysearch.db"; + + if (queryString=="test") // user test + { + bool dbOk = dirExists(indexDir); + if (dbOk) + { + std::cout << "Test successful."; + } + else + { + std::cout << "Test failed: cannot find search index " << indexDir; + } + exit(0); + } + + // create query + Xapian::Database db(indexDir); + Xapian::Enquire enquire(db); + Xapian::Query query; + std::vector<std::string> words = split(searchFor,' '); + for (std::vector<std::string>::const_iterator it=words.begin();it!=words.end();++it) + { + query = Xapian::Query(Xapian::Query::OP_OR,query,Xapian::Query(*it)); + } + enquire.set_query(query); + + // get results + Xapian::MSet matches = enquire.get_mset(page*num,num); + unsigned int hits = matches.get_matches_estimated(); + unsigned int offset = page*num; + unsigned int pages = num>0 ? (hits+num-1)/num : 0; + if (offset>hits) offset=hits; + if (offset+num>hits) num=hits-offset; + + // write results as JSONP + std::cout << callback.c_str() << "("; + std::cout << "{" << std::endl + << " \"hits\":" << hits << "," << std::endl + << " \"first\":" << offset << "," << std::endl + << " \"count\":" << num << "," << std::endl + << " \"page\":" << page << "," << std::endl + << " \"pages\":" << pages << "," << std::endl + << " \"query\": \"" << escapeString(searchFor) << "\"," << std::endl + << " \"items\":[" << std::endl; + // foreach search result + unsigned int o = offset; + for (Xapian::MSetIterator i = matches.begin(); i != matches.end(); ++i,++o) + { + std::vector<Fragment> hl; + Xapian::Document doc = i.get_document(); + highlighter(doc.get_value(FIELD_DOC),words,hl); + std::cout << " {\"type\": \"" << doc.get_value(FIELD_TYPE) << "\"," << std::endl + << " \"name\": \"" << doc.get_value(FIELD_NAME) << doc.get_value(FIELD_ARGS) << "\"," << std::endl + << " \"tag\": \"" << doc.get_value(FIELD_TAG) << "\"," << std::endl + << " \"url\": \"" << doc.get_value(FIELD_URL) << "\"," << std::endl; + std::cout << " \"fragments\":[" << std::endl; + int c=0; + bool first=true; + for (std::vector<Fragment>::const_iterator it = hl.begin();it!=hl.end() && c<3;++it,++c) + { + if (!first) std::cout << "," << std::endl; + std::cout << " \"" << escapeString((*it).text) << "\""; + first=false; + } + if (!first) std::cout << std::endl; + std::cout << " ]" << std::endl; + std::cout << " }"; + if (o<offset+num-1) std::cout << ","; + std::cout << std::endl; + } + std::cout << " ]" << std::endl << "})" << std::endl; + } + catch (const Xapian::Error &e) // Xapian exception + { + showError(callback,e.get_description()); + } + catch (...) // Any other exception + { + showError(callback,"Unknown Exception!"); + exit(1); + } + return 0; +} diff --git a/addon/doxysearch/doxysearch.pro.in b/addon/doxysearch/doxysearch.pro.in new file mode 100644 index 0000000..fce6d82 --- /dev/null +++ b/addon/doxysearch/doxysearch.pro.in @@ -0,0 +1,12 @@ +TEMPLATE = app.t +CONFIG = console warn_on debug +HEADERS = +SOURCES = doxysearch.cpp +LIBS += -lxapian +DESTDIR = +OBJECTS_DIR = ../../objects +TARGET = ../../bin/doxysearch.cgi +INCLUDEPATH += +DEPENDPATH += +TARGETDEPS = + |