mirror of
https://github.com/kiwix/libkiwix.git
synced 2026-09-08 11:38:54 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2aebffb27c | ||
|
|
da247b3242 | ||
|
|
f85ec9ea6f | ||
|
|
b4fac9d0df | ||
|
|
4f2ede80e5 | ||
|
|
c2ecb9d126 | ||
|
|
5883dba0ef | ||
|
|
7ad6aedd66 | ||
|
|
67170709bb | ||
|
|
dfd16155af | ||
|
|
0db06d98a8 | ||
|
|
742156d366 | ||
|
|
598dd3c175 | ||
|
|
49c0c5ff47 | ||
|
|
65ebc7fe7f | ||
|
|
2f4636e2df | ||
|
|
f4e9148b1d | ||
|
|
891666b8c4 | ||
|
|
57a2b98e7a | ||
|
|
9b4419f3fc | ||
|
|
15d5b4ed58 | ||
|
|
2f91149da3 | ||
|
|
6ee174b546 | ||
|
|
2a6772b76d | ||
|
|
660d5d7fb7 | ||
|
|
157c1c939c | ||
|
|
bd91e89785 | ||
|
|
1245d4e467 | ||
|
|
420be55bfa | ||
|
|
651cb9165c | ||
|
|
49046248fd | ||
|
|
e42e061d45 | ||
|
|
d90774450d | ||
|
|
9e36c876f5 | ||
|
|
1a4c434e3c | ||
|
|
3294508d87 | ||
|
|
351e573bce | ||
|
|
a32363e6a2 | ||
|
|
56f8b7a876 | ||
|
|
87dc145dc7 | ||
|
|
a13244dc0e | ||
|
|
78dbd66522 | ||
|
|
fdc291b7c2 | ||
|
|
d372cea146 | ||
|
|
e1fcd12e48 |
No files matched your search
@@ -0,0 +1,12 @@
|
|||||||
|
codecov:
|
||||||
|
notify:
|
||||||
|
require_ci_to_pass: yes
|
||||||
|
|
||||||
|
coverage:
|
||||||
|
status:
|
||||||
|
project:
|
||||||
|
default:
|
||||||
|
threshold: 1%
|
||||||
|
|
||||||
|
ignore:
|
||||||
|
- "test"
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
.idea/
|
.idea/
|
||||||
*.swp
|
*.swp
|
||||||
|
subprojects/googletest-release*
|
||||||
@@ -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"
|
||||||
|
|||||||
@@ -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,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, ...).
|
||||||
|
|
||||||
[](https://bintray.com/kiwix/kiwix/kiwixlib/_latestVersion)
|
[](https://bintray.com/kiwix/kiwix/kiwixlib/_latestVersion)
|
||||||
|
[](https://aur.archlinux.org/packages/kiwix-lib/)
|
||||||
[](https://travis-ci.org/kiwix/kiwix-lib)
|
[](https://travis-ci.org/kiwix/kiwix-lib)
|
||||||
[](https://www.codefactor.io/repository/github/kiwix/kiwix-lib)
|
[](https://www.codefactor.io/repository/github/kiwix/kiwix-lib)
|
||||||
|
[](https://codecov.io/gh/kiwix/kiwix-lib)
|
||||||
[](https://www.gnu.org/licenses/gpl-3.0)
|
[](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'
|
||||||
|
|||||||
@@ -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
@@ -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.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
@@ -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'])
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
@@ -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
@@ -3,7 +3,10 @@
|
|||||||
tests = [
|
tests = [
|
||||||
'parseUrl',
|
'parseUrl',
|
||||||
'library',
|
'library',
|
||||||
'regex'
|
'regex',
|
||||||
|
'tagParsing',
|
||||||
|
'stringTools',
|
||||||
|
'pathTools'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -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
@@ -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
|
||||||
Reference in new issue
Block a user