Compare commits

...
45 Commits
Author SHA1 Message Date
Matthieu Gautier 2aebffb27c New version 8.1.0 2019-09-26 15:56:30 +02:00
Kelson da247b3242 Merge pull request #287 from kiwix/codecov-badge
Add Codecov badge to README
2019-09-26 13:40:57 +03:00
Kelson f85ec9ea6f Add Codecov badge to README 2019-09-26 12:37:57 +02:00
Matthieu Gautier b4fac9d0df Remove test directory from codecoverage (#286)
Remove test directory from codecoverage
2019-09-26 12:14:03 +02:00
Matthieu Gautier 4f2ede80e5 Remove test directory from codecoverage 2019-09-26 12:00:33 +02:00
Matthieu Gautier c2ecb9d126 Add code coverage in travis CI. (#285)
Add code coverage in travis CI.
2019-09-26 11:58:49 +02:00
Matthieu Gautier 5883dba0ef Add code coverage in travis CI.
Fix #284
2019-09-26 11:34:35 +02:00
Matthieu Gautier 7ad6aedd66 Convert path get from windows environment to utf8. (#283)
Convert path get from windows environment to utf8.
2019-09-25 18:13:59 +02:00
Matthieu Gautier 67170709bb Convert path get from windows environment to utf8.
Fix kiwix/kiwix-desktop#203
2019-09-25 18:07:42 +02:00
Matthieu Gautier dfd16155af Add missing implementation of android's getArticleCount and getM… (#282)
Add missing implementation of android's getArticleCount and getMediaCount.
2019-09-24 11:57:00 +02:00
Matthieu Gautier 0db06d98a8 Add missing implementation of android's getArticleCount and getMediaCount.
Fix #281
2019-09-24 11:47:05 +02:00
Matthieu Gautier 742156d366 [API Break] Fix pathTools (and a bit stringTools). (#280)
[API Break] Fix pathTools (and a bit stringTools).
2019-09-24 10:58:51 +02:00
Matthieu Gautier 598dd3c175 [API Break] Fix pathTools (and a bit stringTools).
Api changes :
 - removeLastPathElement do not takes extra arguments
   `removePreSeparator` and `removePostSeparator`.
   This is not needed as path do not need special tailing separator.
 - Only one function `split`. Arguments can be implicitly convert to
   string. No need for overloading functions to explicitly cast them.
 - `split` function takes another argument `trimEmpty`. If true, empty
   element are removed.

Path manipulation now almost pass trough a vector<string> to store each
path's part.

Most of the complex works is now made in the normalizeParts function.
2019-09-19 18:16:06 +02:00
Matthieu Gautier 49c0c5ff47 Fix join (#279)
Fix join
2019-09-17 16:28:52 +02:00
Matthieu Gautier 65ebc7fe7f New version 8.0.1 2019-09-17 16:22:28 +02:00
Matthieu Gautier 2f4636e2df Fix stringTools join function. 2019-09-17 16:22:28 +02:00
Kelson f4e9148b1d Small fix of OSes names in the README 2019-09-17 12:20:17 +02:00
Matthieu Gautier 891666b8c4 new version 8.0.0 2019-09-17 11:47:18 +02:00
Matthieu Gautier 57a2b98e7a [ABI Break] Correctly detect the executable path in appimage. (#277)
[ABI Break] Correctly detect the executable path in appimage.
2019-09-17 11:41:15 +02:00
Matthieu Gautier 9b4419f3fc [ABI Break] Correctly detect the executable path in appimage.
There are two executable path :
- The user one (the appimage path)
- The real one (in the appimage archive)

When we search of `library.xml` we need the user one.
But when we search of `aria2c` or `kiwix-serve` we need the real one.

Fix kiwix/kiwix-desktop#256
2019-09-17 11:23:16 +02:00
Matthieu Gautier 15d5b4ed58 Metadata (#276)
Metadata
2019-09-17 11:21:19 +02:00
Matthieu Gautier 2f91149da3 Update .gitignore. 2019-09-17 10:38:16 +02:00
Matthieu Gautier 6ee174b546 Add a method to get the value of a specific tag.
Fix #258
2019-09-17 10:37:53 +02:00
Matthieu Gautier 2a6772b76d [API Change] Convert tags to the new convention.
Use the new convention describe here : https://wiki.openzim.org/wiki/Tags
2019-09-17 10:30:24 +02:00
Matthieu Gautier 660d5d7fb7 [API Change] Rename getMatatag to getMetadata. 2019-09-16 10:36:04 +02:00
Matthieu Gautier 157c1c939c Add a string tool to join a list of strings together. 2019-09-16 09:42:10 +02:00
Matthieu Gautier bd91e89785 Add missing method to get the zim metadata.
According to https://wiki.openzim.org/wiki/Metadata
2019-09-12 15:33:07 +02:00
Matthieu Gautier 1245d4e467 Use a macro to get the content of the metadata. 2019-09-12 15:26:53 +02:00
Matthieu Gautier 420be55bfa Reorder methods to get metadata.
Use the same order than https://wiki.openzim.org/wiki/Metadata
2019-09-12 15:24:17 +02:00
Matthieu Gautier 651cb9165c Win kiwix serve (#274)
Win kiwix serve
2019-09-11 15:25:41 +02:00
Matthieu Gautier 49046248fd New version 7.0.0 2019-09-11 14:04:21 +02:00
Matthieu Gautier e42e061d45 Add a way to specify a library to use with kiwix-serve.
If kiwix-desktop use a `library.xml` in the same directory than the
executable, we need to use it instead of the default one.

Instead of detect again the `library.xml` to use, let `kiwix-desktop` set
the library to use.

This also fix a issue when `/` is not a valid path separator in windows.
2019-09-11 14:04:21 +02:00
Kelson d90774450d Slightly bigger top-padding for kiwix-serve 2019-09-11 13:50:33 +02:00
Matthieu Gautier 9e36c876f5 New version 6.0.4 2019-09-10 15:54:15 +02:00
Matthieu Gautier 1a4c434e3c Correctly cast double to int. (#273)
Correctly cast double to int.
2019-09-10 15:09:09 +02:00
Matthieu Gautier 3294508d87 Correctly cast double to int.
Ms cl compiler complains about the implicit conversion.
2019-09-10 14:10:40 +02:00
Matthieu Gautier 351e573bce Correctly detect the executable path if we use a AppImage. (#272)
Correctly detect the executable path if we use a AppImage.
2019-09-09 18:37:37 +02:00
Matthieu Gautier a32363e6a2 Correctly detect the executable path if we use a AppImage.
AppImage works by decompressing the "program" in a temporary directory.
So the executable path is not the path of the AppImage file.

By using the environment variables set by appimage we can find the correct
"path" of the executable.

Fix kiwix/kiwix-desktop#46
2019-09-09 18:27:53 +02:00
Matthieu Gautier 56f8b7a876 Fix search (#271)
Fix search
2019-09-09 14:59:35 +02:00
Matthieu Gautier 87dc145dc7 Correctly set searcher information even if resultStart equals resultEnd. 2019-09-09 14:43:51 +02:00
Matthieu Gautier a13244dc0e Rename hasResult to hasResults 2019-09-09 14:43:51 +02:00
Matthieu Gautier 78dbd66522 [HTML Rendering] Do not render page navigation buttons if only one page. 2019-09-09 14:43:51 +02:00
Matthieu Gautier fdc291b7c2 [HTML Rendering] Do not do division by zero.
We must correctly handle the case if resultStart is equal to resultEnd.
2019-09-09 14:43:51 +02:00
Kelson d372cea146 Merge pull request #270 from kiwix/aur-badges
Add AUR badge
2019-09-08 17:06:49 +02:00
Kelson e1fcd12e48 Add AUR badge 2019-09-08 17:05:44 +02:00
28 changed files with 979 additions and 262 deletions

No files matched your search

+12
View File
@@ -0,0 +1,12 @@
codecov:
notify:
require_ci_to_pass: yes
coverage:
status:
project:
default:
threshold: 1%
ignore:
- "test"
+1
View File
@@ -1,2 +1,3 @@
.idea/ .idea/
*.swp *.swp
subprojects/googletest-release*
+5
View File
@@ -31,6 +31,11 @@ addons:
- gcc-mingw-w64-i686 - gcc-mingw-w64-i686
- gcc-mingw-w64-base - gcc-mingw-w64-base
- mingw-w64-tools - mingw-w64-tools
- gcovr
homebrew:
update: true
packages:
- gcovr
matrix: matrix:
include: include:
- env: PLATFORM="native_dyn" - env: PLATFORM="native_dyn"
+34
View File
@@ -1,3 +1,37 @@
kiwix-lib 8.1.0
===============
* Fix pathTools manipulation.
* Add missing implementation of getArticleCount and getMediaCount on android.
* Correctly convert windows path to utf8.
* Add code coverage in the CI
kiwix-lib 8.0.1
===============
* Fix join function
kiwix-lib 8.0.0
===============
* Add new methods to get all (and new) metadata from the zim file.
* Add methods to get the value of a specific tag.
* [API Change] Convert tags value to the new convention.
* [API Change] Rename `getMatatag` method to `getMetadata`
* [ABI Change] Correctly detect executable path in appimage.
kiwix-lib 7.0.0
===============
* [API break] Add a argument to kiwix-serve to specify the library to use.
kiwix-lib 6.0.4
===============
* Fix HTML rendering of the search result if there is no result.
* Do not crash at html rendering if request ask for 0 results (start == end)
* Correctly find the executable path if we are using AppImage
kiwix-lib 6.0.3 kiwix-lib 6.0.3
=============== ===============
+3 -1
View File
@@ -3,11 +3,13 @@ Kiwix library
The Kiwix library provides the [Kiwix](https://kiwix.org) software The Kiwix library provides the [Kiwix](https://kiwix.org) software
suite core. It contains the code shared by all Kiwix ports (Windows, suite core. It contains the code shared by all Kiwix ports (Windows,
Linux, OSX, Android, ...). GNU/Linux, macOS, Android, iOS, ...).
[![Download](https://api.bintray.com/packages/kiwix/kiwix/kiwixlib/images/download.svg)](https://bintray.com/kiwix/kiwix/kiwixlib/_latestVersion) [![Download](https://api.bintray.com/packages/kiwix/kiwix/kiwixlib/images/download.svg)](https://bintray.com/kiwix/kiwix/kiwixlib/_latestVersion)
[![AUR version](https://img.shields.io/aur/version/kiwix-lib)](https://aur.archlinux.org/packages/kiwix-lib/)
[![Build Status](https://travis-ci.org/kiwix/kiwix-lib.svg?branch=master)](https://travis-ci.org/kiwix/kiwix-lib) [![Build Status](https://travis-ci.org/kiwix/kiwix-lib.svg?branch=master)](https://travis-ci.org/kiwix/kiwix-lib)
[![CodeFactor](https://www.codefactor.io/repository/github/kiwix/kiwix-lib/badge)](https://www.codefactor.io/repository/github/kiwix/kiwix-lib) [![CodeFactor](https://www.codefactor.io/repository/github/kiwix/kiwix-lib/badge)](https://www.codefactor.io/repository/github/kiwix/kiwix-lib)
[![Codecov](https://codecov.io/gh/kiwix/kiwix-lib/branch/master/graph/badge.svg)](https://codecov.io/gh/kiwix/kiwix-lib)
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
Disclaimer Disclaimer
@@ -26,7 +26,7 @@ task writePom {
project { project {
groupId 'org.kiwix.kiwixlib' groupId 'org.kiwix.kiwixlib'
artifactId 'kiwixlib' artifactId 'kiwixlib'
version '6.0.3' + (System.env.KIWIXLIB_BUILDVERSION == null ? '' : '-'+System.env.KIWIXLIB_BUILDVERSION) version '8.1.0' + (System.env.KIWIXLIB_BUILDVERSION == null ? '' : '-'+System.env.KIWIXLIB_BUILDVERSION)
packaging 'aar' packaging 'aar'
name 'kiwixlib' name 'kiwixlib'
url 'https://github.com/kiwix/kiwix-lib' url 'https://github.com/kiwix/kiwix-lib'
+3 -1
View File
@@ -2,6 +2,7 @@
#define KIWIXLIB_KIWIX_SERVE_H_ #define KIWIXLIB_KIWIX_SERVE_H_
#include <memory> #include <memory>
#include <string>
class Subprocess; class Subprocess;
namespace kiwix { namespace kiwix {
@@ -9,7 +10,7 @@ namespace kiwix {
class KiwixServe class KiwixServe
{ {
public: public:
KiwixServe(int port = 8181); KiwixServe(const std::string& libraryPath, int port = 8181);
~KiwixServe(); ~KiwixServe();
void run(); void run();
@@ -20,6 +21,7 @@ class KiwixServe
private: private:
std::unique_ptr<Subprocess> mp_kiwixServe; std::unique_ptr<Subprocess> mp_kiwixServe;
int m_port; int m_port;
std::string m_libraryPath;
}; };
}; //end namespace kiwix }; //end namespace kiwix
+100 -35
View File
@@ -158,31 +158,7 @@ class Reader
* @param[out] value The value will be set to the content of the metadata. * @param[out] value The value will be set to the content of the metadata.
* @return True if it was possible to get the content of the metadata. * @return True if it was possible to get the content of the metadata.
*/ */
bool getMetatag(const string& name, string& value) const; bool getMetadata(const string& name, string& value) const;
/**
* Get the title of the zim file.
*
* @return The title of zim file as specified in the zim metadata.
* If no title has been set, return a title computed from the
* file path.
*/
string getTitle() const;
/**
* Get the description of the zim file.
*
* @return The description of the zim file as specified in the zim metadata.
* If no description has been set, return the subtitle.
*/
string getDescription() const;
/**
* Get the language of the zim file.
*
* @return The language of the zim file as specified in the zim metadata.
*/
string getLanguage() const;
/** /**
* Get the name of the zim file. * Get the name of the zim file.
@@ -192,18 +168,13 @@ class Reader
string getName() const; string getName() const;
/** /**
* Get the tags of the zim file. * Get the title of the zim file.
* *
* @return The tags of the zim file as specified in the zim metadata. * @return The title of zim file as specified in the zim metadata.
* If no title has been set, return a title computed from the
* file path.
*/ */
string getTags() const; string getTitle() const;
/**
* Get the date of the zim file.
*
* @return The date of the zim file as specified in the zim metadata.
*/
string getDate() const;
/** /**
* Get the creator of the zim file. * Get the creator of the zim file.
@@ -219,6 +190,100 @@ class Reader
*/ */
string getPublisher() const; string getPublisher() const;
/**
* Get the date of the zim file.
*
* @return The date of the zim file as specified in the zim metadata.
*/
string getDate() const;
/**
* Get the description of the zim file.
*
* @return The description of the zim file as specified in the zim metadata.
* If no description has been set, return the subtitle.
*/
string getDescription() const;
/**
* Get the long description of the zim file.
*
* @return The long description of the zim file as specifed in the zim metadata.
*/
string getLongDescription() const;
/**
* Get the language of the zim file.
*
* @return The language of the zim file as specified in the zim metadata.
*/
string getLanguage() const;
/**
* Get the license of the zim file.
*
* @return The license of the zim file as specified in the zim metadata.
*/
string getLicense() const;
/**
* Get the tags of the zim file.
*
* @param original If true, return the original tags as specified in the zim metadata.
* Else, try to convert it to the new 'normalized' format.
* @return The tags of the zim file.
*/
string getTags(bool original=false) const;
/**
* Get the value (as a string) of a specific tag.
*
* According to https://wiki.openzim.org/wiki/Tags
*
* @return The value of the specified tag.
* @throw std::out_of_range if the specified tag is not found.
*/
string getTagStr(const std::string& tagName) const;
/**
* Get the boolean value of a specific tag.
*
* According to https://wiki.openzim.org/wiki/Tags
*
* @return The boolean value of the specified tag.
* @throw std::out_of_range if the specified tag is not found.
* std::domain_error if the value of the tag cannot be convert to bool.
*/
bool getTagBool(const std::string& tagName) const;
/**
* Get the relations of the zim file.
*
* @return The relation of the zim file as specified in the zim metadata.
*/
string getRelation() const;
/**
* Get the flavour of the zim file.
*
* @return The flavour of the zim file as specified in the zim metadata.
*/
string getFlavour() const;
/**
* Get the source of the zim file.
*
* @return The source of the zim file as specified in the zim metadata.
*/
string getSource() const;
/**
* Get the scraper of the zim file.
*
* @return The scraper of the zim file as specified in the zim metadata.
*/
string getScraper() const;
/** /**
* Get the origId of the zim file. * Get the origId of the zim file.
* *
+2 -4
View File
@@ -25,9 +25,7 @@
bool isRelativePath(const std::string& path); bool isRelativePath(const std::string& path);
std::string computeAbsolutePath(const std::string& path, const std::string& relativePath); std::string computeAbsolutePath(const std::string& path, const std::string& relativePath);
std::string computeRelativePath(const std::string& path, const std::string& absolutePath); std::string computeRelativePath(const std::string& path, const std::string& absolutePath);
std::string removeLastPathElement(const std::string& path, std::string removeLastPathElement(const std::string& path);
const bool removePreSeparator = false,
const bool removePostSeparator = false);
std::string appendToDirectory(const std::string& directoryPath, const std::string& filename); std::string appendToDirectory(const std::string& directoryPath, const std::string& filename);
unsigned int getFileSize(const std::string& path); unsigned int getFileSize(const std::string& path);
@@ -38,7 +36,7 @@ bool makeDirectory(const std::string& path);
std::string makeTmpDirectory(); std::string makeTmpDirectory();
bool copyFile(const std::string& sourcePath, const std::string& destPath); bool copyFile(const std::string& sourcePath, const std::string& destPath);
std::string getLastPathElement(const std::string& path); std::string getLastPathElement(const std::string& path);
std::string getExecutablePath(); std::string getExecutablePath(bool realPathOnly = false);
std::string getCurrentDirectory(); std::string getCurrentDirectory();
std::string getDataDirectory(); std::string getDataDirectory();
bool writeTextFile(const std::string& path, const std::string& content); bool writeTextFile(const std::string& path, const std::string& content);
+2 -4
View File
@@ -43,10 +43,8 @@ void loadICUExternalTables();
std::string urlEncode(const std::string& value, bool encodeReserved = false); std::string urlEncode(const std::string& value, bool encodeReserved = false);
std::string urlDecode(const std::string& value, bool component = false); std::string urlDecode(const std::string& value, bool component = false);
std::vector<std::string> split(const std::string&, const std::string&); std::vector<std::string> split(const std::string&, const std::string&, bool trimEmpty = true);
std::vector<std::string> split(const char*, const char*); std::string join(const std::vector<std::string>& list, const std::string& sep);
std::vector<std::string> split(const std::string&, const char*);
std::vector<std::string> split(const char*, const std::string&);
std::string ucAll(const std::string& word); std::string ucAll(const std::string& word);
std::string lcAll(const std::string& word); std::string lcAll(const std::string& word);
+1 -1
View File
@@ -1,5 +1,5 @@
project('kiwix-lib', 'cpp', project('kiwix-lib', 'cpp',
version : '6.0.3', # Also change this in android-kiwix-lib-publisher/kiwixLibAndroid/build.gradle version : '8.1.0', # Also change this in android-kiwix-lib-publisher/kiwixLibAndroid/build.gradle
license : 'GPL', license : 'GPL',
default_options : ['c_std=c11', 'cpp_std=c++11', 'werror=true']) default_options : ['c_std=c11', 'cpp_std=c++11', 'werror=true'])
+29
View File
@@ -424,6 +424,35 @@ Java_org_kiwix_kiwixlib_JNIKiwixReader_getDescription(JNIEnv* env, jobject obj)
return description; return description;
} }
JNIEXPORT jint JNICALL
Java_org_kiwix_kiwixlib_JNIKiwixReader_getArticleCount(JNIEnv* env, jobject obj)
{
jint articleCount = 0;
try {
auto cArticleCount = READER->getArticleCount();
articleCount = c2jni(cArticleCount);
} catch (std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get article count.");
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what());
}
return articleCount;
}
JNIEXPORT jint JNICALL
Java_org_kiwix_kiwixlib_JNIKiwixReader_getMediaCount(JNIEnv* env, jobject obj)
{
jint mediaCount = 0;
try {
auto cMediaCount = READER->getMediaCount();
mediaCount = c2jni(cMediaCount);
} catch (std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "kiwix", "Unable to get media count.");
__android_log_print(ANDROID_LOG_ERROR, "kiwix", e.what());
}
return mediaCount;
}
JNIEXPORT jboolean JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getRandomPage( JNIEXPORT jboolean JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getRandomPage(
JNIEnv* env, jobject obj, jobject urlObj) JNIEnv* env, jobject obj, jobject urlObj)
{ {
+1 -1
View File
@@ -49,7 +49,7 @@ Aria2::Aria2():
m_secret = "token:"+m_secret; m_secret = "token:"+m_secret;
std::string aria2cmd = appendToDirectory( std::string aria2cmd = appendToDirectory(
removeLastPathElement(getExecutablePath(), true, true), removeLastPathElement(getExecutablePath(true)),
ARIA2_CMD); ARIA2_CMD);
if (fileExists(aria2cmd)) { if (fileExists(aria2cmd)) {
// A local aria2c exe exists (packaged with kiwix-desktop), use it. // A local aria2c exe exists (packaged with kiwix-desktop), use it.
+5 -4
View File
@@ -14,7 +14,9 @@
namespace kiwix { namespace kiwix {
KiwixServe::KiwixServe(int port) : m_port(port) KiwixServe::KiwixServe(const std::string& libraryPath, int port)
: m_port(port),
m_libraryPath(libraryPath)
{ {
} }
@@ -34,7 +36,7 @@ void KiwixServe::run()
std::vector<const char*> callCmd; std::vector<const char*> callCmd;
std::string kiwixServeCmd = appendToDirectory( std::string kiwixServeCmd = appendToDirectory(
removeLastPathElement(getExecutablePath(), true, true), removeLastPathElement(getExecutablePath(true)),
KIWIXSERVE_CMD); KIWIXSERVE_CMD);
if (fileExists(kiwixServeCmd)) { if (fileExists(kiwixServeCmd)) {
// A local kiwix-serve exe exists (packaged with kiwix-desktop), use it. // A local kiwix-serve exe exists (packaged with kiwix-desktop), use it.
@@ -43,13 +45,12 @@ void KiwixServe::run()
// Try to use a potential installed kiwix-serve. // Try to use a potential installed kiwix-serve.
callCmd.push_back(KIWIXSERVE_CMD); callCmd.push_back(KIWIXSERVE_CMD);
} }
std::string libraryPath = getDataDirectory() + "/library.xml";
std::string attachProcessOpt = "-a" + to_string(pid); std::string attachProcessOpt = "-a" + to_string(pid);
std::string portOpt = "-p" + to_string(m_port); std::string portOpt = "-p" + to_string(m_port);
callCmd.push_back(attachProcessOpt.c_str()); callCmd.push_back(attachProcessOpt.c_str());
callCmd.push_back(portOpt.c_str()); callCmd.push_back(portOpt.c_str());
callCmd.push_back("-l"); callCmd.push_back("-l");
callCmd.push_back(libraryPath.c_str()); callCmd.push_back(m_libraryPath.c_str());
mp_kiwixServe = Subprocess::run(callCmd); mp_kiwixServe = Subprocess::run(callCmd);
} }
+1 -1
View File
@@ -115,7 +115,7 @@ unsigned int Library::getBookCount(const bool localBooks,
bool Library::writeToFile(const std::string& path) bool Library::writeToFile(const std::string& path)
{ {
auto baseDir = removeLastPathElement(path, true, false); auto baseDir = removeLastPathElement(path);
LibXMLDumper dumper(this); LibXMLDumper dumper(this);
dumper.setBaseDir(baseDir); dumper.setBaseDir(baseDir);
return writeTextFile(path, dumper.dumpLibXMLContent(getBooksIds())); return writeTextFile(path, dumper.dumpLibXMLContent(getBooksIds()));
+2 -2
View File
@@ -59,7 +59,7 @@ bool Manager::parseXmlDom(const pugi::xml_document& doc,
book.setReadOnly(readOnly); book.setReadOnly(readOnly);
book.updateFromXml(bookNode, book.updateFromXml(bookNode,
removeLastPathElement(libraryPath, true, false)); removeLastPathElement(libraryPath));
/* Update the book properties with the new importer */ /* Update the book properties with the new importer */
if (libraryVersion.empty() if (libraryVersion.empty()
@@ -177,7 +177,7 @@ std::string Manager::addBookFromPathAndGetId(const std::string& pathToOpen,
if (pathToSave != pathToOpen) { if (pathToSave != pathToOpen) {
book.setPath(isRelativePath(pathToSave) book.setPath(isRelativePath(pathToSave)
? computeAbsolutePath( ? computeAbsolutePath(
removeLastPathElement(writableLibraryPath, true, false), removeLastPathElement(writableLibraryPath),
pathToSave) pathToSave)
: pathToSave); : pathToSave);
} }
+134 -28
View File
@@ -278,7 +278,7 @@ string Reader::getZimFilePath() const
return this->zimFilePath; return this->zimFilePath;
} }
/* Return a metatag value */ /* Return a metatag value */
bool Reader::getMetatag(const string& name, string& value) const bool Reader::getMetadata(const string& name, string& value) const
{ {
try { try {
auto entry = getEntryFromPath("M/"+name); auto entry = getEntryFromPath("M/"+name);
@@ -289,10 +289,17 @@ bool Reader::getMetatag(const string& name, string& value) const
} }
} }
#define METADATA(NAME) std::string v; getMetadata(NAME, v); return v;
string Reader::getName() const
{
METADATA("Name")
}
string Reader::getTitle() const string Reader::getTitle() const
{ {
string value; string value;
this->getMetatag("Title", value); this->getMetadata("Title", value);
if (value.empty()) { if (value.empty()) {
value = getLastPathElement(zimFileHandler->getFilename()); value = getLastPathElement(zimFileHandler->getFilename());
std::replace(value.begin(), value.end(), '_', ' '); std::replace(value.begin(), value.end(), '_', ' ');
@@ -302,65 +309,164 @@ string Reader::getTitle() const
return value; return value;
} }
string Reader::getName() const string Reader::getCreator() const
{ {
string value; METADATA("Creator")
this->getMetatag("Name", value);
return value;
} }
string Reader::getTags() const string Reader::getPublisher() const
{ {
string value; METADATA("Publisher")
this->getMetatag("Tags", value); }
return value;
string Reader::getDate() const
{
METADATA("Date")
} }
string Reader::getDescription() const string Reader::getDescription() const
{ {
string value; string value;
this->getMetatag("Description", value); this->getMetadata("Description", value);
/* Mediawiki Collection tends to use the "Subtitle" name */ /* Mediawiki Collection tends to use the "Subtitle" name */
if (value.empty()) { if (value.empty()) {
this->getMetatag("Subtitle", value); this->getMetadata("Subtitle", value);
} }
return value; return value;
} }
string Reader::getLongDescription() const
{
METADATA("LongDescription")
}
string Reader::getLanguage() const string Reader::getLanguage() const
{ {
string value; METADATA("Language")
this->getMetatag("Language", value);
return value;
} }
string Reader::getDate() const string Reader::getLicense() const
{ {
string value; METADATA("License")
this->getMetatag("Date", value);
return value;
} }
string Reader::getCreator() const std::vector<std::string> convertTags(const std::string& tags_str)
{ {
string value; auto tags = split(tags_str, ";");
this->getMetatag("Creator", value); std::vector<std::string> tagsList;
return value; bool picSeen(false), vidSeen(false), detSeen(false), indexSeen(false);
for (auto tag: tags) {
picSeen |= (tag == "nopic" || startsWith(tag, "_pictures:"));
vidSeen |= (tag == "novid" || startsWith(tag, "_videos:"));
detSeen |= (tag == "nodet" || startsWith(tag, "_details:"));
indexSeen |= startsWith(tag, "_ftindex");
if (tag == "nopic") {
tagsList.push_back("_pictures:no");
} else if (tag == "novid") {
tagsList.push_back("_videos:no");
} else if (tag == "nodet") {
tagsList.push_back("_details:no");
} else if (tag == "_ftindex") {
tagsList.push_back("_ftindex:yes");
} else {
tagsList.push_back(tag);
}
}
if (!indexSeen) {
tagsList.push_back("_ftindex:no");
}
if (!picSeen) {
tagsList.push_back("_pictures:yes");
}
if (!vidSeen) {
tagsList.push_back("_videos:yes");
}
if (!detSeen) {
tagsList.push_back("_details:yes");
}
return tagsList;
} }
string Reader::getPublisher() const string Reader::getTags(bool original) const
{ {
string value; string tags_str;
this->getMetatag("Publisher", value); getMetadata("Tags", tags_str);
return value; if (original) {
return tags_str;
}
auto tags = convertTags(tags_str);
return join(tags, ";");
} }
string getTagValueFromTagList(const std::vector<std::string>& tagList, const std::string& tagName)
{
for (auto tag: tagList) {
if (tag[0] == '_') {
auto delimPos = tag.find(':');
if (delimPos == string::npos) {
// No delimiter... what to do ?
continue;
}
auto cTagName = tag.substr(1, delimPos-1);
auto cTagValue = tag.substr(delimPos+1);
if (cTagName == tagName) {
return cTagValue;
}
}
}
std::stringstream ss;
ss << tagName << " cannot be found";
throw std::out_of_range(ss.str());
}
string Reader::getTagStr(const std::string& tagName) const
{
string tags_str;
getMetadata("Tags", tags_str);
return getTagValueFromTagList(convertTags(tags_str), tagName);
}
bool Reader::getTagBool(const std::string& tagName) const
{
auto tagValue = getTagStr(tagName);
if (tagValue == "yes") {
return true;
} else if (tagValue == "no") {
return false;
} else {
std::stringstream ss;
ss << "Tag value '" << tagValue << "' for " << tagName << " cannot be converted to bool.";
throw std::domain_error(ss.str());
}
}
string Reader::getRelation() const
{
METADATA("Relation")
}
string Reader::getFlavour() const
{
METADATA("Flavour")
}
string Reader::getSource() const
{
METADATA("Source")
}
string Reader::getScraper() const
{
METADATA("Scraper")
}
#undef METADATA
string Reader::getOrigId() const string Reader::getOrigId() const
{ {
string value; string value;
this->getMetatag("startfileuid", value); this->getMetadata("startfileuid", value);
if (value.empty()) { if (value.empty()) {
return ""; return "";
} }
+19 -18
View File
@@ -96,27 +96,29 @@ std::string SearchRenderer::getHtml()
auto resultEnd = mp_searcher->getResultEnd(); auto resultEnd = mp_searcher->getResultEnd();
auto resultCountPerPage = resultEnd - resultStart; auto resultCountPerPage = resultEnd - resultStart;
auto estimatedResultCount = mp_searcher->getEstimatedResultCount(); auto estimatedResultCount = mp_searcher->getEstimatedResultCount();
auto currentPage = 0U;
unsigned int pageStart auto pageStart = 0U;
= resultStart / resultCountPerPage >= 5 auto pageEnd = 0U;
? resultStart / resultCountPerPage - 4 auto lastPageStart = 0U;
: 0; if (resultCountPerPage) {
unsigned int pageCount currentPage = resultStart/resultCountPerPage;
= estimatedResultCount / resultCountPerPage + 1 - pageStart; pageStart = currentPage > 4 ? currentPage-4 : 0;
pageEnd = currentPage + 5;
if (pageCount > 10) { if (pageEnd > estimatedResultCount / resultCountPerPage) {
pageCount = 10; pageEnd = estimatedResultCount / resultCountPerPage;
} else if (pageCount == 1) { }
pageCount = 0; if (estimatedResultCount > resultCountPerPage) {
lastPageStart = static_cast<int>(round(estimatedResultCount/resultCountPerPage)) * resultCountPerPage;
}
} }
for (unsigned int i = pageStart; i < pageStart + pageCount; i++) { for (unsigned int i = pageStart; i < pageEnd; i++) {
kainjow::mustache::data page; kainjow::mustache::data page;
page.set("label", to_string(i + 1)); page.set("label", to_string(i + 1));
page.set("start", to_string(i * resultCountPerPage)); page.set("start", to_string(i * resultCountPerPage));
page.set("end", to_string((i + 1) * resultCountPerPage)); page.set("end", to_string((i + 1) * resultCountPerPage));
if (i * resultCountPerPage == resultStart) { if (i == currentPage) {
page.set("selected", true); page.set("selected", true);
} }
pages.push_back(page); pages.push_back(page);
@@ -128,16 +130,15 @@ std::string SearchRenderer::getHtml()
kainjow::mustache::data allData; kainjow::mustache::data allData;
allData.set("results", results); allData.set("results", results);
allData.set("pages", pages); allData.set("pages", pages);
allData.set("hasResult", estimatedResultCount != 0); allData.set("hasResults", estimatedResultCount != 0);
allData.set("hasPages", pageStart != pageEnd);
allData.set("count", kiwix::beautifyInteger(estimatedResultCount)); allData.set("count", kiwix::beautifyInteger(estimatedResultCount));
allData.set("searchPattern", kiwix::encodeDiples(this->searchPattern)); allData.set("searchPattern", kiwix::encodeDiples(this->searchPattern));
allData.set("searchPatternEncoded", urlEncode(this->searchPattern)); allData.set("searchPatternEncoded", urlEncode(this->searchPattern));
allData.set("resultStart", to_string(resultStart + 1)); allData.set("resultStart", to_string(resultStart + 1));
allData.set("resultEnd", to_string(min(resultEnd, estimatedResultCount))); allData.set("resultEnd", to_string(min(resultEnd, estimatedResultCount)));
allData.set("resultRange", to_string(resultCountPerPage)); allData.set("resultRange", to_string(resultCountPerPage));
allData.set("resultLastPageStart", to_string(estimatedResultCount > resultCountPerPage allData.set("resultLastPageStart", to_string(lastPageStart));
? round(estimatedResultCount / resultCountPerPage) * resultCountPerPage
: 0));
allData.set("lastResult", to_string(estimatedResultCount)); allData.set("lastResult", to_string(estimatedResultCount));
allData.set("protocolPrefix", this->protocolPrefix); allData.set("protocolPrefix", this->protocolPrefix);
allData.set("searchProtocolPrefix", this->searchProtocolPrefix); allData.set("searchProtocolPrefix", this->searchProtocolPrefix);
+8 -8
View File
@@ -108,12 +108,12 @@ void Searcher::search(std::string& search,
cout << "Performing query `" << search << "'" << endl; cout << "Performing query `" << search << "'" << endl;
} }
this->searchPattern = search;
this->resultStart = resultStart;
this->resultEnd = resultEnd;
/* Try to find results */ /* Try to find results */
if (resultStart != resultEnd) { if (resultStart != resultEnd) {
/* Perform the search */ /* Perform the search */
this->searchPattern = search;
this->resultStart = resultStart;
this->resultEnd = resultEnd;
string unaccentedSearch = removeAccents(search); string unaccentedSearch = removeAccents(search);
std::vector<const zim::File*> zims; std::vector<const zim::File*> zims;
for (auto current = this->readers.begin(); current != this->readers.end(); for (auto current = this->readers.begin(); current != this->readers.end();
@@ -146,11 +146,6 @@ void Searcher::geo_search(float latitude, float longitude, float distance,
cout << "Performing geo query `" << distance << "&(" << latitude << ";" << longitude << ")'" << endl; cout << "Performing geo query `" << distance << "&(" << latitude << ";" << longitude << ")'" << endl;
} }
/* Try to find results */
if (resultStart == resultEnd) {
return;
}
/* Perform the search */ /* Perform the search */
std::ostringstream oss; std::ostringstream oss;
oss << "Articles located less than " << distance << " meters of " << latitude << ";" << longitude; oss << "Articles located less than " << distance << " meters of " << latitude << ";" << longitude;
@@ -158,6 +153,11 @@ void Searcher::geo_search(float latitude, float longitude, float distance,
this->resultStart = resultStart; this->resultStart = resultStart;
this->resultEnd = resultEnd; this->resultEnd = resultEnd;
/* Try to find results */
if (resultStart == resultEnd) {
return;
}
std::vector<const zim::File*> zims; std::vector<const zim::File*> zims;
for (auto current = this->readers.begin(); current != this->readers.end(); for (auto current = this->readers.begin(); current != this->readers.end();
current++) { current++) {
+149 -102
View File
@@ -39,11 +39,12 @@
#include <fstream> #include <fstream>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <algorithm>
#ifdef _WIN32 #ifdef _WIN32
const std::string SEPARATOR("\\"); #define SEPARATOR "\\"
#else #else
const std::string SEPARATOR("/"); #define SEPARATOR "/"
#include <unistd.h> #include <unistd.h>
#endif #endif
@@ -53,6 +54,25 @@ const std::string SEPARATOR("/");
#define PATH_MAX 1024 #define PATH_MAX 1024
#endif #endif
#ifdef _WIN32
std::string WideToUtf8(const std::wstring& wstr)
{
auto needed_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr.size(), NULL, 0, NULL, NULL);
std::string ret(needed_size, 0);
WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr.size(), &ret[0], needed_size, NULL, NULL);
return ret;
}
std::wstring Utf8ToWide(const std::string& str)
{
auto needed_size = MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), NULL, 0);
std::wstring ret(needed_size, 0);
MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), &ret[0], needed_size);
return ret;
}
#endif
bool isRelativePath(const std::string& path) bool isRelativePath(const std::string& path)
{ {
#ifdef _WIN32 #ifdef _WIN32
@@ -62,11 +82,65 @@ bool isRelativePath(const std::string& path)
#endif #endif
} }
std::vector<std::string> normalizeParts(std::vector<std::string> parts, bool absolute)
{
std::vector<std::string> ret;
#ifdef _WIN32
//Special case if we have a drive directory not at first.
//Starts from there.
auto it = find_if(parts.rbegin(), parts.rend(),
[](const std::string& p) ->bool
{ return p.length() == 2 && p[1] == ':'; });
if (it != parts.rend()) {
parts.erase(parts.begin(), it.base()-1);
}
#endif
size_t index = 0;
for (auto& part: parts) {
index++;
if (part == "..") {
if (absolute) {
// We try to remove as far as possible.
if (ret.size() > 1) {
ret.pop_back();
}
} else {
// We remove only if we can remove it.
// Else we add it.
if (!ret.empty() && ret.back() != "..") {
ret.pop_back();
} else {
ret.push_back("..");
}
}
continue;
}
if (part == "") {
#ifndef _WIN32
if (ret.empty() && (absolute || index<parts.size())) {
ret.push_back("");
}
#endif
continue;
}
if (part == ".") {
continue;
}
ret.push_back(part);
}
#ifndef _WIN32
if (absolute && ret.size() == 1 && ret.back() == "") {
ret.push_back("");
}
#endif
return ret;
}
std::string computeRelativePath(const std::string& path, const std::string& absolutePath) std::string computeRelativePath(const std::string& path, const std::string& absolutePath)
{ {
std::vector<std::string> pathParts = kiwix::split(path, SEPARATOR); auto pathParts = normalizeParts(kiwix::split(path, SEPARATOR, false), false);
std::vector<std::string> absolutePathParts auto absolutePathParts = kiwix::split(absolutePath, SEPARATOR, false);
= kiwix::split(absolutePath, SEPARATOR);
unsigned int commonCount = 0; unsigned int commonCount = 0;
while (commonCount < pathParts.size() while (commonCount < pathParts.size()
@@ -75,108 +149,60 @@ std::string computeRelativePath(const std::string& path, const std::string& abso
commonCount++; commonCount++;
} }
std::string relativePath; std::vector<std::string> relativeParts;
#ifdef _WIN32
/* On Windows you have a token more because the root is represented
by a letter */
if (commonCount == 0) {
relativePath = ".." + SEPARATOR;
}
#endif
for (unsigned int i = commonCount; i < pathParts.size(); i++) { for (unsigned int i = commonCount; i < pathParts.size(); i++) {
relativePath += ".." + SEPARATOR; relativeParts.push_back("..");
} }
for (unsigned int i = commonCount; i < absolutePathParts.size(); i++) { for (unsigned int i = commonCount; i < absolutePathParts.size(); i++) {
relativePath += absolutePathParts[i]; relativeParts.push_back(absolutePathParts[i]);
relativePath += i + 1 < absolutePathParts.size() ? SEPARATOR : "";
} }
return relativePath; auto ret = kiwix::join(normalizeParts(relativeParts, false), SEPARATOR);
return ret;
} }
#ifdef _WIN32
# define STRTOK strtok_s
#else
# define STRTOK strtok_r
#endif
/* Warning: the relative path must be with slashes */
std::string computeAbsolutePath(const std::string& path, const std::string& relativePath) std::string computeAbsolutePath(const std::string& path, const std::string& relativePath)
{ {
std::string absolutePath; std::string absolutePath = path;
if (path.empty()) { if (path.empty()) {
char* path = NULL; absolutePath = getCurrentDirectory();
size_t size = 0;
#ifdef _WIN32
path = _getcwd(path, size);
#else
path = getcwd(path, size);
#endif
absolutePath = std::string(path) + SEPARATOR;
} else {
absolutePath = path.substr(path.length() - 1, 1) == SEPARATOR
? path
: path + SEPARATOR;
} }
#if _WIN32 auto absoluteParts = normalizeParts(kiwix::split(absolutePath, SEPARATOR, false), true);
char* cRelativePath = _strdup(relativePath.c_str()); auto relativeParts = kiwix::split(relativePath, SEPARATOR, false);
#else
char* cRelativePath = strdup(relativePath.c_str());
#endif
char* saveptr = nullptr;
char* token = STRTOK(cRelativePath, "/", &saveptr);
while (token != NULL) { absoluteParts.insert(absoluteParts.end(), relativeParts.begin(), relativeParts.end());
if (std::string(token) == "..") { auto ret = kiwix::join(normalizeParts(absoluteParts, true), SEPARATOR);
absolutePath = removeLastPathElement(absolutePath, true, false); return ret;
token = STRTOK(NULL, "/", &saveptr);
} else if (strcmp(token, ".") && strcmp(token, "")) {
absolutePath += std::string(token);
token = STRTOK(NULL, "/", &saveptr);
if (token != NULL) {
absolutePath += SEPARATOR;
}
} else {
token = STRTOK(NULL, "/", &saveptr);
}
}
free(cRelativePath);
return absolutePath;
} }
std::string removeLastPathElement(const std::string& path, std::string removeLastPathElement(const std::string& path)
const bool removePreSeparator,
const bool removePostSeparator)
{ {
std::string newPath = path; auto parts = normalizeParts(kiwix::split(path, SEPARATOR, false), false);
size_t offset = newPath.find_last_of(SEPARATOR); if (!parts.empty()) {
if (removePreSeparator && parts.pop_back();
#ifndef _WIN32
offset != newPath.find_first_of(SEPARATOR) &&
#endif
offset == newPath.length() - 1) {
newPath = newPath.substr(0, offset);
offset = newPath.find_last_of(SEPARATOR);
} }
newPath = removePostSeparator ? newPath.substr(0, offset) auto ret = kiwix::join(parts, SEPARATOR);
: newPath.substr(0, offset + 1); return ret;
return newPath;
} }
std::string appendToDirectory(const std::string& directoryPath, const std::string& filename) std::string appendToDirectory(const std::string& directoryPath, const std::string& filename)
{ {
std::string newPath = directoryPath + SEPARATOR + filename; std::string newPath = directoryPath;
if (!directoryPath.empty() && directoryPath.back() != SEPARATOR[0]) {
newPath += SEPARATOR;
}
newPath += filename;
return newPath; return newPath;
} }
std::string getLastPathElement(const std::string& path) std::string getLastPathElement(const std::string& path)
{ {
return path.substr(path.find_last_of(SEPARATOR) + 1); auto parts = normalizeParts(kiwix::split(path, SEPARATOR), false);
if (parts.empty()) {
return "";
}
auto ret = parts.back();
return ret;
} }
unsigned int getFileSize(const std::string& path) unsigned int getFileSize(const std::string& path)
@@ -216,7 +242,7 @@ std::string getFileContent(const std::string& path)
bool fileExists(const std::string& path) bool fileExists(const std::string& path)
{ {
#ifdef _WIN32 #ifdef _WIN32
return PathFileExists(path.c_str()); return PathFileExistsW(Utf8ToWide(path).c_str());
#else #else
bool flag = false; bool flag = false;
std::fstream fin; std::fstream fin;
@@ -232,7 +258,7 @@ bool fileExists(const std::string& path)
bool makeDirectory(const std::string& path) bool makeDirectory(const std::string& path)
{ {
#ifdef _WIN32 #ifdef _WIN32
int status = _mkdir(path.c_str()); int status = _wmkdir(Utf8ToWide(path).c_str());
#else #else
int status = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); int status = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
#endif #endif
@@ -242,15 +268,15 @@ bool makeDirectory(const std::string& path)
std::string makeTmpDirectory() std::string makeTmpDirectory()
{ {
#ifdef _WIN32 #ifdef _WIN32
char cbase[MAX_PATH]; wchar_t cbase[MAX_PATH];
char ctmp[MAX_PATH]; wchar_t ctmp[MAX_PATH];
GetTempPath(MAX_PATH-14, cbase); GetTempPathW(MAX_PATH-14, cbase);
// This create a file for us, ensure it is unique. // This create a file for us, ensure it is unique.
// So we need to delete it and create the directory using the same name. // So we need to delete it and create the directory using the same name.
GetTempFileName(cbase, "kiwix", 0, ctmp); GetTempFileNameW(cbase, L"kiwix", 0, ctmp);
DeleteFile(ctmp); DeleteFileW(ctmp);
_mkdir(ctmp); _wmkdir(ctmp);
return std::string(ctmp); return WideToUtf8(ctmp);
#else #else
char _template_array[] = {"/tmp/kiwix-lib_XXXXXX"}; char _template_array[] = {"/tmp/kiwix-lib_XXXXXX"};
std::string dir = mkdtemp(_template_array); std::string dir = mkdtemp(_template_array);
@@ -279,25 +305,38 @@ bool copyFile(const std::string& sourcePath, const std::string& destPath)
return true; return true;
} }
std::string getExecutablePath() std::string getExecutablePath(bool realPathOnly)
{ {
char binRootPath[PATH_MAX]; if (!realPathOnly) {
char* cAppImage = ::getenv("APPIMAGE");
if (cAppImage) {
char* cArgv0 = ::getenv("ARGV0");
char* cOwd = ::getenv("OWD");
if (cArgv0 && cOwd) {
auto ret = appendToDirectory(cOwd, cArgv0);
return ret;
}
}
}
#ifdef _WIN32 #ifdef _WIN32
GetModuleFileName(NULL, binRootPath, PATH_MAX); std::wstring binRootPath(PATH_MAX, 0);
return std::string(binRootPath); GetModuleFileNameW(NULL, &binRootPath[0], PATH_MAX);
std::string ret = WideToUtf8(binRootPath);
return ret;
#elif __APPLE__ #elif __APPLE__
char binRootPath[PATH_MAX];
uint32_t max = (uint32_t)PATH_MAX; uint32_t max = (uint32_t)PATH_MAX;
_NSGetExecutablePath(binRootPath, &max); _NSGetExecutablePath(binRootPath, &max);
return std::string(binRootPath); return std::string(binRootPath);
#else #else
char binRootPath[PATH_MAX];
ssize_t size = readlink("/proc/self/exe", binRootPath, PATH_MAX); ssize_t size = readlink("/proc/self/exe", binRootPath, PATH_MAX);
if (size != -1) { if (size != -1) {
return std::string(binRootPath, size); return std::string(binRootPath, size);
} }
#endif
return ""; return "";
#endif
} }
bool writeTextFile(const std::string& path, const std::string& content) bool writeTextFile(const std::string& path, const std::string& content)
@@ -311,10 +350,16 @@ bool writeTextFile(const std::string& path, const std::string& content)
std::string getCurrentDirectory() std::string getCurrentDirectory()
{ {
char* a_cwd = getcwd(NULL, 0); #ifdef _WIN32
std::string s_cwd(a_cwd); wchar_t* a_cwd = _wgetcwd(NULL, 0);
std::string ret = WideToUtf8(a_cwd);
free(a_cwd); free(a_cwd);
return s_cwd; #else
char* a_cwd = getcwd(NULL, 0);
std::string ret(a_cwd);
free(a_cwd);
#endif
return ret;
} }
std::string getDataDirectory() std::string getDataDirectory()
@@ -325,8 +370,9 @@ std::string getDataDirectory()
char* cDataDir = ::getenv("KIWIX_DATA_DIR"); char* cDataDir = ::getenv("KIWIX_DATA_DIR");
#endif #endif
std::string dataDir = cDataDir==nullptr ? "" : cDataDir; std::string dataDir = cDataDir==nullptr ? "" : cDataDir;
if (!dataDir.empty()) if (!dataDir.empty()) {
return dataDir; return dataDir;
}
#ifdef _WIN32 #ifdef _WIN32
cDataDir = ::getenv("USERPROFILE"); cDataDir = ::getenv("USERPROFILE");
dataDir = cDataDir==nullptr ? getCurrentDirectory() : cDataDir; dataDir = cDataDir==nullptr ? getCurrentDirectory() : cDataDir;
@@ -340,7 +386,8 @@ std::string getDataDirectory()
dataDir = appendToDirectory(dataDir, "share"); dataDir = appendToDirectory(dataDir, "share");
} }
#endif #endif
return appendToDirectory(dataDir, "kiwix"); auto ret = appendToDirectory(dataDir, "kiwix");
return ret;
} }
static std::map<std::string, std::string> extMimeTypes = { static std::map<std::string, std::string> extMimeTypes = {
+26 -20
View File
@@ -267,36 +267,42 @@ std::string kiwix::urlDecode(const std::string& value, bool component)
/* Split string in a token array */ /* Split string in a token array */
std::vector<std::string> kiwix::split(const std::string& str, std::vector<std::string> kiwix::split(const std::string& str,
const std::string& delims = " *-") const std::string& delims,
bool trimEmpty)
{ {
std::string::size_type lastPos = str.find_first_not_of(delims, 0); std::string::size_type lastPos = 0;
std::string::size_type pos = str.find_first_of(delims, lastPos); std::string::size_type pos = 0;
std::vector<std::string> tokens; std::vector<std::string> tokens;
while( (pos = str.find_first_of(delims, lastPos)) < str.length() )
while (std::string::npos != pos || std::string::npos != lastPos) { {
tokens.push_back(str.substr(lastPos, pos - lastPos)); auto token = str.substr(lastPos, pos - lastPos);
lastPos = str.find_first_not_of(delims, pos); if (!trimEmpty || !token.empty()) {
pos = str.find_first_of(delims, lastPos); tokens.push_back(token);
}
lastPos = pos + 1;
} }
auto token = str.substr(lastPos);
if (!trimEmpty || !token.empty()) {
tokens.push_back(token);
}
return tokens; return tokens;
} }
std::vector<std::string> kiwix::split(const char* lhs, const char* rhs) std::string kiwix::join(const std::vector<std::string>& list, const std::string& sep)
{ {
const std::string m1(lhs), m2(rhs); std::stringstream ss;
return split(m1, m2); bool first = true;
for (auto& s:list) {
if (!first) {
ss << sep;
}
first = false;
ss << s;
}
return ss.str();
} }
std::vector<std::string> kiwix::split(const char* lhs, const std::string& rhs)
{
return split(lhs, rhs.c_str());
}
std::vector<std::string> kiwix::split(const std::string& lhs, const char* rhs)
{
return split(lhs.c_str(), rhs);
}
std::string kiwix::ucFirst(const std::string& word) std::string kiwix::ucFirst(const std::string& word)
{ {
+1 -1
View File
@@ -124,7 +124,7 @@ label[for=kiwixsearchbox] {
} }
body { body {
padding-top: 40px !important; padding-top: 3em !important;
} }
/* Try to fix buggy stuff in jquery-ui autocomplete */ /* Try to fix buggy stuff in jquery-ui autocomplete */
+30 -28
View File
@@ -95,7 +95,7 @@
</head> </head>
<body bgcolor="white"> <body bgcolor="white">
<div class="header"> <div class="header">
{{#hasResult}} {{#hasResults}}
Results Results
<b> <b>
{{resultStart}}-{{resultEnd}} {{resultStart}}-{{resultEnd}}
@@ -104,10 +104,10 @@
</b> for <b> </b> for <b>
{{searchPattern}} {{searchPattern}}
</b> </b>
{{/hasResult}} {{/hasResults}}
{{^hasResult}} {{^hasResults}}
No results were found for <b>{{searchPattern}}</b> No results were found for <b>{{searchPattern}}</b>
{{/hasResult}} {{/hasResults}}
</div> </div>
<div class="results"> <div class="results">
@@ -129,30 +129,32 @@
</div> </div>
<div class="footer"> <div class="footer">
<ul> {{#hasPages}}
{{#resultLastPageStart}} <ul>
<li> {{#resultLastPageStart}}
<a href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start=0&end={{resultRange}}"> <li>
<a href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start=0&end={{resultRange}}">
</a>
</li> </a>
{{/resultLastPageStart}} </li>
{{#pages}} {{/resultLastPageStart}}
<li> {{#pages}}
<a {{#selected}}class="selected"{{/selected}} <li>
href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start={{start}}&end={{end}}"> <a {{#selected}}class="selected"{{/selected}}
{{label}} href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start={{start}}&end={{end}}">
</a> {{label}}
</li> </a>
{{/pages}} </li>
{{#resultLastPageStart}} {{/pages}}
<li> {{#resultLastPageStart}}
<a href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start={{resultLastPageStart}}&end={{lastResult}}"> <li>
<a href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start={{resultLastPageStart}}&end={{lastResult}}">
</a>
</li> </a>
{{/resultLastPageStart}} </li>
</ul> {{/resultLastPageStart}}
</ul>
{{/hasPages}}
</div> </div>
</body> </body>
</html> </html>
+4 -1
View File
@@ -3,7 +3,10 @@
tests = [ tests = [
'parseUrl', 'parseUrl',
'library', 'library',
'regex' 'regex',
'tagParsing',
'stringTools',
'pathTools'
] ]
+234
View File
@@ -0,0 +1,234 @@
/*
* Copyright (C) 2019 Matthieu Gautier
*
* 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
* is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
* NON-INFRINGEMENT. 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 "gtest/gtest.h"
#include <string>
#include <vector>
#include "../include/tools/pathTools.h"
#ifdef _WIN32
# define S "\\"
# define AS "c:"
#else
# define S "/"
# define AS ""
#endif
#define P2(a, b) a S b
#define P3(a, b, c) P2(P2(a, b), c)
#define P4(a, b, c, d) P2(P3(a, b, c), d)
#define P5(a, b, c, d, e) P2(P4(a, b, c, d), e)
#define P6(a, b, c, d, e, f) P2(P5(a, b, c ,d, e), f)
#define A1(a) P2(AS,a)
#define A2(a, b) A1(P2(a, b))
#define A3(a, b, c) A1(P3(a, b, c))
#define A4(a, b, c, d) A1(P4(a, b, c, d))
#define A5(a, b, c, d, e) A1(P5(a, b, c, d, e))
std::vector<std::string> normalizeParts(std::vector<std::string> parts, bool absolute);
#ifdef _WIN32
std::wstring Utf8ToWide(const std::string& str);
std::string WideToUtf8(const std::wstring& wstr);
#endif
namespace
{
#define V std::vector<std::string>
TEST(pathTools, normalizePartsAbsolute)
{
#define N(...) normalizeParts(__VA_ARGS__, true)
ASSERT_EQ(N({}), V({}));
#ifdef _WIN32
ASSERT_EQ(N({"c:"}), V({"c:"}));
#else
ASSERT_EQ(N({""}), V({"", ""}));
#endif
ASSERT_EQ(N({AS, "a"}), V({AS, "a"}));
ASSERT_EQ(N({AS, "a", "b"}), V({AS, "a", "b"}));
ASSERT_EQ(N({AS, "a", "b", ".."}), V({AS, "a"}));
#ifdef _WIN32
ASSERT_EQ(N({AS, "a", "b", "..", ".."}), V({AS}));
#else
ASSERT_EQ(N({AS, "a", "b", "..", ".."}), V({AS, ""}));
#endif
ASSERT_EQ(N({AS, "a", "b", "..", "..", "..", "foo"}), V({AS, "foo"}));
ASSERT_EQ(N({AS, "..", "..", "c", "d", ".", "..", "foo"}), V({AS, "c", "foo"}));
ASSERT_EQ(N({AS, "a", "b", ".", "c", "d", "..", "foo"}), V({AS, "a", "b", "c", "foo"}));
#ifdef _WIN32
ASSERT_EQ(N({"c:", "a", "b", ".", "c", "d:", "..", "foo"}), V({"d:", "foo"}));
#endif
#undef N
}
TEST(pathTools, normalizePartsRelative)
{
#define N(...) normalizeParts(__VA_ARGS__, false)
ASSERT_EQ(N({}), V({}));
ASSERT_EQ(N({""}), V({}));
ASSERT_EQ(N({"a"}), V({"a"}));
ASSERT_EQ(N({"a", "b"}), V({"a", "b"}));
ASSERT_EQ(N({"a", "b", ".."}), V({"a"}));
ASSERT_EQ(N({"a", "b", "..", ".."}), V({}));
ASSERT_EQ(N({"a", "b", "..", "..", "..", "foo"}), V({"..", "foo"}));
ASSERT_EQ(N({"..", "..", "c", "d", ".", "..", "foo"}), V({"..", "..", "c", "foo"}));
ASSERT_EQ(N({"a", "b", ".", "c", "d", "..", "foo"}), V({"a", "b", "c", "foo"}));
#undef N
}
TEST(pathTools, isRelativePath)
{
ASSERT_TRUE(isRelativePath("foo"));
ASSERT_TRUE(isRelativePath(P2("foo","bar")));
ASSERT_TRUE(isRelativePath(P3(".","foo","bar")));
ASSERT_TRUE(isRelativePath(P2("..","foo")));
ASSERT_TRUE(isRelativePath(P4("foo","","bar","")));
ASSERT_FALSE(isRelativePath(A1("foo")));
ASSERT_FALSE(isRelativePath(A2("foo", "bar")));
}
TEST(pathTools, computeAbsolutePath)
{
ASSERT_EQ(computeAbsolutePath(A2("a","b"), "foo"),
A3("a","b","foo"));
ASSERT_EQ(computeAbsolutePath(A3("a","b",""), "foo"),
A3("a","b","foo"));
ASSERT_EQ(computeAbsolutePath(A2("a","b"), P2(".","foo")),
A3("a","b","foo"));
ASSERT_EQ(computeAbsolutePath(A2("a","b"), P2("..","foo")),
A2("a","foo"));
ASSERT_EQ(computeAbsolutePath(A3("a","b",""), P2("..","foo")),
A2("a","foo"));
ASSERT_EQ(computeAbsolutePath(A5("a","b","c","d","e"), P2("..","foo")),
A5("a","b","c","d","foo"));
ASSERT_EQ(computeAbsolutePath(A5("a","b","c","d","e"), P5("..","..","..","g","foo")),
A4("a","b","g","foo"));
}
TEST(pathTools, computeRelativePath)
{
ASSERT_EQ(computeRelativePath(A2("a","b"), A3("a","b","foo")),
"foo");
ASSERT_EQ(computeRelativePath(A3("a","b",""), A3("a","b","foo")),
"foo");
ASSERT_EQ(computeRelativePath(A2("a","b"), A2("a","foo")),
P2("..","foo"));
ASSERT_EQ(computeRelativePath(A3("a","b",""), A2("a","foo")),
P2("..","foo"));
ASSERT_EQ(computeRelativePath(A5("a","b","c","d","e"), A5("a","b","c","d","foo")),
P2("..","foo"));
ASSERT_EQ(computeRelativePath(A5("a","b","c","d","e"), A4("a","b","g","foo")),
P5("..","..","..","g","foo"));
}
TEST(pathTools, removeLastPathElement)
{
ASSERT_EQ(removeLastPathElement(P3("a","b","c")),
P2("a","b"));
ASSERT_EQ(removeLastPathElement(A3("a","b","c")),
A2("a","b"));
ASSERT_EQ(removeLastPathElement(P4("a","b","c","")),
P2("a","b"));
ASSERT_EQ(removeLastPathElement(A4("a","b","c","")),
A2("a","b"));
}
TEST(pathTools, appendToDirectory)
{
ASSERT_EQ(appendToDirectory(P3("a","b","c"), "foo.xml"),
P4("a","b","c","foo.xml"));
ASSERT_EQ(appendToDirectory(P4("a","b","c",""), "foo.xml"),
P4("a","b","c","foo.xml"));
ASSERT_EQ(appendToDirectory(P3("a","b","c"), P2("d","foo.xml")),
P5("a","b","c","d","foo.xml"));
ASSERT_EQ(appendToDirectory(P4("a","b","c",""), P2("d","foo.xml")),
P5("a","b","c","d","foo.xml"));
ASSERT_EQ(appendToDirectory(P3("a","b","c"), P2(".","foo.xml")),
P5("a","b","c",".","foo.xml"));
ASSERT_EQ(appendToDirectory(P4("a","b","c",""), P2(".","foo.xml")),
P5("a","b","c",".","foo.xml"));
}
TEST(pathTools, goUp)
{
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), ".."),
A2("a", "b"));
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P2("..","..")),
A1("a"));
#ifdef _WIN32
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P3("..","..","..")),
"c:");
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P4("..","..","..","..")),
"c:");
#else
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P3("..","..","..")),
"/");
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P4("..","..","..","..")),
"/");
#endif
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P2("..", "foo")),
A3("a", "b","foo"));
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P3("..","..","foo")),
A2("a","foo"));
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P4("..","..","..","foo")),
A1("foo"));
ASSERT_EQ(computeAbsolutePath(A3("a","b","c"), P5("..","..","..","..","foo")),
A1("foo"));
}
#ifdef _WIN32
TEST(pathTools, dirChange)
{
std::string p1("c:\\a\\b\\c");
std::string p2("d:\\d\\e\\foo.xml");
std::string relative_path = computeRelativePath(p1, p2);
ASSERT_EQ(relative_path, "d:\\d\\e\\foo.xml");
std::string abs_path = computeAbsolutePath(p1, relative_path);
ASSERT_EQ(abs_path, p2);
ASSERT_EQ(computeAbsolutePath(p1, "..\\..\\..\\..\\..\\d:\\d\\e\\foo.xml"), p2);
}
TEST(pathTools, Utf8ToWide)
{
ASSERT_EQ(Utf8ToWide(u8""), L"");
ASSERT_EQ(Utf8ToWide(u8"test"), L"test");
ASSERT_EQ(Utf8ToWide(u8"testé`œà"), L"testé`œà");
}
TEST(pathTools, WideToUtf8)
{
ASSERT_EQ(WideToUtf8(L""), u8"");
ASSERT_EQ(WideToUtf8(L"test"), u8"test");
ASSERT_EQ(WideToUtf8(L"testé`œà"), u8"testé`œà");
}
#endif
};
int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
+61
View File
@@ -0,0 +1,61 @@
/*
* Copyright (C) 2019 Matthieu Gautier
*
* 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
* is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
* NON-INFRINGEMENT. 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 "gtest/gtest.h"
#include <string>
#include <vector>
namespace kiwix {
std::string join(const std::vector<std::string>& list, const std::string& sep);
std::vector<std::string> split(const std::string& base, const std::string& sep, bool trimEmpty);
};
using namespace kiwix;
#define parse_tag getTagValueFromTagList
namespace
{
TEST(stringTools, join)
{
std::vector<std::string> list = { "a", "b", "c" };
ASSERT_EQ(join(list, ";"), "a;b;c");
}
TEST(stringTools, split)
{
std::vector<std::string> list1 = { "a", "b", "c" };
ASSERT_EQ(split("a;b;c", ";", false), list1);
ASSERT_EQ(split("a;b;c", ";", true), list1);
std::vector<std::string> list2 = { "", "a", "b", "c" };
ASSERT_EQ(split(";a;b;c", ";", false), list2);
ASSERT_EQ(split(";a;b;c", ";", true), list1);
std::vector<std::string> list3 = { "", "a", "b", "c", ""};
ASSERT_EQ(split(";a;b;c;", ";", false), list3);
ASSERT_EQ(split(";a;b;c;", ";", true), list1);
std::vector<std::string> list4 = { "", "a", "b", "", "c", ""};
ASSERT_EQ(split(";a;b;;c;", ";", false), list4);
ASSERT_EQ(split(";a;b;;c;", ";", true), list1);
}
};
int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
+102
View File
@@ -0,0 +1,102 @@
/*
* Copyright (C) 2019 Matthieu Gautier
*
* 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
* is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
* NON-INFRINGEMENT. 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 "gtest/gtest.h"
#include <string>
#include <vector>
namespace kiwix {
std::vector<std::string> convertTags(const std::string& tags);
std::string getTagValueFromTagList(const std::vector<std::string>& tagList, const std::string& tagName);
};
using namespace kiwix;
#define parse_tag getTagValueFromTagList
namespace
{
TEST(ParseTagTest, convert)
{
{
std::string tagStr = "";
std::vector<std::string> tagList = {"_ftindex:no", "_pictures:yes", "_videos:yes", "_details:yes"};
ASSERT_EQ(convertTags(tagStr), tagList);
}
{
std::string tagStr = "_category:foo;bar";
std::vector<std::string> tagList = {"_category:foo", "bar", "_ftindex:no", "_pictures:yes", "_videos:yes", "_details:yes"};
ASSERT_EQ(convertTags(tagStr), tagList);
}
{
std::string tagStr = "_ftindex:no;_pictures:yes;_videos:yes;_details:yes;_category:foo;bar";
std::vector<std::string> tagList = {"_ftindex:no", "_pictures:yes", "_videos:yes", "_details:yes", "_category:foo", "bar"};
ASSERT_EQ(convertTags(tagStr), tagList);
}
{
std::string tagStr = "_ftindex:yes;_pictures:no;_videos:no;_details:no;_category:foo;bar";
std::vector<std::string> tagList = {"_ftindex:yes", "_pictures:no", "_videos:no", "_details:no", "_category:foo", "bar"};
ASSERT_EQ(convertTags(tagStr), tagList);
}
{
std::string tagStr = "_ftindex;nopic;novid;nodet;foo;bar";
std::vector<std::string> tagList = {"_ftindex:yes", "_pictures:no", "_videos:no", "_details:no", "foo", "bar"};
ASSERT_EQ(convertTags(tagStr), tagList);
}
}
TEST(ParseTagTest, valid)
{
std::string tagStr = "_ftindex:yes;_pictures:no;_videos:no;_details:yes;_category:foo;bar";
auto tagList = convertTags(tagStr);
ASSERT_EQ(parse_tag(tagList, "ftindex"), "yes");
ASSERT_EQ(parse_tag(tagList, "pictures"), "no");
ASSERT_EQ(parse_tag(tagList, "category"), "foo");
ASSERT_EQ(parse_tag(tagList, "details"), "yes");
ASSERT_THROW(parse_tag(tagList, "detail"), std::out_of_range);
}
TEST(ParseTagTest, compat)
{
std::string tagStr = "_ftindex;nopic;foo;bar";
auto tagList = convertTags(tagStr);
ASSERT_EQ(parse_tag(tagList, "ftindex"), "yes");
ASSERT_EQ(parse_tag(tagList, "pictures"), "no");
ASSERT_EQ(parse_tag(tagList, "videos"), "yes");
ASSERT_EQ(parse_tag(tagList, "details"), "yes");
}
TEST(ParseTagTest, invalid)
{
std::string tagStr = "_ftindex:y;_pictures;_videos:;_details:yes;_details:no;_category:foo;bar";
auto tagList = convertTags(tagStr);
ASSERT_EQ(parse_tag(tagList, "ftindex"), "y");
ASSERT_EQ(parse_tag(tagList, "pictures"), "yes");
ASSERT_EQ(parse_tag(tagList, "videos"), "");
ASSERT_EQ(parse_tag(tagList, "details"), "yes");
}
};
int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
+9 -1
View File
@@ -38,6 +38,10 @@ else
export PKG_CONFIG_PATH=${INSTALL_DIR}/lib/x86_64-linux-gnu/pkgconfig export PKG_CONFIG_PATH=${INSTALL_DIR}/lib/x86_64-linux-gnu/pkgconfig
fi fi
export CPPFLAGS="-I${INSTALL_DIR}/include" export CPPFLAGS="-I${INSTALL_DIR}/include"
if [[ "$TEST" == "1" ]]
then
MESON_OPTION="${MESON_OPTION} -Db_coverage=true"
fi
meson . build ${MESON_OPTION} meson . build ${MESON_OPTION}
cd build cd build
ninja ninja
@@ -45,5 +49,9 @@ if [[ "$TEST" == "1" ]]
then then
echo "Running test" echo "Running test"
export LD_LIBRARY_PATH=${INSTALL_DIR}/lib:${INSTALL_DIR}/lib64:${INSTALL_DIR}/lib/x86_64-linux-gnu export LD_LIBRARY_PATH=${INSTALL_DIR}/lib:${INSTALL_DIR}/lib64:${INSTALL_DIR}/lib/x86_64-linux-gnu
ninja test meson test --verbose
ninja coverage
cd ..
echo "*** Publish code coverage"
bash <(curl -s https://codecov.io/bash)
fi fi