mirror of
https://github.com/kiwix/libkiwix.git
synced 2025-12-30 18:08:01 -05:00
Compare commits
90 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8095a87bf1 | ||
|
|
bb55527508 | ||
|
|
b7c5e5f339 | ||
|
|
316deeb485 | ||
|
|
721d981825 | ||
|
|
2244074f3c | ||
|
|
0a1e01eb2b | ||
|
|
abd2fa3bf3 | ||
|
|
ea3349f37c | ||
|
|
e3c6ca0d1b | ||
|
|
52e165cf78 | ||
|
|
3b7c805183 | ||
|
|
9c4867a95a | ||
|
|
223f7ee78a | ||
|
|
20690bd5f5 | ||
|
|
de7b7c34b5 | ||
|
|
f0ac66aea1 | ||
|
|
20a2c78733 | ||
|
|
9850be7267 | ||
|
|
0dd996c6a3 | ||
|
|
2500cc8e63 | ||
|
|
bd6797143c | ||
|
|
c9a15c9961 | ||
|
|
f1d55f8e86 | ||
|
|
a2c2955f41 | ||
|
|
9975e0b369 | ||
|
|
efe1c2dea3 | ||
|
|
2af9ba4eab | ||
|
|
c007373b46 | ||
|
|
e1acf9acff | ||
|
|
daaadf3e1c | ||
|
|
74bd482335 | ||
|
|
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 | ||
|
|
828bd032c4 | ||
|
|
26d32a36ad | ||
|
|
c031547461 | ||
|
|
d0833bdcd4 | ||
|
|
1bb5e278ed | ||
|
|
0a331f8ba9 | ||
|
|
b1a4bbd345 | ||
|
|
14dbe843b9 | ||
|
|
e111316636 | ||
|
|
bbb346b685 | ||
|
|
56a08f49b2 | ||
|
|
fc2ad81185 | ||
|
|
af78aa5fd0 |
12
.codecov.yml
Normal file
12
.codecov.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
codecov:
|
||||
notify:
|
||||
require_ci_to_pass: yes
|
||||
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
threshold: 1%
|
||||
|
||||
ignore:
|
||||
- "test"
|
||||
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: https://kiwix.org/support-us/
|
||||
27
.github/move.yml
vendored
Normal file
27
.github/move.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# Configuration for Move Issues - https://github.com/dessant/move-issues
|
||||
|
||||
# Delete the command comment when it contains no other content
|
||||
deleteCommand: true
|
||||
|
||||
# Close the source issue after moving
|
||||
closeSourceIssue: true
|
||||
|
||||
# Lock the source issue after moving
|
||||
lockSourceIssue: false
|
||||
|
||||
# Mention issue and comment authors
|
||||
mentionAuthors: true
|
||||
|
||||
# Preserve mentions in the issue content
|
||||
keepContentMentions: true
|
||||
|
||||
# Move labels that also exist on the target repository
|
||||
moveLabels: true
|
||||
|
||||
# Set custom aliases for targets
|
||||
# aliases:
|
||||
# r: repo
|
||||
# or: owner/repo
|
||||
|
||||
# Repository to extend settings from
|
||||
# _extends: repo
|
||||
15
.github/stale.yml
vendored
Normal file
15
.github/stale.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
daysUntilClose: false
|
||||
staleLabel: stale
|
||||
|
||||
issues:
|
||||
daysUntilStale: 60
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be now be reviewed manually. Thank you
|
||||
for your contributions.
|
||||
pulls:
|
||||
daysUntilStale: 7
|
||||
markComment: >
|
||||
This pull request has been automatically marked as stale because it has not had
|
||||
recent activity. It will be now be reviewed manually. Thank you
|
||||
for your contributions.
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
.idea/
|
||||
*.swp
|
||||
subprojects/googletest-release*
|
||||
|
||||
@@ -31,6 +31,11 @@ addons:
|
||||
- gcc-mingw-w64-i686
|
||||
- gcc-mingw-w64-base
|
||||
- mingw-w64-tools
|
||||
- gcovr
|
||||
homebrew:
|
||||
update: true
|
||||
packages:
|
||||
- gcovr
|
||||
matrix:
|
||||
include:
|
||||
- env: PLATFORM="native_dyn"
|
||||
|
||||
4
COPYING
4
COPYING
@@ -77,7 +77,7 @@ modification follow.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
@@ -510,7 +510,7 @@ actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
|
||||
59
ChangeLog
59
ChangeLog
@@ -1,9 +1,66 @@
|
||||
kiwix-lib 8.2.2
|
||||
===============
|
||||
|
||||
* Improve a few compilation scripts
|
||||
|
||||
kiwix-lib 8.2.1
|
||||
===============
|
||||
|
||||
* Reintroduce kiwix-serve taskbar
|
||||
|
||||
kiwix-lib 8.2.0
|
||||
===============
|
||||
|
||||
* More debug information if aria2c command fails
|
||||
* Allow to set kiwix-serve port
|
||||
* Better (dead) bookmarks mgmt
|
||||
|
||||
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
|
||||
===============
|
||||
|
||||
* force one column suggestion in kiwix-serve suggestions
|
||||
* fix fulltext search link in suggestions
|
||||
* UI fixes in kiwix-serve rendering
|
||||
|
||||
kiwix-lib 6.0.2
|
||||
===============
|
||||
|
||||
* Correctly set the groupId in the pom file.
|
||||
|
||||
|
||||
kiwix-lib 6.0.1
|
||||
===============
|
||||
|
||||
|
||||
27
README.md
27
README.md
@@ -1,12 +1,15 @@
|
||||
Kiwix library
|
||||
=============
|
||||
|
||||
The Kiwix library provides the Kiwix software core. It contains the
|
||||
code shared by all Kiwix ports (Windows, Linux, OSX, Android, ...).
|
||||
The Kiwix library provides the [Kiwix](https://kiwix.org) software
|
||||
suite core. It contains the code shared by all Kiwix ports (Windows,
|
||||
GNU/Linux, macOS, Android, iOS, ...).
|
||||
|
||||
[](https://bintray.com/kiwix/kiwix/kiwixlib/_latestVersion)
|
||||
[](https://travis-ci.org/kiwix/kiwix-lib)
|
||||
[](https://aur.archlinux.org/packages/kiwix-lib/)
|
||||
[](https://travis-ci.com/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)
|
||||
|
||||
Disclaimer
|
||||
@@ -57,9 +60,9 @@ The Kiwix library builds using [Meson](https://mesonbuild.com/) version
|
||||
compilation tools.
|
||||
|
||||
Install first the few common compilation tools:
|
||||
* Meson
|
||||
* Ninja
|
||||
* Pkg-config
|
||||
* [Meson](https://mesonbuild.com/)
|
||||
* [Ninja](https://ninja-build.org/)
|
||||
* [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/)
|
||||
|
||||
These tools should be packaged if you use a cutting edge operating
|
||||
system. If not, have a look to the [Troubleshooting](#Troubleshooting)
|
||||
@@ -70,7 +73,6 @@ Compilation
|
||||
|
||||
Once all dependencies are installed, you can compile the Kiwix library
|
||||
with:
|
||||
|
||||
```bash
|
||||
meson . build
|
||||
ninja -C build
|
||||
@@ -88,32 +90,29 @@ Installation
|
||||
|
||||
If you want to install the Kiwix library and the headers you just have
|
||||
compiled on your system, here we go:
|
||||
|
||||
```bash
|
||||
ninja -C build install
|
||||
```
|
||||
|
||||
You might need to run the command as root (or using `sudo`), depending
|
||||
where you want to install the libraries. After the installation
|
||||
succeeded, you may need to run `ldconfig` (as root).
|
||||
succeeded, you may need to run `ldconfig` (as `root`).
|
||||
|
||||
Uninstallation
|
||||
------------
|
||||
|
||||
If you want to uninstall the Kiwix library:
|
||||
|
||||
```bash
|
||||
ninja -C build uninstall
|
||||
```
|
||||
|
||||
Like for the installation, you might need to run the command as root
|
||||
Like for the installation, you might need to run the command as `root`
|
||||
(or using `sudo`).
|
||||
|
||||
Troubleshooting
|
||||
---------------
|
||||
|
||||
If you need to install Meson "manually":
|
||||
|
||||
```bash
|
||||
virtualenv -p python3 ./ # Create virtualenv
|
||||
source bin/activate # Activate the virtualenv
|
||||
@@ -122,7 +121,6 @@ hash -r # Refresh bash paths
|
||||
```
|
||||
|
||||
If you need to install Ninja "manually":
|
||||
|
||||
```bash
|
||||
git clone git://github.com/ninja-build/ninja.git
|
||||
cd ninja
|
||||
@@ -142,4 +140,5 @@ repository.
|
||||
License
|
||||
-------
|
||||
|
||||
GPLv3 or later, see COPYING for more details.
|
||||
[GPLv3](https://www.gnu.org/licenses/gpl-3.0) or later, see
|
||||
[COPYING](COPYING) for more details.
|
||||
|
||||
0
android-kiwix-lib-publisher/gradlew
vendored
Normal file → Executable file
0
android-kiwix-lib-publisher/gradlew
vendored
Normal file → Executable file
0
android-kiwix-lib-publisher/gradlew.bat
vendored
Normal file → Executable file
0
android-kiwix-lib-publisher/gradlew.bat
vendored
Normal file → Executable file
@@ -26,7 +26,7 @@ task writePom {
|
||||
project {
|
||||
groupId 'org.kiwix.kiwixlib'
|
||||
artifactId 'kiwixlib'
|
||||
version '6.0.2'
|
||||
version '8.2.2' + (System.env.KIWIXLIB_BUILDVERSION == null ? '' : '-'+System.env.KIWIXLIB_BUILDVERSION)
|
||||
packaging 'aar'
|
||||
name 'kiwixlib'
|
||||
url 'https://github.com/kiwix/kiwix-lib'
|
||||
|
||||
@@ -97,6 +97,7 @@ class Downloader
|
||||
|
||||
size_t getNbDownload() { return m_knownDownloads.size(); }
|
||||
std::vector<std::string> getDownloadIds();
|
||||
const std::string &getAria2LaunchCmd();
|
||||
|
||||
private:
|
||||
std::map<std::string, std::unique_ptr<Download>> m_knownDownloads;
|
||||
|
||||
@@ -64,14 +64,14 @@ class Entry
|
||||
* @return the path of the entry.
|
||||
*/
|
||||
std::string getPath() const;
|
||||
|
||||
|
||||
/**
|
||||
* Get the title of the entry.
|
||||
*
|
||||
* @return the title of the entry.
|
||||
*/
|
||||
std::string getTitle() const;
|
||||
|
||||
|
||||
/**
|
||||
* Get the content of the entry.
|
||||
*
|
||||
@@ -81,7 +81,7 @@ class Entry
|
||||
* @return the content of the entry.
|
||||
*/
|
||||
std::string getContent() const;
|
||||
|
||||
|
||||
/**
|
||||
* Get the blob of the entry.
|
||||
*
|
||||
@@ -91,7 +91,7 @@ class Entry
|
||||
* @return the blob of the entry.
|
||||
*/
|
||||
zim::Blob getBlob(offset_type offset = 0) const;
|
||||
|
||||
|
||||
/**
|
||||
* Get the blob of the entry.
|
||||
*
|
||||
@@ -102,7 +102,7 @@ class Entry
|
||||
* @return the blob of the entry.
|
||||
*/
|
||||
zim::Blob getBlob(offset_type offset, size_type size) const;
|
||||
|
||||
|
||||
/**
|
||||
* Get the info for direct access to the content of the entry.
|
||||
*
|
||||
@@ -118,7 +118,7 @@ class Entry
|
||||
* Return <"",0> if is not possible to read directly.
|
||||
*/
|
||||
std::pair<std::string, offset_type> getDirectAccessInfo() const;
|
||||
|
||||
|
||||
/**
|
||||
* Get the size of the entry.
|
||||
*
|
||||
@@ -132,8 +132,8 @@ class Entry
|
||||
* @return the mime_type of the entry.
|
||||
*/
|
||||
std::string getMimetype() const;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get if the entry is a redirect entry.
|
||||
*
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define KIWIXLIB_KIWIX_SERVE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class Subprocess;
|
||||
namespace kiwix {
|
||||
@@ -9,17 +10,19 @@ namespace kiwix {
|
||||
class KiwixServe
|
||||
{
|
||||
public:
|
||||
KiwixServe(int port = 8181);
|
||||
KiwixServe(const std::string& libraryPath, int port = 8181);
|
||||
~KiwixServe();
|
||||
|
||||
void run();
|
||||
void shutDown();
|
||||
bool isRunning();
|
||||
int getPort() { return m_port; }
|
||||
int setPort(int port);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Subprocess> mp_kiwixServe;
|
||||
int m_port;
|
||||
std::string m_libraryPath;
|
||||
};
|
||||
|
||||
}; //end namespace kiwix
|
||||
|
||||
@@ -208,7 +208,7 @@ class Library
|
||||
*
|
||||
* @return A list of bookmarks
|
||||
*/
|
||||
const std::vector<kiwix::Bookmark>& getBookmarks() { return m_bookmarks; }
|
||||
const std::vector<kiwix::Bookmark> getBookmarks(bool onlyValidBookmarks = true);
|
||||
|
||||
/**
|
||||
* Get all book ids of the books in the library.
|
||||
|
||||
135
include/reader.h
135
include/reader.h
@@ -158,31 +158,7 @@ class Reader
|
||||
* @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.
|
||||
*/
|
||||
bool getMetatag(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;
|
||||
bool getMetadata(const string& name, string& value) const;
|
||||
|
||||
/**
|
||||
* Get the name of the zim file.
|
||||
@@ -192,18 +168,13 @@ class Reader
|
||||
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;
|
||||
|
||||
/**
|
||||
* Get the date of the zim file.
|
||||
*
|
||||
* @return The date of the zim file as specified in the zim metadata.
|
||||
*/
|
||||
string getDate() const;
|
||||
string getTitle() const;
|
||||
|
||||
/**
|
||||
* Get the creator of the zim file.
|
||||
@@ -219,6 +190,100 @@ class Reader
|
||||
*/
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -25,9 +25,7 @@
|
||||
bool isRelativePath(const std::string& path);
|
||||
std::string computeAbsolutePath(const std::string& path, const std::string& relativePath);
|
||||
std::string computeRelativePath(const std::string& path, const std::string& absolutePath);
|
||||
std::string removeLastPathElement(const std::string& path,
|
||||
const bool removePreSeparator = false,
|
||||
const bool removePostSeparator = false);
|
||||
std::string removeLastPathElement(const std::string& path);
|
||||
std::string appendToDirectory(const std::string& directoryPath, const std::string& filename);
|
||||
|
||||
unsigned int getFileSize(const std::string& path);
|
||||
@@ -38,7 +36,7 @@ bool makeDirectory(const std::string& path);
|
||||
std::string makeTmpDirectory();
|
||||
bool copyFile(const std::string& sourcePath, const std::string& destPath);
|
||||
std::string getLastPathElement(const std::string& path);
|
||||
std::string getExecutablePath();
|
||||
std::string getExecutablePath(bool realPathOnly = false);
|
||||
std::string getCurrentDirectory();
|
||||
std::string getDataDirectory();
|
||||
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 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 char*, const char*);
|
||||
std::vector<std::string> split(const std::string&, const char*);
|
||||
std::vector<std::string> split(const char*, const std::string&);
|
||||
std::vector<std::string> split(const std::string&, const std::string&, bool trimEmpty = true);
|
||||
std::string join(const std::vector<std::string>& list, const std::string& sep);
|
||||
|
||||
std::string ucAll(const std::string& word);
|
||||
std::string lcAll(const std::string& word);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
project('kiwix-lib', 'cpp',
|
||||
version : '6.0.2',
|
||||
version : '8.2.2', # Also change this in android-kiwix-lib-publisher/kiwixLibAndroid/build.gradle
|
||||
license : 'GPL',
|
||||
default_options : ['c_std=c11', 'cpp_std=c++11', 'werror=true'])
|
||||
|
||||
|
||||
@@ -81,12 +81,12 @@ class Resource:
|
||||
data_identifier="_".join([""]+self.identifier),
|
||||
resource_content=",\n ".join(", ".join("{:#04x}".format(i) for i in r) for r in sliced),
|
||||
resource_len=len(self.data),
|
||||
namespaces_open=" ".join("namespace {} {{".format(id) for id in self.identifier[:-1]),
|
||||
namespaces_open=" ".join("namespace {} {{".format(id) for id in self.identifier[:-1]),
|
||||
namespaces_close=" ".join(["}"]*(len(self.identifier)-1)),
|
||||
identifier=self.identifier[-1],
|
||||
env_identifier="RES_"+"_".join(self.identifier)+"_PATH"
|
||||
)
|
||||
|
||||
|
||||
def dump_getter(self):
|
||||
return resource_getter_template.format(
|
||||
common_name=self.filename,
|
||||
@@ -95,11 +95,11 @@ class Resource:
|
||||
|
||||
def dump_decl(self):
|
||||
return resource_decl_template.format(
|
||||
namespaces_open=" ".join("namespace {} {{".format(id) for id in self.identifier[:-1]),
|
||||
namespaces_open=" ".join("namespace {} {{".format(id) for id in self.identifier[:-1]),
|
||||
namespaces_close=" ".join(["}"]*(len(self.identifier)-1)),
|
||||
identifier=self.identifier[-1]
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
master_c_template = """//This file is automaically generated. Do not modify it.
|
||||
@@ -113,7 +113,7 @@ static std::string init_resource(const char* name, const unsigned char* content,
|
||||
char * resPath = getenv(name);
|
||||
if (NULL == resPath)
|
||||
return std::string(reinterpret_cast<const char*>(content), len);
|
||||
|
||||
|
||||
std::ifstream ifs(resPath);
|
||||
if (!ifs.good())
|
||||
return std::string(reinterpret_cast<const char*>(content), len);
|
||||
@@ -137,7 +137,7 @@ def gen_c_file(resources, basename):
|
||||
include_file=basename,
|
||||
basename=to_identifier(basename)
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
master_h_template = """//This file is automaically generated. Do not modify it.
|
||||
|
||||
@@ -353,14 +353,17 @@ Java_org_kiwix_kiwixlib_JNIKiwixReader_searchSuggestions(JNIEnv* env,
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_org_kiwix_kiwixlib_JNIKiwixReader_getNextSuggestion(JNIEnv* env,
|
||||
jobject obj,
|
||||
jobject titleObj)
|
||||
jobject titleObj,
|
||||
jobject urlObj)
|
||||
{
|
||||
jboolean retVal = JNI_FALSE;
|
||||
std::string cTitle;
|
||||
std::string cUrl;
|
||||
|
||||
try {
|
||||
if (READER->getNextSuggestion(cTitle)) {
|
||||
if (READER->getNextSuggestion(cTitle, cUrl)) {
|
||||
setStringObjValue(cTitle, titleObj, env);
|
||||
setStringObjValue(cUrl, urlObj, env);
|
||||
retVal = JNI_TRUE;
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
@@ -424,6 +427,35 @@ Java_org_kiwix_kiwixlib_JNIKiwixReader_getDescription(JNIEnv* env, jobject obj)
|
||||
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(
|
||||
JNIEnv* env, jobject obj, jobject urlObj)
|
||||
{
|
||||
|
||||
@@ -102,7 +102,7 @@ public class JNIKiwixReader
|
||||
|
||||
public native boolean searchSuggestions(String prefix, int count);
|
||||
|
||||
public native boolean getNextSuggestion(JNIKiwixString title);
|
||||
public native boolean getNextSuggestion(JNIKiwixString title, JNIKiwixString url);
|
||||
|
||||
public native boolean getPageUrlFromTitle(String title, JNIKiwixString url);
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ Aria2::Aria2():
|
||||
m_secret = "token:"+m_secret;
|
||||
|
||||
std::string aria2cmd = appendToDirectory(
|
||||
removeLastPathElement(getExecutablePath(), true, true),
|
||||
removeLastPathElement(getExecutablePath(true)),
|
||||
ARIA2_CMD);
|
||||
if (fileExists(aria2cmd)) {
|
||||
// A local aria2c exe exists (packaged with kiwix-desktop), use it.
|
||||
@@ -78,18 +78,32 @@ Aria2::Aria2():
|
||||
callCmd.push_back("--max-concurrent-downloads=42");
|
||||
callCmd.push_back("--rpc-max-request-size=6M");
|
||||
callCmd.push_back("--file-allocation=none");
|
||||
for (auto &cmd : callCmd) {
|
||||
m_launchCmd.append(cmd).append(" ");
|
||||
}
|
||||
mp_aria = Subprocess::run(callCmd);
|
||||
mp_curl = curl_easy_init();
|
||||
char errbuf[CURL_ERROR_SIZE];
|
||||
curl_easy_setopt(mp_curl, CURLOPT_URL, "http://localhost/rpc");
|
||||
curl_easy_setopt(mp_curl, CURLOPT_PORT, m_port);
|
||||
curl_easy_setopt(mp_curl, CURLOPT_POST, 1L);
|
||||
curl_easy_setopt(mp_curl, CURLOPT_ERRORBUFFER, errbuf);
|
||||
|
||||
int watchdog = 50;
|
||||
while(--watchdog) {
|
||||
sleep(10);
|
||||
errbuf[0] = 0;
|
||||
auto res = curl_easy_perform(mp_curl);
|
||||
if (res == CURLE_OK) {
|
||||
break;
|
||||
} else if (watchdog == 1) {
|
||||
std::cerr <<" curl_easy_perform() failed." << std::endl;
|
||||
fprintf(stderr, "\nlibcurl: (%d) ", res);
|
||||
if (errbuf[0] != 0) {
|
||||
std::cerr << errbuf << std::endl;
|
||||
} else {
|
||||
std::cerr << curl_easy_strerror(res) << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!watchdog) {
|
||||
|
||||
@@ -26,6 +26,7 @@ class Aria2
|
||||
std::string m_downloadDir;
|
||||
CURL* mp_curl;
|
||||
pthread_mutex_t m_lock;
|
||||
std::string m_launchCmd;
|
||||
|
||||
std::string doRequest(const MethodCall& methodCall);
|
||||
|
||||
@@ -43,6 +44,7 @@ class Aria2
|
||||
void pause(const std::string& gid);
|
||||
void unpause(const std::string& gid);
|
||||
void remove(const std::string& gid);
|
||||
const std::string &getLaunchCmd() { return m_launchCmd; };
|
||||
};
|
||||
|
||||
}; //end namespace kiwix
|
||||
|
||||
@@ -127,17 +127,24 @@ void Download::cancelDownload()
|
||||
Downloader::Downloader() :
|
||||
mp_aria(new Aria2())
|
||||
{
|
||||
for (auto gid : mp_aria->tellActive()) {
|
||||
m_knownDownloads[gid] = std::unique_ptr<Download>(new Download(mp_aria, gid));
|
||||
m_knownDownloads[gid]->updateStatus();
|
||||
try {
|
||||
for (auto gid : mp_aria->tellActive()) {
|
||||
m_knownDownloads[gid] = std::unique_ptr<Download>(new Download(mp_aria, gid));
|
||||
m_knownDownloads[gid]->updateStatus();
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
std::cerr << "aria2 tellActive failed : " << e.what();
|
||||
}
|
||||
for (auto gid : mp_aria->tellWaiting()) {
|
||||
m_knownDownloads[gid] = std::unique_ptr<Download>(new Download(mp_aria, gid));
|
||||
m_knownDownloads[gid]->updateStatus();
|
||||
try {
|
||||
for (auto gid : mp_aria->tellWaiting()) {
|
||||
m_knownDownloads[gid] = std::unique_ptr<Download>(new Download(mp_aria, gid));
|
||||
m_knownDownloads[gid]->updateStatus();
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
std::cerr << "aria2 tellWaiting failed : " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Destructor */
|
||||
Downloader::~Downloader()
|
||||
{
|
||||
@@ -156,6 +163,11 @@ std::vector<std::string> Downloader::getDownloadIds() {
|
||||
return ret;
|
||||
}
|
||||
|
||||
const std::string &Downloader::getAria2LaunchCmd()
|
||||
{
|
||||
return mp_aria->getLaunchCmd();
|
||||
}
|
||||
|
||||
Download* Downloader::startDownload(const std::string& uri)
|
||||
{
|
||||
for (auto& p: m_knownDownloads) {
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
|
||||
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::string kiwixServeCmd = appendToDirectory(
|
||||
removeLastPathElement(getExecutablePath(), true, true),
|
||||
removeLastPathElement(getExecutablePath(true)),
|
||||
KIWIXSERVE_CMD);
|
||||
if (fileExists(kiwixServeCmd)) {
|
||||
// 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.
|
||||
callCmd.push_back(KIWIXSERVE_CMD);
|
||||
}
|
||||
std::string libraryPath = getDataDirectory() + "/library.xml";
|
||||
std::string attachProcessOpt = "-a" + to_string(pid);
|
||||
std::string portOpt = "-p" + to_string(m_port);
|
||||
callCmd.push_back(attachProcessOpt.c_str());
|
||||
callCmd.push_back(portOpt.c_str());
|
||||
callCmd.push_back("-l");
|
||||
callCmd.push_back(libraryPath.c_str());
|
||||
callCmd.push_back(m_libraryPath.c_str());
|
||||
mp_kiwixServe = Subprocess::run(callCmd);
|
||||
}
|
||||
|
||||
@@ -67,4 +68,14 @@ bool KiwixServe::isRunning()
|
||||
return false;
|
||||
}
|
||||
|
||||
int KiwixServe::setPort(int port)
|
||||
{
|
||||
if (port >= 1 && port <= 65535) {
|
||||
m_port = port;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
return m_port;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ unsigned int Library::getBookCount(const bool localBooks,
|
||||
|
||||
bool Library::writeToFile(const std::string& path)
|
||||
{
|
||||
auto baseDir = removeLastPathElement(path, true, false);
|
||||
auto baseDir = removeLastPathElement(path);
|
||||
LibXMLDumper dumper(this);
|
||||
dumper.setBaseDir(baseDir);
|
||||
return writeTextFile(path, dumper.dumpLibXMLContent(getBooksIds()));
|
||||
@@ -184,6 +184,21 @@ std::vector<std::string> Library::getBooksPublishers()
|
||||
return booksPublishers;
|
||||
}
|
||||
|
||||
const std::vector<kiwix::Bookmark> Library::getBookmarks(bool onlyValidBookmarks)
|
||||
{
|
||||
if (!onlyValidBookmarks) {
|
||||
return m_bookmarks;
|
||||
}
|
||||
std::vector<kiwix::Bookmark> validBookmarks;
|
||||
auto booksId = getBooksIds();
|
||||
for(auto& bookmark:m_bookmarks) {
|
||||
if (std::find(booksId.begin(), booksId.end(), bookmark.getBookId()) != booksId.end()) {
|
||||
validBookmarks.push_back(bookmark);
|
||||
}
|
||||
}
|
||||
return validBookmarks;
|
||||
}
|
||||
|
||||
std::vector<std::string> Library::getBooksIds()
|
||||
{
|
||||
std::vector<std::string> bookIds;
|
||||
|
||||
@@ -59,7 +59,7 @@ bool Manager::parseXmlDom(const pugi::xml_document& doc,
|
||||
|
||||
book.setReadOnly(readOnly);
|
||||
book.updateFromXml(bookNode,
|
||||
removeLastPathElement(libraryPath, true, false));
|
||||
removeLastPathElement(libraryPath));
|
||||
|
||||
/* Update the book properties with the new importer */
|
||||
if (libraryVersion.empty()
|
||||
@@ -177,7 +177,7 @@ std::string Manager::addBookFromPathAndGetId(const std::string& pathToOpen,
|
||||
if (pathToSave != pathToOpen) {
|
||||
book.setPath(isRelativePath(pathToSave)
|
||||
? computeAbsolutePath(
|
||||
removeLastPathElement(writableLibraryPath, true, false),
|
||||
removeLastPathElement(writableLibraryPath),
|
||||
pathToSave)
|
||||
: pathToSave);
|
||||
}
|
||||
|
||||
162
src/reader.cpp
162
src/reader.cpp
@@ -278,7 +278,7 @@ string Reader::getZimFilePath() const
|
||||
return this->zimFilePath;
|
||||
}
|
||||
/* Return a metatag value */
|
||||
bool Reader::getMetatag(const string& name, string& value) const
|
||||
bool Reader::getMetadata(const string& name, string& value) const
|
||||
{
|
||||
try {
|
||||
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 value;
|
||||
this->getMetatag("Title", value);
|
||||
this->getMetadata("Title", value);
|
||||
if (value.empty()) {
|
||||
value = getLastPathElement(zimFileHandler->getFilename());
|
||||
std::replace(value.begin(), value.end(), '_', ' ');
|
||||
@@ -302,65 +309,164 @@ string Reader::getTitle() const
|
||||
return value;
|
||||
}
|
||||
|
||||
string Reader::getName() const
|
||||
string Reader::getCreator() const
|
||||
{
|
||||
string value;
|
||||
this->getMetatag("Name", value);
|
||||
return value;
|
||||
METADATA("Creator")
|
||||
}
|
||||
|
||||
string Reader::getTags() const
|
||||
string Reader::getPublisher() const
|
||||
{
|
||||
string value;
|
||||
this->getMetatag("Tags", value);
|
||||
return value;
|
||||
METADATA("Publisher")
|
||||
}
|
||||
|
||||
string Reader::getDate() const
|
||||
{
|
||||
METADATA("Date")
|
||||
}
|
||||
|
||||
string Reader::getDescription() const
|
||||
{
|
||||
string value;
|
||||
this->getMetatag("Description", value);
|
||||
this->getMetadata("Description", value);
|
||||
|
||||
/* Mediawiki Collection tends to use the "Subtitle" name */
|
||||
if (value.empty()) {
|
||||
this->getMetatag("Subtitle", value);
|
||||
this->getMetadata("Subtitle", value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
string Reader::getLongDescription() const
|
||||
{
|
||||
METADATA("LongDescription")
|
||||
}
|
||||
|
||||
string Reader::getLanguage() const
|
||||
{
|
||||
string value;
|
||||
this->getMetatag("Language", value);
|
||||
return value;
|
||||
METADATA("Language")
|
||||
}
|
||||
|
||||
string Reader::getDate() const
|
||||
string Reader::getLicense() const
|
||||
{
|
||||
string value;
|
||||
this->getMetatag("Date", value);
|
||||
return value;
|
||||
METADATA("License")
|
||||
}
|
||||
|
||||
string Reader::getCreator() const
|
||||
std::vector<std::string> convertTags(const std::string& tags_str)
|
||||
{
|
||||
string value;
|
||||
this->getMetatag("Creator", value);
|
||||
return value;
|
||||
auto tags = split(tags_str, ";");
|
||||
std::vector<std::string> tagsList;
|
||||
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;
|
||||
this->getMetatag("Publisher", value);
|
||||
return value;
|
||||
string tags_str;
|
||||
getMetadata("Tags", tags_str);
|
||||
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 value;
|
||||
this->getMetatag("startfileuid", value);
|
||||
this->getMetadata("startfileuid", value);
|
||||
if (value.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -96,27 +96,29 @@ std::string SearchRenderer::getHtml()
|
||||
auto resultEnd = mp_searcher->getResultEnd();
|
||||
auto resultCountPerPage = resultEnd - resultStart;
|
||||
auto estimatedResultCount = mp_searcher->getEstimatedResultCount();
|
||||
|
||||
unsigned int pageStart
|
||||
= resultStart / resultCountPerPage >= 5
|
||||
? resultStart / resultCountPerPage - 4
|
||||
: 0;
|
||||
unsigned int pageCount
|
||||
= estimatedResultCount / resultCountPerPage + 1 - pageStart;
|
||||
|
||||
if (pageCount > 10) {
|
||||
pageCount = 10;
|
||||
} else if (pageCount == 1) {
|
||||
pageCount = 0;
|
||||
auto currentPage = 0U;
|
||||
auto pageStart = 0U;
|
||||
auto pageEnd = 0U;
|
||||
auto lastPageStart = 0U;
|
||||
if (resultCountPerPage) {
|
||||
currentPage = resultStart/resultCountPerPage;
|
||||
pageStart = currentPage > 4 ? currentPage-4 : 0;
|
||||
pageEnd = currentPage + 5;
|
||||
if (pageEnd > estimatedResultCount / resultCountPerPage) {
|
||||
pageEnd = estimatedResultCount / resultCountPerPage;
|
||||
}
|
||||
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;
|
||||
page.set("label", to_string(i + 1));
|
||||
page.set("start", to_string(i * resultCountPerPage));
|
||||
page.set("end", to_string((i + 1) * resultCountPerPage));
|
||||
|
||||
if (i * resultCountPerPage == resultStart) {
|
||||
if (i == currentPage) {
|
||||
page.set("selected", true);
|
||||
}
|
||||
pages.push_back(page);
|
||||
@@ -128,16 +130,15 @@ std::string SearchRenderer::getHtml()
|
||||
kainjow::mustache::data allData;
|
||||
allData.set("results", results);
|
||||
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("searchPattern", kiwix::encodeDiples(this->searchPattern));
|
||||
allData.set("searchPatternEncoded", urlEncode(this->searchPattern));
|
||||
allData.set("resultStart", to_string(resultStart + 1));
|
||||
allData.set("resultEnd", to_string(min(resultEnd, estimatedResultCount)));
|
||||
allData.set("resultRange", to_string(resultCountPerPage));
|
||||
allData.set("resultLastPageStart", to_string(estimatedResultCount > resultCountPerPage
|
||||
? round(estimatedResultCount / resultCountPerPage) * resultCountPerPage
|
||||
: 0));
|
||||
allData.set("resultLastPageStart", to_string(lastPageStart));
|
||||
allData.set("lastResult", to_string(estimatedResultCount));
|
||||
allData.set("protocolPrefix", this->protocolPrefix);
|
||||
allData.set("searchProtocolPrefix", this->searchProtocolPrefix);
|
||||
|
||||
@@ -108,12 +108,12 @@ void Searcher::search(std::string& search,
|
||||
cout << "Performing query `" << search << "'" << endl;
|
||||
}
|
||||
|
||||
this->searchPattern = search;
|
||||
this->resultStart = resultStart;
|
||||
this->resultEnd = resultEnd;
|
||||
/* Try to find results */
|
||||
if (resultStart != resultEnd) {
|
||||
/* Perform the search */
|
||||
this->searchPattern = search;
|
||||
this->resultStart = resultStart;
|
||||
this->resultEnd = resultEnd;
|
||||
string unaccentedSearch = removeAccents(search);
|
||||
std::vector<const zim::File*> zims;
|
||||
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;
|
||||
}
|
||||
|
||||
/* Try to find results */
|
||||
if (resultStart == resultEnd) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Perform the search */
|
||||
std::ostringstream oss;
|
||||
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->resultEnd = resultEnd;
|
||||
|
||||
/* Try to find results */
|
||||
if (resultStart == resultEnd) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<const zim::File*> zims;
|
||||
for (auto current = this->readers.begin(); current != this->readers.end();
|
||||
current++) {
|
||||
|
||||
@@ -519,7 +519,7 @@ Response InternalServer::handle_suggest(const RequestContext& request)
|
||||
if (reader->hasFulltextIndex()) {
|
||||
kainjow::mustache::data result;
|
||||
result.set("label", "containing '" + term + "'...");
|
||||
result.set("value", term);
|
||||
result.set("value", term + " ");
|
||||
result.set("first", first);
|
||||
results.push_back(result);
|
||||
}
|
||||
@@ -870,23 +870,11 @@ Response InternalServer::handle_content(const RequestContext& request)
|
||||
zim::Blob raw_content = entry.getBlob();
|
||||
content = string(raw_content.data(), raw_content.size());
|
||||
auto response = get_default_response();
|
||||
response.set_mimeType(mimeType);
|
||||
|
||||
/* Special rewrite URL in case of ZIM file use intern *asbolute* url like
|
||||
* /A/Kiwix */
|
||||
if (mimeType.find("text/html") != string::npos) {
|
||||
content = replaceRegex(content,
|
||||
"$1$2" + m_root + "/" + bookName + "/$3/",
|
||||
"(href|src)(=[\"|\']{0,1})/([A-Z|\\-])/");
|
||||
content = replaceRegex(content,
|
||||
"$1$2" + m_root + "/" + bookName + "/$3/",
|
||||
"(@import[ ]+)([\"|\']{0,1})/([A-Z|\\-])/");
|
||||
if (mimeType.find("text/html") != string::npos)
|
||||
response.set_taskbar(bookName, reader->getTitle());
|
||||
} else if (mimeType.find("text/css") != string::npos) {
|
||||
content = replaceRegex(content,
|
||||
"$1$2" + m_root + "/" + bookName + "/$3/",
|
||||
"(url|URL)(\\([\"|\']{0,1})/([A-Z|\\-])/");
|
||||
}
|
||||
|
||||
response.set_mimeType(mimeType);
|
||||
response.set_content(content);
|
||||
response.set_compress(true);
|
||||
response.set_cache(true);
|
||||
|
||||
@@ -140,7 +140,7 @@ int Response::send(const RequestContext& request, MHD_Connection* connection)
|
||||
bool shouldCompress = m_compress && request.can_compress();
|
||||
shouldCompress &= m_mimeType.find("text/") != string::npos
|
||||
|| m_mimeType.find("application/javascript") != string::npos
|
||||
|| m_mimeType.find("application/json") != string::npos;
|
||||
|| m_mimeType.find("application/json") != string::npos;
|
||||
|
||||
shouldCompress &= (m_content.size() > KIWIX_MIN_CONTENT_SIZE_TO_DEFLATE);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
base64.cpp and base64.h
|
||||
|
||||
Copyright (C) 2004-2008 René Nyffenegger
|
||||
@@ -27,7 +27,7 @@
|
||||
#include <tools/base64.h>
|
||||
#include <iostream>
|
||||
|
||||
static const std::string base64_chars =
|
||||
static const std::string base64_chars =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
@@ -39,11 +39,12 @@
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef _WIN32
|
||||
const std::string SEPARATOR("\\");
|
||||
#define SEPARATOR "\\"
|
||||
#else
|
||||
const std::string SEPARATOR("/");
|
||||
#define SEPARATOR "/"
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
@@ -53,6 +54,25 @@ const std::string SEPARATOR("/");
|
||||
#define PATH_MAX 1024
|
||||
#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)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
@@ -62,11 +82,65 @@ bool isRelativePath(const std::string& path)
|
||||
#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::vector<std::string> pathParts = kiwix::split(path, SEPARATOR);
|
||||
std::vector<std::string> absolutePathParts
|
||||
= kiwix::split(absolutePath, SEPARATOR);
|
||||
auto pathParts = normalizeParts(kiwix::split(path, SEPARATOR, false), false);
|
||||
auto absolutePathParts = kiwix::split(absolutePath, SEPARATOR, false);
|
||||
|
||||
unsigned int commonCount = 0;
|
||||
while (commonCount < pathParts.size()
|
||||
@@ -75,108 +149,60 @@ std::string computeRelativePath(const std::string& path, const std::string& abso
|
||||
commonCount++;
|
||||
}
|
||||
|
||||
std::string relativePath;
|
||||
#ifdef _WIN32
|
||||
/* On Windows you have a token more because the root is represented
|
||||
by a letter */
|
||||
if (commonCount == 0) {
|
||||
relativePath = ".." + SEPARATOR;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::vector<std::string> relativeParts;
|
||||
for (unsigned int i = commonCount; i < pathParts.size(); i++) {
|
||||
relativePath += ".." + SEPARATOR;
|
||||
relativeParts.push_back("..");
|
||||
}
|
||||
for (unsigned int i = commonCount; i < absolutePathParts.size(); i++) {
|
||||
relativePath += absolutePathParts[i];
|
||||
relativePath += i + 1 < absolutePathParts.size() ? SEPARATOR : "";
|
||||
relativeParts.push_back(absolutePathParts[i]);
|
||||
}
|
||||
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 absolutePath;
|
||||
|
||||
std::string absolutePath = path;
|
||||
if (path.empty()) {
|
||||
char* path = NULL;
|
||||
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;
|
||||
absolutePath = getCurrentDirectory();
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
char* cRelativePath = _strdup(relativePath.c_str());
|
||||
#else
|
||||
char* cRelativePath = strdup(relativePath.c_str());
|
||||
#endif
|
||||
char* saveptr = nullptr;
|
||||
char* token = STRTOK(cRelativePath, "/", &saveptr);
|
||||
auto absoluteParts = normalizeParts(kiwix::split(absolutePath, SEPARATOR, false), true);
|
||||
auto relativeParts = kiwix::split(relativePath, SEPARATOR, false);
|
||||
|
||||
while (token != NULL) {
|
||||
if (std::string(token) == "..") {
|
||||
absolutePath = removeLastPathElement(absolutePath, true, false);
|
||||
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;
|
||||
absoluteParts.insert(absoluteParts.end(), relativeParts.begin(), relativeParts.end());
|
||||
auto ret = kiwix::join(normalizeParts(absoluteParts, true), SEPARATOR);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string removeLastPathElement(const std::string& path,
|
||||
const bool removePreSeparator,
|
||||
const bool removePostSeparator)
|
||||
std::string removeLastPathElement(const std::string& path)
|
||||
{
|
||||
std::string newPath = path;
|
||||
size_t offset = newPath.find_last_of(SEPARATOR);
|
||||
if (removePreSeparator &&
|
||||
#ifndef _WIN32
|
||||
offset != newPath.find_first_of(SEPARATOR) &&
|
||||
#endif
|
||||
offset == newPath.length() - 1) {
|
||||
newPath = newPath.substr(0, offset);
|
||||
offset = newPath.find_last_of(SEPARATOR);
|
||||
auto parts = normalizeParts(kiwix::split(path, SEPARATOR, false), false);
|
||||
if (!parts.empty()) {
|
||||
parts.pop_back();
|
||||
}
|
||||
newPath = removePostSeparator ? newPath.substr(0, offset)
|
||||
: newPath.substr(0, offset + 1);
|
||||
return newPath;
|
||||
auto ret = kiwix::join(parts, SEPARATOR);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -216,7 +242,7 @@ std::string getFileContent(const std::string& path)
|
||||
bool fileExists(const std::string& path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return PathFileExists(path.c_str());
|
||||
return PathFileExistsW(Utf8ToWide(path).c_str());
|
||||
#else
|
||||
bool flag = false;
|
||||
std::fstream fin;
|
||||
@@ -232,7 +258,7 @@ bool fileExists(const std::string& path)
|
||||
bool makeDirectory(const std::string& path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
int status = _mkdir(path.c_str());
|
||||
int status = _wmkdir(Utf8ToWide(path).c_str());
|
||||
#else
|
||||
int status = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||
#endif
|
||||
@@ -242,15 +268,15 @@ bool makeDirectory(const std::string& path)
|
||||
std::string makeTmpDirectory()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
char cbase[MAX_PATH];
|
||||
char ctmp[MAX_PATH];
|
||||
GetTempPath(MAX_PATH-14, cbase);
|
||||
wchar_t cbase[MAX_PATH];
|
||||
wchar_t ctmp[MAX_PATH];
|
||||
GetTempPathW(MAX_PATH-14, cbase);
|
||||
// This create a file for us, ensure it is unique.
|
||||
// So we need to delete it and create the directory using the same name.
|
||||
GetTempFileName(cbase, "kiwix", 0, ctmp);
|
||||
DeleteFile(ctmp);
|
||||
_mkdir(ctmp);
|
||||
return std::string(ctmp);
|
||||
GetTempFileNameW(cbase, L"kiwix", 0, ctmp);
|
||||
DeleteFileW(ctmp);
|
||||
_wmkdir(ctmp);
|
||||
return WideToUtf8(ctmp);
|
||||
#else
|
||||
char _template_array[] = {"/tmp/kiwix-lib_XXXXXX"};
|
||||
std::string dir = mkdtemp(_template_array);
|
||||
@@ -279,25 +305,38 @@ bool copyFile(const std::string& sourcePath, const std::string& destPath)
|
||||
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
|
||||
GetModuleFileName(NULL, binRootPath, PATH_MAX);
|
||||
return std::string(binRootPath);
|
||||
std::wstring binRootPath(PATH_MAX, 0);
|
||||
GetModuleFileNameW(NULL, &binRootPath[0], PATH_MAX);
|
||||
std::string ret = WideToUtf8(binRootPath);
|
||||
return ret;
|
||||
#elif __APPLE__
|
||||
char binRootPath[PATH_MAX];
|
||||
uint32_t max = (uint32_t)PATH_MAX;
|
||||
_NSGetExecutablePath(binRootPath, &max);
|
||||
return std::string(binRootPath);
|
||||
#else
|
||||
char binRootPath[PATH_MAX];
|
||||
ssize_t size = readlink("/proc/self/exe", binRootPath, PATH_MAX);
|
||||
if (size != -1) {
|
||||
return std::string(binRootPath, size);
|
||||
}
|
||||
#endif
|
||||
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
char* a_cwd = getcwd(NULL, 0);
|
||||
std::string s_cwd(a_cwd);
|
||||
#ifdef _WIN32
|
||||
wchar_t* a_cwd = _wgetcwd(NULL, 0);
|
||||
std::string ret = WideToUtf8(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()
|
||||
@@ -325,8 +370,9 @@ std::string getDataDirectory()
|
||||
char* cDataDir = ::getenv("KIWIX_DATA_DIR");
|
||||
#endif
|
||||
std::string dataDir = cDataDir==nullptr ? "" : cDataDir;
|
||||
if (!dataDir.empty())
|
||||
if (!dataDir.empty()) {
|
||||
return dataDir;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
cDataDir = ::getenv("USERPROFILE");
|
||||
dataDir = cDataDir==nullptr ? getCurrentDirectory() : cDataDir;
|
||||
@@ -340,7 +386,8 @@ std::string getDataDirectory()
|
||||
dataDir = appendToDirectory(dataDir, "share");
|
||||
}
|
||||
#endif
|
||||
return appendToDirectory(dataDir, "kiwix");
|
||||
auto ret = appendToDirectory(dataDir, "kiwix");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> extMimeTypes = {
|
||||
|
||||
@@ -267,36 +267,42 @@ std::string kiwix::urlDecode(const std::string& value, bool component)
|
||||
|
||||
/* Split string in a token array */
|
||||
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 pos = str.find_first_of(delims, lastPos);
|
||||
std::string::size_type lastPos = 0;
|
||||
std::string::size_type pos = 0;
|
||||
std::vector<std::string> tokens;
|
||||
|
||||
while (std::string::npos != pos || std::string::npos != lastPos) {
|
||||
tokens.push_back(str.substr(lastPos, pos - lastPos));
|
||||
lastPos = str.find_first_not_of(delims, pos);
|
||||
pos = str.find_first_of(delims, lastPos);
|
||||
while( (pos = str.find_first_of(delims, lastPos)) < str.length() )
|
||||
{
|
||||
auto token = str.substr(lastPos, pos - lastPos);
|
||||
if (!trimEmpty || !token.empty()) {
|
||||
tokens.push_back(token);
|
||||
}
|
||||
lastPos = pos + 1;
|
||||
}
|
||||
|
||||
auto token = str.substr(lastPos);
|
||||
if (!trimEmpty || !token.empty()) {
|
||||
tokens.push_back(token);
|
||||
}
|
||||
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);
|
||||
return split(m1, m2);
|
||||
std::stringstream ss;
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
||||
<ShortName>Zim catalog search</ShortName>
|
||||
<Description>Search zim files in the catalog.</Description>
|
||||
<Url type="application/atom+xml;profile=opds-catalog"
|
||||
<Url type="application/atom+xml;profile=opds-catalog"
|
||||
xmlns:atom="http://www.w3.org/2005/Atom"
|
||||
indexOffset="0"
|
||||
template="/{{root}}/catalog/search?q={searchTerms}&lang={language}&count={count}&start={startIndex}"/>
|
||||
|
||||
@@ -124,7 +124,7 @@ label[for=kiwixsearchbox] {
|
||||
}
|
||||
|
||||
body {
|
||||
padding-top: 40px !important;
|
||||
padding-top: 3em !important;
|
||||
}
|
||||
|
||||
/* Try to fix buggy stuff in jquery-ui autocomplete */
|
||||
@@ -132,6 +132,7 @@ body {
|
||||
.ui-autocomplete {
|
||||
background: white !important;
|
||||
border: solid 1px grey !important;
|
||||
column-count: 1 !important;
|
||||
}
|
||||
|
||||
li.ui-state-focus {
|
||||
|
||||
@@ -31,14 +31,14 @@
|
||||
.book:hover { background-color: #F9F9F9; box-shadow: none;}
|
||||
.book__background { background-repeat: no-repeat; background-size: auto; background-position: top right; }
|
||||
.book__title {
|
||||
padding: 0 55px 0 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
font-size: 18px; color: #0645ad;
|
||||
padding: 0 55px 0 0;overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
font-size: 18px; color: #0645ad; line-height: 1em;
|
||||
}
|
||||
.book__description {
|
||||
padding: 5px 55px 5px 0px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
font-size: 15px;
|
||||
font-size: 15px; line-height: 1em;
|
||||
}
|
||||
.book__info { line-height: 18px; color: #777; font-weight: bold; font-size: 13px; }
|
||||
.book__info { color: #777; font-weight: bold; font-size: 13px; line-height: 1em; }
|
||||
</style>
|
||||
<script type="text/javascript" src="{{root}}/skin/taskbar.js" async></script>
|
||||
</head>
|
||||
@@ -47,15 +47,13 @@
|
||||
<div class="kiwix">
|
||||
<div class='book__list'>
|
||||
{{#books}}
|
||||
<a href="{{root}}/{{name}}">
|
||||
<div class='book'>
|
||||
<a href="{{root}}/{{name}}"><div class='book'>
|
||||
<div class='book__background' style="background-image: url('{{root}}/meta?content={{#urlencoded}}{{{name}}}{{/urlencoded}}&name=favicon');">
|
||||
<div class='book__title' title='{{title}}'>{{title}}</div>
|
||||
<div class='book__description' title='{{description}}'>{{description}}</div>
|
||||
<div class='book__info'>{{articleCount}} articles, {{mediaCount}} medias</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div></a>
|
||||
{{/books}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
</head>
|
||||
<body bgcolor="white">
|
||||
<div class="header">
|
||||
{{#hasResult}}
|
||||
{{#hasResults}}
|
||||
Results
|
||||
<b>
|
||||
{{resultStart}}-{{resultEnd}}
|
||||
@@ -104,10 +104,10 @@
|
||||
</b> for <b>
|
||||
{{searchPattern}}
|
||||
</b>
|
||||
{{/hasResult}}
|
||||
{{^hasResult}}
|
||||
{{/hasResults}}
|
||||
{{^hasResults}}
|
||||
No results were found for <b>{{searchPattern}}</b>
|
||||
{{/hasResult}}
|
||||
{{/hasResults}}
|
||||
</div>
|
||||
|
||||
<div class="results">
|
||||
@@ -129,30 +129,32 @@
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<ul>
|
||||
{{#resultLastPageStart}}
|
||||
<li>
|
||||
<a href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start=0&end={{resultRange}}">
|
||||
◀
|
||||
</a>
|
||||
</li>
|
||||
{{/resultLastPageStart}}
|
||||
{{#pages}}
|
||||
<li>
|
||||
<a {{#selected}}class="selected"{{/selected}}
|
||||
href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start={{start}}&end={{end}}">
|
||||
{{label}}
|
||||
</a>
|
||||
</li>
|
||||
{{/pages}}
|
||||
{{#resultLastPageStart}}
|
||||
<li>
|
||||
<a href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start={{resultLastPageStart}}&end={{lastResult}}">
|
||||
▶
|
||||
</a>
|
||||
</li>
|
||||
{{/resultLastPageStart}}
|
||||
</ul>
|
||||
{{#hasPages}}
|
||||
<ul>
|
||||
{{#resultLastPageStart}}
|
||||
<li>
|
||||
<a href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start=0&end={{resultRange}}">
|
||||
◀
|
||||
</a>
|
||||
</li>
|
||||
{{/resultLastPageStart}}
|
||||
{{#pages}}
|
||||
<li>
|
||||
<a {{#selected}}class="selected"{{/selected}}
|
||||
href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start={{start}}&end={{end}}">
|
||||
{{label}}
|
||||
</a>
|
||||
</li>
|
||||
{{/pages}}
|
||||
{{#resultLastPageStart}}
|
||||
<li>
|
||||
<a href="{{searchProtocolPrefix}}pattern={{searchPatternEncoded}}{{#contentId}}&content={{.}}{{/contentId}}&start={{resultLastPageStart}}&end={{lastResult}}">
|
||||
▶
|
||||
</a>
|
||||
</li>
|
||||
{{/resultLastPageStart}}
|
||||
</ul>
|
||||
{{/hasPages}}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -23,4 +23,3 @@
|
||||
</div>
|
||||
</span>
|
||||
</span>
|
||||
<div style="display: block; height: 5em;"></div>
|
||||
|
||||
18
test/kiwixserve.cpp
Normal file
18
test/kiwixserve.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "../include/kiwixserve.h"
|
||||
|
||||
TEST(KiwixServeTest, PortTest)
|
||||
{
|
||||
kiwix::KiwixServe kiwixServe("libraryPath", 8181);
|
||||
EXPECT_EQ(kiwixServe.getPort(), 8181);
|
||||
kiwixServe.setPort(8484);
|
||||
EXPECT_EQ(kiwixServe.getPort(), 8484);
|
||||
EXPECT_EQ(kiwixServe.setPort(0), -1);
|
||||
EXPECT_EQ(kiwixServe.setPort(3456789), -1);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -180,6 +180,7 @@ const char * sampleOpdsStream = R"(
|
||||
|
||||
#include "../include/library.h"
|
||||
#include "../include/manager.h"
|
||||
#include "../include/bookmark.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -191,9 +192,34 @@ class LibraryTest : public ::testing::Test {
|
||||
manager.readOpds(sampleOpdsStream, "foo.urlHost");
|
||||
}
|
||||
|
||||
kiwix::Bookmark createBookmark(const std::string &id) {
|
||||
kiwix::Bookmark bookmark;
|
||||
bookmark.setBookId(id);
|
||||
return bookmark;
|
||||
};
|
||||
|
||||
kiwix::Library lib;
|
||||
};
|
||||
|
||||
TEST_F(LibraryTest, getBookMarksTest)
|
||||
{
|
||||
auto bookId1 = lib.getBooksIds()[0];
|
||||
auto bookId2 = lib.getBooksIds()[1];
|
||||
|
||||
lib.addBookmark(createBookmark(bookId1));
|
||||
lib.addBookmark(createBookmark("invalid-bookmark-id"));
|
||||
lib.addBookmark(createBookmark(bookId2));
|
||||
auto onlyValidBookmarks = lib.getBookmarks();
|
||||
auto allBookmarks = lib.getBookmarks(false);
|
||||
|
||||
EXPECT_EQ(onlyValidBookmarks[0].getBookId(), bookId1);
|
||||
EXPECT_EQ(onlyValidBookmarks[1].getBookId(), bookId2);
|
||||
|
||||
EXPECT_EQ(allBookmarks[0].getBookId(), bookId1);
|
||||
EXPECT_EQ(allBookmarks[1].getBookId(), "invalid-bookmark-id");
|
||||
EXPECT_EQ(allBookmarks[2].getBookId(), bookId2);
|
||||
}
|
||||
|
||||
TEST_F(LibraryTest, sanityCheck)
|
||||
{
|
||||
EXPECT_EQ(lib.getBookCount(true, true), 10U);
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
tests = [
|
||||
'parseUrl',
|
||||
'library',
|
||||
'regex'
|
||||
'regex',
|
||||
'tagParsing',
|
||||
'stringTools',
|
||||
'pathTools',
|
||||
'kiwixserve'
|
||||
]
|
||||
|
||||
|
||||
|
||||
234
test/pathTools.cpp
Normal file
234
test/pathTools.cpp
Normal 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
test/stringTools.cpp
Normal file
61
test/stringTools.cpp
Normal 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
test/tagParsing.cpp
Normal file
102
test/tagParsing.cpp
Normal 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();
|
||||
}
|
||||
@@ -38,6 +38,10 @@ else
|
||||
export PKG_CONFIG_PATH=${INSTALL_DIR}/lib/x86_64-linux-gnu/pkgconfig
|
||||
fi
|
||||
export CPPFLAGS="-I${INSTALL_DIR}/include"
|
||||
if [[ "$TEST" == "1" ]]
|
||||
then
|
||||
MESON_OPTION="${MESON_OPTION} -Db_coverage=true"
|
||||
fi
|
||||
meson . build ${MESON_OPTION}
|
||||
cd build
|
||||
ninja
|
||||
@@ -45,5 +49,9 @@ if [[ "$TEST" == "1" ]]
|
||||
then
|
||||
echo "Running test"
|
||||
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
|
||||
|
||||
@@ -8,6 +8,8 @@ REPO_NAME=${TRAVIS_REPO_SLUG#*/}
|
||||
cd $HOME
|
||||
if [[ "$TRAVIS_OS_NAME" == "osx" ]]
|
||||
then
|
||||
# upgrade wget as openssl is updated by travis
|
||||
brew upgrade wget
|
||||
pip3 install meson==0.49.2
|
||||
|
||||
wget https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-mac.zip
|
||||
|
||||
Reference in New Issue
Block a user