This commit is contained in:
Jean-Francois Dockes 2022-01-15 01:49:07 -08:00
commit 0673f0f03e
80 changed files with 1314 additions and 2497 deletions

View File

@ -14,8 +14,8 @@ share/pixmaps/recoll.png
%%DATADIR%%/filters/hotrecoll.py %%DATADIR%%/filters/hotrecoll.py
%%DATADIR%%/filters/rclabw %%DATADIR%%/filters/rclabw
%%DATADIR%%/filters/rclaptosidman %%DATADIR%%/filters/rclaptosidman
%%DATADIR%%/filters/rclaudio %%DATADIR%%/filters/rclaudio.py
%%DATADIR%%/filters/rclchm %%DATADIR%%/filters/rclchm.py
%%DATADIR%%/filters/rcldjvu %%DATADIR%%/filters/rcldjvu
%%DATADIR%%/filters/rcldoc %%DATADIR%%/filters/rcldoc
%%DATADIR%%/filters/rcldvi %%DATADIR%%/filters/rcldvi
@ -23,11 +23,11 @@ share/pixmaps/recoll.png
%%DATADIR%%/filters/rclfb2 %%DATADIR%%/filters/rclfb2
%%DATADIR%%/filters/rclflac %%DATADIR%%/filters/rclflac
%%DATADIR%%/filters/rclgaim %%DATADIR%%/filters/rclgaim
%%DATADIR%%/filters/rclics %%DATADIR%%/filters/rclics.py
%%DATADIR%%/filters/rclid3 %%DATADIR%%/filters/rclid3
%%DATADIR%%/filters/rclimg %%DATADIR%%/filters/rclimg
%%DATADIR%%/filters/rclinfo %%DATADIR%%/filters/rclinfo.py
%%DATADIR%%/filters/rclkar %%DATADIR%%/filters/rclkar.py
%%DATADIR%%/filters/rclkwd %%DATADIR%%/filters/rclkwd
%%DATADIR%%/filters/rcllatinclass.py %%DATADIR%%/filters/rcllatinclass.py
%%DATADIR%%/filters/rcllatinstops.zip %%DATADIR%%/filters/rcllatinstops.zip
@ -41,7 +41,7 @@ share/pixmaps/recoll.png
%%DATADIR%%/filters/rclps %%DATADIR%%/filters/rclps
%%DATADIR%%/filters/rclpurple %%DATADIR%%/filters/rclpurple
%%DATADIR%%/filters/rclpython %%DATADIR%%/filters/rclpython
%%DATADIR%%/filters/rclrar %%DATADIR%%/filters/rclrar.py
%%DATADIR%%/filters/rclrtf %%DATADIR%%/filters/rclrtf
%%DATADIR%%/filters/rclscribus %%DATADIR%%/filters/rclscribus
%%DATADIR%%/filters/rclshowinfo %%DATADIR%%/filters/rclshowinfo
@ -51,11 +51,11 @@ share/pixmaps/recoll.png
%%DATADIR%%/filters/rcltex %%DATADIR%%/filters/rcltex
%%DATADIR%%/filters/rcltext %%DATADIR%%/filters/rcltext
%%DATADIR%%/filters/rcluncomp %%DATADIR%%/filters/rcluncomp
%%DATADIR%%/filters/rclwar %%DATADIR%%/filters/rclwar.py
%%DATADIR%%/filters/rclwpd %%DATADIR%%/filters/rclwpd
%%DATADIR%%/filters/rclxls %%DATADIR%%/filters/rclxls
%%DATADIR%%/filters/rclzip %%DATADIR%%/filters/rclzip.py
%%DATADIR%%/filters/rcl7z %%DATADIR%%/filters/rcl7z.py
%%DATADIR%%/filters/xdg-open %%DATADIR%%/filters/xdg-open
%%DATADIR%%/images/aptosid-book.png %%DATADIR%%/images/aptosid-book.png
%%DATADIR%%/images/aptosid-manual.png %%DATADIR%%/images/aptosid-manual.png

View File

@ -85,7 +85,7 @@ done
### KIO. ### KIO.
series="bionic focal groovy hirsute impish" #series="bionic focal hirsute impish"
series= series=
debdir=debiankio debdir=debiankio

View File

@ -60,40 +60,40 @@ index f41a9f39..dc3085a4 100755
# #
# This Source Code Form is subject to the terms of the Mozilla Public # This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this # License, v. 2.0. If a copy of the MPL was not distributed with this
diff --git filters/rcl7z filters/rcl7z diff --git filters/rcl7z.py filters/rcl7z.py
index c68c8bcb..ac50c4ec 100755 index c68c8bcb..ac50c4ec 100755
--- filters/rcl7z --- filters/rcl7z.py
+++ filters/rcl7z +++ filters/rcl7z.py
@@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
-#!/usr/bin/env python2 -#!/usr/bin/env python2
+#!/usr/bin/env python2.7 +#!/usr/bin/env python2.7
# 7-Zip file filter for Recoll # 7-Zip file filter for Recoll
diff --git filters/rclaudio filters/rclaudio diff --git filters/rclaudio.py filters/rclaudio.py
index 94ca0be7..08d6375a 100755 index 94ca0be7..08d6375a 100755
--- filters/rclaudio --- filters/rclaudio.py
+++ filters/rclaudio +++ filters/rclaudio.py
@@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
-#!/usr/bin/env python2 -#!/usr/bin/env python2
+#!/usr/bin/env python2.7 +#!/usr/bin/env python2.7
# Audio tag filter for Recoll, using mutagen # Audio tag filter for Recoll, using mutagen
diff --git filters/rclchm filters/rclchm diff --git filters/rclchm.py filters/rclchm.py
index f9811c37..3bc9b16d 100755 index f9811c37..3bc9b16d 100755
--- filters/rclchm --- filters/rclchm.py
+++ filters/rclchm +++ filters/rclchm.py
@@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
-#!/usr/bin/env python2 -#!/usr/bin/env python2
+#!/usr/bin/env python2.7 +#!/usr/bin/env python2.7
"""Extract Html files from a Microsoft Compiled Html Help file (.chm) """Extract Html files from a Microsoft Compiled Html Help file (.chm)
Needs at least python 2.2 for HTMLParser (chmlib needs 2.2 too)""" Needs at least python 2.2 for HTMLParser (chmlib needs 2.2 too)"""
diff --git filters/rcldia filters/rcldia diff --git filters/rcldia.py filters/rcldia.py
index 282148eb..a480294b 100755 index 282148eb..a480294b 100755
--- filters/rcldia --- filters/rcldia.py
+++ filters/rcldia +++ filters/rcldia.py
@@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
-#!/usr/bin/env python2 -#!/usr/bin/env python2
+#!/usr/bin/env python2.7 +#!/usr/bin/env python2.7
@ -120,30 +120,30 @@ index e8fa1831..b92b185d 100755
from __future__ import print_function from __future__ import print_function
import rclexecm import rclexecm
diff --git filters/rclepub filters/rclepub diff --git filters/rclepub.py filters/rclepub.py
index 8042d7f9..51786af1 100755 index 8042d7f9..51786af1 100755
--- filters/rclepub --- filters/rclepub.py
+++ filters/rclepub +++ filters/rclepub.py
@@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
-#!/usr/bin/env python2 -#!/usr/bin/env python2
+#!/usr/bin/env python2.7 +#!/usr/bin/env python2.7
"""Extract Html content from an EPUB file (.epub)""" """Extract Html content from an EPUB file (.epub)"""
from __future__ import print_function from __future__ import print_function
diff --git filters/rclepub1 filters/rclepub1 diff --git filters/rclepub.py1 filters/rclepub.py1
index bd44f635..a7ea6c06 100755 index bd44f635..a7ea6c06 100755
--- filters/rclepub1 --- filters/rclepub.py1
+++ filters/rclepub1 +++ filters/rclepub.py1
@@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
-#!/usr/bin/env python2 -#!/usr/bin/env python2
+#!/usr/bin/env python2.7 +#!/usr/bin/env python2.7
"""Extract Html content from an EPUB file (.chm), concatenating all sections""" """Extract Html content from an EPUB file (.chm), concatenating all sections"""
from __future__ import print_function from __future__ import print_function
diff --git filters/rclics filters/rclics diff --git filters/rclics.py filters/rclics.py
index 0ef04f2d..de177024 100755 index 0ef04f2d..de177024 100755
--- filters/rclics --- filters/rclics.py
+++ filters/rclics +++ filters/rclics.py
@@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
-#!/usr/bin/env python2 -#!/usr/bin/env python2
+#!/usr/bin/env python2.7 +#!/usr/bin/env python2.7
@ -160,20 +160,20 @@ index 7eb1da91..4eb6c9b0 100755
# Python-based Image Tag extractor for Recoll. This is less thorough # Python-based Image Tag extractor for Recoll. This is less thorough
# than the Perl-based rclimg script, but useful if you don't want to # than the Perl-based rclimg script, but useful if you don't want to
diff --git filters/rclinfo filters/rclinfo diff --git filters/rclinfo.py filters/rclinfo.py
index f353d19e..36cf34e0 100755 index f353d19e..36cf34e0 100755
--- filters/rclinfo --- filters/rclinfo.py
+++ filters/rclinfo +++ filters/rclinfo.py
@@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
-#!/usr/bin/env python2 -#!/usr/bin/env python2
+#!/usr/bin/env python2.7 +#!/usr/bin/env python2.7
# Read a file in GNU info format and output its nodes as subdocs, # Read a file in GNU info format and output its nodes as subdocs,
# interfacing with recoll execm # interfacing with recoll execm
diff --git filters/rclkar filters/rclkar diff --git filters/rclkar.py filters/rclkar.py
index d6570dd5..34b8d2a2 100755 index d6570dd5..34b8d2a2 100755
--- filters/rclkar --- filters/rclkar.py
+++ filters/rclkar +++ filters/rclkar.py
@@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
-#!/usr/bin/env python2 -#!/usr/bin/env python2
+#!/usr/bin/env python2.7 +#!/usr/bin/env python2.7
@ -230,10 +230,10 @@ index 615455b3..1e411890 100755
# -*- coding: iso-8859-1 -*- # -*- coding: iso-8859-1 -*-
""" """
MoinMoin - Python source parser and colorizer MoinMoin - Python source parser and colorizer
diff --git filters/rclrar filters/rclrar diff --git filters/rclrar.py filters/rclrar.py
index 8f723fa5..5f6adfb0 100755 index 8f723fa5..5f6adfb0 100755
--- filters/rclrar --- filters/rclrar.py
+++ filters/rclrar +++ filters/rclrar.py
@@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
-#!/usr/bin/env python2 -#!/usr/bin/env python2
+#!/usr/bin/env python2.7 +#!/usr/bin/env python2.7
@ -280,10 +280,10 @@ index 8c1b8aea..cee17324 100755
# Copyright (C) 2014 J.F.Dockes # Copyright (C) 2014 J.F.Dockes
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
diff --git filters/rcltar filters/rcltar diff --git filters/rcltar.py filters/rcltar.py
index d8bf100d..ab4b306e 100755 index d8bf100d..ab4b306e 100755
--- filters/rcltar --- filters/rcltar.py
+++ filters/rcltar +++ filters/rcltar.py
@@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
-#!/usr/bin/env python2 -#!/usr/bin/env python2
+#!/usr/bin/env python2.7 +#!/usr/bin/env python2.7
@ -320,10 +320,10 @@ index 32a11c1a..eab3b257 100644
from __future__ import print_function from __future__ import print_function
import rclexecm import rclexecm
diff --git filters/rclwar filters/rclwar diff --git filters/rclwar.py filters/rclwar.py
index b654f3b3..301e28e9 100755 index b654f3b3..301e28e9 100755
--- filters/rclwar --- filters/rclwar.py
+++ filters/rclwar +++ filters/rclwar.py
@@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
-#!/usr/bin/env python2 -#!/usr/bin/env python2
+#!/usr/bin/env python2.7 +#!/usr/bin/env python2.7
@ -360,10 +360,10 @@ index 158e1222..602769af 100755
# Copyright (C) 2016 J.F.Dockes # Copyright (C) 2016 J.F.Dockes
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
diff --git filters/rclzip filters/rclzip diff --git filters/rclzip.py filters/rclzip.py
index 35739625..0c597fbd 100755 index 35739625..0c597fbd 100755
--- filters/rclzip --- filters/rclzip.py
+++ filters/rclzip +++ filters/rclzip.py
@@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
-#!/usr/bin/env python2 -#!/usr/bin/env python2
+#!/usr/bin/env python2.7 +#!/usr/bin/env python2.7

View File

@ -686,31 +686,31 @@ filters/openxml-xls-body.xsl \
filters/openxml-word-body.xsl \ filters/openxml-word-body.xsl \
filters/openxml-meta.xsl \ filters/openxml-meta.xsl \
filters/ppt-dump.py \ filters/ppt-dump.py \
filters/rcl7z \ filters/rcl7z.py \
filters/rclaptosidman \ filters/rclaptosidman \
filters/rclaudio \ filters/rclaudio.py \
filters/rclbasehandler.py \ filters/rclbasehandler.py \
filters/rclbibtex.sh \ filters/rclbibtex.sh \
filters/rclcheckneedretry.sh \ filters/rclcheckneedretry.sh \
filters/rclchm \ filters/rclchm.py \
filters/rcldia \ filters/rcldia.py \
filters/rcldjvu.py \ filters/rcldjvu.py \
filters/rcldoc.py \ filters/rcldoc.py \
filters/rcldvi \ filters/rcldvi \
filters/rclepub \ filters/rclepub.py \
filters/rclepub1 \ filters/rclepub1.py \
filters/rclexec1.py \ filters/rclexec1.py \
filters/rclexecm.py \ filters/rclexecm.py \
filters/rclfb2.py \ filters/rclfb2.py \
filters/rclgaim \ filters/rclgaim \
filters/rclgenxslt.py \ filters/rclgenxslt.py \
filters/rclhwp.py \ filters/rclhwp.py \
filters/rclics \ filters/rclics.py \
filters/rclimg \ filters/rclimg \
filters/rclimg.py \ filters/rclimg.py \
filters/rclinfo \ filters/rclinfo.py \
filters/rclipynb.py \ filters/rclipynb.py \
filters/rclkar \ filters/rclkar.py \
filters/rclkwd \ filters/rclkwd \
filters/rcllatinclass.py \ filters/rcllatinclass.py \
filters/rcllatinstops.zip \ filters/rcllatinstops.zip \
@ -729,21 +729,21 @@ filters/rclps \
filters/rclpst.py \ filters/rclpst.py \
filters/rclpurple \ filters/rclpurple \
filters/rclpython.py \ filters/rclpython.py \
filters/rclrar \ filters/rclrar.py \
filters/rclrtf.py \ filters/rclrtf.py \
filters/rclscribus \ filters/rclscribus \
filters/rclshowinfo \ filters/rclshowinfo \
filters/rcltar \ filters/rcltar.py \
filters/rcltex \ filters/rcltex \
filters/rcltext.py \ filters/rcltext.py \
filters/rcluncomp \ filters/rcluncomp \
filters/rcluncomp.py \ filters/rcluncomp.py \
filters/rclwar \ filters/rclwar.py \
filters/rclxls.py \ filters/rclxls.py \
filters/rclxml.py \ filters/rclxml.py \
filters/rclxmp.py \ filters/rclxmp.py \
filters/rclxslt.py \ filters/rclxslt.py \
filters/rclzip \ filters/rclzip.py \
filters/recoll-we-move-files.py \ filters/recoll-we-move-files.py \
filters/recollepub.zip \ filters/recollepub.zip \
filters/svg.xsl \ filters/svg.xsl \

View File

@ -2763,8 +2763,8 @@ Chapter 4. Programming interface
If you can program and want to write an execm handler, it should not be If you can program and want to write an execm handler, it should not be
too difficult to make sense of one of the existing modules. For example, too difficult to make sense of one of the existing modules. For example,
look at rclzip which uses Zip file paths as identifiers (ipath), and look at rclzip.py which uses Zip file paths as identifiers (ipath), and
rclics, which uses an integer index. Also have a look at the comments rclics.py, which uses an integer index. Also have a look at the comments
inside the internfile/mh_execm.h file and possibly at the corresponding inside the internfile/mh_execm.h file and possibly at the corresponding
module. module.
@ -2819,7 +2819,7 @@ Chapter 4. Programming interface
text/rtf = exec unrtf --nopict --html; charset=iso-8859-1; mimetype=text/html text/rtf = exec unrtf --nopict --html; charset=iso-8859-1; mimetype=text/html
application/x-chm = execm rclchm application/x-chm = execm rclchm.py
The fragment specifies that: The fragment specifies that:

View File

@ -1 +1 @@
1.31.6 1.32.0

View File

@ -125,7 +125,7 @@
#define PACKAGE_NAME "Recoll" #define PACKAGE_NAME "Recoll"
/* Define to the full name and version of this package. */ /* Define to the full name and version of this package. */
#define PACKAGE_STRING "Recoll 1.31.6" #define PACKAGE_STRING "Recoll 1.32.0"
/* Define to the one symbol short name of this package. */ /* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "recoll" #define PACKAGE_TARNAME "recoll"
@ -134,7 +134,7 @@
#define PACKAGE_URL "" #define PACKAGE_URL ""
/* Define to the version of this package. */ /* Define to the version of this package. */
#define PACKAGE_VERSION "1.31.6" #define PACKAGE_VERSION "1.32.0"
/* putenv parameter is const */ /* putenv parameter is const */
/* #undef PUTENV_ARG_CONST */ /* #undef PUTENV_ARG_CONST */

View File

@ -118,7 +118,7 @@
#define PACKAGE_NAME "Recoll" #define PACKAGE_NAME "Recoll"
/* Define to the full name and version of this package. */ /* Define to the full name and version of this package. */
#define PACKAGE_STRING "Recoll 1.31.6" #define PACKAGE_STRING "Recoll 1.32.0"
/* Define to the one symbol short name of this package. */ /* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "recoll" #define PACKAGE_TARNAME "recoll"
@ -127,13 +127,13 @@
#define PACKAGE_URL "" #define PACKAGE_URL ""
/* Define to the version of this package. */ /* Define to the version of this package. */
#define PACKAGE_VERSION "1.31.6" #define PACKAGE_VERSION "1.32.0"
/* putenv parameter is const */ /* putenv parameter is const */
/* #undef PUTENV_ARG_CONST */ /* #undef PUTENV_ARG_CONST */
/* Real time monitoring option */ /* Real time monitoring option */
#undef RCL_MONITOR #define RCL_MONITOR 1
/* Split camelCase words */ /* Split camelCase words */
/* #undef RCL_SPLIT_CAMELCASE */ /* #undef RCL_SPLIT_CAMELCASE */

View File

@ -58,6 +58,16 @@
using namespace std; using namespace std;
// Naming the directory for platform-specific default config files, overriding the top-level ones
// E.g. /usr/share/recoll/examples/windows
#ifdef _WIN32
static const string confsysdir{"windows"};
#elif defined(_APPLE__)
static const string confsysdir{"macos"};
#else
static const string confsysdir;
#endif
// Static, logically const, RclConfig members or module static // Static, logically const, RclConfig members or module static
// variables are initialized once from the first object build during // variables are initialized once from the first object build during
// process initialization. // process initialization.
@ -303,8 +313,15 @@ RclConfig::RclConfig(const string *argcnf)
m_cdirs.push_back(cp); m_cdirs.push_back(cp);
} }
// Base/installation config // Base/installation config, and its platform-specific overrides
m_cdirs.push_back(path_cat(m_datadir, "examples")); std::string defaultsdir = path_cat(m_datadir, "examples");
if (!confsysdir.empty()) {
std::string sdir = path_cat(defaultsdir, confsysdir);
if (path_isdir(sdir)) {
m_cdirs.push_back(sdir);
}
}
m_cdirs.push_back(defaultsdir);
string cnferrloc; string cnferrloc;
for (const auto& dir : m_cdirs) { for (const auto& dir : m_cdirs) {
@ -376,6 +393,7 @@ bool RclConfig::updateMainConfig()
{ {
ConfStack<ConfTree> *newconf = new ConfStack<ConfTree>("recoll.conf", m_cdirs, true); ConfStack<ConfTree> *newconf = new ConfStack<ConfTree>("recoll.conf", m_cdirs, true);
if (newconf == 0 || !newconf->ok()) { if (newconf == 0 || !newconf->ok()) {
std::cerr << "updateMainConfig: new Confstack not ok\n";
if (m_conf) if (m_conf)
return false; return false;
m_ok = false; m_ok = false;
@ -1633,6 +1651,7 @@ vector<string> RclConfig::getDaemSkippedPaths() const
// and filtersdir from the config file to the PATH, then use execmd::which() // and filtersdir from the config file to the PATH, then use execmd::which()
string RclConfig::findFilter(const string &icmd) const string RclConfig::findFilter(const string &icmd) const
{ {
LOGDEB2("findFilter: " << icmd << "\n");
// If the path is absolute, this is it // If the path is absolute, this is it
if (path_isabsolute(icmd)) if (path_isabsolute(icmd))
return icmd; return icmd;
@ -1680,12 +1699,18 @@ bool RclConfig::processFilterCmd(std::vector<std::string>& cmd) const
LOGDEB0("processFilterCmd: in: " << stringsToString(cmd) << "\n"); LOGDEB0("processFilterCmd: in: " << stringsToString(cmd) << "\n");
auto it = cmd.begin(); auto it = cmd.begin();
// Special-case python and perl on windows: we need to also locate the #ifdef _WIN32
// first argument which is the script name "python somescript.py". // Special-case interpreters on windows: we used to have an additional 1st argument "python" in
// On Unix, thanks to #!, we usually just run "somescript.py", but need // mimeconf, but we now rely on the .py extension for better sharing of mimeconf.
// the same change if we ever want to use the same cmd line as windows std::string ext = path_suffix(*it);
bool hasinterp = !stringlowercmp("python", *it) || if ("py" == ext) {
!stringlowercmp("perl", *it); it = cmd.insert(it, findFilter("python"));
it++;
} else if ("pl" == ext) {
it = cmd.insert(it, findFilter("perl"));
it++;
}
#endif
// Note that, if the cmd vector size is 1, post-incrementing the // Note that, if the cmd vector size is 1, post-incrementing the
// iterator in the following statement, which works on x86, leads // iterator in the following statement, which works on x86, leads
@ -1694,25 +1719,15 @@ bool RclConfig::processFilterCmd(std::vector<std::string>& cmd) const
// whatever... We do it later then. // whatever... We do it later then.
*it = findFilter(*it); *it = findFilter(*it);
if (hasinterp) {
if (cmd.size() < 2) {
LOGERR("processFilterCmd: python/perl cmd: no script?. [" <<
stringsToString(cmd) << "]\n");
return false;
} else {
++it;
*it = findFilter(*it);
}
}
LOGDEB0("processFilterCmd: out: " << stringsToString(cmd) << "\n"); LOGDEB0("processFilterCmd: out: " << stringsToString(cmd) << "\n");
return true; return true;
} }
bool RclConfig::pythonCmd(const std::string& scriptname, // This now does nothing more than processFilterCmd (after we changed to relying on the py extension)
std::vector<std::string>& cmd) const bool RclConfig::pythonCmd(const std::string& scriptname, std::vector<std::string>& cmd) const
{ {
#ifdef _WIN32 #ifdef _WIN32
cmd = {"python", scriptname}; cmd = {scriptname};
#else #else
cmd = {scriptname}; cmd = {scriptname};
#endif #endif

View File

@ -596,6 +596,7 @@ bool TextSplit::text_to_words(const string &in)
clearsplitstate(); clearsplitstate();
bool pagepending = false; bool pagepending = false;
bool nlpending = false;
bool softhyphenpending = false; bool softhyphenpending = false;
// Running count of non-alphanum chars. Reset when we see one; // Running count of non-alphanum chars. Reset when we see one;
@ -705,6 +706,10 @@ bool TextSplit::text_to_words(const string &in)
pagepending = false; pagepending = false;
newpage(m_wordpos); newpage(m_wordpos);
} }
if (nlpending) {
nlpending = false;
newline(m_wordpos);
}
break; break;
case WILD: case WILD:
@ -745,6 +750,12 @@ bool TextSplit::text_to_words(const string &in)
break; break;
} }
} else { } else {
// Note about dangling hyphens: we always strip '-' found before whitespace,
// even before a newline, then generate two terms, before and after the line
// break. We have no way to know if '-' is there because a word was broken by
// justification or if it was part of an actual compound word (would need a
// dictionary to check). As soft-hyphen *should* be used if the '-' is not part
// of the text.
if (nextc == -1 || isvisiblewhite(nextc)) { if (nextc == -1 || isvisiblewhite(nextc)) {
goto SPACE; goto SPACE;
} }
@ -844,19 +855,10 @@ bool TextSplit::text_to_words(const string &in)
break; break;
case '\n': case '\n':
nlpending = true;
/* FALLTHROUGH */
case '\r': case '\r':
if (m_span.length() && *m_span.rbegin() == '-') { if (softhyphenpending) {
// if '-' is the last char before end of line, we
// strip it. We have no way to know if this is added
// because of the line split or if it was part of an
// actual compound word (would need a dictionary to
// check). As soft-hyphen *should* be used if the '-'
// is not part of the text, it is better to properly
// process a real compound word, and produce wrong
// output from wrong text. The word-emitting routine
// will strip the trailing '-'.
goto SPACE;
} else if (softhyphenpending) {
// Don't reset soft-hyphen // Don't reset soft-hyphen
continue; continue;
} else { } else {

View File

@ -73,6 +73,9 @@ public:
* just don't know about pages. */ * just don't know about pages. */
virtual void newpage(int /*pos*/) {} virtual void newpage(int /*pos*/) {}
/** Called when we encounter newline \n 0x0a. Override to use the event. */
virtual void newline(int /*pos*/) {}
// Static utility functions: // Static utility functions:
/** Count words in string, as the splitter would generate them */ /** Count words in string, as the splitter would generate them */

View File

@ -148,7 +148,7 @@ not set, the daemon uses skippedPaths.
.TP .TP
.BI "zipUseSkippedNames = "bool .BI "zipUseSkippedNames = "bool
Use skippedNames inside Zip archives. Fetched Use skippedNames inside Zip archives. Fetched
directly by the rclzip handler. Skip the patterns defined by skippedNames directly by the rclzip.py handler. Skip the patterns defined by skippedNames
inside Zip archives. Can be redefined for subdirectories. inside Zip archives. Can be redefined for subdirectories.
See https://www.lesbonscomptes.com/recoll/faqsandhowtos/FilteringOutZipArchiveMembers.html See https://www.lesbonscomptes.com/recoll/faqsandhowtos/FilteringOutZipArchiveMembers.html
@ -195,7 +195,7 @@ lets you turn off md5 computation for selected types. It is global (no
redefinition for subtrees). At the moment, it only has an effect for redefinition for subtrees). At the moment, it only has an effect for
external handlers (exec and execm). The file types can be specified by external handlers (exec and execm). The file types can be specified by
listing either MIME types (e.g. audio/mpeg) or handler names listing either MIME types (e.g. audio/mpeg) or handler names
(e.g. rclaudio). (e.g. rclaudio.py).
.TP .TP
.BI "compressedfilemaxkbs = "int .BI "compressedfilemaxkbs = "int
Size limit for compressed Size limit for compressed

View File

@ -112,7 +112,7 @@ not set, the daemon uses skippedPaths.
<varlistentry id="RCL.INSTALL.CONFIG.RECOLLCONF.ZIPUSESKIPPEDNAMES"> <varlistentry id="RCL.INSTALL.CONFIG.RECOLLCONF.ZIPUSESKIPPEDNAMES">
<term><varname>zipUseSkippedNames</varname></term> <term><varname>zipUseSkippedNames</varname></term>
<listitem><para>Use skippedNames inside Zip archives. Fetched <listitem><para>Use skippedNames inside Zip archives. Fetched
directly by the rclzip handler. Skip the patterns defined by skippedNames directly by the rclzip.py handler. Skip the patterns defined by skippedNames
inside Zip archives. Can be redefined for subdirectories. inside Zip archives. Can be redefined for subdirectories.
See https://www.lesbonscomptes.com/recoll/faqsandhowtos/FilteringOutZipArchiveMembers.html See https://www.lesbonscomptes.com/recoll/faqsandhowtos/FilteringOutZipArchiveMembers.html
</para></listitem></varlistentry> </para></listitem></varlistentry>
@ -162,7 +162,7 @@ lets you turn off md5 computation for selected types. It is global (no
redefinition for subtrees). At the moment, it only has an effect for redefinition for subtrees). At the moment, it only has an effect for
external handlers (exec and execm). The file types can be specified by external handlers (exec and execm). The file types can be specified by
listing either MIME types (e.g. audio/mpeg) or handler names listing either MIME types (e.g. audio/mpeg) or handler names
(e.g. rclaudio). (e.g. rclaudio.py).
</para></listitem></varlistentry> </para></listitem></varlistentry>
<varlistentry id="RCL.INSTALL.CONFIG.RECOLLCONF.COMPRESSEDFILEMAXKBS"> <varlistentry id="RCL.INSTALL.CONFIG.RECOLLCONF.COMPRESSEDFILEMAXKBS">
<term><varname>compressedfilemaxkbs</varname></term> <term><varname>compressedfilemaxkbs</varname></term>

View File

@ -10,7 +10,7 @@
<link rel="stylesheet" type="text/css" href="docbook-xsl.css"> <link rel="stylesheet" type="text/css" href="docbook-xsl.css">
<meta name="generator" content="DocBook XSL Stylesheets V1.79.1"> <meta name="generator" content="DocBook XSL Stylesheets V1.79.1">
<meta name="description" content= <meta name="description" content=
"Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license can be found at the following location: GNU web site. This document introduces full text search notions and describes the installation and use of the Recoll application. This version describes Recoll 1.31."> "Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license can be found at the following location: GNU web site. This document introduces full text search notions and describes the installation and use of the Recoll application. This version describes Recoll 1.32.">
</head> </head>
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" <body bgcolor="white" text="black" link="#0000FF" vlink="#840084"
alink="#0000FF"> alink="#0000FF">
@ -53,7 +53,7 @@ alink="#0000FF">
and describes the installation and use of the and describes the installation and use of the
<span class="application">Recoll</span> application. <span class="application">Recoll</span> application.
This version describes <span class= This version describes <span class=
"application">Recoll</span> 1.31.</p> "application">Recoll</span> 1.32.</p>
</div> </div>
</div> </div>
</div> </div>
@ -443,7 +443,7 @@ alink="#0000FF">
<p>This document introduces full text search notions and <p>This document introduces full text search notions and
describes the installation and use of the <span class= describes the installation and use of the <span class=
"application">Recoll</span> application. It is updated for "application">Recoll</span> application. It is updated for
<span class="application">Recoll</span> 1.31.</p> <span class="application">Recoll</span> 1.32.</p>
<p><span class="application">Recoll</span> was for a long <p><span class="application">Recoll</span> was for a long
time dedicated to Unix-like systems. It was only lately time dedicated to Unix-like systems. It was only lately
(2015) ported to <span class="application">MS-Windows</span>. (2015) ported to <span class="application">MS-Windows</span>.
@ -6581,9 +6581,10 @@ text/html [file:///Users/uncrypted-dockes/projets/bateaux/ilur/factEtCie/r
<p>All the Python handlers share at least the <p>All the Python handlers share at least the
<code class="filename">rclexecm.py</code> module, <code class="filename">rclexecm.py</code> module,
which handles the communication. Have a look at, which handles the communication. Have a look at,
for example, <code class="filename">rclzip</code> for example, <code class=
for a handler which uses <code class= "filename">rclzip.py</code> for a handler which
"filename">rclexecm.py</code> directly.</p> uses <code class="filename">rclexecm.py</code>
directly.</p>
</li> </li>
<li class="listitem"> <li class="listitem">
<p>Most Python handlers which process <p>Most Python handlers which process
@ -6633,7 +6634,7 @@ text/html [file:///Users/uncrypted-dockes/projets/bateaux/ilur/factEtCie/r
"_top">Git repository</a> (the sample not in the "_top">Git repository</a> (the sample not in the
distributed release at the moment).</p> distributed release at the moment).</p>
<p>You can also have a look at the slightly more complex <p>You can also have a look at the slightly more complex
<span class="command"><strong>rclzip</strong></span> <span class="command"><strong>rclzip.py</strong></span>
which uses Zip file paths as identifiers (<code class= which uses Zip file paths as identifiers (<code class=
"literal">ipath</code>).</p> "literal">ipath</code>).</p>
<p><code class="literal">execm</code> handlers sometimes <p><code class="literal">execm</code> handlers sometimes
@ -6726,7 +6727,7 @@ text/html [file:///Users/uncrypted-dockes/projets/bateaux/ilur/factEtCie/r
text/rtf = exec unrtf --nopict --html; charset=iso-8859-1; mimetype=text/html text/rtf = exec unrtf --nopict --html; charset=iso-8859-1; mimetype=text/html
application/x-chm = execm rclchm application/x-chm = execm rclchm.py
</pre> </pre>
<p>The fragment specifies that:</p> <p>The fragment specifies that:</p>
<div class="itemizedlist"> <div class="itemizedlist">
@ -6880,7 +6881,7 @@ text/html [file:///Users/uncrypted-dockes/projets/bateaux/ilur/factEtCie/r
"literal">setfield()</code> helper method. This avoids "literal">setfield()</code> helper method. This avoids
the necessity to produce HTML, and any issue with HTML the necessity to produce HTML, and any issue with HTML
quoting. See, for example, <code class= quoting. See, for example, <code class=
"filename">rclaudio</code> in <span class= "filename">rclaudio.py</code> in <span class=
"application">Recoll</span> 1.23 and later for an example "application">Recoll</span> 1.23 and later for an example
of handler which outputs <code class= of handler which outputs <code class=
"literal">text/plain</code> and uses <code class= "literal">text/plain</code> and uses <code class=
@ -9081,9 +9082,10 @@ hasextract = False
"RCL.INSTALL.CONFIG.RECOLLCONF.ZIPUSESKIPPEDNAMES"></a><span class="term"><code class="varname">zipUseSkippedNames</code></span></dt> "RCL.INSTALL.CONFIG.RECOLLCONF.ZIPUSESKIPPEDNAMES"></a><span class="term"><code class="varname">zipUseSkippedNames</code></span></dt>
<dd> <dd>
<p>Use skippedNames inside Zip archives. Fetched <p>Use skippedNames inside Zip archives. Fetched
directly by the rclzip handler. Skip the patterns directly by the rclzip.py handler. Skip the
defined by skippedNames inside Zip archives. Can patterns defined by skippedNames inside Zip
be redefined for subdirectories. See archives. Can be redefined for subdirectories.
See
https://www.lesbonscomptes.com/recoll/faqsandhowtos/FilteringOutZipArchiveMembers.html</p> https://www.lesbonscomptes.com/recoll/faqsandhowtos/FilteringOutZipArchiveMembers.html</p>
</dd> </dd>
<dt><a name= <dt><a name=
@ -9155,7 +9157,8 @@ hasextract = False
moment, it only has an effect for external moment, it only has an effect for external
handlers (exec and execm). The file types can be handlers (exec and execm). The file types can be
specified by listing either MIME types (e.g. specified by listing either MIME types (e.g.
audio/mpeg) or handler names (e.g. rclaudio).</p> audio/mpeg) or handler names (e.g.
rclaudio.py).</p>
</dd> </dd>
<dt><a name= <dt><a name=
"RCL.INSTALL.CONFIG.RECOLLCONF.COMPRESSEDFILEMAXKBS" "RCL.INSTALL.CONFIG.RECOLLCONF.COMPRESSEDFILEMAXKBS"
@ -10758,17 +10761,28 @@ other = rclcat:other
<li class="listitem"> <li class="listitem">
<p><b>%p.&nbsp;</b>Page index. Only significant for <p><b>%p.&nbsp;</b>Page index. Only significant for
a subset of document types, currently only PDF, a subset of document types, currently only PDF,
Postscript and DVI files. Can be used to start the Postscript and DVI files. If it is set, a
editor at the right page for a match or significant term will be chosen in the query, and
snippet.</p> %p will be substituted with the first page where
the term appears. Can be used to start the editor
at the right page for a match or snippet.</p>
</li>
<li class="listitem">
<p><b>%l.&nbsp;</b>Line number. Only significant
for document types with relevant line breaks,
mostly text/plain and analogs. If it is set, a
significant term will be chosen in the query, and
%p will be substituted with the first line where
the term appears.</p>
</li> </li>
<li class="listitem"> <li class="listitem">
<p><b>%s.&nbsp;</b>Search term. The value will only <p><b>%s.&nbsp;</b>Search term. The value will only
be set for documents with indexed page numbers (ie: be set for documents with indexed page or line
PDF). The value will be one of the matched search numbers and if %p or %l is also used. The value
terms. It would allow pre-setting the value in the will be one of the matched search terms. It would
"Find" entry inside Evince for example, for easy allow pre-setting the value in the "Find" entry
highlighting of the term.</p> inside Evince for example, for easy highlighting of
the term.</p>
</li> </li>
<li class="listitem"> <li class="listitem">
<p><b>%u.&nbsp;</b>Url.</p> <p><b>%u.&nbsp;</b>Url.</p>

View File

@ -5,7 +5,7 @@
<!ENTITY RCL "<application>Recoll</application>"> <!ENTITY RCL "<application>Recoll</application>">
<!ENTITY RCLAPPS "<ulink url='http://www.recoll.org/pages/features.html#doctypes'>http://www.recoll.org/pages/features.html</ulink>"> <!ENTITY RCLAPPS "<ulink url='http://www.recoll.org/pages/features.html#doctypes'>http://www.recoll.org/pages/features.html</ulink>">
<!ENTITY RCLVERSION "1.31"> <!ENTITY RCLVERSION "1.32">
<!ENTITY XAP "<application>Xapian</application>"> <!ENTITY XAP "<application>Xapian</application>">
<!ENTITY WIN "<application>Windows</application>"> <!ENTITY WIN "<application>Windows</application>">
<!ENTITY LIN "<application>Unix</application>-like systems"> <!ENTITY LIN "<application>Unix</application>-like systems">
@ -4957,7 +4957,7 @@ text/html [file:///Users/uncrypted-dockes/projets/bateaux/ilur/factEtCie/r
is).</para></listitem> <listitem><para>All the Python handlers is).</para></listitem> <listitem><para>All the Python handlers
share at least the <filename>rclexecm.py</filename> module, which share at least the <filename>rclexecm.py</filename> module, which
handles the communication. Have a look at, for handles the communication. Have a look at, for
example, <filename>rclzip</filename> for a handler which example, <filename>rclzip.py</filename> for a handler which
uses <filename>rclexecm.py</filename> uses <filename>rclexecm.py</filename>
directly.</para></listitem> <listitem><para>Most Python handlers directly.</para></listitem> <listitem><para>Most Python handlers
which process single-document files by executing another command which process single-document files by executing another command
@ -4994,7 +4994,7 @@ text/html [file:///Users/uncrypted-dockes/projets/bateaux/ilur/factEtCie/r
the moment).</para> the moment).</para>
<para>You can also have a look at the slightly more complex <para>You can also have a look at the slightly more complex
<command>rclzip</command> which uses Zip <command>rclzip.py</command> which uses Zip
file paths as identifiers (<literal>ipath</literal>).</para> file paths as identifiers (<literal>ipath</literal>).</para>
<para><literal>execm</literal> handlers sometimes need to make <para><literal>execm</literal> handlers sometimes need to make
@ -5062,7 +5062,7 @@ text/html [file:///Users/uncrypted-dockes/projets/bateaux/ilur/factEtCie/r
text/rtf = exec unrtf --nopict --html; charset=iso-8859-1; mimetype=text/html text/rtf = exec unrtf --nopict --html; charset=iso-8859-1; mimetype=text/html
application/x-chm = execm rclchm application/x-chm = execm rclchm.py
</programlisting> </programlisting>
<para>The fragment specifies that: <para>The fragment specifies that:
@ -5205,7 +5205,7 @@ text/html [file:///Users/uncrypted-dockes/projets/bateaux/ilur/factEtCie/r
method to produce metadata, by calling the method to produce metadata, by calling the
<literal>setfield()</literal> helper method. This avoids the <literal>setfield()</literal> helper method. This avoids the
necessity to produce HTML, and any issue with HTML quoting. See, necessity to produce HTML, and any issue with HTML quoting. See,
for example, <filename>rclaudio</filename> in &RCL; 1.23 and for example, <filename>rclaudio.py</filename> in &RCL; 1.23 and
later for an example of handler which outputs later for an example of handler which outputs
<literal>text/plain</literal> and uses <literal>text/plain</literal> and uses
<literal>setfield()</literal> to produce metadata.</para> <literal>setfield()</literal> to produce metadata.</para>
@ -7114,28 +7114,37 @@ other = rclcat:other
(possibly a script) to be able to handle it.</para></formalpara> (possibly a script) to be able to handle it.</para></formalpara>
</listitem> </listitem>
<listitem><formalpara><title>%M</title> <listitem>
<para>MIME type</para></formalpara> <formalpara><title>%M</title><para>MIME type</para></formalpara>
</listitem> </listitem>
<listitem><formalpara><title>%p</title> <listitem>
<para>Page index. Only significant for a subset of document <formalpara><title>%p</title><para>Page index. Only significant for a subset of
types, currently only PDF, Postscript and DVI files. Can be document types, currently only PDF, Postscript and DVI files. If it is set, a
used to start the editor at the right page for a match or significant term will be chosen in the query, and %p will be substituted with the
snippet.</para></formalpara> first page where the term appears. Can be used to start the editor at the right page
for a match or snippet.</para></formalpara>
</listitem> </listitem>
<listitem><formalpara><title>%s</title> <listitem>
<para>Search term. The value will only be set for documents <formalpara><title>%l</title><para>Line number. Only significant for document
with indexed page numbers (ie: PDF). The value will be one of types with relevant line breaks, mostly text/plain and analogs. If it is set, a
the matched search terms. It would allow pre-setting the significant term will be chosen in the query, and %p will be substituted with the
value in the "Find" entry inside Evince for example, for easy first line where the term appears.</para></formalpara>
highlighting of the term.</para></formalpara>
</listitem> </listitem>
<listitem><formalpara><title>%u</title> <listitem>
<para>Url.</para></formalpara> <formalpara><title>%s</title><para>Search term. The value will only be set for
documents with indexed page or line numbers and if %p or %l is also used. The value
will be one of the matched search terms. It would allow pre-setting the value in the
"Find" entry inside Evince for example, for easy highlighting of the
term.</para></formalpara>
</listitem> </listitem>
<listitem>
<formalpara><title>%u</title><para>Url.</para></formalpara>
</listitem>
</itemizedlist> </itemizedlist>
<para>In addition to the predefined values above, all strings like <para>In addition to the predefined values above, all strings like

View File

@ -1,4 +1,4 @@
#!/usr/bin/python3 #!/usr/bin/env python3
################################# #################################
# Copyright (C) 2020 J.F.Dockes # Copyright (C) 2020 J.F.Dockes
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify

View File

@ -3,7 +3,7 @@
# 7-Zip file filter for Recoll # 7-Zip file filter for Recoll
# Thanks to Recoll user Martin Ziegler # Thanks to Recoll user Martin Ziegler
# This is a modified version of rclzip, with some help from rcltar # This is a modified version of rclzip.py, with some help from rcltar.py
# #
# Normally using py7zr https://github.com/miurahr/py7zr # Normally using py7zr https://github.com/miurahr/py7zr
# #

View File

@ -6,7 +6,7 @@ from __future__ import print_function
# stefan.friedel@iwr.uni-heidelberg.de 2012 # stefan.friedel@iwr.uni-heidelberg.de 2012
# #
# add the following to ~/.recoll/mimeconf into the [index] section: # add the following to ~/.recoll/mimeconf into the [index] section:
# application/x-dia-diagram = execm rcldia;mimetype=text/plain;charset=utf-8 # application/x-dia-diagram = execm rcldia.py;mimetype=text/plain;charset=utf-8
# and into the [icons] section: # and into the [icons] section:
# application/x-dia-diagram = drawing # application/x-dia-diagram = drawing
# and finally under [categories]: # and finally under [categories]:

View File

@ -1,4 +1,4 @@
#!/usr/bin/python3 #!/usr/bin/env python3
"""Extract Html content from an EPUB file (.epub)""" """Extract Html content from an EPUB file (.epub)"""
from __future__ import print_function from __future__ import print_function

View File

@ -372,7 +372,7 @@ def main(proto, extract):
params = {'filename' : makebytes(path)} params = {'filename' : makebytes(path)}
# Some filters (e.g. rclaudio) need/get a MIME type from the indexer. # Some filters (e.g. rclaudio.py) need/get a MIME type from the indexer.
# We make a half-assed attempt to emulate: # We make a half-assed attempt to emulate:
mimetype = _g_config.mimeType(path) mimetype = _g_config.mimeType(path)
if not mimetype and not _g_mswindows: if not mimetype and not _g_mswindows:

View File

@ -1,4 +1,4 @@
#!/usr/bin/python3 #!/usr/bin/env python3
# Copyright (C) 2020 J.F.Dockes # Copyright (C) 2020 J.F.Dockes
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by

View File

@ -141,7 +141,7 @@ class InfoSimpleSplitter:
if name == b'File': if name == b'File':
infofile = value infofile = value
except Exception as err: except Exception as err:
print("rclinfo: bad line in %s: [%s] %s\n" % \ print("rclinfo.py: bad line in %s: [%s] %s\n" % \
(infofile, line, err), file = sys.stderr) (infofile, line, err), file = sys.stderr)
nodename = prevnodename nodename = prevnodename
node += line node += line

View File

@ -1,4 +1,4 @@
#!/usr/bin/python3 #!/usr/bin/env python3
################################# #################################
# Copyright (C) 2019 J.F.Dockes # Copyright (C) 2019 J.F.Dockes
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify

View File

@ -1,4 +1,4 @@
#!/usr/bin/python3 #!/usr/bin/env python3
# Rclpython is based on "colorize.py" from: # Rclpython is based on "colorize.py" from:
# http://chrisarndt.de/en/software/python/colorize.html # http://chrisarndt.de/en/software/python/colorize.html

View File

@ -60,7 +60,7 @@ except Exception as ex:
# (https://www.rarlab.com/rar_add.htm). The unrar-free version fails # (https://www.rarlab.com/rar_add.htm). The unrar-free version fails
# with the message "Failed the read enough data" # with the message "Failed the read enough data"
# #
# This is identical to rclzip except I did a search/replace from zip # This is identical to rclzip.py except I did a search/replace from zip
# to rar, and changed this comment. # to rar, and changed this comment.
class RarExtractor: class RarExtractor:
def __init__(self, em): def __init__(self, em):

View File

@ -2,7 +2,7 @@
# Tar-file filter for Recoll # Tar-file filter for Recoll
# Thanks to Recoll user Martin Ziegler # Thanks to Recoll user Martin Ziegler
# This is a modified version of /usr/share/recoll/filters/rclzip # This is a modified version of /usr/share/recoll/filters/rclzip.py
# It works not only for tar-files, but automatically for gzipped and # It works not only for tar-files, but automatically for gzipped and
# bzipped tar-files at well. # bzipped tar-files at well.

View File

@ -51,7 +51,7 @@ if not hasrclconfig:
# and stores it in the catalog as an unicode object. Else it uses the # and stores it in the catalog as an unicode object. Else it uses the
# binary string, which it decodes as CP437 (zip standard). # binary string, which it decodes as CP437 (zip standard).
# #
# When reading the file, the input file name is used by rclzip # When reading the file, the input file name is used by rclzip.py
# directly as an index into the catalog. # directly as an index into the catalog.
# #
# When we send the file name data to the indexer, we have to serialize # When we send the file name data to the indexer, we have to serialize

View File

@ -1,7 +1,7 @@
#include "autoconfig.h" #include "autoconfig.h"
#ifdef RCL_MONITOR #ifdef RCL_MONITOR
/* Copyright (C) 2006 J.F.Dockes /* Copyright (C) 2006-2022 J.F.Dockes
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation; either version 2 of the License, or
@ -122,7 +122,7 @@ struct DelayPat {
*/ */
class RclEQData { class RclEQData {
public: public:
int m_opts; int m_opts{0};
// Queue for normal files (unlimited reindex) // Queue for normal files (unlimited reindex)
queue_type m_iqueue; queue_type m_iqueue;
// Queue for delayed reindex files // Queue for delayed reindex files
@ -135,27 +135,21 @@ public:
delays_type m_delays; delays_type m_delays;
// Configured intervals for path patterns, read from the configuration. // Configured intervals for path patterns, read from the configuration.
vector<DelayPat> m_delaypats; vector<DelayPat> m_delaypats;
RclConfig *m_config; RclConfig *m_config{nullptr};
bool m_ok; bool m_ok{true};
std::mutex m_mutex; std::mutex m_mutex;
std::condition_variable m_cond; std::condition_variable m_cond;
RclEQData()
: m_config(0), m_ok(true)
{
}
void readDelayPats(int dfltsecs); void readDelayPats(int dfltsecs);
DelayPat searchDelayPats(const string& path) DelayPat searchDelayPats(const string& path) {
{ for (const auto& dpat: m_delaypats) {
for (vector<DelayPat>::iterator it = m_delaypats.begin(); if (fnmatch(dpat.pattern.c_str(), path.c_str(), 0) == 0) {
it != m_delaypats.end(); it++) { return dpat;
if (fnmatch(it->pattern.c_str(), path.c_str(), 0) == 0) {
return *it;
}
} }
return DelayPat();
} }
return DelayPat();
}
void delayInsert(const queue_type::iterator &qit); void delayInsert(const queue_type::iterator &qit);
}; };
@ -170,22 +164,21 @@ void RclEQData::readDelayPats(int dfltsecs)
vector<string> dplist; vector<string> dplist;
if (!stringToStrings(patstring, dplist)) { if (!stringToStrings(patstring, dplist)) {
LOGERR("rclEQData: bad pattern list: [" << (patstring) << "]\n" ); LOGERR("rclEQData: bad pattern list: [" << patstring << "]\n");
return; return;
} }
for (vector<string>::iterator it = dplist.begin(); for (const auto& entry : dplist) {
it != dplist.end(); it++) { string::size_type pos = entry.find_last_of(":");
string::size_type pos = it->find_last_of(":");
DelayPat dp; DelayPat dp;
dp.pattern = it->substr(0, pos); dp.pattern = entry.substr(0, pos);
if (pos != string::npos && pos != it->size()-1) { if (pos != string::npos && pos != entry.size() - 1) {
dp.seconds = atoi(it->substr(pos+1).c_str()); dp.seconds = atoi(entry.substr(pos+1).c_str());
} else { } else {
dp.seconds = dfltsecs; dp.seconds = dfltsecs;
} }
m_delaypats.push_back(dp); m_delaypats.push_back(dp);
LOGDEB2("rclmon::readDelayPats: add [" << (dp.pattern) << "] " << (dp.seconds) << "\n" ); LOGDEB2("rclmon::readDelayPats: add [" << dp.pattern << "] " << dp.seconds << "\n");
} }
} }
@ -194,10 +187,8 @@ void RclEQData::readDelayPats(int dfltsecs)
// when necessary. // when necessary.
void RclEQData::delayInsert(const queue_type::iterator &qit) void RclEQData::delayInsert(const queue_type::iterator &qit)
{ {
MONDEB("RclEQData::delayInsert: minclock " << qit->second.m_minclock << MONDEB("RclEQData::delayInsert: minclock " << qit->second.m_minclock << "\n");
std::endl); for (delays_type::iterator dit = m_delays.begin(); dit != m_delays.end(); dit++) {
for (delays_type::iterator dit = m_delays.begin();
dit != m_delays.end(); dit++) {
queue_type::iterator qit1 = *dit; queue_type::iterator qit1 = *dit;
if ((*qit1).second.m_minclock > qit->second.m_minclock) { if ((*qit1).second.m_minclock > qit->second.m_minclock) {
m_delays.insert(dit, qit); m_delays.insert(dit, qit);
@ -230,7 +221,7 @@ std::unique_lock<std::mutex> RclMonEventQueue::wait(int seconds, bool *top)
{ {
std::unique_lock<std::mutex> lock(m_data->m_mutex); std::unique_lock<std::mutex> lock(m_data->m_mutex);
MONDEB("RclMonEventQueue::wait, seconds: " << seconds << std::endl); MONDEB("RclMonEventQueue::wait, seconds: " << seconds << "\n");
if (!empty()) { if (!empty()) {
MONDEB("RclMonEventQueue:: immediate return\n"); MONDEB("RclMonEventQueue:: immediate return\n");
return lock; return lock;
@ -324,7 +315,7 @@ bool RclMonEventQueue::empty()
RclMonEvent RclMonEventQueue::pop() RclMonEvent RclMonEventQueue::pop()
{ {
time_t now = time(0); time_t now = time(0);
MONDEB("RclMonEventQueue::pop(), now " << now << std::endl); MONDEB("RclMonEventQueue::pop(), now " << now << "\n");
// Look at the delayed events, get rid of the expired/unactive // Look at the delayed events, get rid of the expired/unactive
// ones, possibly return an expired/needidx one. // ones, possibly return an expired/needidx one.
@ -332,7 +323,7 @@ RclMonEvent RclMonEventQueue::pop()
delays_type::iterator dit = m_data->m_delays.begin(); delays_type::iterator dit = m_data->m_delays.begin();
queue_type::iterator qit = *dit; queue_type::iterator qit = *dit;
MONDEB("RclMonEventQueue::pop(): in delays: evt minclock " << MONDEB("RclMonEventQueue::pop(): in delays: evt minclock " <<
qit->second.m_minclock << std::endl); qit->second.m_minclock << "\n");
if (qit->second.m_minclock <= now) { if (qit->second.m_minclock <= now) {
if (qit->second.m_needidx) { if (qit->second.m_needidx) {
RclMonEvent ev = qit->second; RclMonEvent ev = qit->second;
@ -371,7 +362,7 @@ RclMonEvent RclMonEventQueue::pop()
// special processing to limit their reindexing rate. // special processing to limit their reindexing rate.
bool RclMonEventQueue::pushEvent(const RclMonEvent &ev) bool RclMonEventQueue::pushEvent(const RclMonEvent &ev)
{ {
MONDEB("RclMonEventQueue::pushEvent for " << ev.m_path << std::endl); MONDEB("RclMonEventQueue::pushEvent for " << ev.m_path << "\n");
std::unique_lock<std::mutex> lock(m_data->m_mutex); std::unique_lock<std::mutex> lock(m_data->m_mutex);
DelayPat pat = m_data->searchDelayPats(ev.m_path); DelayPat pat = m_data->searchDelayPats(ev.m_path);
@ -381,8 +372,7 @@ bool RclMonEventQueue::pushEvent(const RclMonEvent &ev)
queue_type::iterator qit = m_data->m_dqueue.find(ev.m_path); queue_type::iterator qit = m_data->m_dqueue.find(ev.m_path);
if (qit == m_data->m_dqueue.end()) { if (qit == m_data->m_dqueue.end()) {
// Not there yet, insert new // Not there yet, insert new
qit = qit = m_data->m_dqueue.insert(queue_type::value_type(ev.m_path, ev)).first;
m_data->m_dqueue.insert(queue_type::value_type(ev.m_path, ev)).first;
// Set the time to next index to "now" as it has not been // Set the time to next index to "now" as it has not been
// indexed recently (otherwise it would still be in the // indexed recently (otherwise it would still be in the
// queue), and add the iterator to the delay queue. // queue), and add the iterator to the delay queue.
@ -452,6 +442,8 @@ bool startMonitor(RclConfig *conf, int opts)
auxinterval = dfltauxinterval; auxinterval = dfltauxinterval;
if (!conf->getConfParam("monixinterval", &ixinterval)) if (!conf->getConfParam("monixinterval", &ixinterval))
ixinterval = dfltixinterval; ixinterval = dfltixinterval;
bool doweb{false};
conf->getConfParam("processwebqueue", &doweb);
rclEQ.setConfig(conf); rclEQ.setConfig(conf);
rclEQ.setopts(opts); rclEQ.setopts(opts);
@ -471,10 +463,12 @@ bool startMonitor(RclConfig *conf, int opts)
while (true) { while (true) {
time_t now = time(0); time_t now = time(0);
if (now - lastmovetime > ixinterval) { #ifndef DISABLE_WEB_INDEXER
if (doweb && (now - lastmovetime > ixinterval)) {
lastmovetime = now; lastmovetime = now;
runWebFilesMoverScript(conf); runWebFilesMoverScript(conf);
} }
#endif // DISABLE_WEB_INDEXER
{ {
// Wait for event or timeout. // Wait for event or timeout.
@ -487,7 +481,7 @@ bool startMonitor(RclConfig *conf, int opts)
#ifndef _WIN32 #ifndef _WIN32
bool x11dead = !(opts & RCLMON_NOX11) && !x11IsAlive(); bool x11dead = !(opts & RCLMON_NOX11) && !x11IsAlive();
if (x11dead) if (x11dead)
LOGDEB("RclMonprc: x11 is dead\n" ); LOGDEB("RclMonprc: x11 is dead\n");
#else #else
bool x11dead = false; bool x11dead = false;
#endif #endif
@ -508,26 +502,27 @@ bool startMonitor(RclConfig *conf, int opts)
modified.push_back(ev.m_path); modified.push_back(ev.m_path);
break; break;
case RclMonEvent::RCLEVT_DELETE: case RclMonEvent::RCLEVT_DELETE:
LOGDEB0("Monitor: Delete on " << (ev.m_path) << "\n" ); LOGDEB0("Monitor: Delete on " << ev.m_path << "\n");
// If this is for a directory (which the caller should // If this is for a directory (which the caller should tell us because he
// tell us because he knows), we should purge the db // knows), we should purge the db of all the subtree entries, because on a
// of all the subtree, because on a directory rename, // directory rename, inotify will only generate one event for the renamed top,
// inotify will only generate one event for the // not the subentries. The entries from the new subtree are updated when the
// renamed top, not the subentries. This is relatively // monitor walks it on the DIRCREATE event.
// complicated to do though, and we currently do not
// do it, and just wait for a restart to do a full run and
// purge.
deleted.push_back(ev.m_path); deleted.push_back(ev.m_path);
if (ev.evflags() & RclMonEvent::RCLEVT_ISDIR) { #ifndef _WIN32
// We don't know the type of deleted entries on
// win32. So do the subtree things always.
if (ev.evflags() & RclMonEvent::RCLEVT_ISDIR)
#endif
{
vector<string> paths; vector<string> paths;
if (subtreelist(conf, ev.m_path, paths)) { if (subtreelist(conf, ev.m_path, paths)) {
deleted.insert(deleted.end(), deleted.insert(deleted.end(), paths.begin(), paths.end());
paths.begin(), paths.end());
} }
} }
break; break;
default: default:
LOGDEB("Monitor: got Other on [" << (ev.m_path) << "]\n" ); LOGDEB("Monitor: got Other on [" << ev.m_path << "]\n");
} }
} }
} }
@ -572,17 +567,19 @@ bool startMonitor(RclConfig *conf, int opts)
} }
} }
#ifndef _WIN32
// Check for a config change // Check for a config change
if (!(opts & RCLMON_NOCONFCHECK) && o_reexec && conf->sourceChanged()) { if (!(opts & RCLMON_NOCONFCHECK) && o_reexec && conf->sourceChanged()) {
LOGDEB("Rclmonprc: config changed, reexecuting myself\n" ); LOGDEB("Rclmonprc: config changed, reexecuting myself\n");
// We never want to have a -n option after a config // We never want to have a -n option after a config
// change. -n was added by the reexec after the initial // change. -n was added by the reexec after the initial
// pass even if it was not given on the command line // pass even if it was not given on the command line
o_reexec->removeArg("-n"); o_reexec->removeArg("-n");
o_reexec->reexec(); o_reexec->reexec();
} }
#endif // ! _WIN32
} }
LOGDEB("Rclmonprc: calling queue setTerminate\n" ); LOGDEB("Rclmonprc: calling queue setTerminate\n");
rclEQ.setTerminate(); rclEQ.setTerminate();
// We used to wait for the receiver thread here before returning, // We used to wait for the receiver thread here before returning,
@ -590,9 +587,8 @@ bool startMonitor(RclConfig *conf, int opts)
// during our limited time window for exiting. To be reviewed if // during our limited time window for exiting. To be reviewed if
// we ever need several monitor invocations in the same process // we ever need several monitor invocations in the same process
// (can't foresee any reason why we'd want to do this). // (can't foresee any reason why we'd want to do this).
LOGDEB("Monitor: returning\n" ); LOGDEB("Monitor: returning\n");
return true; return true;
} }
#endif // RCL_MONITOR #endif // RCL_MONITOR

View File

@ -1,6 +1,6 @@
#include "autoconfig.h" #include "autoconfig.h"
#ifdef RCL_MONITOR #ifdef RCL_MONITOR
/* Copyright (C) 2006 J.F.Dockes /* Copyright (C) 2006-2021 J.F.Dockes
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation; either version 2 of the License, or
@ -16,6 +16,35 @@
* Free Software Foundation, Inc., * Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
/* The code for the Win32 version of the monitor was largely copied from efsw:
* https://github.com/SpartanJ/efsw
* LICENSE for the original WIN32 code:
* Copyright (c) 2020 Martín Lucas Golini
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This software is a fork of the "simplefilewatcher" by James Wynn (james@jameswynn.com)
* http://code.google.com/p/simplefilewatcher/ also MIT licensed.
*/
#include "autoconfig.h" #include "autoconfig.h"
#include <errno.h> #include <errno.h>
@ -31,12 +60,10 @@
/** /**
* Recoll real time monitor event receiver. This file has code to interface * Recoll real time monitor event receiver. This file has code to interface
* to FAM or inotify and place events on the event queue. * to FAM, inotify, etc. and place events on the event queue.
*/ */
/** A small virtual interface for monitors. Lets /** Virtual interface for the actual filesystem monitoring module. */
* either fam/gamin or raw imonitor hide behind
*/
class RclMonitor { class RclMonitor {
public: public:
RclMonitor() {} RclMonitor() {}
@ -46,8 +73,12 @@ public:
virtual bool getEvent(RclMonEvent& ev, int msecs = -1) = 0; virtual bool getEvent(RclMonEvent& ev, int msecs = -1) = 0;
virtual bool ok() const = 0; virtual bool ok() const = 0;
// Does this monitor generate 'exist' events at startup? // Does this monitor generate 'exist' events at startup?
virtual bool generatesExist() const = 0; virtual bool generatesExist() const {
return false;
}
virtual bool isRecursive() const {
return false;
}
// Save significant errno after monitor calls // Save significant errno after monitor calls
int saved_errno{0}; int saved_errno{0};
}; };
@ -72,8 +103,8 @@ public:
virtual FsTreeWalker::Status processone( virtual FsTreeWalker::Status processone(
const string &fn, const struct PathStat *st, FsTreeWalker::CbFlag flg) { const string &fn, const struct PathStat *st, FsTreeWalker::CbFlag flg) {
MONDEB("rclMonRcvRun: processone " << fn << " m_mon " << m_mon << MONDEB("walkerCB: processone " << fn << " m_mon " << m_mon <<
" m_mon->ok " << (m_mon ? m_mon->ok() : false) << std::endl); " m_mon->ok " << (m_mon ? m_mon->ok() : false) << "\n");
if (flg == FsTreeWalker::FtwDirEnter || flg == FsTreeWalker::FtwDirReturn) { if (flg == FsTreeWalker::FtwDirEnter || flg == FsTreeWalker::FtwDirReturn) {
m_config->setKeyDir(fn); m_config->setKeyDir(fn);
@ -90,17 +121,18 @@ public:
if (ev.m_etyp != RclMonEvent::RCLEVT_NONE) if (ev.m_etyp != RclMonEvent::RCLEVT_NONE)
m_queue->pushEvent(ev); m_queue->pushEvent(ev);
} else { } else {
MONDEB("rclMonRcvRun: no event pending\n"); MONDEB("walkerCB: no event pending\n");
break; break;
} }
} }
if (!m_mon || !m_mon->ok()) if (!m_mon || !m_mon->ok())
return FsTreeWalker::FtwError; return FsTreeWalker::FtwError;
// We do nothing special if addWatch fails for a reasonable reason // We do nothing special if addWatch fails for a reasonable reason
if (!m_mon->addWatch(fn, true)) { if (!m_mon->isRecursive() && !m_mon->addWatch(fn, true)) {
if (m_mon->saved_errno != EACCES && if (m_mon->saved_errno != EACCES && m_mon->saved_errno != ENOENT) {
m_mon->saved_errno != ENOENT) LOGINF("walkerCB: addWatch failed\n");
return FsTreeWalker::FtwError; return FsTreeWalker::FtwError;
}
} }
} else if (!m_mon->generatesExist() && flg == FsTreeWalker::FtwRegular) { } else if (!m_mon->generatesExist() && flg == FsTreeWalker::FtwRegular) {
// Have to synthetize events for regular files existence // Have to synthetize events for regular files existence
@ -113,8 +145,8 @@ public:
// monitoring ? There should be another way: maybe start // monitoring ? There should be another way: maybe start
// monitoring without actually handling events (just // monitoring without actually handling events (just
// queue), then run incremental then start handling // queue), then run incremental then start handling
// events ? But we also have to do it on a directory // events ? ** But we also have to do it on a directory
// move! So keep it // move! So keep it ** We could probably skip it on the initial run though.
RclMonEvent ev; RclMonEvent ev;
ev.m_path = fn; ev.m_path = fn;
ev.m_etyp = RclMonEvent::RCLEVT_MODIFY; ev.m_etyp = RclMonEvent::RCLEVT_MODIFY;
@ -130,6 +162,96 @@ private:
FsTreeWalker& m_walker; FsTreeWalker& m_walker;
}; };
static bool rclMonAddTopWatches(
FsTreeWalker& walker, RclConfig& lconfig, RclMonitor *mon, RclMonEventQueue *queue)
{
// Get top directories from config. Special monitor sublist if
// set, else full list.
vector<string> tdl = lconfig.getTopdirs(true);
if (tdl.empty()) {
LOGERR("rclMonRcvRun:: top directory list (topdirs param.) not found "
"in configuration or topdirs list parse error");
queue->setTerminate();
return false;
}
// Walk the directory trees to add watches
WalkCB walkcb(&lconfig, mon, queue, walker);
for (const auto& dir : tdl) {
lconfig.setKeyDir(dir);
// Adjust the follow symlinks options
bool follow;
if (lconfig.getConfParam("followLinks", &follow) && follow) {
walker.setOpts(FsTreeWalker::FtwFollow);
} else {
walker.setOpts(FsTreeWalker::FtwOptNone);
}
if (path_isdir(dir, follow)) {
LOGDEB("rclMonRcvRun: walking " << dir << "\n");
// If the fs watcher is recursive, we add the watches for the topdirs here, and walk the
// tree just for generating initial events.
if (mon->isRecursive() && !mon->addWatch(dir, true)) {
if (mon->saved_errno != EACCES && mon->saved_errno != ENOENT) {
LOGERR("rclMonAddTopWatches: addWatch failed for [" << dir << "]\n");
return false;
}
}
if (walker.walk(dir, walkcb) != FsTreeWalker::FtwOk) {
LOGERR("rclMonRcvRun: tree walk failed\n");
return false;
}
if (walker.getErrCnt() > 0) {
LOGINFO("rclMonRcvRun: fs walker errors: " << walker.getReason() << "\n");
}
} else {
// We have to special-case regular files which are part of the topdirs list because the
// tree walker only adds watches for directories
if (!mon->addWatch(dir, false)) {
LOGSYSERR("rclMonRcvRun", "addWatch", dir);
}
}
}
bool doweb = false;
lconfig.getConfParam("processwebqueue", &doweb);
if (doweb) {
string webqueuedir = lconfig.getWebQueueDir();
if (!mon->addWatch(webqueuedir, true)) {
LOGERR("rclMonRcvRun: addwatch (webqueuedir) failed\n");
if (mon->saved_errno != EACCES && mon->saved_errno != ENOENT)
return false;
}
}
return true;
}
static bool rclMonAddSubWatches(
const std::string& path, FsTreeWalker& walker, RclConfig& lconfig,
RclMonitor *mon, RclMonEventQueue *queue)
{
WalkCB walkcb(&lconfig, mon, queue, walker);
if (walker.walk(path, walkcb) != FsTreeWalker::FtwOk) {
LOGERR("rclMonRcvRun: walking new dir " << path << " : " << walker.getReason() << "\n");
return false;
}
if (walker.getErrCnt() > 0) {
LOGINFO("rclMonRcvRun: fs walker errors: " << walker.getReason() << "\n");
}
return true;
}
// Don't push events for skipped files. This would get filtered on the processing side
// anyway, but causes unnecessary wakeups and messages. Do not test skippedPaths here,
// this would be incorrect (because a topdir can be under a skippedPath and this was
// handled while adding the watches). Also we let the other side process onlyNames.
static bool rclMonShouldSkip(const std::string& path, RclConfig& lconfig, FsTreeWalker& walker)
{
lconfig.setKeyDir(path_getfather(path));
walker.setSkippedNames(lconfig.getSkippedNames());
if (walker.inSkippedNames(path_getsimple(path)))
return true;
return false;
}
// Main thread routine: create watches, then forever wait for and queue events // Main thread routine: create watches, then forever wait for and queue events
void *rclMonRcvRun(void *q) void *rclMonRcvRun(void *q)
{ {
@ -150,104 +272,34 @@ void *rclMonRcvRun(void *q)
return 0; return 0;
} }
// Get top directories from config. Special monitor sublist if
// set, else full list.
vector<string> tdl = lconfig.getTopdirs(true);
if (tdl.empty()) {
LOGERR("rclMonRcvRun:: top directory list (topdirs param.) not found "
"in configuration or topdirs list parse error");
queue->setTerminate();
return 0;
}
// Walk the directory trees to add watches
FsTreeWalker walker; FsTreeWalker walker;
walker.setSkippedPaths(lconfig.getDaemSkippedPaths()); walker.setSkippedPaths(lconfig.getDaemSkippedPaths());
WalkCB walkcb(&lconfig, mon, queue, walker);
for (auto it = tdl.begin(); it != tdl.end(); it++) {
lconfig.setKeyDir(*it);
// Adjust the follow symlinks options
bool follow;
if (lconfig.getConfParam("followLinks", &follow) &&
follow) {
walker.setOpts(FsTreeWalker::FtwFollow);
} else {
walker.setOpts(FsTreeWalker::FtwOptNone);
}
// We have to special-case regular files which are part of the topdirs
// list because we the tree walker only adds watches for directories
if (path_isdir(*it, follow)) {
LOGDEB("rclMonRcvRun: walking " << *it << "\n");
if (walker.walk(*it, walkcb) != FsTreeWalker::FtwOk) {
LOGERR("rclMonRcvRun: tree walk failed\n");
goto terminate;
}
if (walker.getErrCnt() > 0) {
LOGINFO("rclMonRcvRun: fs walker errors: " <<
walker.getReason() << "\n");
}
} else {
if (!mon->addWatch(*it, false)) {
LOGERR("rclMonRcvRun: addWatch failed for " << *it <<
" errno " << mon->saved_errno << std::endl);
}
}
}
{ if (!rclMonAddTopWatches(walker, lconfig, mon, queue)) {
bool doweb = false; LOGERR("rclMonRcvRun: addtopwatches failed\n");
lconfig.getConfParam("processwebqueue", &doweb); goto terminate;
if (doweb) {
string webqueuedir = lconfig.getWebQueueDir();
if (!mon->addWatch(webqueuedir, true)) {
LOGERR("rclMonRcvRun: addwatch (webqueuedir) failed\n");
if (mon->saved_errno != EACCES && mon->saved_errno != ENOENT)
goto terminate;
}
}
} }
// Forever wait for monitoring events and add them to queue: // Forever wait for monitoring events and add them to queue:
MONDEB("rclMonRcvRun: waiting for events. q->ok(): " << queue->ok() << MONDEB("rclMonRcvRun: waiting for events. q->ok(): " << queue->ok() << "\n");
std::endl);
while (queue->ok() && mon->ok()) { while (queue->ok() && mon->ok()) {
RclMonEvent ev; RclMonEvent ev;
// Note: I could find no way to get the select // Note: I could find no way to get the select call to return when a signal is delivered to
// call to return when a signal is delivered to the process // the process (it goes to the main thread, from which I tried to close or write to the
// (it goes to the main thread, from which I tried to close or // select fd, with no effect). So set a timeout so that an intr will be detected
// write to the select fd, with no effect). So set a
// timeout so that an intr will be detected
if (mon->getEvent(ev, 2000)) { if (mon->getEvent(ev, 2000)) {
// Don't push events for skipped files. This would get if (rclMonShouldSkip(ev.m_path, lconfig, walker))
// filtered on the processing side anyway, but causes
// unnecessary wakeups and messages. Do not test
// skippedPaths here, this would be incorrect (because a
// topdir can be under a skippedPath and this was handled
// while adding the watches).
// Also we let the other side process onlyNames.
lconfig.setKeyDir(path_getfather(ev.m_path));
walker.setSkippedNames(lconfig.getSkippedNames());
if (walker.inSkippedNames(path_getsimple(ev.m_path)))
continue; continue;
if (ev.m_etyp == RclMonEvent::RCLEVT_DIRCREATE) { if (ev.m_etyp == RclMonEvent::RCLEVT_DIRCREATE) {
// Recursive addwatch: there may already be stuff // Recursive addwatch: there may already be stuff inside this directory. E.g.: files
// inside this directory. Ie: files were quickly // were quickly created, or this is actually the target of a directory move. This is
// created, or this is actually the target of a // necessary for inotify, but it seems that fam/gamin is doing the job for us so
// directory move. This is necessary for inotify, but // that we are generating double events here (no big deal as prc will sort/merge).
// it seems that fam/gamin is doing the job for us so
// that we are generating double events here (no big
// deal as prc will sort/merge).
LOGDEB("rclMonRcvRun: walking new dir " << ev.m_path << "\n"); LOGDEB("rclMonRcvRun: walking new dir " << ev.m_path << "\n");
if (walker.walk(ev.m_path, walkcb) != FsTreeWalker::FtwOk) { if (!rclMonAddSubWatches(ev.m_path, walker, lconfig, mon, queue)) {
LOGERR("rclMonRcvRun: walking new dir " << ev.m_path <<
" : " << walker.getReason() << "\n");
goto terminate; goto terminate;
} }
if (walker.getErrCnt() > 0) {
LOGINFO("rclMonRcvRun: fs walker errors: " <<
walker.getReason() << "\n");
}
} }
if (ev.m_etyp != RclMonEvent::RCLEVT_NONE) if (ev.m_etyp != RclMonEvent::RCLEVT_NONE)
@ -271,7 +323,7 @@ bool eraseWatchSubTree(map<int, string>& idtopath, const string& top)
while (it != idtopath.end()) { while (it != idtopath.end()) {
if (it->second.find(top) == 0) { if (it->second.find(top) == 0) {
found = true; found = true;
idtopath.erase(it++); it = idtopath.erase(it);
} else { } else {
it++; it++;
} }
@ -364,7 +416,7 @@ bool RclFAM::addWatch(const string& path, bool isdir)
return false; return false;
bool ret = false; bool ret = false;
MONDEB("RclFAM::addWatch: adding " << path << std::endl); MONDEB("RclFAM::addWatch: adding " << path << "\n");
// It happens that the following call block forever. // It happens that the following call block forever.
// We'd like to be able to at least terminate on a signal here, but // We'd like to be able to at least terminate on a signal here, but
@ -410,7 +462,7 @@ bool RclFAM::getEvent(RclMonEvent& ev, int msecs)
FD_ZERO(&readfds); FD_ZERO(&readfds);
FD_SET(fam_fd, &readfds); FD_SET(fam_fd, &readfds);
MONDEB("RclFAM::getEvent: select. fam_fd is " << fam_fd << std::endl); MONDEB("RclFAM::getEvent: select. fam_fd is " << fam_fd << "\n");
// Fam / gamin is sometimes a bit slow to send events. Always add // Fam / gamin is sometimes a bit slow to send events. Always add
// a little timeout, because if we fail to retrieve enough events, // a little timeout, because if we fail to retrieve enough events,
// we risk deadlocking in addwatch() // we risk deadlocking in addwatch()
@ -432,7 +484,7 @@ bool RclFAM::getEvent(RclMonEvent& ev, int msecs)
return false; return false;
} }
MONDEB("RclFAM::getEvent: select returned " << ret << std::endl); MONDEB("RclFAM::getEvent: select returned " << ret << "\n");
if (!FD_ISSET(fam_fd, &readfds)) if (!FD_ISSET(fam_fd, &readfds))
return false; return false;
@ -464,8 +516,7 @@ bool RclFAM::getEvent(RclMonEvent& ev, int msecs)
ev.m_path = fe.filename; ev.m_path = fe.filename;
} }
MONDEB("RclFAM::getEvent: " << event_name(fe.code) < " " << MONDEB("RclFAM::getEvent: " << event_name(fe.code) < " " << ev.m_path << "\n");
ev.m_path << std::endl);
switch (fe.code) { switch (fe.code) {
case FAMCreated: case FAMCreated:
@ -517,23 +568,20 @@ bool RclFAM::getEvent(RclMonEvent& ev, int msecs)
class RclIntf : public RclMonitor { class RclIntf : public RclMonitor {
public: public:
RclIntf() RclIntf()
: m_ok(false), m_fd(-1), m_evp(0), m_ep(0) : m_ok(false), m_fd(-1), m_evp(0), m_ep(0) {
{ if ((m_fd = inotify_init()) < 0) {
if ((m_fd = inotify_init()) < 0) { LOGERR("RclIntf:: inotify_init failed, errno " << errno << "\n");
LOGERR("RclIntf:: inotify_init failed, errno " << errno << "\n"); return;
return;
}
m_ok = true;
}
virtual ~RclIntf()
{
close();
} }
m_ok = true;
}
virtual ~RclIntf() {
close();
}
virtual bool addWatch(const string& path, bool isdir); virtual bool addWatch(const string& path, bool isdir);
virtual bool getEvent(RclMonEvent& ev, int msecs = -1); virtual bool getEvent(RclMonEvent& ev, int msecs = -1);
bool ok() const {return m_ok;} bool ok() const {return m_ok;}
virtual bool generatesExist() const {return false;}
private: private:
bool m_ok; bool m_ok;
@ -586,7 +634,7 @@ bool RclIntf::addWatch(const string& path, bool)
{ {
if (!ok()) if (!ok())
return false; return false;
MONDEB("RclIntf::addWatch: adding " << path << std::endl); MONDEB("RclIntf::addWatch: adding " << path << "\n");
// CLOSE_WRITE is covered through MODIFY. CREATE is needed for mkdirs // CLOSE_WRITE is covered through MODIFY. CREATE is needed for mkdirs
uint32_t mask = IN_MODIFY | IN_CREATE uint32_t mask = IN_MODIFY | IN_CREATE
| IN_MOVED_FROM | IN_MOVED_TO | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO | IN_DELETE
@ -636,9 +684,8 @@ bool RclIntf::getEvent(RclMonEvent& ev, int msecs)
} }
int ret; int ret;
MONDEB("RclIntf::getEvent: select\n"); MONDEB("RclIntf::getEvent: select\n");
if ((ret = select(m_fd + 1, &readfds, 0, 0, msecs >= 0 ? &timeout : 0)) if ((ret = select(m_fd + 1, &readfds, 0, 0, msecs >= 0 ? &timeout : 0)) < 0) {
< 0) { LOGSYSERR("RclIntf::getEvent", "select", "");
LOGERR("RclIntf::getEvent: select failed, errno " << errno << "\n");
close(); close();
return false; return false;
} else if (ret == 0) { } else if (ret == 0) {
@ -652,8 +699,7 @@ bool RclIntf::getEvent(RclMonEvent& ev, int msecs)
return false; return false;
int rret; int rret;
if ((rret=read(m_fd, m_evbuf, sizeof(m_evbuf))) <= 0) { if ((rret=read(m_fd, m_evbuf, sizeof(m_evbuf))) <= 0) {
LOGERR("RclIntf::getEvent: read failed, " << sizeof(m_evbuf) << LOGSYSERR("RclIntf::getEvent", "read", sizeof(m_evbuf));
"->" << rret << " errno " << errno << "\n");
close(); close();
return false; return false;
} }
@ -679,8 +725,7 @@ bool RclIntf::getEvent(RclMonEvent& ev, int msecs)
ev.m_path = path_cat(ev.m_path, evp->name); ev.m_path = path_cat(ev.m_path, evp->name);
} }
MONDEB("RclIntf::getEvent: " << event_name(evp->mask) << " " << MONDEB("RclIntf::getEvent: " << event_name(evp->mask) << " " << ev.m_path << "\n");
ev.m_path << std::endl);
if ((evp->mask & IN_MOVED_FROM) && (evp->mask & IN_ISDIR)) { if ((evp->mask & IN_MOVED_FROM) && (evp->mask & IN_ISDIR)) {
// We get this when a directory is renamed. Erase the subtree // We get this when a directory is renamed. Erase the subtree
@ -725,20 +770,472 @@ bool RclIntf::getEvent(RclMonEvent& ev, int msecs)
#endif // RCL_USE_INOTIFY #endif // RCL_USE_INOTIFY
#ifdef _WIN32
/*
* WIN32 VERSION NOTES:
*
* - When using non-recursive watches (one per dir), it appeared that
* watching a subdirectory of a given directory prevented renaming
* the top directory, Windows says: can't rename because open or a
* file in it is open. This is mostly why we use recursive watches
* on the topdirs only.
*/
#include <string>
#include <vector>
#include <thread>
#include <queue>
#include "safewindows.h"
typedef long WatchID;
class WatcherWin32;
class RclFSWatchWin32;
enum class Action {Add = 1, Delete = 2, Modify = 3, Move = 4};
// Virtual interface for the monitor callback. Note: this for compatibility with the efsw code, as
// rclmon uses a pull, not push interface. The callback pushes the events to a local queue from
// which they are then pulled by the upper level code.
class FileWatchListener {
public:
virtual ~FileWatchListener() {}
virtual void handleFileAction(WatchID watchid, const std::string& dir, const std::string& fn,
Action action, bool isdir, std::string oldfn = "" ) = 0;
};
// Internal watch data. This piggy-back our actual data pointer to the MS overlapped pointer. This
// is a bit of a hack, and we could probably use event Ids instead.
struct WatcherStructWin32
{
OVERLAPPED Overlapped;
WatcherWin32 *Watch;
};
// Actual data structure for one directory watch
class WatcherWin32 {
public:
WatchID ID;
FileWatchListener *Listener{nullptr};
bool Recursive;
std::string DirName;
std::string OldFileName;
HANDLE DirHandle{nullptr};
// do NOT make this bigger than 64K because it will fail if the folder being watched is on the
// network! (see http://msdn.microsoft.com/en-us/library/windows/desktop/aa365465(v=vs.85).aspx)
BYTE Buffer[8 * 1024];
DWORD NotifyFilter{0};
bool StopNow{false};
RclFSWatchWin32 *Watch{nullptr};
};
// The efsw top level file system watcher: manages all the directory watches.
class RclFSWatchWin32 {
public:
RclFSWatchWin32();
virtual ~RclFSWatchWin32();
// Add a directory watch
// On error returns -1
WatchID addWatch(const std::string& directory, FileWatchListener *watcher, bool recursive);
// 2nd stage of action processing (after the static handler which just reads the data)
void handleAction(WatcherWin32 *watch, const std::string& fn, unsigned long action);
bool ok() const {
return mInitOK;
}
// Fetch events, with msecs timeout if there are no more
void run(DWORD msecs);
private:
HANDLE mIOCP;
// Using a vector because we don't remove watches. Change to list if needed.
std::vector<WatcherStructWin32*> mWatches;
bool mInitOK{false};
WatchID mLastWatchID{0};
std::mutex mWatchesLock;
bool pathInWatches(const std::string& path);
/// Remove all directory watches.
void removeAllWatches();
};
// Adapter for the rclmon interface
class RclMonitorWin32 : public RclMonitor, public FileWatchListener {
public:
virtual ~RclMonitorWin32() {}
virtual bool addWatch(const string& path, bool /*isDir*/) override {
MONDEB("RclMonitorWin32::addWatch: " << path << "\n");
return m_fswatcher.addWatch(path, this, true) != -1;
}
virtual bool getEvent(RclMonEvent& ev, int msecs = -1) {
PRETEND_USE(msecs);
if (!m_events.empty()) {
ev = m_events.front();
m_events.pop();
return true;
}
m_fswatcher.run(msecs);
if (!m_events.empty()) {
ev = m_events.front();
m_events.pop();
return true;
}
return false;
}
virtual bool ok() const override {
return m_fswatcher.ok();
}
// Does this monitor generate 'exist' events at startup?
virtual bool generatesExist() const override {
return false;
}
// Can the caller avoid setting watches on subdirs ?
virtual bool isRecursive() const override {
return true;
}
virtual void handleFileAction(WatchID watchid, const std::string& dir, const std::string& fn,
Action action, bool isdir, std::string oldfn = "") {
MONDEB("RclMonitorWin32::handleFileAction: dir [" << dir << "] fn [" << fn << "] act " <<
int(action) << " isdir " << isdir << " oldfn [" << oldfn << "]\n");
RclMonEvent event;
switch (action) {
case Action::Move:
case Action::Add: event.m_etyp = isdir ?
RclMonEvent::RCLEVT_DIRCREATE : RclMonEvent::RCLEVT_MODIFY; break;
case Action::Delete:
event.m_etyp = RclMonEvent::RCLEVT_DELETE;
if (isdir) {
event.m_etyp |= RclMonEvent::RCLEVT_ISDIR;
}
break;
case Action::Modify: event.m_etyp = RclMonEvent::RCLEVT_MODIFY; break;
}
event.m_path = path_cat(dir, fn);
m_events.push(event);
}
// Save significant errno after monitor calls
int saved_errno{0};
private:
std::queue<RclMonEvent> m_events;
RclFSWatchWin32 m_fswatcher;
};
/// Stops monitoring a directory.
void DestroyWatch(WatcherStructWin32 *pWatch)
{
if (pWatch) {
WatcherWin32 *ww32 = pWatch->Watch;
ww32->StopNow = true;
CancelIoEx(ww32->DirHandle, &pWatch->Overlapped);
CloseHandle(ww32->DirHandle);
delete ww32;
// Shouldn't we call heapfree on the parameter here ??
}
}
/// Refreshes the directory monitoring.
bool RefreshWatch(WatcherStructWin32 *pWatch)
{
WatcherWin32 *ww32 = pWatch->Watch;
return ReadDirectoryChangesW(
ww32->DirHandle,
ww32->Buffer,
sizeof(ww32->Buffer),
ww32->Recursive,
ww32->NotifyFilter,
NULL,
&pWatch->Overlapped,
NULL
) != 0;
}
/// Starts monitoring a directory.
WatcherStructWin32 *CreateWatch(LPCWSTR szDirectory, bool recursive, DWORD NotifyFilter, HANDLE iocp)
{
WatcherStructWin32 *wsw32;
size_t ptrsize = sizeof(*wsw32);
wsw32 =static_cast<WatcherStructWin32*>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ptrsize));
WatcherWin32 *ww32 = new WatcherWin32();
wsw32->Watch = ww32;
ww32->DirHandle = CreateFileW(
szDirectory,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL
);
if (ww32->DirHandle != INVALID_HANDLE_VALUE &&
CreateIoCompletionPort(ww32->DirHandle, iocp, 0, 1)) {
ww32->NotifyFilter = NotifyFilter;
ww32->Recursive = recursive;
if (RefreshWatch(wsw32)) {
return wsw32;
}
}
CloseHandle(ww32->DirHandle);
delete ww32;
HeapFree(GetProcessHeap(), 0, wsw32);
return NULL;
}
RclFSWatchWin32::RclFSWatchWin32()
: mLastWatchID(0)
{
mIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
if (mIOCP && mIOCP != INVALID_HANDLE_VALUE)
mInitOK = true;
}
RclFSWatchWin32::~RclFSWatchWin32()
{
mInitOK = false;
if (mIOCP && mIOCP != INVALID_HANDLE_VALUE) {
PostQueuedCompletionStatus(mIOCP, 0, reinterpret_cast<ULONG_PTR>(this), NULL);
}
removeAllWatches();
CloseHandle(mIOCP);
}
WatchID RclFSWatchWin32::addWatch(const std::string& _dir,FileWatchListener *watcher,bool recursive)
{
LOGDEB("RclFSWatchWin32::addWatch: " << _dir << " recursive " << recursive << "\n");
std::string dir(_dir);
path_slashize(dir);
if (!path_isdir(dir)) {
LOGDEB("RclFSWatchWin32::addWatch: not a directory: " << dir << "\n");
return 0;
}
if (!path_readable(dir)) {
LOGINF("RclFSWatchWin32::addWatch: not readable: " << dir << "\n");
return 0;
}
path_catslash(dir);
auto wdir = utf8towchar(dir);
std::unique_lock<std::mutex> lock(mWatchesLock);
if (pathInWatches(dir)) {
MONDEB("RclFSWatchWin32::addWatch: already in watches: " << dir << "\n");
return 0;
}
WatchID watchid = ++mLastWatchID;
WatcherStructWin32 *watch = CreateWatch(
wdir.get(), recursive,
FILE_NOTIFY_CHANGE_CREATION |
FILE_NOTIFY_CHANGE_LAST_WRITE |
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_SIZE,
mIOCP
);
if (nullptr == watch) {
LOGINF("RclFSWatchWin32::addWatch: CreateWatch failed\n");
return -1;
}
// Add the handle to the handles vector
watch->Watch->ID = watchid;
watch->Watch->Watch = this;
watch->Watch->Listener = watcher;
watch->Watch->DirName = dir;
mWatches.push_back(watch);
return watchid;
}
void RclFSWatchWin32::removeAllWatches()
{
std::unique_lock<std::mutex> lock(mWatchesLock);
for( auto& watchp : mWatches) {
DestroyWatch(watchp);
}
mWatches.clear();
}
// Unpacks events and passes them to the event processor
void CALLBACK WatchCallback(DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{
if (dwNumberOfBytesTransfered == 0 || NULL == lpOverlapped) {
return;
}
WatcherStructWin32 *wsw32 = (WatcherStructWin32*)lpOverlapped;
WatcherWin32 *ww32 = wsw32->Watch;
PFILE_NOTIFY_INFORMATION pNotify;
size_t offset = 0;
do {
pNotify = (PFILE_NOTIFY_INFORMATION) &ww32->Buffer[offset];
offset += pNotify->NextEntryOffset;
std::string sfn;
wchartoutf8(pNotify->FileName, sfn, pNotify->FileNameLength / sizeof(WCHAR));
ww32->Watch->handleAction(ww32, sfn, pNotify->Action);
} while (pNotify->NextEntryOffset != 0);
if (!ww32->StopNow) {
RefreshWatch(wsw32);
}
}
void RclFSWatchWin32::run(DWORD msecs)
{
if (!mWatches.empty()) {
DWORD numOfBytes = 0;
OVERLAPPED* ov = NULL;
ULONG_PTR compKey = 0;
BOOL res = FALSE;
DWORD ms = msecs == -1 ? INFINITE : msecs;
while ((res = GetQueuedCompletionStatus(mIOCP, &numOfBytes, &compKey, &ov, ms))) {
if (compKey != 0 && compKey == reinterpret_cast<ULONG_PTR>(this)) {
// Called from ~RclFSWatchWin32. Must exit.
MONDEB("RclFSWatchWin32::run: queuedcompletion said need exit\n");
return;
} else {
std::unique_lock<std::mutex> lock(mWatchesLock);
WatchCallback(numOfBytes, ov);
}
}
} else {
// No watches yet.
MONDEB("RclFSWatchWin32::run: no watches yet\n");
DWORD ms = msecs == -1 ? 1000 : msecs;
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}
}
void RclFSWatchWin32::handleAction(WatcherWin32 *watch, const std::string& _fn, unsigned long action)
{
std::string fn(_fn);
Action fwAction;
path_slashize(fn);
MONDEB("handleAction: fn [" << fn << "] action " << action << "\n");
// In case fn is not a simple name but a relative path (probably
// possible/common if recursive is set ?), sort out the directory
// path and simple file name.
std::string newpath = path_cat(watch->DirName, fn);
bool isdir = path_isdir(newpath);
std::string simplefn = path_getsimple(newpath);
std::string folderPath = path_getfather(newpath);
switch (action) {
case FILE_ACTION_RENAMED_OLD_NAME:
watch->OldFileName = fn;
/* FALLTHROUGH */
case FILE_ACTION_REMOVED:
fwAction = Action::Delete;
// The system does not tell us if this was a directory, but we
// need the info. Check if it was in the watches.
// TBD: for a delete, we should delete all watches on the subtree !
path_catslash(newpath);
for (auto& watchp : mWatches) {
if (watchp->Watch->DirName == newpath) {
isdir = true;
break;
}
}
break;
case FILE_ACTION_ADDED:
fwAction = Action::Add;
break;
case FILE_ACTION_MODIFIED:
fwAction = Action::Modify;
break;
case FILE_ACTION_RENAMED_NEW_NAME: {
fwAction = Action::Move;
// If this is a directory, possibly update the watches. TBD: this seems wrong because we
// should process the whole subtree ? Also probably not needed at all because we are
// recursive and only set watches on the top directories.
if (isdir) {
// Update the new directory path
std::string oldpath = path_cat(watch->DirName, watch->OldFileName);
path_catslash(oldpath);
for (auto& watchp : mWatches) {
if (watchp->Watch->DirName == oldpath) {
watchp->Watch->DirName = newpath;
break;
}
}
}
std::string oldFolderPath = watch->DirName +
watch->OldFileName.substr(0, watch->OldFileName.find_last_of("/\\"));
if (folderPath == oldFolderPath) {
watch->Listener->handleFileAction(watch->ID, folderPath, simplefn, fwAction, isdir,
path_getsimple(watch->OldFileName));
} else {
// Calling the client with non-simple paths??
watch->Listener->handleFileAction(watch->ID, watch->DirName, fn, fwAction, isdir,
watch->OldFileName);
}
return;
}
default:
return;
};
watch->Listener->handleFileAction(watch->ID, folderPath, simplefn, fwAction, isdir);
}
bool RclFSWatchWin32::pathInWatches(const std::string& path)
{
for (const auto& wsw32 : mWatches) {
if (wsw32->Watch->DirName == path ) {
return true;
}
}
return false;
}
#endif // _WIN32
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
// The monitor 'factory' // The monitor 'factory'
static RclMonitor *makeMonitor() static RclMonitor *makeMonitor()
{ {
#ifdef RCL_USE_INOTIFY #ifdef _WIN32
return new RclMonitorWin32;
#else
# ifdef RCL_USE_INOTIFY
return new RclIntf; return new RclIntf;
#endif # elif defined(RCL_USE_FAM)
#ifndef RCL_USE_INOTIFY
#ifdef RCL_USE_FAM
return new RclFAM; return new RclFAM;
#endif # endif
#endif #endif
LOGINFO("RclMonitor: neither Inotify nor Fam was compiled as file system " LOGINFO("RclMonitor: neither Inotify nor Fam was compiled as file system "
"change notification interface\n"); "change notification interface\n");
return 0; return 0;
} }
#endif // RCL_MONITOR #endif // RCL_MONITOR

View File

@ -103,7 +103,9 @@ static struct option long_options[] = {
{0, 0, 0, 0} {0, 0, 0, 0}
}; };
#ifndef _WIN32
ReExec *o_reexec; ReExec *o_reexec;
#endif
// Globals for atexit cleanup // Globals for atexit cleanup
static ConfIndexer *confindexer; static ConfIndexer *confindexer;
@ -867,11 +869,13 @@ int main(int argc, char *argv[])
LOGDEB("recollindex: sleeping " << sleepsecs << "\n"); LOGDEB("recollindex: sleeping " << sleepsecs << "\n");
for (int i = 0; i < sleepsecs; i++) { for (int i = 0; i < sleepsecs; i++) {
sleep(1); sleep(1);
#ifndef _WIN32
// Check that x11 did not go away while we were sleeping. // Check that x11 did not go away while we were sleeping.
if (!(op_flags & OPT_x) && !x11IsAlive()) { if (!(op_flags & OPT_x) && !x11IsAlive()) {
LOGDEB("X11 session went away during initial sleep period\n"); LOGDEB("X11 session went away during initial sleep period\n");
exit(0); exit(0);
} }
#endif
} }
} }

View File

@ -26,13 +26,18 @@
#include "subtreelist.h" #include "subtreelist.h"
#include "log.h" #include "log.h"
bool subtreelist(RclConfig *config, const string& top, bool subtreelist(RclConfig *config, const string& _top, vector<string>& paths)
vector<string>& paths)
{ {
LOGDEB("subtreelist: top: [" << (top) << "]\n" ); std::string top(_top);
#ifdef _WIN32
// Need to convert c:path to /c/path because this is how paths are indexed
top = path_slashdrive(top);
#endif
LOGDEB("subtreelist: top: [" << top << "]\n");
Rcl::Db rcldb(config); Rcl::Db rcldb(config);
if (!rcldb.open(Rcl::Db::DbRO)) { if (!rcldb.open(Rcl::Db::DbRO)) {
LOGERR("subtreelist: can't open database in [" << config->getDbDir() << LOGERR("subtreelist: can't open index in [" << config->getDbDir() <<
"]: " << rcldb.getReason() << "\n"); "]: " << rcldb.getReason() << "\n");
return false; return false;
} }

View File

@ -51,7 +51,7 @@ using namespace std;
// The internal path element separator. This can't be the same as the rcldb // The internal path element separator. This can't be the same as the rcldb
// file to ipath separator : "|" // file to ipath separator : "|"
// We replace it with a control char if it comes out of a filter (ie: // We replace it with a control char if it comes out of a filter (ie:
// rclzip or rclchm can do this). If you want the SOH control char // rclzip.py or rclchm.py can do this). If you want the SOH control char
// inside an ipath, you're out of luck (and a bit weird). // inside an ipath, you're out of luck (and a bit weird).
static const string cstr_isep(":"); static const string cstr_isep(":");
@ -562,13 +562,13 @@ bool FileInterner::dijontorcl(Rcl::Doc& doc)
const string *val = 0; const string *val = 0;
if (!doc.peekmeta(Rcl::Doc::keymd5, &val) || val->empty()) if (!doc.peekmeta(Rcl::Doc::keymd5, &val) || val->empty())
doc.meta[Rcl::Doc::keymd5] = ent.second; doc.meta[Rcl::Doc::keymd5] = ent.second;
} else if (ent.first == cstr_dj_keymt || } else if (ent.first == cstr_dj_keymt || ent.first == cstr_dj_keycharset) {
ent.first == cstr_dj_keycharset) {
// don't need/want these. // don't need/want these.
} else { } else {
LOGDEB2("dijontorcl: " << m_cfg->fieldCanon(ent.first) << " -> " << LOGDEB2("dijontorcl: " << m_cfg->fieldCanon(ent.first) << " -> " << ent.second << "\n");
ent.second << endl); if (!ent.second.empty()) {
doc.addmeta(m_cfg->fieldCanon(ent.first), ent.second); doc.meta[m_cfg->fieldCanon(ent.first)] = ent.second;
}
} }
} }
if (doc.meta[Rcl::Doc::keyabs].empty() && if (doc.meta[Rcl::Doc::keyabs].empty() &&
@ -583,7 +583,7 @@ const set<string> nocopyfields{cstr_dj_keycontent, cstr_dj_keymd,
cstr_dj_keyanc, cstr_dj_keyorigcharset, cstr_dj_keyfn, cstr_dj_keyanc, cstr_dj_keyorigcharset, cstr_dj_keyfn,
cstr_dj_keymt, cstr_dj_keycharset, cstr_dj_keyds}; cstr_dj_keymt, cstr_dj_keycharset, cstr_dj_keyds};
static void copymeta(const RclConfig *cfg,Rcl::Doc& doc, const RecollFilter* hp) static void copymeta(const RclConfig *cfg, Rcl::Doc& doc, const RecollFilter* hp)
{ {
for (const auto& entry : hp->get_meta_data()) { for (const auto& entry : hp->get_meta_data()) {
if (nocopyfields.find(entry.first) == nocopyfields.end()) { if (nocopyfields.find(entry.first) == nocopyfields.end()) {
@ -650,7 +650,7 @@ void FileInterner::collectIpathAndMT(Rcl::Doc& doc) const
// handlers to use setfield() instead of embedding // handlers to use setfield() instead of embedding
// metadata in the HTML meta tags. // metadata in the HTML meta tags.
if (i == 0 || !pathelprev.empty()) { if (i == 0 || !pathelprev.empty()) {
copymeta(m_cfg, doc, m_handlers[i]); copymeta(m_cfg, doc, m_handlers[i == 0 ? 0 : i-1]);
} }
if (doc.fbytes.empty()) { if (doc.fbytes.empty()) {
lltodecstr(m_handlers[i]->get_docsize(), doc.fbytes); lltodecstr(m_handlers[i]->get_docsize(), doc.fbytes);
@ -744,8 +744,7 @@ int FileInterner::addHandler()
LOGINFO("FileInterner::addHandler: no filter for [" << mimetype << "]\n"); LOGINFO("FileInterner::addHandler: no filter for [" << mimetype << "]\n");
return ADD_CONTINUE; return ADD_CONTINUE;
} }
newflt->set_property(Dijon::Filter::OPERATING_MODE, newflt->set_property(Dijon::Filter::OPERATING_MODE, m_forPreview ? "view" : "index");
m_forPreview ? "view" : "index");
if (!charset.empty()) if (!charset.empty())
newflt->set_property(Dijon::Filter::DEFAULT_CHARSET, charset); newflt->set_property(Dijon::Filter::DEFAULT_CHARSET, charset);

View File

@ -82,8 +82,7 @@ bool MimeHandlerExec::set_document_file_impl(const std::string& mt,
tpsread = true; tpsread = true;
if (!nomd5tps.empty()) { if (!nomd5tps.empty()) {
if (params.size() && if (params.size() &&
nomd5tps.find(path_getsimple(params[0])) != nomd5tps.find(path_getsimple(params[0])) != nomd5tps.end()) {
nomd5tps.end()) {
m_handlernomd5 = true; m_handlernomd5 = true;
} }
// On windows the 1st param is often a script interp // On windows the 1st param is often a script interp

View File

@ -73,7 +73,7 @@ text/plainData: 10
* time). Absent during indexing (ipaths are generated and sent back from * time). Absent during indexing (ipaths are generated and sent back from
* the script) * the script)
* - Mimetype: this is the mime type for the (possibly container) file. * - Mimetype: this is the mime type for the (possibly container) file.
* Can be useful to filters which handle multiple types, like rclaudio. * Can be useful to filters which handle multiple types, like rclaudio.py.
* *
* The script answers with messages having the following fields: * The script answers with messages having the following fields:
* - Document: translated document data. * - Document: translated document data.

View File

@ -34,6 +34,7 @@
#include "rclmain_w.h" #include "rclmain_w.h"
#include "rclzg.h" #include "rclzg.h"
#include "pathut.h" #include "pathut.h"
#include "unacpp.h"
using namespace std; using namespace std;
@ -42,7 +43,6 @@ static const vector<string> browser_list{
"opera", "google-chrome", "chromium-browser", "opera", "google-chrome", "chromium-browser",
"palemoon", "iceweasel", "firefox", "konqueror", "epiphany"}; "palemoon", "iceweasel", "firefox", "konqueror", "epiphany"};
// Start native viewer or preview for input Doc. This is used to allow // Start native viewer or preview for input Doc. This is used to allow
// using recoll from another app (e.g. Unity Scope) to view embedded // using recoll from another app (e.g. Unity Scope) to view embedded
// result docs (docs with an ipath). . We act as a proxy to extract // result docs (docs with an ipath). . We act as a proxy to extract
@ -155,13 +155,27 @@ void RclMain::openWith(Rcl::Doc doc, string cmdspec)
execViewer(subs, false, execname, lcmd, cmdspec, doc); execViewer(subs, false, execname, lcmd, cmdspec, doc);
} }
void RclMain::startNativeViewer(Rcl::Doc doc, int pagenum, QString term) static bool pagenumNeeded(const std::string& cmd)
{ {
return cmd.find("%p") != std::string::npos;
}
static bool linenumNeeded(const std::string& cmd)
{
return cmd.find("%l") != std::string::npos;
}
static bool termNeeded(const std::string& cmd)
{
return cmd.find("%s") != std::string::npos;
}
void RclMain::startNativeViewer(Rcl::Doc doc, int pagenum, QString qterm)
{
std::string term = qs2utf8s(qterm);
string apptag; string apptag;
doc.getmeta(Rcl::Doc::keyapptg, &apptag); doc.getmeta(Rcl::Doc::keyapptg, &apptag);
LOGDEB("RclMain::startNativeViewer: mtype [" << doc.mimetype << LOGDEB("RclMain::startNativeViewer: mtype [" << doc.mimetype <<
"] apptag [" << apptag << "] page " << pagenum << " term [" << "] apptag [" << apptag << "] page " << pagenum << " term [" <<
qs2utf8s(term) << "] url [" << doc.url << "] ipath [" << term << "] url [" << doc.url << "] ipath [" <<
doc.ipath << "]\n"); doc.ipath << "]\n");
// Look for appropriate viewer // Look for appropriate viewer
@ -377,19 +391,19 @@ void RclMain::startNativeViewer(Rcl::Doc doc, int pagenum, QString term)
// If we are not called with a page number (which would happen for a call // If we are not called with a page number (which would happen for a call
// from the snippets window), see if we can compute a page number anyway. // from the snippets window), see if we can compute a page number anyway.
if (pagenum == -1) { if (m_source && pagenum == -1 && (pagenumNeeded(cmd) || termNeeded(cmd)|| linenumNeeded(cmd))) {
pagenum = 1; pagenum = m_source->getFirstMatchPage(doc, term);
string lterm;
if (m_source)
pagenum = m_source->getFirstMatchPage(doc, lterm);
if (pagenum == -1) if (pagenum == -1)
pagenum = 1; pagenum = 1;
else // We get the match term used to compute the page
term = QString::fromUtf8(lterm.c_str());
} }
char cpagenum[20];
sprintf(cpagenum, "%d", pagenum);
int line = 1;
if (m_source && !term.empty() && linenumNeeded(cmd)) {
if (doc.text.empty()) {
rcldb->getDocRawText(doc);
}
line = m_source->getFirstMatchLine(doc, term);
}
// Substitute %xx inside arguments // Substitute %xx inside arguments
string efftime; string efftime;
@ -408,9 +422,10 @@ void RclMain::startNativeViewer(Rcl::Doc doc, int pagenum, QString term)
subs["f"] = fn; subs["f"] = fn;
subs["F"] = fn; subs["F"] = fn;
subs["i"] = FileInterner::getLastIpathElt(doc.ipath); subs["i"] = FileInterner::getLastIpathElt(doc.ipath);
subs["l"] = ulltodecstr(line);
subs["M"] = doc.mimetype; subs["M"] = doc.mimetype;
subs["p"] = cpagenum; subs["p"] = ulltodecstr(pagenum);
subs["s"] = (const char*)term.toLocal8Bit(); subs["s"] = term;
subs["U"] = url_encode(url); subs["U"] = url_encode(url);
subs["u"] = url; subs["u"] = url;
// Let %(xx) access all metadata. // Let %(xx) access all metadata.

View File

@ -140,8 +140,7 @@ public slots:
virtual void showActionsSearch(); virtual void showActionsSearch();
virtual void startPreview(int docnum, Rcl::Doc doc, int keymods); virtual void startPreview(int docnum, Rcl::Doc doc, int keymods);
virtual void startPreview(Rcl::Doc); virtual void startPreview(Rcl::Doc);
virtual void startNativeViewer(Rcl::Doc, int pagenum = -1, virtual void startNativeViewer(Rcl::Doc, int pagenum = -1, QString term = QString());
QString term = QString());
virtual void openWith(Rcl::Doc, string); virtual void openWith(Rcl::Doc, string);
virtual void saveDocToFile(Rcl::Doc); virtual void saveDocToFile(Rcl::Doc);
virtual void previewNextInTab(Preview *, int sid, int docnum); virtual void previewNextInTab(Preview *, int sid, int docnum);

View File

@ -117,40 +117,39 @@ INCLUDEPATH += ../common ../index ../internfile ../query ../unac \
../utils ../aspell ../rcldb ../qtgui ../xaposix \ ../utils ../aspell ../rcldb ../qtgui ../xaposix \
confgui widgets confgui widgets
windows { windows {
DEFINES += PSAPI_VERSION=1 DEFINES += PSAPI_VERSION=1
DEFINES += __WIN32__ DEFINES += __WIN32__
DEFINES += UNICODE DEFINES += UNICODE
RC_FILE = recoll.rc RC_FILE = recoll.rc
HEADERS += \ HEADERS += \
winschedtool.h winschedtool.h
SOURCES += \ SOURCES += \
winschedtool.cpp winschedtool.cpp
FORMS += \ FORMS += \
winschedtool.ui winschedtool.ui
contains(QMAKE_CC, gcc){ contains(QMAKE_CC, gcc){
# MingW # MingW
QMAKE_CXXFLAGS += -std=c++11 -Wno-unused-parameter QMAKE_CXXFLAGS += -std=c++11 -Wno-unused-parameter
LIBS += C:/recoll/src/windows/build-librecoll-Desktop_Qt_5_8_0_MinGW_32bit-Release/release/librecoll.dll LIBS += \
} C:/recoll/src/windows/build-librecoll-Desktop_Qt_5_8_0_MinGW_32bit-Release/release/librecoll.dll
}
contains(QMAKE_CC, cl){ contains(QMAKE_CC, cl){
# MSVC # MSVC
RECOLLDEPS = ../../../recolldeps/msvc RECOLLDEPS = ../../../recolldeps/msvc
DEFINES += USING_STATIC_LIBICONV DEFINES += USING_STATIC_LIBICONV
PRE_TARGETDEPS = \
../windows/build-librecoll-Desktop_Qt_5_14_2_MSVC2017_32bit-Release/release/librecoll.lib
LIBS += \ LIBS += \
-L../windows/build-librecoll-Desktop_Qt_5_14_2_MSVC2017_32bit-Release/release \ -L../windows/build-librecoll-Desktop_Qt_5_14_2_MSVC2017_32bit-Release/release -llibrecoll \
-llibrecoll \
$$RECOLLDEPS/libxml2/libxml2-2.9.4+dfsg1/win32/bin.msvc/libxml2.lib \ $$RECOLLDEPS/libxml2/libxml2-2.9.4+dfsg1/win32/bin.msvc/libxml2.lib \
$$RECOLLDEPS/libxslt/libxslt-1.1.29/win32/bin.msvc/libxslt.lib \ $$RECOLLDEPS/libxslt/libxslt-1.1.29/win32/bin.msvc/libxslt.lib \
-L../windows/build-libxapian-Desktop_Qt_5_14_2_MSVC2017_32bit-Release/release \ -L../windows/build-libxapian-Desktop_Qt_5_14_2_MSVC2017_32bit-Release/release -llibxapian \
-llibxapian \ -L$$RECOLLDEPS/build-libiconv-Desktop_Qt_5_14_2_MSVC2017_32bit-Release/release -llibiconv \
-L$$RECOLLDEPS/build-libiconv-Desktop_Qt_5_14_2_MSVC2017_32bit-Release/release \
-llibiconv \
$$RECOLLDEPS/zlib-1.2.11/zdll.lib \ $$RECOLLDEPS/zlib-1.2.11/zdll.lib \
-lrpcrt4 -lws2_32 -luser32 \ -lrpcrt4 -lws2_32 -luser32 -lshell32 -lshlwapi -lpsapi -lkernel32
-lshell32 -lshlwapi -lpsapi -lkernel32
} }
} }
@ -170,30 +169,32 @@ mac {
rtitool.cpp rtitool.cpp
FORMS += \ FORMS += \
crontool.ui \ crontool.ui \
rtitool.ui rtitool.ui
LIBS += \ LIBS += \
../windows/build-librecoll-Desktop_Qt_5_14_2_clang_64bit-Release/liblibrecoll.a \ ../windows/build-librecoll-Desktop_Qt_5_14_2_clang_64bit-Release/liblibrecoll.a \
../../../xapian-core-1.4.18/.libs/libxapian.a \ ../../../xapian-core-1.4.18/.libs/libxapian.a \
-lxslt -lxml2 -liconv -lz -lxslt -lxml2 -liconv -lz
ICON = images/recoll.icns ICON = images/recoll.icns
system(cp ../sampleconf/mimeview.mac ../mimeview)
APP_EXAMPLES.files = \ APP_EXAMPLES.files = \
../sampleconf/fragment-buttons.xml \ ../sampleconf/fragment-buttons.xml \
../sampleconf/fields \ ../sampleconf/fields \
../sampleconf/recoll.conf \ ../sampleconf/recoll.conf \
../sampleconf/mimeconf \ ../sampleconf/mimeconf \
../sampleconf/recoll.qss \ ../sampleconf/mimeview \
../sampleconf/recoll-dark.qss \ ../sampleconf/mimemap \
../sampleconf/recoll-dark.css \ ../sampleconf/recoll.qss \
../sampleconf/mimemap \ ../sampleconf/recoll-dark.qss \
../mimeview ../sampleconf/recoll-dark.css
APP_EXAMPLES.path = Contents/Resources/examples APP_EXAMPLES.path = Contents/Resources/examples
APP_EXAMPLES_MAC.files = \
../sampleconf/macos/mimeview
APP_EXAMPLES_MAC.path = Contents/Resources/examples/macos
APP_FILTERS.files = \ APP_FILTERS.files = \
../filters/abiword.xsl \ ../filters/abiword.xsl \
../filters/cmdtalk.py \ ../filters/cmdtalk.py \
@ -209,30 +210,30 @@ mac {
../filters/openxml-word-body.xsl \ ../filters/openxml-word-body.xsl \
../filters/openxml-meta.xsl \ ../filters/openxml-meta.xsl \
../filters/ppt-dump.py \ ../filters/ppt-dump.py \
../filters/rcl7z \ ../filters/rcl7z.py \
../filters/rclaptosidman \ ../filters/rclaptosidman \
../filters/rclaudio \ ../filters/rclaudio.py \
../filters/rclbasehandler.py \ ../filters/rclbasehandler.py \
../filters/rclbibtex.sh \ ../filters/rclbibtex.sh \
../filters/rclcheckneedretry.sh \ ../filters/rclcheckneedretry.sh \
../filters/rclchm \ ../filters/rclchm.py \
../filters/rcldia \ ../filters/rcldia.py \
../filters/rcldjvu.py \ ../filters/rcldjvu.py \
../filters/rcldoc.py \ ../filters/rcldoc.py \
../filters/rcldvi \ ../filters/rcldvi \
../filters/rclepub \ ../filters/rclepub.py \
../filters/rclepub1 \ ../filters/rclepub1.py \
../filters/rclexec1.py \ ../filters/rclexec1.py \
../filters/rclexecm.py \ ../filters/rclexecm.py \
../filters/rclfb2.py \ ../filters/rclfb2.py \
../filters/rclgaim \ ../filters/rclgaim \
../filters/rclgenxslt.py \ ../filters/rclgenxslt.py \
../filters/rclhwp.py \ ../filters/rclhwp.py \
../filters/rclics \ ../filters/rclics.py \
../filters/rclimg \ ../filters/rclimg \
../filters/rclimg.py \ ../filters/rclimg.py \
../filters/rclinfo \ ../filters/rclinfo.py \
../filters/rclkar \ ../filters/rclkar.py \
../filters/rclkwd \ ../filters/rclkwd \
../filters/rcllatinclass.py \ ../filters/rcllatinclass.py \
../filters/rcllatinstops.zip \ ../filters/rcllatinstops.zip \
@ -250,21 +251,21 @@ mac {
../filters/rclpst.py \ ../filters/rclpst.py \
../filters/rclpurple \ ../filters/rclpurple \
../filters/rclpython.py \ ../filters/rclpython.py \
../filters/rclrar \ ../filters/rclrar.py \
../filters/rclrtf.py \ ../filters/rclrtf.py \
../filters/rclscribus \ ../filters/rclscribus \
../filters/rclshowinfo \ ../filters/rclshowinfo \
../filters/rcltar \ ../filters/rcltar.py \
../filters/rcltex \ ../filters/rcltex \
../filters/rcltext.py \ ../filters/rcltext.py \
../filters/rcluncomp \ ../filters/rcluncomp \
../filters/rcluncomp.py \ ../filters/rcluncomp.py \
../filters/rclwar \ ../filters/rclwar.py \
../filters/rclxls.py \ ../filters/rclxls.py \
../filters/rclxml.py \ ../filters/rclxml.py \
../filters/rclxmp.py \ ../filters/rclxmp.py \
../filters/rclxslt.py \ ../filters/rclxslt.py \
../filters/rclzip \ ../filters/rclzip.py \
../filters/recoll-we-move-files.py \ ../filters/recoll-we-move-files.py \
../filters/recollepub.zip \ ../filters/recollepub.zip \
../filters/svg.xsl \ ../filters/svg.xsl \

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2012 J.F.Dockes /* Copyright (C) 2012-2021 J.F.Dockes
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation; either version 2 of the License, or
@ -67,8 +67,7 @@ using namespace std;
class PlainToRichQtSnippets : public PlainToRich { class PlainToRichQtSnippets : public PlainToRich {
public: public:
virtual string startMatch(unsigned int) { virtual string startMatch(unsigned int) {
return string("<span class='rclmatch' style='") return string("<span class='rclmatch' style='") + qs2utf8s(prefs.qtermstyle) + string("'>");
+ qs2utf8s(prefs.qtermstyle) + string("'>");
} }
virtual string endMatch() { virtual string endMatch() {
return string("</span>"); return string("</span>");
@ -82,12 +81,10 @@ void SnippetsW::init()
QPushButton *searchButton = new QPushButton(tr("Search")); QPushButton *searchButton = new QPushButton(tr("Search"));
searchButton->setAutoDefault(false); searchButton->setAutoDefault(false);
buttonBox->addButton(searchButton, QDialogButtonBox::ActionRole); buttonBox->addButton(searchButton, QDialogButtonBox::ActionRole);
// setWindowFlags(Qt::WindowStaysOnTopHint);
searchFM->hide(); searchFM->hide();
onNewShortcuts(); onNewShortcuts();
connect(&SCBase::scBase(), SIGNAL(shortcutsChanged()), connect(&SCBase::scBase(), SIGNAL(shortcutsChanged()), this, SLOT(onNewShortcuts()));
this, SLOT(onNewShortcuts()));
QPushButton *closeButton = buttonBox->button(QDialogButtonBox::Close); QPushButton *closeButton = buttonBox->button(QDialogButtonBox::Close);
if (closeButton) if (closeButton)
@ -105,11 +102,9 @@ void SnippetsW::init()
browserw = new QWebView(this); browserw = new QWebView(this);
verticalLayout->insertWidget(0, browserw); verticalLayout->insertWidget(0, browserw);
browser->setUrl(QUrl(QString::fromUtf8("about:blank"))); browser->setUrl(QUrl(QString::fromUtf8("about:blank")));
connect(browser, SIGNAL(linkClicked(const QUrl &)), connect(browser, SIGNAL(linkClicked(const QUrl &)), this, SLOT(onLinkClicked(const QUrl &)));
this, SLOT(onLinkClicked(const QUrl &)));
browser->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); browser->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
browser->page()->currentFrame()->setScrollBarPolicy(Qt::Horizontal, browser->page()->currentFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
Qt::ScrollBarAlwaysOff);
QWEBSETTINGS *ws = browser->page()->settings(); QWEBSETTINGS *ws = browser->page()->settings();
if (prefs.reslistfontfamily != "") { if (prefs.reslistfontfamily != "") {
ws->setFontFamily(QWEBSETTINGS::StandardFont, prefs.reslistfontfamily); ws->setFontFamily(QWEBSETTINGS::StandardFont, prefs.reslistfontfamily);
@ -136,8 +131,7 @@ void SnippetsW::init()
#else #else
browserw = new QTextBrowser(this); browserw = new QTextBrowser(this);
verticalLayout->insertWidget(0, browserw); verticalLayout->insertWidget(0, browserw);
connect(browser, SIGNAL(anchorClicked(const QUrl &)), connect(browser, SIGNAL(anchorClicked(const QUrl &)), this, SLOT(onLinkClicked(const QUrl &)));
this, SLOT(onLinkClicked(const QUrl &)));
browser->setReadOnly(true); browser->setReadOnly(true);
browser->setUndoRedoEnabled(false); browser->setUndoRedoEnabled(false);
browser->setOpenLinks(false); browser->setOpenLinks(false);
@ -183,8 +177,7 @@ void SnippetsW::createPopupMenu(const QPoint& pos)
{ {
QMenu *popup = new QMenu(this); QMenu *popup = new QMenu(this);
if (m_sortingByPage) { if (m_sortingByPage) {
popup->addAction(tr("Sort By Relevance"), this, popup->addAction(tr("Sort By Relevance"), this, SLOT(reloadByRelevance()));
SLOT(reloadByRelevance()));
} else { } else {
popup->addAction(tr("Sort By Page"), this, SLOT(reloadByPage())); popup->addAction(tr("Sort By Page"), this, SLOT(reloadByPage()));
} }
@ -230,29 +223,22 @@ void SnippetsW::onSetDoc(Rcl::Doc doc, std::shared_ptr<DocSequence> source)
source->getTerms(hdata); source->getTerms(hdata);
ostringstream oss; ostringstream oss;
oss << oss << "<html><head>"
"<html><head>" "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">";
"<meta http-equiv=\"content-type\" "
"content=\"text/html; charset=utf-8\">";
oss << "<style type=\"text/css\">\nbody,table,select,input {\n"; oss << "<style type=\"text/css\">\nbody,table,select,input {\n";
oss << "color: " + qs2utf8s(prefs.fontcolor) + ";\n"; oss << "color: " + qs2utf8s(prefs.fontcolor) + ";\n";
oss << "}\n</style>\n"; oss << "}\n</style>\n";
oss << qs2utf8s(prefs.darkreslistheadertext) << qs2utf8s(prefs.reslistheadertext); oss << qs2utf8s(prefs.darkreslistheadertext) << qs2utf8s(prefs.reslistheadertext);
oss << oss << "</head><body><table class=\"snippets\">";
"</head>"
"<body>"
"<table class=\"snippets\">"
;
g_hiliter.set_inputhtml(false); g_hiliter.set_inputhtml(false);
bool nomatch = true; bool nomatch = true;
for (const auto& snippet : vpabs) { for (const auto& snippet : vpabs) {
if (snippet.page == -1) { if (snippet.page == -1) {
oss << "<tr><td colspan=\"2\">" << oss << "<tr><td colspan=\"2\">" << snippet.snippet << "</td></tr>" << "\n";
snippet.snippet << "</td></tr>" << endl;
continue; continue;
} }
list<string> lr; list<string> lr;
@ -263,13 +249,12 @@ void SnippetsW::onSetDoc(Rcl::Doc doc, std::shared_ptr<DocSequence> source)
nomatch = false; nomatch = false;
oss << "<tr><td>"; oss << "<tr><td>";
if (snippet.page > 0) { if (snippet.page > 0) {
oss << "<a href=\"http://h/P" << snippet.page << "T" << oss << "<a href=\"http://h/P" << snippet.page << "T" << snippet.term << "\">" <<
snippet.term << "\">" "P.&nbsp;" << snippet.page << "</a>";
<< "P.&nbsp;" << snippet.page << "</a>";
} }
oss << "</td><td>" << lr.front().c_str() << "</td></tr>" << endl; oss << "</td><td>" << lr.front().c_str() << "</td></tr>" << "\n";
} }
oss << "</table>" << endl; oss << "</table>" << "\n";
if (nomatch) { if (nomatch) {
oss.str("<html><head></head><body>\n"); oss.str("<html><head></head><body>\n");
oss << qs2utf8s(tr("<p>Sorry, no exact match was found within limits. " oss << qs2utf8s(tr("<p>Sorry, no exact match was found within limits. "
@ -278,12 +263,12 @@ void SnippetsW::onSetDoc(Rcl::Doc doc, std::shared_ptr<DocSequence> source)
} }
oss << "\n</body></html>"; oss << "\n</body></html>";
#if defined(USING_WEBKIT) || defined(USING_WEBENGINE) #if defined(USING_WEBKIT) || defined(USING_WEBENGINE)
browser->setHtml(QString::fromUtf8(oss.str().c_str())); browser->setHtml(u8s2qs(oss.str()));
#else #else
browser->clear(); browser->clear();
browser->append("."); browser->append(".");
browser->clear(); browser->clear();
browser->insertHtml(QString::fromUtf8(oss.str().c_str())); browser->insertHtml(u8s2qs(oss.str()));
browser->moveCursor (QTextCursor::Start); browser->moveCursor (QTextCursor::Start);
browser->ensureCursorVisible(); browser->ensureCursorVisible();
#endif #endif
@ -354,8 +339,7 @@ void SnippetsW::onLinkClicked(const QUrl &url)
string term; string term;
if (termpos != string::npos) if (termpos != string::npos)
term = ascurl.substr(termpos+1); term = ascurl.substr(termpos+1);
emit startNativeViewer(m_doc, page, emit startNativeViewer(m_doc, page, u8s2qs(term));
QString::fromUtf8(term.c_str()));
return; return;
} }
} }

View File

@ -111,6 +111,9 @@ public:
virtual int getFirstMatchPage(Rcl::Doc&, std::string&) { virtual int getFirstMatchPage(Rcl::Doc&, std::string&) {
return -1; return -1;
} }
virtual int getFirstMatchLine(const Rcl::Doc&, const std::string&) {
return 1;
}
/** Get duplicates. */ /** Get duplicates. */
virtual bool docDups(const Rcl::Doc&, std::vector<Rcl::Doc>&) { virtual bool docDups(const Rcl::Doc&, std::vector<Rcl::Doc>&) {
return false; return false;

View File

@ -126,6 +126,17 @@ int DocSequenceDb::getFirstMatchPage(Rcl::Doc &doc, string& term)
return -1; return -1;
} }
int DocSequenceDb::getFirstMatchLine(const Rcl::Doc &doc, const string& term)
{
std::unique_lock<std::mutex> locker(o_dblock);
if (!setQuery())
return false;
if (m_q->whatDb()) {
return m_q->getFirstMatchLine(doc, term);
}
return 1;
}
list<string> DocSequenceDb::expand(Rcl::Doc &doc) list<string> DocSequenceDb::expand(Rcl::Doc &doc)
{ {
std::unique_lock<std::mutex> locker(o_dblock); std::unique_lock<std::mutex> locker(o_dblock);

View File

@ -43,6 +43,7 @@ public:
virtual bool getAbstract(Rcl::Doc &doc, std::vector<std::string>&) override; virtual bool getAbstract(Rcl::Doc &doc, std::vector<std::string>&) override;
virtual int getFirstMatchPage(Rcl::Doc&, std::string& term) override; virtual int getFirstMatchPage(Rcl::Doc&, std::string& term) override;
virtual int getFirstMatchLine(const Rcl::Doc&, const std::string& term) override;
virtual bool docDups(const Rcl::Doc& doc, std::vector<Rcl::Doc>& dups) virtual bool docDups(const Rcl::Doc& doc, std::vector<Rcl::Doc>& dups)
override; override;
virtual std::string getDescription() override; virtual std::string getDescription() override;

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2004 J.F.Dockes /* Copyright (C) 2004-2021 J.F.Dockes
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation; either version 2 of the License, or
@ -60,8 +60,7 @@ public:
* @param in raw text out of internfile. * @param in raw text out of internfile.
* @param out rich text output, divided in chunks (to help our caller * @param out rich text output, divided in chunks (to help our caller
* avoid inserting half tags into textedit which doesnt like it) * avoid inserting half tags into textedit which doesnt like it)
* @param in hdata terms and groups to be highlighted. These are * @param in hdata terms and groups to be highlighted. See utils/hldata.h
* lowercase and unaccented.
* @param chunksize max size of chunks in output list * @param chunksize max size of chunks in output list
*/ */
virtual bool plaintorich(const std::string &in, std::list<std::string> &out, virtual bool plaintorich(const std::string &in, std::list<std::string> &out,

View File

@ -141,11 +141,9 @@ public:
// add/update fragment definition. // add/update fragment definition.
virtual bool takeword(const std::string& term, int pos, int bts, int bte) { virtual bool takeword(const std::string& term, int pos, int bts, int bte) {
LOGDEB1("takeword: [" << term << "] bytepos: "<<bts<<":"<<bte<<endl); LOGDEB1("takeword: [" << term << "] bytepos: "<<bts<<":"<<bte<<endl);
// Limit time taken with monster documents. The resulting // Limit time taken with monster documents. The resulting abstract will be incorrect or
// abstract will be incorrect or inexistent, but this is // inexistent, but this is better than taking forever (the default cutoff value comes from
// better than taking forever (the default cutoff value comes // the snippetMaxPosWalk configuration parameter, and is 10E6)
// from the snippetMaxPosWalk configuration parameter, and is
// 10E6)
if (maxtermcount && termcount++ > maxtermcount) { if (maxtermcount && termcount++ > maxtermcount) {
LOGINF("Rclabsfromtext: stopping because maxtermcount reached: "<< LOGINF("Rclabsfromtext: stopping because maxtermcount reached: "<<
maxtermcount << endl); maxtermcount << endl);
@ -154,8 +152,7 @@ public:
} }
// Also limit the number of fragments (just in case safety) // Also limit the number of fragments (just in case safety)
if (m_fragments.size() > maxtermcount / 100) { if (m_fragments.size() > maxtermcount / 100) {
LOGINF("Rclabsfromtext: stopping because maxfragments reached: "<< LOGINF("Rclabsfromtext: stopping: max fragments count: " << maxtermcount/100 << "\n");
maxtermcount/100 << endl);
retflags |= ABSRES_TRUNC; retflags |= ABSRES_TRUNC;
return false; return false;
} }
@ -193,8 +190,7 @@ public:
m_curterm = term; m_curterm = term;
m_curtermcoef = coef; m_curtermcoef = coef;
} else { } else {
LOGDEB2("Extending current fragment: " << m_remainingWords << LOGDEB2("Extending current fragment: "<<m_remainingWords<<" -> "<<m_ctxwords<< "\n");
" -> " << m_ctxwords << endl);
m_extcount++; m_extcount++;
#ifdef COMPUTE_HLZONES #ifdef COMPUTE_HLZONES
if (m_prevwordhit) { if (m_prevwordhit) {
@ -215,9 +211,8 @@ public:
m_curfragcoef += coef; m_curfragcoef += coef;
m_remainingWords = m_ctxwords + 1; m_remainingWords = m_ctxwords + 1;
if (m_extcount > 5) { if (m_extcount > 5) {
// Limit expansion of contiguous fragments (this is to // Limit expansion of contiguous fragments (this is to avoid common terms in search
// avoid common terms in search causing long // causing long heavyweight meaningless fragments. Also, limit length).
// heavyweight meaningless fragments. Also, limit length).
m_remainingWords = 1; m_remainingWords = 1;
m_extcount = 0; m_extcount = 0;
} }
@ -247,18 +242,14 @@ public:
LOGDEB1("FRAGMENT: from byte " << m_curfrag.first << LOGDEB1("FRAGMENT: from byte " << m_curfrag.first <<
" to byte " << m_curfrag.second << endl); " to byte " << m_curfrag.second << endl);
LOGDEB1("FRAGMENT TEXT [" << m_rawtext.substr( LOGDEB1("FRAGMENT TEXT [" << m_rawtext.substr(
m_curfrag.first, m_curfrag.second-m_curfrag.first) m_curfrag.first, m_curfrag.second-m_curfrag.first) << "]\n");
<< "]\n"); // We used to not push weak fragments if we had a lot already. This can cause
// We used to not push weak fragments if we had a lot // problems if the fragments we drop are actually group fragments (which have not
// already. This can cause problems if the fragments // got their boost yet). The right cut value is difficult to determine, because the
// we drop are actually group fragments (which have // absolute values of the coefs depend on many things (index size, etc.) The old
// not got their boost yet). The right cut value is // test was if (m_totalcoef < 5.0 || m_curfragcoef >= 1.0) We now just avoid
// difficult to determine, because the absolute values // creating a monster by testing the current fragments count at the top of the
// of the coefs depend on many things (index size, // function
// etc.) The old test was if (m_totalcoef < 5.0 ||
// m_curfragcoef >= 1.0) We now just avoid creating a
// monster by testing the current fragments count at
// the top of the function
m_fragments.push_back(MatchFragment(m_curfrag.first, m_fragments.push_back(MatchFragment(m_curfrag.first,
m_curfrag.second, m_curfrag.second,
m_curfragcoef, m_curfragcoef,
@ -298,8 +289,7 @@ public:
m_curtermcoef = 0.0; m_curtermcoef = 0.0;
} }
LOGDEB("TextSplitABS: stored total " << m_fragments.size() << LOGDEB("TextSplitABS: stored total " << m_fragments.size() << " fragments" << endl);
" fragments" << endl);
vector<GroupMatchEntry> tboffs; vector<GroupMatchEntry> tboffs;
// Look for matches to PHRASE and NEAR term groups and finalize // Look for matches to PHRASE and NEAR term groups and finalize
@ -340,9 +330,8 @@ public:
} }
auto fragit = m_fragments.begin(); auto fragit = m_fragments.begin();
for (const auto& grpmatch : tboffs) { for (const auto& grpmatch : tboffs) {
LOGDEB2("LOOKING FOR FRAGMENT: group: " << grpmatch.offs.first << LOGDEB2("LOOKING FOR FRAGMENT: group: " << grpmatch.offs.first << "-" <<
"-" << grpmatch.offs.second << " curfrag " << grpmatch.offs.second<<" curfrag "<<fragit->start<<"-"<<fragit->stop<<"\n");
fragit->start << "-" << fragit->stop << endl);
while (fragit->stop < grpmatch.offs.first) { while (fragit->stop < grpmatch.offs.first) {
fragit++; fragit++;
if (fragit == m_fragments.end()) { if (fragit == m_fragments.end()) {
@ -417,21 +406,19 @@ int Query::Native::abstractFromText(
bool sortbypage bool sortbypage
) )
{ {
(void)chron; PRETEND_USE(chron);
LOGABS("abstractFromText: entry: " << chron.millis() << "mS\n"); LOGABS("abstractFromText: entry: " << chron.millis() << "mS\n");
string rawtext; string rawtext;
if (!ndb->getRawText(docid, rawtext)) { if (!ndb->getRawText(docid, rawtext)) {
LOGDEB0("abstractFromText: can't fetch text\n"); LOGDEB0("abstractFromText: can't fetch text\n");
return ABSRES_ERROR; return ABSRES_ERROR;
} }
LOGABS("abstractFromText: got raw text: size " << rawtext.size() << " " << LOGABS("abstractFromText: got raw text: size "<<rawtext.size()<<" "<<chron.millis()<<"mS\n");
chron.millis() << "mS\n");
#if 0 && ! (XAPIAN_MAJOR_VERSION <= 1 && XAPIAN_MINOR_VERSION <= 2) && \ #if 0 && XAPIAN_AT_LEAST(1,3,5)
(defined(RAWTEXT_IN_DATA))
// Tryout the Xapian internal method. // Tryout the Xapian internal method.
string snippet = xmset.snippet(rawtext); string snippet = xmset.snippet(rawtext, 60);
LOGDEB("SNIPPET: [" << snippet << "] END SNIPPET\n"); std::cerr << "XAPIAN SNIPPET: [" << snippet << "] END SNIPPET\n";
#endif #endif
// We need the q coefs for individual terms // We need the q coefs for individual terms
@ -452,8 +439,7 @@ int Query::Native::abstractFromText(
} }
LOGABS("abstractFromText: getterms: " << chron.millis() << "mS\n"); LOGABS("abstractFromText: getterms: " << chron.millis() << "mS\n");
TextSplitABS splitter(rawtext, matchTerms, hld, wordcoefs, ctxwords, TextSplitABS splitter(rawtext, matchTerms, hld, wordcoefs, ctxwords, TextSplit::TXTS_NONE,
TextSplit::TXTS_NONE,
m_q->m_snipMaxPosWalk); m_q->m_snipMaxPosWalk);
splitter.text_to_words(rawtext); splitter.text_to_words(rawtext);
LOGABS("abstractFromText: text_to_words: " << chron.millis() << "mS\n"); LOGABS("abstractFromText: text_to_words: " << chron.millis() << "mS\n");
@ -484,8 +470,7 @@ int Query::Native::abstractFromText(
// main term and the page positions. // main term and the page positions.
unsigned int count = 0; unsigned int count = 0;
for (const auto& entry : result) { for (const auto& entry : result) {
string frag( string frag(fixfrag(rawtext.substr(entry.start, entry.stop - entry.start)));
fixfrag(rawtext.substr(entry.start, entry.stop - entry.start)));
#ifdef COMPUTE_HLZONES #ifdef COMPUTE_HLZONES
// This would need to be modified to take tag parameters // This would need to be modified to take tag parameters
@ -506,8 +491,7 @@ int Query::Native::abstractFromText(
if (page < 0) if (page < 0)
page = 0; page = 0;
} }
LOGDEB0("=== FRAGMENT: p. " << page << " Coef: " << entry.coef << LOGDEB0("=== FRAGMENT: p. " << page << " Coef: " << entry.coef << ": " << frag << endl);
": " << frag << endl);
vabs.push_back(Snippet(page, frag).setTerm(entry.term)); vabs.push_back(Snippet(page, frag).setTerm(entry.term));
if (count++ >= maxtotaloccs) if (count++ >= maxtotaloccs)
break; break;
@ -515,4 +499,45 @@ int Query::Native::abstractFromText(
return ABSRES_OK | splitter.getretflags(); return ABSRES_OK | splitter.getretflags();
} }
class TermLineSplitter : public TextSplit {
public:
TermLineSplitter(const std::string& term)
: TextSplit(TextSplit::TXTS_NOSPANS), m_term(term) {
}
bool takeword(const std::string& _term, int, int, int) override {
std::string term;
if (o_index_stripchars) {
if (!unacmaybefold(_term, term, "UTF-8", UNACOP_UNACFOLD)) {
LOGINFO("PlainToRich::takeword: unac failed for [" << term << "]\n");
return true;
}
}
if (term == m_term) {
return false;
}
return true;
}
void newline(int) override {
m_line++;
}
int getline() {
return m_line;
}
private:
int m_line{1};
std::string m_term;
};
int Query::getFirstMatchLine(const Doc &doc, const std::string& term)
{
int line = 1;
TermLineSplitter splitter(term);
bool ret = splitter.text_to_words(doc.text);
// The splitter takeword() breaks by returning false as soon as the term is found
if (ret == false) {
line = splitter.getline();
}
return line;
}
} }

View File

@ -254,7 +254,7 @@ double Query::Native::qualityTerms(Xapian::docid docid,
} }
// Return page number for first match of "significant" term. // Choose most interesting term and return the page number for its first match
int Query::Native::getFirstMatchPage(Xapian::docid docid, string& term) int Query::Native::getFirstMatchPage(Xapian::docid docid, string& term)
{ {
LOGDEB("Query::Native::getFirstMatchPage\n"); LOGDEB("Query::Native::getFirstMatchPage\n");
@ -286,9 +286,7 @@ int Query::Native::getFirstMatchPage(Xapian::docid docid, string& term)
qualityTerms(docid, terms, byQ); qualityTerms(docid, terms, byQ);
for (auto mit = byQ.rbegin(); mit != byQ.rend(); mit++) { for (auto mit = byQ.rbegin(); mit != byQ.rend(); mit++) {
for (vector<string>::const_iterator qit = mit->second.begin(); for (const auto& qterm : mit->second) {
qit != mit->second.end(); qit++) {
string qterm = *qit;
Xapian::PositionIterator pos; Xapian::PositionIterator pos;
string emptys; string emptys;
try { try {
@ -619,9 +617,8 @@ int Query::Native::abstractFromIndex(
// possibly retried by our caller. // possibly retried by our caller.
// //
// @param[out] vabs the abstract is returned as a vector of snippets. // @param[out] vabs the abstract is returned as a vector of snippets.
int Query::Native::makeAbstract(Xapian::docid docid, int Query::Native::makeAbstract(
vector<Snippet>& vabs, Xapian::docid docid, vector<Snippet>& vabs, int imaxoccs, int ictxwords, bool sortbypage)
int imaxoccs, int ictxwords, bool sortbypage)
{ {
chron.restart(); chron.restart();
LOGDEB("makeAbstract: docid " << docid << " imaxoccs " << LOGDEB("makeAbstract: docid " << docid << " imaxoccs " <<

View File

@ -96,10 +96,13 @@ const string pathelt_prefix = "XP";
static const string udi_prefix("Q"); static const string udi_prefix("Q");
static const string parent_prefix("F"); static const string parent_prefix("F");
// Special terms to mark begin/end of field (for anchored searches), and // Special terms to mark begin/end of field (for anchored searches).
// page breaks
string start_of_field_term; string start_of_field_term;
string end_of_field_term; string end_of_field_term;
// Special term for page breaks. Note that we use a complicated mechanism for multiple page
// breaks at the same position, when it would have been probably simpler to use XXPG/n terms
// instead (did not try to implement though). A change would force users to reindex.
const string page_break_term = "XXPG/"; const string page_break_term = "XXPG/";
// Special term to mark documents with children. // Special term to mark documents with children.
@ -1846,16 +1849,14 @@ bool Db::addOrUpdate(const string &udi, const string &parent_udi, Doc &doc)
} }
} }
// If empty pages (multiple break at same pos) were recorded, save // If empty pages (multiple break at same pos) were recorded, save them (this is
// them (this is because we have no way to record them in the // because we have no way to record them in the Xapian list)
// Xapian list
if (!tpidx.m_pageincrvec.empty()) { if (!tpidx.m_pageincrvec.empty()) {
ostringstream multibreaks; ostringstream multibreaks;
for (unsigned int i = 0; i < tpidx.m_pageincrvec.size(); i++) { for (unsigned int i = 0; i < tpidx.m_pageincrvec.size(); i++) {
if (i != 0) if (i != 0)
multibreaks << ","; multibreaks << ",";
multibreaks << tpidx.m_pageincrvec[i].first << "," << multibreaks << tpidx.m_pageincrvec[i].first << "," << tpidx.m_pageincrvec[i].second;
tpidx.m_pageincrvec[i].second;
} }
RECORD_APPEND(record, string(cstr_mbreaks), multibreaks.str()); RECORD_APPEND(record, string(cstr_mbreaks), multibreaks.str());
} }

View File

@ -360,7 +360,6 @@ int Query::getFirstMatchPage(const Doc &doc, string& term)
return m_reason.empty() ? pagenum : -1; return m_reason.empty() ? pagenum : -1;
} }
// Mset size // Mset size
// Note: times for retrieving (multiple times)all docs from a sample // Note: times for retrieving (multiple times)all docs from a sample
// 25k docs db (q: mime:*) // 25k docs db (q: mime:*)
@ -511,8 +510,7 @@ vector<string> Query::expand(const Doc &doc)
Xapian::ESet eset = m_nq->xenquire->get_eset(20, rset, false); Xapian::ESet eset = m_nq->xenquire->get_eset(20, rset, false);
LOGDEB("ESet terms:\n"); LOGDEB("ESet terms:\n");
// We filter out the special terms // We filter out the special terms
for (Xapian::ESetIterator it = eset.begin(); for (Xapian::ESetIterator it = eset.begin(); it != eset.end(); it++) {
it != eset.end(); it++) {
LOGDEB(" [" << (*it) << "]\n"); LOGDEB(" [" << (*it) << "]\n");
if ((*it).empty() || has_prefix(*it)) if ((*it).empty() || has_prefix(*it))
continue; continue;

View File

@ -115,10 +115,18 @@ public:
// Returned as a vector of pair<page,snippet> page is 0 if unknown // Returned as a vector of pair<page,snippet> page is 0 if unknown
int makeDocAbstract(const Doc &doc, std::vector<Snippet>& abst, int makeDocAbstract(const Doc &doc, std::vector<Snippet>& abst,
int maxoccs= -1, int ctxwords= -1,bool sortbypage=false); int maxoccs= -1, int ctxwords= -1,bool sortbypage=false);
/** Retrieve page number for first match for "significant" query term
* @param term returns the chosen term */ /** Choose most interesting term and return the page number for its first match
* @param term returns the chosen term
* @return page number or -1 if term not found or other issue
*/
int getFirstMatchPage(const Doc &doc, std::string& term); int getFirstMatchPage(const Doc &doc, std::string& term);
/** Compute line number for first match of term. Only works if doc.text has text.
* This uses a text split. Both this and the above getFirstMaxPage() could be done and saved
* while we compute the abstracts, quite a lot of waste here. */
int getFirstMatchLine(const Doc &doc, const std::string& term);
/** Retrieve a reference to the searchData we are using */ /** Retrieve a reference to the searchData we are using */
std::shared_ptr<SearchData> getSD() { std::shared_ptr<SearchData> getSD() {
return m_sd; return m_sd;

View File

@ -0,0 +1,9 @@
# External viewers, launched by the recoll GUI when you click on a result
# 'Open' link - MAC version
# On the MAC, we use "open" for everything, no exceptions at the moment.
xallexcepts =
[view]
# Pseudo entry used if the 'use desktop' preference is set in the GUI
application/x-all = open %f

View File

@ -148,25 +148,25 @@ application/vnd.sun.xml.writer.template = \
body content.xml opendoc-body.xsl body content.xml opendoc-body.xsl
#application/x-mobipocket-ebook = execm rclmobi #application/x-mobipocket-ebook = execm rclmobi
#application/x-tar = execm rcltar #application/x-tar = execm rcltar.py
application/epub+zip = execm rclepub application/epub+zip = execm rclepub.py
application/x-ipynb+json = exec jupyter nbconvert --to script --stdout ; mimetype = text/plain application/x-ipynb+json = exec jupyter nbconvert --to script --stdout ; mimetype = text/plain
application/javascript = internal text/plain application/javascript = internal text/plain
application/ogg = execm rclaudio application/ogg = execm rclaudio.py
application/pdf = execm rclpdf.py application/pdf = execm rclpdf.py
application/postscript = exec rclps application/postscript = exec rclps
application/sql = internal text/plain application/sql = internal text/plain
application/vnd.wordperfect = exec wpd2html;mimetype=text/html application/vnd.wordperfect = exec wpd2html;mimetype=text/html
application/x-7z-compressed = execm rcl7z application/x-7z-compressed = execm rcl7z.py
application/x-abiword = internal xsltproc abiword.xsl application/x-abiword = internal xsltproc abiword.xsl
application/x-awk = internal text/plain application/x-awk = internal text/plain
application/x-chm = execm rclchm application/x-chm = execm rclchm.py
application/x-dia-diagram = execm rcldia;mimetype=text/plain application/x-dia-diagram = execm rcldia.py;mimetype=text/plain
application/x-dvi = exec rcldvi application/x-dvi = exec rcldvi
application/x-flac = execm rclaudio application/x-flac = execm rclaudio.py
application/x-gnote = execm rclxml.py application/x-gnote = execm rclxml.py
application/x-gnuinfo = execm rclinfo application/x-gnuinfo = execm rclinfo.py
application/x-gnumeric = internal xsltproc gnumeric.xsl application/x-gnumeric = internal xsltproc gnumeric.xsl
application/x-hwp = execm rclhwp.py application/x-hwp = execm rclhwp.py
application/x-kword = exec rclkwd application/x-kword = exec rclkwd
@ -175,22 +175,22 @@ application/x-mimehtml = internal message/rfc822
application/x-okular-notes = internal xsltproc okular-note.xsl application/x-okular-notes = internal xsltproc okular-note.xsl
application/x-perl = internal text/plain application/x-perl = internal text/plain
application/x-php = internal text/plain application/x-php = internal text/plain
application/x-rar = execm rclrar;charset=default application/x-rar = execm rclrar.py;charset=default
application/x-ruby = internal text/plain application/x-ruby = internal text/plain
application/x-scribus = exec rclscribus application/x-scribus = exec rclscribus
application/x-shellscript = internal text/plain application/x-shellscript = internal text/plain
application/x-tex = exec rcltex application/x-tex = exec rcltex
application/x-webarchive = execm rclwar application/x-webarchive = execm rclwar.py
application/x-zerosize = internal application/x-zerosize = internal
application/zip = execm rclzip;charset=default application/zip = execm rclzip.py;charset=default
audio/aac = execm rclaudio audio/aac = execm rclaudio.py
audio/ape = execm rclaudio audio/ape = execm rclaudio.py
audio/mp4 = execm rclaudio audio/mp4 = execm rclaudio.py
audio/mpeg = execm rclaudio audio/mpeg = execm rclaudio.py
audio/ogg = execm rclaudio audio/ogg = execm rclaudio.py
audio/x-karaoke = execm rclkar audio/x-karaoke = execm rclkar.py
audio/x-musepack = execm rclaudio audio/x-musepack = execm rclaudio.py
audio/x-wavpack = execm rclaudio audio/x-wavpack = execm rclaudio.py
image/gif = execm rclimg image/gif = execm rclimg
image/jp2 = execm rclimg image/jp2 = execm rclimg
image/jpeg = execm rclimg image/jpeg = execm rclimg
@ -203,7 +203,7 @@ image/x-xcf = execm rclimg
inode/symlink = internal inode/symlink = internal
inode/x-empty = internal application/x-zerosize inode/x-empty = internal application/x-zerosize
message/rfc822 = internal message/rfc822 = internal
text/calendar = execm rclics;mimetype=text/plain text/calendar = execm rclics.py;mimetype=text/plain
text/css = internal text/plain text/css = internal text/plain
text/html = internal text/html = internal
text/plain = internal text/plain = internal
@ -234,7 +234,7 @@ text/x-ruby = internal
text/x-shellscript = internal text/plain text/x-shellscript = internal text/plain
text/x-srt = internal text/plain text/x-srt = internal text/plain
text/x-tex = exec rcltex text/x-tex = exec rcltex
video/mp4 = execm rclaudio video/mp4 = execm rclaudio.py
video/x-msvideo = execm rclimg video/x-msvideo = execm rclimg

View File

@ -15,6 +15,8 @@
.rst = text/plain .rst = text/plain
.md = text/plain .md = text/plain
.gv = text/plain .gv = text/plain
.desktop = text/plain
.json = text/plain
# .log is in the default noContentSuffixes, so this will also need a recoll.conf setting to do # .log is in the default noContentSuffixes, so this will also need a recoll.conf setting to do
# anything # anything
@ -58,6 +60,7 @@
.ipynb = application/x-ipynb+json .ipynb = application/x-ipynb+json
.xml = text/xml .xml = text/xml
.opf = text/xml
.note = application/x-gnote .note = application/x-gnote
@ -90,17 +93,21 @@
.svg = image/svg+xml .svg = image/svg+xml
.dia = application/x-dia-diagram .dia = application/x-dia-diagram
# Compressed files
.gz = application/x-gzip .gz = application/x-gzip
.Z = application/x-gzip .Z = application/x-gzip
.bz2 = application/x-bzip2 .bz2 = application/x-bzip2
.rar = application/x-rar .lzma = application/x-lzma
#.Z = application/x-compress .xz = application/x-xz
.zip = application/zip
.7z = application/x-7z-compressed
.maff = application/zip
.zst = application/x-zstd .zst = application/x-zstd
# The rcltar module can handle compressed tar formats internally so we # Archives
.rar = application/x-rar
.zip = application/zip
.maff = application/zip
.7z = application/x-7z-compressed
# The rcltar.py module can handle compressed tar formats internally so we
# use application/x-tar for all tar files compressed or not. Note that tar # use application/x-tar for all tar files compressed or not. Note that tar
# file indexing is disabled by default, you'll need to copy and uncomment # file indexing is disabled by default, you'll need to copy and uncomment
# the application/x-tar commented line from mimeconf into your personal config # the application/x-tar commented line from mimeconf into your personal config
@ -123,7 +130,9 @@
.chm = application/x-chm .chm = application/x-chm
.epub = application/epub+zip .epub = application/epub+zip
.kepub = application/epub+zip
.mobi = application/x-mobipocket-ebook .mobi = application/x-mobipocket-ebook
.lit = application/x-ms-reader
# OpenOffice / opendocument. We handle opendocument as old openoffice files # OpenOffice / opendocument. We handle opendocument as old openoffice files
# for now # for now
@ -200,6 +209,7 @@
.ogg = application/ogg .ogg = application/ogg
.ogx = audio/ogg .ogx = audio/ogg
.opus = audio/ogg .opus = audio/ogg
.wav = audio/x-wav
.wv = audio/x-wavpack .wv = audio/x-wavpack
.mkv = video/x-matroska .mkv = video/x-matroska

View File

@ -1,197 +0,0 @@
# @(#$Id: mimeview,v 1.16 2008-09-15 08:03:37 dockes Exp $ (C) 2004 J.F.Dockes
## ##########################################
# External viewers, launched by the recoll GUI when you click on a result
# 'Open' link - MAC version
# On the MAC, we use "open" for everything...
# Mime types which we should not uncompress if they are found gzipped or
# bzipped because the native viewer knows how to handle. These would be
# exceptions and the list is normally empty
#nouncompforviewmts =
# For releases 1.18 and later: exceptions when using the x-all entry: these
# types will use their local definition. This is useful, e.g.:
#
# - for pdf, where we can pass additional parameters like page to open and
# search string
# - For pages of CHM and EPUB documents where we can choose to open the
# parent document instead of a temporary html file.
#xallexcepts = application/pdf application/postscript application/x-dvi \
# text/html|gnuinfo text/html|chm text/html|epub
[view]
# Pseudo entry used if the 'use desktop' preference is set in the GUI
application/x-all = open %f
application/epub+zip = ebook-viewer %f
# If you want to open the parent epub document for epub parts instead of
# opening them as html documents:
#text/html|epub = ebook-viewer %F;ignoreipath=1
application/x-gnote = gnote %f
application/x-mobipocket-ebook = ebook-viewer %f
application/x-kword = kword %f
application/x-abiword = abiword %f
# Note: the Linux Mint evince clones, atril and xread, have the same options
application/pdf = evince --page-index=%p --find=%s %f
# Or:
#application/pdf = qpdfview --search %s %f#%p
application/postscript = evince --page-index=%p --find=%s %f
application/x-dvi = evince --page-index=%p --find=%s %f
application/x-lyx = lyx %f
application/x-scribus = scribus %f
application/msword = libreoffice %f
application/vnd.ms-excel = libreoffice %f
application/vnd.ms-powerpoint = libreoffice %f
application/vnd.oasis.opendocument.text = libreoffice %f
application/vnd.oasis.opendocument.presentation = libreoffice %f
application/vnd.oasis.opendocument.spreadsheet = libreoffice %f
application/vnd.openxmlformats-officedocument.wordprocessingml.document = \
libreoffice %f
application/vnd.openxmlformats-officedocument.wordprocessingml.template = \
libreoffice %f
application/vnd.openxmlformats-officedocument.presentationml.template = \
libreoffice %f
application/vnd.openxmlformats-officedocument.presentationml.presentation = \
libreoffice %f
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet = \
libreoffice %f
application/vnd.openxmlformats-officedocument.spreadsheetml.template =\
libreoffice %f
application/vnd.sun.xml.calc = libreoffice %f
application/vnd.sun.xml.calc.template = libreoffice %f
application/vnd.sun.xml.draw = libreoffice %f
application/vnd.sun.xml.draw.template = libreoffice %f
application/vnd.sun.xml.impress = libreoffice %f
application/vnd.sun.xml.impress.template = libreoffice %f
application/vnd.sun.xml.math = libreoffice %f
application/vnd.sun.xml.writer = libreoffice %f
application/vnd.sun.xml.writer.global = libreoffice %f
application/vnd.sun.xml.writer.template = libreoffice %f
application/vnd.wordperfect = libreoffice %f
text/rtf = libreoffice %f
application/x-dia-diagram = dia %f
application/x-fsdirectory = dolphin %f
inode/directory = dolphin %f
# Both dolphin and nautilus can pre-select a file inside a
# directory. Thunar can't afaik. xdg-open cant pass an additional
# parameters so these are to be xallexcepts.
application/x-fsdirectory|parentopen = dolphin --select %(childurl) %f
inode/directory|parentopen = dolphin --select %(childurl) %f
#application/x-fsdirectory|parentopen = nautilus %(childurl)
#inode/directory|parentopen = nautilus %(childurl)
application/x-gnuinfo = xterm -e "info -f %f"
application/x-gnumeric = gnumeric %f
application/x-flac = rhythmbox %f
audio/mpeg = rhythmbox %f
application/ogg = rhythmbox %f
audio/x-karaoke = kmid %f
image/jpeg = gwenview %f
image/png = gwenview %f
image/tiff = gwenview %f
image/gif = gwenview %f
image/svg+xml = inkview %f
image/vnd.djvu = djview %f
image/x-xcf = gimp %f
image/bmp = gwenview %f
image/x-ms-bmp = gwenview %f
image/x-xpmi = gwenview %f
image/x-nikon-nef = ufraw %f
# Opening mail messages:
# - Thunderbird will only open a single-message file if it has an .eml
# extension
# - "sylpheed %f" seems to work ok as of version 3.3
# - "kmail --view %u" works
# - claws-mail: works using a small intermediary shell-script, which you
# set as the viewer here. You need to have at least one account inside
# claws-mail, so that it creates ~/Mail/inbox. Script contents example
# follows. Using 1 is probably not a good idea if this is a real account
# (here I am using a bogus one, so that I can overwrite anything inside
# inbox at will):
# #!/bin/bash
# cp $1 ~/Mail/inbox/1
# claws-mail --select ~/Mail/inbox/1
# rm ~/Mail/inbox/1
message/rfc822 = thunderbird -file %f
text/x-mail = thunderbird -file %f
application/x-mimehtml = thunderbird -file %f
text/calendar = evolution %f
application/x-okular-notes = okular %f
application/x-rar = ark %f
application/x-tar = ark %f
application/zip = ark %f
application/x-7z-compressed = ark %f
application/x-awk = emacsclient %f
application/x-perl = emacsclient %f
text/x-perl = emacsclient %f
application/x-shellscript = emacsclient %f
text/x-shellscript = emacsclient %f
# Or firefox -remote "openFile(%u)"
text/html = firefox %u
# gnu info nodes are translated to html with a "gnuinfo"
# rclaptg. rclshowinfo knows how to start the info command on the right
# node
text/html|gnuinfo = rclshowinfo %F %(title);ignoreipath=1
application/x-webarchive = konqueror %f
text/x-fictionbook = ebook-viewer %f
application/x-tex = emacsclient %f
application/xml = emacsclient %f
text/xml = emacsclient %f
text/x-tex = emacsclient %f
text/plain = emacsclient %f
text/x-awk = emacsclient %f
text/x-c = emacsclient %f
text/x-c+ = emacsclient %f
text/x-c++ = emacsclient %f
text/x-csv = libreoffice %f
text/x-html-sidux-man = konqueror %f
text/x-html-aptosid-man = iceweasel %f
application/x-chm = kchmviewer %f
# Html pages inside a chm have a chm rclaptg set by the filter. Kchmviewer
# knows how to use the ipath (which is the internal chm path) to open the
# file at the right place
text/html|chm = kchmviewer --url %i %F
text/x-ini = emacsclient %f
text/x-man = xterm -u8 -e "groff -T ascii -man %f | more"
text/x-python = idle %f
text/x-gaim-log = emacsclient %f
text/x-purple-html-log = emacsclient %f
text/x-purple-log = emacsclient %f
# The video types will usually be handled by the desktop default, but they
# need entries here to get an "Open" link
video/3gpp = open %f
video/mp2p = open %f
video/mp2t = open %f
video/mp4 = open %f
video/mpeg = open %f
video/quicktime = open %f
video/x-matroska = open %f
video/x-ms-asf = open %f
video/x-msvideo = open %f

View File

@ -88,8 +88,8 @@ onlyNames =
# can be redefined for subdirectories.</descr></var> # can be redefined for subdirectories.</descr></var>
noContentSuffixes = .md5 .map \ noContentSuffixes = .md5 .map \
.o .lib .dll .a .sys .exe .com \ .o .lib .dll .a .sys .exe .com \
.mpp .mpt .vsd \ .mpp .mpt .vsd .sqlite \
.img .img.gz .img.bz2 .img.xz .image .image.gz .image.bz2 .image.xz \ .img .img.gz .img.bz2 .img.xz .image .image.gz .image.bz2 .image.xz .ttf \
.dat .bak .rdf .log.gz .log .db .msf .pid \ .dat .bak .rdf .log.gz .log .db .msf .pid \
,v ~ # ,v ~ #
@ -155,7 +155,7 @@ skippedPaths = /media
# <var name="zipUseSkippedNames" type="bool"> # <var name="zipUseSkippedNames" type="bool">
# #
# <brief>Use skippedNames inside Zip archives.</brief><descr>Fetched # <brief>Use skippedNames inside Zip archives.</brief><descr>Fetched
# directly by the rclzip handler. Skip the patterns defined by skippedNames # directly by the rclzip.py handler. Skip the patterns defined by skippedNames
# inside Zip archives. Can be redefined for subdirectories. # inside Zip archives. Can be redefined for subdirectories.
# See https://www.lesbonscomptes.com/recoll/faqsandhowtos/FilteringOutZipArchiveMembers.html # See https://www.lesbonscomptes.com/recoll/faqsandhowtos/FilteringOutZipArchiveMembers.html
# </descr></var> # </descr></var>
@ -206,9 +206,9 @@ skippedPaths = /media
# redefinition for subtrees). At the moment, it only has an effect for # redefinition for subtrees). At the moment, it only has an effect for
# external handlers (exec and execm). The file types can be specified by # external handlers (exec and execm). The file types can be specified by
# listing either MIME types (e.g. audio/mpeg) or handler names # listing either MIME types (e.g. audio/mpeg) or handler names
# (e.g. rclaudio).</descr> # (e.g. rclaudio.py).</descr>
# </var> # </var>
nomd5types = rclaudio nomd5types = rclaudio.py
# <var name="compressedfilemaxkbs" type="int"><brief>Size limit for compressed # <var name="compressedfilemaxkbs" type="int"><brief>Size limit for compressed
# files.</brief><descr>We need to decompress these in a # files.</brief><descr>We need to decompress these in a

View File

@ -38,8 +38,8 @@ AM_CPPFLAGS = -Wall -Wno-unused -std=c++11 \
-D_GNU_SOURCE \ -D_GNU_SOURCE \
$(DEFS) $(DEFS)
noinst_PROGRAMS = plaintorich textsplit utf8iter fstreewalk rclconfig hldata unac mbox \ noinst_PROGRAMS = plaintorich textsplit fstreewalk rclconfig hldata unac mbox \
circache wipedir mimetype pathut fileudi x11mon trqrstore ecrontab circache wipedir mimetype fileudi x11mon trqrstore ecrontab
ecrontab_SOURCES = trecrontab.cpp ecrontab_SOURCES = trecrontab.cpp
ecrontab_LDADD = ../librecoll.la ecrontab_LDADD = ../librecoll.la
@ -62,9 +62,6 @@ mbox_LDADD = ../librecoll.la
mimetype_SOURCES = trmimetype.cpp mimetype_SOURCES = trmimetype.cpp
mimetype_LDADD = ../librecoll.la mimetype_LDADD = ../librecoll.la
pathut_SOURCES = trpathut.cpp
pathut_LDADD = ../librecoll.la
rclconfig_SOURCES = trrclconfig.cpp rclconfig_SOURCES = trrclconfig.cpp
rclconfig_LDADD = ../librecoll.la rclconfig_LDADD = ../librecoll.la
@ -77,9 +74,6 @@ plaintorich_LDADD = ../librecoll.la
unac_SOURCES = trunac.cpp unac_SOURCES = trunac.cpp
unac_LDADD = ../librecoll.la unac_LDADD = ../librecoll.la
utf8iter_SOURCES = trutf8iter.cpp
utf8iter_LDADD = ../librecoll.la
wipedir_SOURCES = trwipedir.cpp wipedir_SOURCES = trwipedir.cpp
wipedir_LDADD = ../librecoll.la wipedir_LDADD = ../librecoll.la

View File

@ -1,384 +0,0 @@
#include "autoconfig.h"
#include "execmd.h"
#include <stdio.h>
#include <stdlib.h>
#include "safeunistd.h"
#include <string.h>
#ifndef _WIN32
#include <signal.h>
#endif
#include <string>
#include <iostream>
#include <sstream>
#include <vector>
#include "log.h"
#include "cancelcheck.h"
#include "execmd.h"
#include "smallut.h"
using namespace std;
// Testing the rclexecm protocol outside of recoll. Here we use the
// rcldoc.py filter, you can try with rclaudio too, adjust the file
// arg accordingly. This simplified driver only really works with
// single-doc files (else it extracts only the first doc, usually the
// empty self-doc).
bool exercise_mhexecm(const string& cmdstr, const string& mimetype,
vector<string>& files)
{
if (files.empty())
return false;
ExecCmd cmd;
vector<string> myparams;
#ifdef _WIN32
// Hack for windows: the command is always "Python somescript"
myparams.push_back(files[0]);
files.erase(files.begin());
#endif
if (cmd.startExec(cmdstr, myparams, 1, 1) < 0) {
cerr << "startExec " << cmdstr << " failed. Missing command?\n";
return false;
}
for (vector<string>::const_iterator it = files.begin();
it != files.end(); it++) {
// Build request message
ostringstream obuf;
obuf << "Filename: " << (*it).length() << "\n" << (*it);
obuf << "Mimetype: " << mimetype.length() << "\n" << mimetype;
// Bogus parameter should be skipped by filter
obuf << "BogusParam: " << string("bogus").length() << "\n" << "bogus";
obuf << "\n";
cerr << "SENDING: [" << obuf.str() << "]\n";
// Send it
if (cmd.send(obuf.str()) < 0) {
// The real code calls zapchild here, but we don't need it as
// this will be handled by ~ExecCmd
//cmd.zapChild();
cerr << "send error\n";
return false;
}
// Read answer
for (int loop=0;;loop++) {
string name, data;
// Code from mh_execm.cpp: readDataElement
string ibuf;
// Read name and length
if (cmd.getline(ibuf) <= 0) {
cerr << "getline error\n";
return false;
}
// Empty line (end of message)
if (!ibuf.compare("\n")) {
cerr << "Got empty line\n";
name.clear();
break;
}
// Filters will sometimes abort before entering the real
// protocol, ie if a module can't be loaded. Check the
// special filter error first word:
if (ibuf.find("RECFILTERROR ") == 0) {
cerr << "Got RECFILTERROR\n";
return false;
}
// We're expecting something like Name: len\n
vector<string> tokens;
stringToTokens(ibuf, tokens);
if (tokens.size() != 2) {
cerr << "bad line in filter output: [" << ibuf << "]\n";
return false;
}
vector<string>::iterator it = tokens.begin();
name = *it++;
string& slen = *it;
int len;
if (sscanf(slen.c_str(), "%d", &len) != 1) {
cerr << "bad line in filter output (no len): [" <<
ibuf << "]\n";
return false;
}
// Read element data
data.erase();
if (len > 0 && cmd.receive(data, len) != len) {
cerr << "MHExecMultiple: expected " << len <<
" bytes of data, got " << data.length() << endl;
return false;
}
// Empty element: end of message
if (name.empty())
break;
cerr << "Got name: [" << name << "] data [" << data << "]\n";
}
}
return true;
}
static char *thisprog;
static char usage [] =
"trexecmd [-c -r -i -o] [-e <fn>] cmd [arg1 arg2 ...]\n"
" -c : test cancellation (ie: trexecmd -c sleep 1000)\n"
" -r : run reexec. Must be separate option.\n"
" -i : command takes input\n"
" -o : command produces output\n"
" -e <fn> : send stderr to file named fn (will truncate it)\n"
" If -i is set, we send /etc/group contents to whatever command is run\n"
" If -o is set, we print whatever comes out\n"
"trexecmd -f bogus filter for testing. Uses same options\n"
"trexecmd -m <filter> <mimetype> <file> [file ...]: test execm:\n"
" <filter> should be the path to an execm filter\n"
" <mimetype> the type of the file parameters\n"
"trexecmd -w cmd : do the 'which' thing\n"
;
static void Usage(FILE *fp = stderr)
{
fprintf(fp, "%s: usage:\n%s", thisprog, usage);
exit(1);
}
static int op_flags;
#define OPT_MOINS 0x1
#define OPT_i 0x4
#define OPT_w 0x8
#define OPT_c 0x10
#define OPT_r 0x20
#define OPT_m 0x40
#define OPT_o 0x80
#define OPT_e 0x100
#define OPT_f 0x200
void childfilter()
{
const int bs = 1024;
char buf[bs];
if (op_flags & OPT_c)
sleep(2000);
if (op_flags& OPT_i) {
while (read(0, buf, bs) > 0);
}
if (op_flags& OPT_o) {
for (int i = 0; i < 10; i++) {
printf("This is DATA 1 2 3\n");
}
}
exit(0);
}
// Data sink for data coming out of the command. We also use it to set
// a cancellation after a moment.
class MEAdv : public ExecCmdAdvise {
public:
void newData(int cnt) {
cerr << "newData(" << cnt << ")" << endl;
if (op_flags & OPT_c) {
static int callcnt;
if (callcnt++ == 5) {
// Just sets the cancellation flag
CancelCheck::instance().setCancel();
// Would be called from somewhere else and throws an
// exception. We call it here for simplicity
cerr << "newData: should throw !\n";
CancelCheck::instance().checkCancel();
}
}
}
};
// Data provider, used if the -i flag is set
class MEPv : public ExecCmdProvide {
public:
string *m_input;
int m_cnt;
MEPv(string *i)
: m_input(i), m_cnt(0) {
}
~MEPv() {
}
void newData() {
if (m_cnt++ < 10) {
char num[30];
sprintf(num, "%d", m_cnt);
*m_input = string("This is an input chunk ") + string(num) +
string("\n");
} else {
m_input->erase();
}
}
void reset() {
m_cnt = 0;
}
};
ReExec reexec;
int main(int argc, char *argv[])
{
#ifndef _WIN32
reexec.init(argc, argv);
if (0) {
// Disabled: For testing reexec arg handling
vector<string> newargs;
newargs.push_back("newarg");
newargs.push_back("newarg1");
newargs.push_back("newarg2");
newargs.push_back("newarg3");
newargs.push_back("newarg4");
reexec.insertArgs(newargs, 2);
}
#endif
string stderrFile;
thisprog = argv[0];
argc--; argv++;
while (argc > 0 && **argv == '-') {
(*argv)++;
if (!(**argv))
/* Cas du "adb - core" */
Usage();
while (**argv)
switch (*(*argv)++) {
case 'c': op_flags |= OPT_c; break;
case 'e':
op_flags |= OPT_e;
if (argc < 2) {
Usage();
}
stderrFile = *(++argv); argc--;
goto b1;
case 'f': op_flags |= OPT_f; break;
case 'h':
for (int i = 0; i < 10; i++) {
cout << "MESSAGE " << i << " FROM TREXECMD\n";
cout.flush();
//sleep(1);
}
return 0;
case 'i': op_flags |= OPT_i; break;
case 'o': op_flags |= OPT_o; break;
case 'm': op_flags |= OPT_m; break;
case 'r': op_flags |= OPT_r; break;
case 'w': op_flags |= OPT_w; break;
default: Usage(); break;
}
b1: argc--; argv++;
}
if (op_flags & OPT_f) {
childfilter();
}
if (argc < 1)
Usage();
string arg1 = *argv++; argc--;
vector<string> l;
while (argc > 0) {
l.push_back(*argv++); argc--;
}
DebugLog::getdbl()->setloglevel(DEBDEB1);
DebugLog::setfilename("stderr");
#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
if (op_flags & OPT_r) {
// Test reexec. Normally only once, next time we fall through
// because we remove the -r option (only works if it was
// isolated, not like -rc
chdir("/");
argv[0] = strdup("");
sleep(1);
cerr << "Calling reexec\n";
// We remove the -r arg from list, otherwise we are going to
// loop (which you can try by commenting out the following
// line)
reexec.removeArg("-r");
reexec.reexec();
}
#endif
if (op_flags & OPT_w) {
// Test "which" method
string path;
if (ExecCmd::which(arg1, path)) {
cout << path << endl;
return 0;
}
return 1;
} else if (op_flags & OPT_m) {
if (l.size() < 2)
Usage();
string mimetype = l[0];
l.erase(l.begin());
return exercise_mhexecm(arg1, mimetype, l) ? 0 : 1;
} else {
// Default: execute command line arguments
ExecCmd mexec;
// Set callback to be called whenever there is new data
// available and at a periodic interval, to check for
// cancellation
MEAdv adv;
mexec.setAdvise(&adv);
//mexec.setTimeout(5);
// Stderr output goes there
if (!stderrFile.empty())
mexec.setStderr(stderrFile);
// A few environment variables. Check with trexecmd env
mexec.putenv("TESTVARIABLE1=TESTVALUE1");
mexec.putenv("TESTVARIABLE2=TESTVALUE2");
mexec.putenv("TESTVARIABLE3=TESTVALUE3");
string input, output;
MEPv pv(&input);
string *ip = 0;
if (op_flags & OPT_i) {
ip = &input;
mexec.setProvide(&pv);
}
string *op = 0;
if (op_flags & OPT_o) {
op = &output;
}
int status = -1;
for (int i = 0; i < 10; i++) {
output.clear();
pv.reset();
try {
status = mexec.doexec(arg1, l, ip, op);
} catch (CancelExcept) {
cerr << "CANCELLED" << endl;
}
//fprintf(stderr, "Status: 0x%x\n", status);
if (op_flags & OPT_o) {
cout << "data received: [" << output << "]\n";
cerr << "iter " << i << " status " <<
status << " bytes received " << output.size() << endl;
}
if (status)
break;
}
return status >> 8;
}
}

View File

@ -1,105 +0,0 @@
#include "pathut.h"
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <iostream>
#include <map>
using namespace std;
static std::map<std::string, int> options {
{"path_home", 0},
{"path_tildexpand", 0},
{"listdir", 0},
{"url_encode", 0},
};
static const char *thisprog;
static void Usage(void)
{
string sopts;
for (const auto& opt: options) {
sopts += "--" + opt.first + "\n";
}
fprintf(stderr, "%s: usage: %s\n%s", thisprog, thisprog, sopts.c_str());
exit(1);
}
int main(int argc, char **argv)
{
thisprog = *argv;
std::vector<struct option> long_options;
for (auto& entry : options) {
struct option opt;
opt.name = entry.first.c_str();
opt.has_arg = 0;
opt.flag = &entry.second;
opt.val = 1;
long_options.push_back(opt);
}
long_options.push_back({0, 0, 0, 0});
while (getopt_long(argc, argv, "", &long_options[0], nullptr) != -1) {
}
if (options["path_home"]) {
if (optind != argc) {
cerr << "Usage: trsmallut --path_home\n";
return 1;
}
cout << "path_home() -> [" << path_home() << "]\n";
} else if (options["path_tildexpand"]) {
if (optind >= argc) {
cerr << "Usage: trsmallut --path_tildexpand <arg>\n";
return 1;
}
string s = argv[optind];
optind++;
if (optind != argc) {
return 1;
}
cout << "path_tildexpand(" << s << ") -> [" << path_tildexpand(s) << "]\n";
} else if (options["url_encode"]) {
if (optind >= argc) {
cerr << "Usage: trsmallut --url_encode <arg> [offs=0]\n";
return 1;
}
string s = argv[optind];
optind++;
int offs = 0;
if (optind != argc) {
offs = atoi(argv[optind]);
optind++;
}
if (optind != argc) {
return 1;
}
cout << "url_encode(" << s << ", " << offs << ") -> [" << url_encode(s, offs) << "]\n";
} else if (options["listdir"]) {
if (optind >= argc) {
cerr << "Usage: trsmallut --listdir <arg>\n";
return 1;
}
std::string path = argv[optind];
optind++;
if (optind != argc) {
cerr << "Usage: trsmallut --listdir <arg>\n";
return 1;
}
std::string reason;
std::set<std::string> entries;
if (!listdir(path, reason, entries)) {
std::cerr<< "listdir(" << path << ") failed : " << reason << "\n";
return 1;
}
for (const auto& entry : entries) {
cout << entry << "\n";
}
} else {
Usage();
}
return 0;
}

View File

@ -1,232 +0,0 @@
/* Copyright (C) 2005 J.F.Dockes
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <iostream>
#include <vector>
#include "log.h"
#include "transcode.h"
#ifndef NO_NAMESPACES
using namespace std;
#endif /* NO_NAMESPACES */
#define UTF8ITER_CHECK
#include "utf8iter.h"
#include "readfile.h"
#include "textsplit.h"
void tryempty()
{
Utf8Iter it("");
cout << "EOF ? " << it.eof() << endl;
TextSplit::isCJK(*it);
exit(0);
}
const char *thisprog;
static char usage [] =
"utf8iter [opts] infile outfile\n"
" converts infile to 32 bits unicode (processor order), for testing\n"
" -v : print stuff as we go\n"
"-t [-w] [-e] <string> <maxlen> : test truncation\n"
"-c <str> : str must be a single utf-8 char. Convert to code then show character bytes count\n"
;
void Usage() {
fprintf(stderr, "%s:%s\n", thisprog, usage);
exit(1);
}
static int op_flags;
#define OPT_v 0x2
#define OPT_t 0x4
#define OPT_w 0x8
#define OPT_e 0x10
#define OPT_c 0x20
int trytruncate(std::string s, int maxlen)
{
int flag = 0;
if (op_flags & OPT_w)
flag |= UTF8T_ATWORD;
if (op_flags & OPT_e)
flag |= UTF8T_ELLIPSIS;
utf8truncate(s, maxlen, flag);
std::cout << "Truncation result:[" << s << "]\n";
return 0;
}
FILE *infout = stdout;
int main(int argc, char **argv)
{
thisprog = argv[0];
argc--; argv++;
while (argc > 0 && **argv == '-') {
(*argv)++;
if (!(**argv))
Usage();
while (**argv)
switch (*(*argv)++) {
case 'e': op_flags |= OPT_e;break;
case 't': op_flags |= OPT_t;break;
case 'v': op_flags |= OPT_v;break;
case 'w': op_flags |= OPT_w;break;
case 'c': op_flags |= OPT_c;break;
default: Usage(); break;
}
argc--;argv++;
}
if (op_flags & OPT_c) {
if (argc != 1)
Usage();
std::string s = *argv++;argc--;
Utf8Iter uit(s);
auto code = *uit;
auto cnt = utf8codepointsize(code);
std::cout << "0x" << std::hex << code << std::dec << " : " << cnt << " byte" <<
(cnt>1?"s":"") << "\n";
return 0;
}
if (op_flags & OPT_t) {
if (argc < 2)
Usage();
std::string s = *argv++;argc--;
int maxlen = atoi(*argv++);argc--;
return trytruncate(s, maxlen);
}
string infile, outfile;
if (argc == 2) {
infile = *argv++;argc--;
outfile = *argv++;argc--;
Usage();
} else if (argc != 0) {
Usage();
}
string in;
if (!file_to_string(infile, in)) {
cerr << "Cant read file\n" << endl;
exit(1);
}
vector<unsigned int>ucsout1;
string out, out1;
Utf8Iter it(in);
FILE *fp = 0;
if (!outfile.empty()) {
fp = fopen(outfile.c_str(), "w");
if (fp == 0) {
cerr << "Can't create " << outfile << endl;
exit(1);
}
}
int nchars = 0;
for (;!it.eof(); it++) {
unsigned int value = *it;
if (value == (unsigned int)-1) {
cerr << "Conversion error occurred at position " << it.getBpos()
<< endl;
exit(1);
}
if (op_flags & OPT_v) {
fprintf(infout, "Value: 0x%04x", value);
if (value < 0x7f)
fprintf(stdout, " (%c) ", value);
fprintf(infout, "\n");
}
// UTF-32LE or BE array
ucsout1.push_back(value);
if (fp) {
// UTF-32LE or BE file
fwrite(&value, 4, 1, fp);
}
// Reconstructed utf8 strings (2 methods)
if (!it.appendchartostring(out))
break;
// conversion to string
out1 += it;
// fprintf(stderr, "%s", string(it).c_str());
nchars++;
}
if (fp) {
fclose(fp);
}
fprintf(infout, "Found %d Unicode characters\n", nchars);
if (in.compare(out)) {
fprintf(stderr, "error: out != in\n");
exit(1);
}
if (in != out1) {
fprintf(stderr, "error: out1 != in\n");
exit(1);
}
// Rewind and do it a second time
vector<unsigned int>ucsout2;
it.rewind();
for (int i = 0; ; i++) {
unsigned int value;
if ((value = it[i]) == (unsigned int)-1) {
break;
}
it++;
ucsout2.push_back(value);
}
if (ucsout1 != ucsout2) {
fprintf(stderr, "error: ucsout1 != ucsout2\n");
exit(1);
}
ucsout2.clear();
int ercnt;
const char *encoding = "UTF-32LE"; // note : use BE on high-endian machine
string ucs, ucs1;
for (const unsigned int i : ucsout1) {
ucs.append((const char *)&i, 4);
}
if (!transcode(ucs, ucs1, encoding, encoding, &ercnt) || ercnt) {
fprintf(stderr, "Transcode check failed, ercount: %d\n", ercnt);
exit(1);
}
if (ucs.compare(ucs1)) {
fprintf(stderr, "error: ucsout1 != ucsout2 after iconv\n");
exit(1);
}
if (!transcode(ucs, ucs1, encoding, "UTF-8", &ercnt) || ercnt) {
fprintf(stderr, "Transcode back to utf-8 check failed, ercount: %d\n",
ercnt);
exit(1);
}
if (ucs1.compare(in)) {
fprintf(stderr, "Transcode back to utf-8 compare to in failed\n");
exit(1);
}
exit(0);
}

View File

@ -125,8 +125,7 @@ public:
* @param readonly if true open readonly, else rw * @param readonly if true open readonly, else rw
* @param tildexp try tilde (home dir) expansion for subkey values * @param tildexp try tilde (home dir) expansion for subkey values
*/ */
ConfSimple(const char *fname, int readonly = 0, bool tildexp = false, ConfSimple(const char *fname, int readonly = 0, bool tildexp = false, bool trimvalues = true);
bool trimvalues = true);
/** /**
* Build the object by reading content from a string * Build the object by reading content from a string
@ -142,8 +141,7 @@ public:
* @param readonly if true open read only, else rw * @param readonly if true open read only, else rw
* @param tildexp try tilde (home dir) expansion for subsection names * @param tildexp try tilde (home dir) expansion for subsection names
*/ */
ConfSimple(int readonly = 0, bool tildexp = false, ConfSimple(int readonly = 0, bool tildexp = false, bool trimvalues = true);
bool trimvalues = true);
virtual ~ConfSimple() {}; virtual ~ConfSimple() {};
@ -184,8 +182,7 @@ public:
* Set value for named integer parameter in specified subsection (or global) * Set value for named integer parameter in specified subsection (or global)
* @return 0 for error, 1 else * @return 0 for error, 1 else
*/ */
virtual int set(const std::string& nm, long long val, virtual int set(const std::string& nm, long long val, const std::string& sk = std::string());
const std::string& sk = std::string());
/** /**
* Remove name and value from config * Remove name and value from config
@ -388,8 +385,7 @@ public:
construct(fns, ro); construct(fns, ro);
} }
/// Construct out of single file name and multiple directories /// Construct out of single file name and multiple directories
ConfStack(const std::string& nm, const std::vector<std::string>& dirs, ConfStack(const std::string& nm, const std::vector<std::string>& dirs, bool ro = true) {
bool ro = true) {
std::vector<std::string> fns; std::vector<std::string> fns;
for (const auto& dir : dirs) { for (const auto& dir : dirs) {
fns.push_back(path_cat(dir, nm)); fns.push_back(path_cat(dir, nm));
@ -501,8 +497,8 @@ public:
const std::string& sk, const char *pattern = 0) const override { const std::string& sk, const char *pattern = 0) const override {
return getNames1(sk, pattern, false); return getNames1(sk, pattern, false);
} }
virtual std::vector<std::string> getNamesShallow(const std::string& sk, virtual std::vector<std::string> getNamesShallow(
const char *patt = 0) const { const std::string& sk, const char *patt = 0) const {
return getNames1(sk, patt, true); return getNames1(sk, patt, true);
} }
@ -570,27 +566,30 @@ private:
} }
} }
/// Common construct from file names code. We used to be ok even /// Common construct from file names.
/// if some files were not readable/parsable. Now fail if any /// Fail if any fails, except for missing files in all but the bottom location, or the
/// fails. /// top one in rw mode.
void construct(const std::vector<std::string>& fns, bool ro) { void construct(const std::vector<std::string>& fns, bool ro) {
bool ok{true}; bool ok{true};
bool first{true}; for (unsigned int i = 0; i < fns.size(); i++) {
for (const auto& fn : fns) { const auto& fn{fns[i]};
T* p = new T(fn.c_str(), ro); T* p = new T(fn.c_str(), ro);
if (p && p->ok()) { if (p && p->ok()) {
m_confs.push_back(p); m_confs.push_back(p);
} else { } else {
delete p; delete p;
// In ro mode, we accept a non-existing topmost file // We accept missing files in all but the bottom/ directory.
// and treat it as an empty one. // In rw mode, the topmost file must be present.
if (!(ro && first && !path_exists(fn))) { if (!path_exists(fn)) {
ok = false; // !ro can only be true for i==0
if (!ro || (i == fns.size() - 1)) {
ok = false;
break;
}
} }
} }
// Only the first file is opened rw // Only the first file is opened rw
ro = true; ro = true;
first = false;
} }
m_ok = ok; m_ok = ok;
} }

View File

@ -39,6 +39,7 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <stdexcept> #include <stdexcept>
#include <sstream>
#ifdef HAVE_SPAWN_H #ifdef HAVE_SPAWN_H
#ifndef __USE_GNU #ifndef __USE_GNU
#define __USE_GNU #define __USE_GNU
@ -994,7 +995,8 @@ int ExecCmd::wait()
LOGERR("ExecCmd::waitpid: returned -1 errno " << errno << "\n"); LOGERR("ExecCmd::waitpid: returned -1 errno " << errno << "\n");
status = -1; status = -1;
} }
LOGDEB("ExecCmd::wait: got status 0x" << (status) << "\n"); LOGDEB("ExecCmd::wait: got status 0x" << std::hex << status << std::dec << ": " <<
waitStatusAsString(status) << "\n");
m->m_pid = -1; m->m_pid = -1;
} }
// Let the ExecCmdRsrc cleanup, it will do the killing/waiting if needed // Let the ExecCmdRsrc cleanup, it will do the killing/waiting if needed
@ -1043,6 +1045,23 @@ bool ExecCmd::backtick(const vector<string> cmd, string& out)
return status == 0; return status == 0;
} }
std::string ExecCmd::waitStatusAsString(int wstatus)
{
std::ostringstream oss;
if (WIFEXITED(wstatus)) {
oss << "Exit status: " << WEXITSTATUS(wstatus);
} else {
if (WIFSIGNALED(wstatus)) {
oss << strsignal(WTERMSIG(wstatus)) << " ";
}
if (WCOREDUMP(wstatus)) {
oss << "(core dumped)";
}
}
return oss.str();
}
/// ReExec class methods /////////////////////////////////////////////////// /// ReExec class methods ///////////////////////////////////////////////////
ReExec::ReExec(int argc, char *args[]) ReExec::ReExec(int argc, char *args[])
{ {

View File

@ -247,6 +247,8 @@ public:
*/ */
static bool backtick(const std::vector<std::string> cmd, std::string& out); static bool backtick(const std::vector<std::string> cmd, std::string& out);
static std::string waitStatusAsString(int wstatus);
class Internal; class Internal;
private: private:
Internal *m; Internal *m;

View File

@ -310,23 +310,30 @@ bool printableUrl(const string& fcharset, const string& in, string& out)
return true; return true;
} }
#ifdef _WIN32
// Convert X:/path to /X/path for path splitting inside the index
string path_slashdrive(const string& path)
{
string npath;
if (path_hasdrive(path)) {
npath.append(1, '/');
npath.append(1, path[0]);
if (path_isdriveabs(path)) {
npath.append(path.substr(2));
} else {
// This should be an error really
npath.append(1, '/');
npath.append(path.substr(2));
}
}
return npath;
}
#endif // _WIN32
string url_gpathS(const string& url) string url_gpathS(const string& url)
{ {
#ifdef _WIN32 #ifdef _WIN32
string u = url_gpath(url); return path_slashdrive(url_gpath(url));
string nu;
if (path_hasdrive(u)) {
nu.append(1, '/');
nu.append(1, u[0]);
if (path_isdriveabs(u)) {
nu.append(u.substr(2));
} else {
// This should be an error really
nu.append(1, '/');
nu.append(u.substr(2));
}
}
return nu;
#else #else
return url_gpath(url); return url_gpath(url);
#endif #endif

View File

@ -48,6 +48,9 @@ extern bool printableUrl(const std::string& fcharset,
const std::string& in, std::string& out); const std::string& in, std::string& out);
/// Same but, in the case of a Windows local path, also turn "c:/" into /// Same but, in the case of a Windows local path, also turn "c:/" into
/// "/c/" This should be used only for splitting the path in rcldb. /// "/c/" This should be used only for splitting the path in rcldb.
#ifdef _WIN32
extern std::string path_slashdrive(const std::string& path);
#endif
extern std::string url_gpathS(const std::string& url); extern std::string url_gpathS(const std::string& url);
/// Like strftime but guaranteed utf-8 output (esp. useful on Windows) /// Like strftime but guaranteed utf-8 output (esp. useful on Windows)

View File

@ -1,212 +0,0 @@
UTF-8 encoded sample plain-text file
‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
Markus Kuhn [ˈmaʳkʊs kuːn] <http://www.cl.cam.ac.uk/~mgk25/> — 2002-07-25
The ASCII compatible UTF-8 encoding used in this plain-text file
is defined in Unicode, ISO 10646-1, and RFC 2279.
Using Unicode/UTF-8, you can write in emails and source code things such as
Mathematics and sciences:
∮ E⋅da = Q, n → ∞, ∑ f(i) = ∏ g(i), ⎧⎡⎛┌─────┐⎞⎤⎫
⎪⎢⎜│a²+b³ ⎟⎥⎪
∀x∈: ⌈x⌉ = x⌋, α ∧ ¬β = ¬(¬α β), ⎪⎢⎜│───── ⎟⎥⎪
⎪⎢⎜⎷ c₈ ⎟⎥⎪
⊆ ℕ₀ ⊂ , ⎨⎢⎜ ⎟⎥⎬
⎪⎢⎜ ∞ ⎟⎥⎪
⊥ < a ≠ b ≡ c ≤ d ≪ ⇒ (⟦A⟧ ⇔ ⟪B⟫), ⎪⎢⎜ ⎲ ⎟⎥⎪
⎪⎢⎜ ⎳aⁱ-bⁱ⎟⎥⎪
2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm ⎩⎣⎝i=1 ⎠⎦⎭
Linguistics and dictionaries:
ði ıntəˈnæʃənəl fəˈnɛtık əsoʊsiˈeıʃn
Y [ˈʏpsilɔn], Yen [jɛn], Yoga [ˈjoːgɑ]
APL:
((VV)=V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈
Nicer typography in plain text files:
╔══════════════════════════════════════════╗
║ ║
║ • single and “double” quotes ║
║ ║
║ • Curly apostrophes: “Weve been here” ║
║ ║
║ • Latin-1 apostrophe and accents: '´` ║
║ ║
║ • deutsche „Anführungszeichen“ ║
║ ║
║ • †, ‡, ‰, •, 34, —, 5/+5, ™, … ║
║ ║
║ • ASCII safety test: 1lI|, 0OD, 8B ║
║ ╭─────────╮ ║
║ • the euro symbol: │ 14.95 € │ ║
║ ╰─────────╯ ║
╚══════════════════════════════════════════╝
Combining characters:
STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑
Greek (in Polytonic):
The Greek anthem:
Σὲ γνωρίζω ἀπὸ τὴν κόψη
τοῦ σπαθιοῦ τὴν τρομερή,
σὲ γνωρίζω ἀπὸ τὴν ὄψη
ποὺ μὲ βία μετράει τὴ γῆ.
᾿Απ᾿ τὰ κόκκαλα βγαλμένη
τῶν ῾Ελλήνων τὰ ἱερά
καὶ σὰν πρῶτα ἀνδρειωμένη
χαῖρε, ὦ χαῖρε, ᾿Ελευθεριά!
From a speech of Demosthenes in the 4th century BC:
Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι,
ὅταν τ᾿ εἰς τὰ πράγματα ἀποβλέψω καὶ ὅταν πρὸς τοὺς
λόγους οὓς ἀκούω· τοὺς μὲν γὰρ λόγους περὶ τοῦ
τιμωρήσασθαι Φίλιππον ὁρῶ γιγνομένους, τὰ δὲ πράγματ᾿
εἰς τοῦτο προήκοντα, ὥσθ᾿ ὅπως μὴ πεισόμεθ᾿ αὐτοὶ
πρότερον κακῶς σκέψασθαι δέον. οὐδέν οὖν ἄλλο μοι δοκοῦσιν
οἱ τὰ τοιαῦτα λέγοντες ἢ τὴν ὑπόθεσιν, περὶ ἧς βουλεύεσθαι,
οὐχὶ τὴν οὖσαν παριστάντες ὑμῖν ἁμαρτάνειν. ἐγὼ δέ, ὅτι μέν
ποτ᾿ ἐξῆν τῇ πόλει καὶ τὰ αὑτῆς ἔχειν ἀσφαλῶς καὶ Φίλιππον
τιμωρήσασθαι, καὶ μάλ᾿ ἀκριβῶς οἶδα· ἐπ᾿ ἐμοῦ γάρ, οὐ πάλαι
γέγονεν ταῦτ᾿ ἀμφότερα· νῦν μέντοι πέπεισμαι τοῦθ᾿ ἱκανὸν
προλαβεῖν ἡμῖν εἶναι τὴν πρώτην, ὅπως τοὺς συμμάχους
σώσομεν. ἐὰν γὰρ τοῦτο βεβαίως ὑπάρξῃ, τότε καὶ περὶ τοῦ
τίνα τιμωρήσεταί τις καὶ ὃν τρόπον ἐξέσται σκοπεῖν· πρὶν δὲ
τὴν ἀρχὴν ὀρθῶς ὑποθέσθαι, μάταιον ἡγοῦμαι περὶ τῆς
τελευτῆς ὁντινοῦν ποιεῖσθαι λόγον.
Δημοσθένους, Γ´ ᾿Ολυνθιακὸς
Georgian:
From a Unicode conference invitation:
გთხოვთ ახლავე გაიაროთ რეგისტრაცია Unicode-ის მეათე საერთაშორისო
კონფერენციაზე დასასწრებად, რომელიც გაიმართება 10-12 მარტს,
ქ. მაინცში, გერმანიაში. კონფერენცია შეჰკრებს ერთად მსოფლიოს
ექსპერტებს ისეთ დარგებში როგორიცაა ინტერნეტი და Unicode-ი,
ინტერნაციონალიზაცია და ლოკალიზაცია, Unicode-ის გამოყენება
ოპერაციულ სისტემებსა, და გამოყენებით პროგრამებში, შრიფტებში,
ტექსტების დამუშავებასა და მრავალენოვან კომპიუტერულ სისტემებში.
Russian:
From a Unicode conference invitation:
Зарегистрируйтесь сейчас на Десятую Международную Конференцию по
Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии.
Конференция соберет широкий круг экспертов по вопросам глобального
Интернета и Unicode, локализации и интернационализации, воплощению и
применению Unicode в различных операционных системах и программных
приложениях, шрифтах, верстке и многоязычных компьютерных системах.
Thai (UCS Level 2):
Excerpt from a poetry on The Romance of The Three Kingdoms (a Chinese
classic 'San Gua'):
[----------------------------|------------------------]
๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่
สิบสองกษัตริย์ก่อนหน้าแลถัดไป สององค์ไซร้โง่เขลาเบาปัญญา
ทรงนับถือขันทีเป็นที่พึ่ง บ้านเมืองจึงวิปริตเป็นนักหนา
โฮจิ๋นเรียกทัพทั่วหัวเมืองมา หมายจะฆ่ามดชั่วตัวสำคัญ
เหมือนขับไสไล่เสือจากเคหา รับหมาป่าเข้ามาเลยอาสัญ
ฝ่ายอ้องอุ้นยุแยกให้แตกกัน ใช้สาวนั้นเป็นชนวนชื่นชวนใจ
พลันลิฉุยกุยกีกลับก่อเหตุ ช่างอาเพศจริงหนาฟ้าร้องไห้
ต้องรบราฆ่าฟันจนบรรลัย ฤๅหาใครค้ำชูกู้บรรลังก์ ฯ
(The above is a two-column text. If combining characters are handled
correctly, the lines of the second column should be aligned with the
| character above.)
Ethiopian:
Proverbs in the Amharic language:
ሰማይ አይታረስ ንጉሥ አይከሰስ።
ብላ ካለኝ እንደአባቴ በቆመጠኝ።
ጌጥ ያለቤቱ ቁምጥና ነው።
ደሀ በሕልሙ ቅቤ ባይጠጣ ንጣት በገደለው።
የአፍ ወለምታ በቅቤ አይታሽም።
አይጥ በበላ ዳዋ ተመታ።
ሲተረጉሙ ይደረግሙ።
ቀስ በቀስ፥ ዕንቁላል በእግሩ ይሄዳል።
ድር ቢያብር አንበሳ ያስር።
ሰው እንደቤቱ እንጅ እንደ ጉረቤቱ አይተዳደርም።
እግዜር የከፈተውን ጉሮሮ ሳይዘጋው አይድርም።
የጎረቤት ሌባ፥ ቢያዩት ይስቅ ባያዩት ያጠልቅ።
ሥራ ከመፍታት ልጄን ላፋታት።
ዓባይ ማደሪያ የለው፥ ግንድ ይዞ ይዞራል።
የእስላም አገሩ መካ የአሞራ አገሩ ዋርካ።
ተንጋሎ ቢተፉ ተመልሶ ባፉ።
ወዳጅህ ማር ቢሆን ጨርስህ አትላሰው።
እግርህን በፍራሽህ ልክ ዘርጋ።
Runes:
ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ
(Old English, which transcribed into Latin reads 'He cwaeth that he
bude thaem lande northweardum with tha Westsae.' and means 'He said
that he lived in the northern land near the Western Sea.')
Braille:
⡌⠁⠧⠑ ⠼⠁⠒ ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌
⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞
⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎
⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂
⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙
⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑
⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲
⡕⠇⠙ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲
⡍⠔⠙⠖ ⡊ ⠙⠕⠝⠰⠞ ⠍⠑⠁⠝ ⠞⠕ ⠎⠁⠹ ⠹⠁⠞ ⡊ ⠅⠝⠪⠂ ⠕⠋ ⠍⠹
⠪⠝ ⠅⠝⠪⠇⠫⠛⠑⠂ ⠱⠁⠞ ⠹⠻⠑ ⠊⠎ ⠏⠜⠞⠊⠊⠥⠇⠜⠇⠹ ⠙⠑⠁⠙ ⠁⠃⠳⠞
⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ ⡊ ⠍⠊⠣⠞ ⠙⠁⠧⠑ ⠃⠑⠲ ⠔⠊⠇⠔⠫⠂ ⠍⠹⠎⠑⠇⠋⠂ ⠞⠕
⠗⠑⠛⠜⠙ ⠁ ⠊⠕⠋⠋⠔⠤⠝⠁⠊⠇ ⠁⠎ ⠹⠑ ⠙⠑⠁⠙⠑⠌ ⠏⠊⠑⠊⠑ ⠕⠋ ⠊⠗⠕⠝⠍⠕⠝⠛⠻⠹
⠔ ⠹⠑ ⠞⠗⠁⠙⠑⠲ ⡃⠥⠞ ⠹⠑ ⠺⠊⠎⠙⠕⠍ ⠕⠋ ⠳⠗ ⠁⠝⠊⠑⠌⠕⠗⠎
⠊⠎ ⠔ ⠹⠑ ⠎⠊⠍⠊⠇⠑⠆ ⠁⠝⠙ ⠍⠹ ⠥⠝⠙⠁⠇⠇⠪⠫ ⠙⠁⠝⠙⠎
⠩⠁⠇⠇ ⠝⠕⠞ ⠙⠊⠌⠥⠗⠃ ⠊⠞⠂ ⠕⠗ ⠹⠑ ⡊⠳⠝⠞⠗⠹⠰⠎ ⠙⠕⠝⠑ ⠋⠕⠗⠲ ⡹⠳
⠺⠊⠇⠇ ⠹⠻⠑⠋⠕⠗⠑ ⠏⠻⠍⠊⠞ ⠍⠑ ⠞⠕ ⠗⠑⠏⠑⠁⠞⠂ ⠑⠍⠏⠙⠁⠞⠊⠊⠁⠇⠇⠹⠂ ⠹⠁⠞
⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲
(The first couple of paragraphs of "A Christmas Carol" by Dickens)
Compact font selection example text:
ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789
abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ
–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд
∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi<>⑀₂ἠḂӥẄɐː⍎אԱა
Greetings in various languages:
Hello world, Καλημέρα κόσμε, コンニチハ
Box drawing alignment tests: █
╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳
║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳
║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳
╠╡ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳
║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎
║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏
╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ ▗▄▖▛▀▜ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█
▝▀▘▙▄▟

View File

@ -1,121 +0,0 @@
/* Copyright (C) 2014 J.F.Dockes
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
// Test program for the workqueue module
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include "safeunistd.h"
#include "workqueue.h"
static char *thisprog;
static char usage [] =
" \n\n"
;
static void
Usage(void)
{
fprintf(stderr, "%s: usage:\n%s", thisprog, usage);
exit(1);
}
static int op_flags;
#define OPT_MOINS 0x1
#define OPT_s 0x2
#define OPT_b 0x4
class Task {
public:
Task()
: m_id(o_id++)
{}
int m_id;
static int o_id;
};
int Task::o_id;
void *worker(void *vtp)
{
fprintf(stderr, "Worker working\n");
WorkQueue<Task> *tqp = (WorkQueue<Task> *)vtp;
Task tsk;
for (;;) {
if (!tqp->take(&tsk)) {
fprintf(stderr, "Worker: take failed\n");
return (void*)0;
}
fprintf(stderr, "WORKER: got task %d\n", tsk.m_id);
if (tsk.m_id > 20) {
tqp->workerExit();
break;
}
}
return (void*)1;
}
int main(int argc, char **argv)
{
int count = 10;
thisprog = argv[0];
argc--; argv++;
while (argc > 0 && **argv == '-') {
(*argv)++;
if (!(**argv))
/* Cas du "adb - core" */
Usage();
while (**argv)
switch (*(*argv)++) {
case 's': op_flags |= OPT_s; break;
case 'b': op_flags |= OPT_b; if (argc < 2) Usage();
if ((sscanf(*(++argv), "%d", &count)) != 1)
Usage();
argc--;
goto b1;
default: Usage(); break;
}
b1: argc--; argv++;
}
if (argc != 0)
Usage();
WorkQueue<Task> wq("testwq", 10);
if (!wq.start(2, &worker, &wq)) {
fprintf(stderr, "Start failed\n");
exit(1);
}
for (;;) {
Task tsk;
fprintf(stderr, "BOSS: put task %d\n", tsk.m_id);
if (!wq.put(tsk)) {
fprintf(stderr, "Boss: put failed\n");
exit(1);
}
if ((tsk.m_id % 10) == 0)
sleep(1);
}
exit(0);
}

View File

@ -1100,17 +1100,6 @@ bool ExecCmd::maybereap(int *status)
} }
} }
// Static
bool ExecCmd::backtick(const vector<string> cmd, string& out)
{
vector<string>::const_iterator it = cmd.begin();
it++;
vector<string> args(it, cmd.end());
ExecCmd mexec;
int status = mexec.doexec(*cmd.begin(), args, 0, &out);
return status == 0;
}
int ExecCmd::doexec(const string &cmd, const vector<string>& args, int ExecCmd::doexec(const string &cmd, const vector<string>& args,
const string *input, string *output) const string *input, string *output)
{ {
@ -1159,3 +1148,23 @@ int ExecCmd::doexec(const string &cmd, const vector<string>& args,
cleaner.inactivate(); cleaner.inactivate();
return wait(); return wait();
} }
// Static
bool ExecCmd::backtick(const vector<string> cmd, string& out)
{
vector<string>::const_iterator it = cmd.begin();
it++;
vector<string> args(it, cmd.end());
ExecCmd mexec;
int status = mexec.doexec(*cmd.begin(), args, 0, &out);
return status == 0;
}
// Static. Unimplemented on windows for now
std::string ExecCmd::waitStatusAsString(int wstatus)
{
std::ostringstream oss;
oss << std::hex << "0x" << wstatus << std::dec;
return oss.str();
}

View File

@ -1,438 +1,38 @@
# (C) 2015 J.F.Dockes # (C) 2015-2022 J.F.Dockes
# This file contains most of the data which determines how we
# handle the different mime types (also see the "mimeview" file).
# #
# This is the version specific to MS-WINDOWS # MS-WINDOWS specific definitions for mimeconf
# #
# Sections:
# top-level: Decompression parameters. Should not be at top-level, historical.
# [index] : Associations of mime types to the filters that translate them
# to plain text or html.
# [icons] : Associations of mime types to result list icons (GUI)
# [categories] : groupings of mime types (media, text, message etc.)
# [guifilters] : defines the filtering checkboxes in the GUI. Uses the
# above categories by default
## ####################################### # Decompression: the windows version always uses 7z, no decompressor parameter is necessary
# Decompression: these types need a first pass to create a temp file to application/gzip = uncompress rcluncomp.py 7z %f %t
# work with. We use a script because uncompress utilities usually work in application/x-gzip = uncompress rcluncomp.py 7z %f %t
# place, which is not suitable. application/x-compress = uncompress rcluncomp.py 7z %f %t
# application/x-bzip2 = uncompress rcluncomp.py 7z %f %t
# Obviously this should be in a [decompress] section or such, but it was once application/x-xz = uncompress rcluncomp.py 7z %f %t
# forgotten and remained global for compatibility... application/x-lzma = uncompress rcluncomp.py 7z %f %t
# application/x-scribus =
# The %t parameter will be substituted to the name of a temporary directory application/x-tex =
# by recoll. This directory is guaranteed empty when calling the filter
#
# The %f parameter will be substituted with the input file.
#
# The script (ie: rcluncomp) must output the uncompressed file name on
# stdout. Note that the windows version will always use 7z, and ignore
# the decompressor parameter in the following lines
application/gzip = uncompress python rcluncomp.py 7z %f %t
application/x-gzip = uncompress python rcluncomp.py 7z %f %t
application/x-compress = uncompress python rcluncomp.py 7z %f %t
application/x-bzip2 = uncompress python rcluncomp.py 7z %f %t
application/x-xz = uncompress python rcluncomp.py 7z %f %t
application/x-lzma = uncompress python rcluncomp.py 7z %f %t
## ###################################
# Filters for indexing and internal preview.
# The "internal" filters are hardwired in the c++ code.
# The external "exec" filters are typically scripts. By default, they output the
# document in simple html format, have a look at the scripts.
# A different format (ie text/plain), and a character set can be defined for
# each filter, see the exemples below (ie: msword)
[index] [index]
application/postscript =
application/msword = execm python rcldoc.py
application/vnd.ms-excel = execm python rclxls.py
application/vnd.ms-outlook = execm python rclpst.py
application/vnd.ms-powerpoint = execm python rclppt.py
# Also Handle the mime type returned by "file -i" for a suffix-less word
# file. This could probably just as well be an excel file, but we have to
# chose one.
application/vnd.ms-office = execm python rcldoc.py
application/vnd.oasis.opendocument.text = \
internal xsltproc meta meta.xml opendoc-meta.xsl \
body content.xml opendoc-body.xsl
application/vnd.oasis.opendocument.text-template = \
internal xsltproc meta meta.xml opendoc-meta.xsl \
body content.xml opendoc-body.xsl
application/vnd.oasis.opendocument.presentation = \
internal xsltproc meta meta.xml opendoc-meta.xsl \
body content.xml opendoc-body.xsl
application/vnd.oasis.opendocument.spreadsheet = \
internal xsltproc meta meta.xml opendoc-meta.xsl \
body content.xml opendoc-body.xsl
application/vnd.oasis.opendocument.graphics = \
internal xsltproc meta meta.xml opendoc-meta.xsl \
body content.xml opendoc-body.xsl
application/vnd.oasis.opendocument.presentation-flat-xml = \
internal xsltproc opendoc-flat.xsl
application/vnd.oasis.opendocument.text-flat-xml = \
internal xsltproc opendoc-flat.xsl
application/vnd.oasis.opendocument.spreadsheet-flat-xml = \
internal xsltproc opendoc-flat.xsl
application/vnd.openxmlformats-officedocument.wordprocessingml.document = \
internal xsltproc meta docProps/core.xml openxml-meta.xsl \
body word/document.xml openxml-word-body.xsl \
body word/footnotes.xml openxml-word-body.xsl \
body word/endnotes.xml openxml-word-body.xsl
application/vnd.openxmlformats-officedocument.wordprocessingml.template = \
internal xsltproc meta docProps/core.xml openxml-meta.xsl \
body word/document.xml openxml-word-body.xsl \
body word/footnotes.xml openxml-word-body.xsl \
body word/endnotes.xml openxml-word-body.xsl
application/vnd.openxmlformats-officedocument.presentationml.template = \
execm python rclopxml.py
application/vnd.openxmlformats-officedocument.presentationml.presentation = \
execm python rclopxml.py
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet = \
internal xsltproc meta docProps/core.xml openxml-meta.xsl \
body xl/sharedStrings.xml openxml-xls-body.xsl
application/vnd.openxmlformats-officedocument.spreadsheetml.template =\
internal xsltproc meta docProps/core.xml openxml-meta.xsl \
body xl/sharedStrings.xml openxml-xls-body.xsl
application/vnd.sun.xml.calc = \
internal xsltproc meta meta.xml opendoc-meta.xsl \
body content.xml opendoc-body.xsl
application/vnd.sun.xml.calc.template = \
internal xsltproc meta meta.xml opendoc-meta.xsl \
body content.xml opendoc-body.xsl
application/vnd.sun.xml.draw = \
internal xsltproc meta meta.xml opendoc-meta.xsl \
body content.xml opendoc-body.xsl
application/vnd.sun.xml.draw.template = \
internal xsltproc meta meta.xml opendoc-meta.xsl \
body content.xml opendoc-body.xsl
application/vnd.sun.xml.impress = \
internal xsltproc meta meta.xml opendoc-meta.xsl \
body content.xml opendoc-body.xsl
application/vnd.sun.xml.impress.template = \
internal xsltproc meta meta.xml opendoc-meta.xsl \
body content.xml opendoc-body.xsl
application/vnd.sun.xml.math = \
internal xsltproc meta meta.xml opendoc-meta.xsl \
body content.xml opendoc-body.xsl
application/vnd.sun.xml.writer = \
internal xsltproc meta meta.xml opendoc-meta.xsl \
body content.xml opendoc-body.xsl
application/vnd.sun.xml.writer.global = \
internal xsltproc meta meta.xml opendoc-meta.xsl \
body content.xml opendoc-body.xsl
application/vnd.sun.xml.writer.template = \
internal xsltproc meta meta.xml opendoc-meta.xsl \
body content.xml opendoc-body.xsl
#application/postscript = exec rclps
#application/x-gnuinfo = execm python rclinfo
#application/x-tar = execm python rcltar
application/epub+zip = execm python rclepub
application/x-ipynb+json = execm python rclipynb.py
application/javascript = internal text/plain
application/ogg = execm python rclaudio
application/pdf = execm python rclpdf.py
application/sql = internal text/plain
application/vnd.wordperfect = exec wpd/wpd2html;mimetype=text/html application/vnd.wordperfect = exec wpd/wpd2html;mimetype=text/html
application/x-7z-compressed = execm python rcl7z application/x-dvi =
application/x-abiword = internal xsltproc abiword.xsl application/x-gnuinfo =
application/x-awk = internal text/plain application/x-ipynb+json = execm rclipynb.py
application/x-chm = execm python rclchm application/x-tar =
application/x-dia-diagram = execm python rcldia;mimetype=text/plain
application/x-flac = execm python rclaudio
application/x-gnote = execm python rclxml.py
application/x-hwp = execm python rclhwp.py
application/x-mimehtml = internal message/rfc822
application/x-perl = internal text/plain
application/x-php = internal text/plain
application/x-rar = execm python rclrar;charset=default
application/x-shellscript = internal text/plain
application/x-webarchive = execm python rclwar
application/x-zerosize = internal
application/zip = execm python rclzip;charset=default
audio/aac = execm python rclaudio
audio/mp4 = execm python rclaudio
audio/mpeg = execm python rclaudio
audio/x-karaoke = execm python rclkar
image/gif = execm rclimg.exe image/gif = execm rclimg.exe
image/jp2 = execm rclimg.exe image/jp2 = execm rclimg.exe
image/jpeg = execm rclimg.exe image/jpeg = execm rclimg.exe
image/png = execm rclimg.exe image/png = execm rclimg.exe
image/svg+xml = internal xsltproc svg.xsl
image/tiff = execm rclimg.exe image/tiff = execm rclimg.exe
image/vnd.djvu = execm python rcldjvu.py image/x-nikon-nef = execm rclimg.exe
inode/symlink = internal
inode/x-empty = internal application/x-zerosize
message/rfc822 = internal
text/calendar = execm python rclics;mimetype=text/plain
text/css = internal text/plain
text/html = internal
text/plain = internal
text/plain1 = internal
#text/rtf = execm python rclrtf.py
text/rtf = exec unrtf --nopict --html;mimetype=text/html
text/x-c = internal
text/x-c+ = internal
text/x-c++ = internal
text/x-chm-html = internal text/html
text/x-csharp = internal text/plain
text/x-csv = internal text/plain
text/x-fictionbook = internal xsltproc fb2.xsl
text/x-ini = internal text/plain
text/x-mail = internal
text/x-orgmode = execm python rclorgmode.py
text/x-perl = internal text/plain
text/x-python = execm python rclpython.py
text/x-shellscript = internal text/plain
text/x-srt = internal text/plain
image/x-xcf = execm rclimg.exe image/x-xcf = execm rclimg.exe
text/x-bibtex =
# Generic XML is best indexed as text, else it generates too many errors text/x-gaim-log =
# All parameter and tag names, attribute values etc, are indexed as text/x-html-aptosid-man =
# text. rclxml.py tries to just index the text content. text/x-man =
#application/xml = execm rclxml.py text/x-purple-log =
#text/xml = execm rclxml.py text/x-tex =
application/xml = internal text/plain video/x-msvideo = execm rclimg.exe
text/xml = internal text/plain
## #############################################
# Icons to be used in the result list if required by gui config
[icons]
application/epub+zip = book
application/javascript = source
application/msword = wordprocessing
application/ogg = sownd
application/pdf = pdf
application/postscript = postscript
application/vnd.ms-excel = spreadsheet
application/vnd.ms-powerpoint = presentation
application/vnd.oasis.opendocument.presentation = presentation
application/vnd.oasis.opendocument.spreadsheet = spreadsheet
application/vnd.oasis.opendocument.text = wordprocessing
application/vnd.openxmlformats-officedocument.presentationml.presentation = presentation
application/vnd.openxmlformats-officedocument.presentationml.template = presentation
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet = spreadsheet
application/vnd.openxmlformats-officedocument.spreadsheetml.template = spreadsheet
application/vnd.openxmlformats-officedocument.wordprocessingml.document = wordprocessing
application/vnd.openxmlformats-officedocument.wordprocessingml.template = wordprocessing
application/vnd.sun.xml.calc = spreadsheet
application/vnd.sun.xml.calc.template = spreadsheet
application/vnd.sun.xml.draw = drawing
application/vnd.sun.xml.draw.template = drawing
application/vnd.sun.xml.impress = presentation
application/vnd.sun.xml.impress.template = presentation
application/vnd.sun.xml.math = wordprocessing
application/vnd.sun.xml.writer = wordprocessing
application/vnd.sun.xml.writer.global = wordprocessing
application/vnd.sun.xml.writer.template = wordprocessing
application/vnd.wordperfect = wordprocessing
application/x-abiword = wordprocessing
application/x-awk = source
application/x-chm = book
application/x-dia-diagram = drawing
application/x-dvi = document
application/x-flac = sownd
application/x-fsdirectory = folder
application/x-gnote = document
#application/x-gnuinfo = book
application/x-gnumeric = spreadsheet
application/x-ipynb+json = document
application/x-kword = wordprocessing
application/x-lyx = wordprocessing
application/x-mimehtml = message
application/x-mobipocket-ebook = document
application/x-okular-notes = document
application/x-perl = source
application/x-php = source
application/x-rar = archive
application/x-scribus = document
application/x-scribus = wordprocessing
application/x-shellscript = source
application/x-tar = archive
application/x-tex = wordprocessing
application/x-webarchive = archive
application/xml = document
application/zip = archive
application/x-7z-compressed = archive
audio/mpeg = sownd
audio/x-karaoke = sownd
image/bmp = image
image/gif = image
image/jp2 = image
image/jpeg = image
image/png = image
image/svg+xml = drawing
image/tiff = image
image/vnd.djvu = document
image/x-xcf = image
image/x-xpmi = image
inode/directory = folder
inode/symlink = emblem-symbolic-link
message/rfc822 = message
text/html = html
text/html|chm = bookchap
text/html|epub = bookchap
#text/html|gnuinfo = bookchap
text/plain = txt
text/rtf = wordprocessing
text/x-c = source
text/x-c+ = source
text/x-c++ = source
text/x-csv = txt
text/x-fictionbook = document
text/x-html-aptosid-man = aptosid-book
text/x-html-sidux-man = sidux-book
text/x-ini = txt
text/x-mail = message
text/x-man = document
text/x-orgmode = document
text/x-perl = source
text/x-purple-html-log = pidgin
text/x-purple-log = pidgin
text/x-python = text-x-python
text/x-shellscript = source
text/x-tex = wordprocessing
text/xml = document
video/3gpp = video
video/mp2p = video
video/mp2t = video
video/mp4 = video
video/mpeg = video
video/quicktime = video
video/x-matroska = video
video/x-ms-asf = video
video/x-msvideo = video
[categories]
# Categories group mime types by "kind". They can be used from the query
# language as an "rclcat" clause. This is fully dynamic, you can change the
# names and groups as you wish, only the mime types are stored in the index.
#
# If you add/remove categories, you may also want to change the
# "guifilters" section below.
text = \
application/epub+zip \
application/msword \
application/pdf \
application/postscript \
application/vnd.oasis.opendocument.text \
application/vnd.openxmlformats-officedocument.wordprocessingml.document \
application/vnd.openxmlformats-officedocument.wordprocessingml.template \
application/vnd.sun.xml.writer \
application/vnd.sun.xml.writer.global \
application/vnd.sun.xml.writer.template \
application/vnd.wordperfect \
application/x-abiword \
application/x-awk \
application/x-chm \
application/x-dvi \
application/x-gnote \
application/x-gnuinfo \
application/x-ipynb+json \
application/x-kword \
application/x-lyx \
application/x-mobipocket-ebook \
application/x-okular-notes \
application/x-perl \
application/x-scribus \
application/x-shellscript \
application/x-tex \
application/xml \
text/xml \
text/x-csv \
text/x-tex \
image/vnd.djvu \
text/calendar \
text/html \
text/plain \
text/rtf \
text/x-c \
text/x-c++ \
text/x-c+ \
text/x-fictionbook \
text/x-html-aptosid-man \
text/x-html-sidux-man \
text/x-ini \
text/x-man \
text/x-orgmode \
text/x-perl \
text/x-python \
text/x-shellscript
spreadsheet = \
application/vnd.ms-excel \
application/vnd.oasis.opendocument.spreadsheet \
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet \
application/vnd.openxmlformats-officedocument.spreadsheetml.template \
application/vnd.sun.xml.calc \
application/vnd.sun.xml.calc.template \
application/x-gnumeric
presentation = \
application/vnd.ms-powerpoint \
application/vnd.oasis.opendocument.presentation \
application/vnd.openxmlformats-officedocument.presentationml.presentation \
application/vnd.openxmlformats-officedocument.presentationml.template \
application/vnd.sun.xml.impress \
application/vnd.sun.xml.impress.template
media = \
application/ogg \
application/x-flac \
audio/* \
image/* \
video/* \
message = message/rfc822 \
text/x-gaim-log \
text/x-mail \
text/x-purple-log \
text/x-purple-html-log \
other = application/vnd.sun.xml.draw \
application/vnd.sun.xml.draw.template \
application/vnd.sun.xml.math \
application/x-dia-diagram \
application/x-fsdirectory \
application/x-mimehtml \
application/x-rar \
application/x-tar \
application/x-webarchive \
application/zip \
application/x-7z-compressed \
inode/directory \
inode/symlink \
[guifilters]
# This defines the top level filters in the GUI (accessed by the the
# radiobuttons above the results area, or a toolbar combobox).
# Each entry defines a label and a query language fragment that will be
# applied to filter the current query if the option is activated.
#
# This does not really belong in mimeconf, but it does belong in the index
# config (not the GUI one), because it's not necessarily the same in all
# configs, it has to go somewhere, and it's not worth a separate config
# file...
#
# By default this filters by document category (see above), but any
# language fragment should be ok. Be aware though that the "document
# history" queries only know about simple "rclcat" filtering.
#
# If you don't want the filter names to be displayed in alphabetic order,
# you can define them with a colon. The part before the colon is not
# displayed but used for ordering, ie: a:zzbutshouldbefirst b:aacomeslast
#
text = rclcat:text
spreadsheet = rclcat:spreadsheet
presentation = rclcat:presentation
media = rclcat:media
message = rclcat:message
other = rclcat:other

View File

@ -1,21 +1,5 @@
## ########################################## # MS WINDOWS system changes for mimeview
# External viewers, launched by the recoll GUI when you click on a result
# 'edit' link
#
# MS WINDOWS VERSION
#
# Mime types which we should not uncompress if they are found gzipped or
# bzipped because the native viewer knows how to handle. These would be
# exceptions and the list is normally empty
#nouncompforviewmts =
# For releases 1.18 and later: exceptions when using the x-all entry: these
# types will use their local definition. This is useful, e.g.:
#
# - for pdf, where we can pass additional parameters like page to open and
# search string
# - For pages of CHM and EPUB documents where we can choose to open the
# parent document instead of a temporary html file.
xallexcepts = \ xallexcepts = \
text/html|epub \ text/html|epub \
application/x-fsdirectory|parentopen inode/directory|parentopen application/x-fsdirectory|parentopen inode/directory|parentopen
@ -44,157 +28,6 @@ application/pdf = C:/users/bill/appdata/local/apps/evince-2.32.0.145/bin/evince
#application/pdf = "C:/Program Files/SumatraPDF/SumatraPDF.exe" -page %p %f #application/pdf = "C:/Program Files/SumatraPDF/SumatraPDF.exe" -page %p %f
#application/pdf = "C:/Program Files (x86)/Foxit Software/Foxit Reader/FoxitReader.exe" %f /A page=%p #application/pdf = "C:/Program Files (x86)/Foxit Software/Foxit Reader/FoxitReader.exe" %f /A page=%p
###### THE FOLLOWING ARE NOT USED AT ALL ON WINDOWS, but the types need to ##########
###### be listed for an "Open" link to appear in the result list # Other MIME types have no specializations on Windows, but the types need to be listed for an "Open"
application/epub+zip = ebook-viewer %f # link to appear in the result list, the listing is in the generic file
application/x-gnote = gnote %f
application/x-mobipocket-ebook = ebook-viewer %f
application/x-kword = kword %f
application/x-abiword = abiword %f
application/postscript = evince --page-index=%p --find=%s %f
application/x-dvi = evince --page-index=%p --find=%s %f
application/x-lyx = lyx %f
application/x-scribus = scribus %f
#application/msword = libreoffice %f
application/msword = \
"C:/Program Files (x86)/LibreOffice 5/program/soffice.exe" %f
application/x-hwp = hanword %f
application/vnd.ms-excel = libreoffice %f
application/vnd.ms-powerpoint = libreoffice %f
application/vnd.oasis.opendocument.text = libreoffice %f
application/vnd.oasis.opendocument.presentation = libreoffice %f
application/vnd.oasis.opendocument.spreadsheet = libreoffice %f
application/vnd.openxmlformats-officedocument.wordprocessingml.document = \
libreoffice %f
application/vnd.openxmlformats-officedocument.wordprocessingml.template = \
libreoffice %f
application/vnd.openxmlformats-officedocument.presentationml.template = \
libreoffice %f
application/vnd.openxmlformats-officedocument.presentationml.presentation = \
libreoffice %f
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet = \
libreoffice %f
application/vnd.openxmlformats-officedocument.spreadsheetml.template =\
libreoffice %f
application/vnd.sun.xml.calc = libreoffice %f
application/vnd.sun.xml.calc.template = libreoffice %f
application/vnd.sun.xml.draw = libreoffice %f
application/vnd.sun.xml.draw.template = libreoffice %f
application/vnd.sun.xml.impress = libreoffice %f
application/vnd.sun.xml.impress.template = libreoffice %f
application/vnd.sun.xml.math = libreoffice %f
application/vnd.sun.xml.writer = libreoffice %f
application/vnd.sun.xml.writer.global = libreoffice %f
application/vnd.sun.xml.writer.template = libreoffice %f
application/vnd.wordperfect = libreoffice %f
text/rtf = libreoffice %f
application/x-dia-diagram = dia %f
application/x-fsdirectory = dolphin %f
inode/directory = dolphin %f
application/x-gnuinfo = xterm -e "info -f %f"
application/x-gnumeric = gnumeric %f
application/x-flac = rhythmbox %f
audio/mpeg = rhythmbox %f
application/ogg = rhythmbox %f
audio/x-karaoke = kmid %f
image/jpeg = gwenview %f
image/png = gwenview %f
image/tiff = gwenview %f
image/gif = gwenview %f
image/svg+xml = inkview %f
image/vnd.djvu = djview %f
image/x-xcf = gimp %f
image/bmp = gwenview %f
image/x-ms-bmp = gwenview %f
image/x-xpmi = gwenview %f
# Opening mail messages not always works.
# - Thunderbird will only open a single-message file if it has an .emf
# extension
# - "sylpheed %f" seems to work ok as of version 3.3
# - "kmail --view %u" works
message/rfc822 = thunderbird -file %f
text/x-mail = thunderbird -file %f
application/x-mimehtml = thunderbird -file %f
text/calendar = evolution %f
application/x-okular-notes = okular %f
application/x-rar = ark %f
application/x-tar = ark %f
application/zip = ark %f
application/x-7z-compressed = ark %f
application/x-awk = emacsclient --no-wait %f
application/x-perl = emacsclient --no-wait %f
text/x-perl = emacsclient --no-wait %f
application/x-shellscript = emacsclient --no-wait %f
text/x-shellscript = emacsclient --no-wait %f
text/x-srt = emacsclient --no-wait %f
# Or firefox -remote "openFile(%u)"
text/html = firefox %u
# gnu info nodes are translated to html with a "gnuinfo"
# rclaptg. rclshowinfo knows how to start the info command on the right
# node
text/html|gnuinfo = rclshowinfo %F %(title);ignoreipath=1
application/x-webarchive = konqueror %f
text/x-fictionbook = ebook-viewer %f
application/x-tex = emacsclient --no-wait %f
application/xml = emacsclient --no-wait %f
text/xml = emacsclient --no-wait %f
text/x-tex = emacsclient --no-wait %f
text/plain = emacsclient --no-wait %f
text/x-awk = emacsclient --no-wait %f
text/x-c = emacsclient --no-wait %f
text/x-c+ = emacsclient --no-wait %f
text/x-c++ = emacsclient --no-wait %f
text/x-csv = libreoffice %f
text/x-html-sidux-man = konqueror %f
text/x-html-aptosid-man = iceweasel %f
application/x-chm = kchmviewer %f
# Html pages inside a chm have a chm rclaptg set by the filter. Kchmviewer
# knows how to use the ipath (which is the internal chm path) to open the
# file at the right place
text/html|chm = kchmviewer --url %i %F
text/x-ini = emacsclient --no-wait %f
text/x-man = xterm -u8 -e "groff -T ascii -man %f | more"
text/x-python = idle %f
text/x-gaim-log = emacsclient --no-wait %f
text/x-purple-html-log = emacsclient --no-wait %f
text/x-purple-log = emacsclient --no-wait %f
# The video types will usually be handled by the desktop default, but they
# need entries here to get an "Open" link
video/3gpp = vlc %f
video/mp2p = vlc %f
video/mp2t = vlc %f
video/mp4 = vlc %f
video/mpeg = vlc %f
video/quicktime = vlc %f
video/x-matroska = vlc %f
video/x-ms-asf = vlc %f
video/x-msvideo = vlc %f

View File

@ -171,13 +171,15 @@ copyrecoll()
chkcp $RCL/doc/user/docbook-xsl.css $DESTDIR/Share/doc chkcp $RCL/doc/user/docbook-xsl.css $DESTDIR/Share/doc
mkdir -p $DESTDIR/Share/doc/webhelp mkdir -p $DESTDIR/Share/doc/webhelp
rsync -av $RCL/doc/user/webhelp/docs/* $DESTDIR/Share/doc/webhelp || exit 1 rsync -av $RCL/doc/user/webhelp/docs/* $DESTDIR/Share/doc/webhelp || exit 1
chkcp $RCL/sampleconf/fields $DESTDIR/Share/examples chkcp $RCL/sampleconf/fields $DESTDIR/Share/examples
chkcp $RCL/sampleconf/fragment-buttons.xml $DESTDIR/Share/examples chkcp $RCL/sampleconf/fragment-buttons.xml $DESTDIR/Share/examples
chkcp $RCL/windows/mimeconf $DESTDIR/Share/examples chkcp $RCL/sampleconf/mimeconf $DESTDIR/Share/examples
chkcp $RCL/sampleconf/mimemap $DESTDIR/Share/examples chkcp $RCL/sampleconf/mimeview $DESTDIR/Share/examples
chkcp $RCL/windows/mimeview $DESTDIR/Share/examples chkcp $RCL/sampleconf/mimemap $DESTDIR/Share/examples
chkcp $RCL/sampleconf/recoll.conf $DESTDIR/Share/examples chkcp $RCL/windows/mimeconf $DESTDIR/Share/examples/windows
chkcp $RCL/sampleconf/recoll.qss $DESTDIR/Share/examples chkcp $RCL/windows/mimeview $DESTDIR/Share/examples/windows
chkcp $RCL/sampleconf/recoll.conf $DESTDIR/Share/examples
chkcp $RCL/sampleconf/recoll.qss $DESTDIR/Share/examples
chkcp $RCL/sampleconf/recoll-dark.qss $DESTDIR/Share/examples chkcp $RCL/sampleconf/recoll-dark.qss $DESTDIR/Share/examples
chkcp $RCL/sampleconf/recoll-dark.css $DESTDIR/Share/examples chkcp $RCL/sampleconf/recoll-dark.css $DESTDIR/Share/examples
@ -349,7 +351,7 @@ test "$VERSION" = "$CFVERS" ||
echo Packaging version $CFVERS echo Packaging version $CFVERS
for d in doc examples filters images translations; do for d in doc examples examples/windows filters images translations; do
test -d $DESTDIR/Share/$d || mkdir -p $DESTDIR/Share/$d || \ test -d $DESTDIR/Share/$d || mkdir -p $DESTDIR/Share/$d || \
fatal mkdir $d failed fatal mkdir $d failed
done done

View File

@ -35,25 +35,23 @@ windows {
LIBS += \ LIBS += \
../build-librecoll-Desktop_Qt_5_8_0_MinGW_32bit-Release/release/librecoll.dll \ ../build-librecoll-Desktop_Qt_5_8_0_MinGW_32bit-Release/release/librecoll.dll \
-lshlwapi -lpsapi -lkernel32 -lshlwapi -lpsapi -lkernel32
} }
contains(QMAKE_CC, cl){ contains(QMAKE_CC, cl){
# MSVC # MSVC
RECOLLDEPS = ../../../../recolldeps/msvc RECOLLDEPS = ../../../../recolldeps/msvc
DEFINES += USING_STATIC_LIBICONV DEFINES += USING_STATIC_LIBICONV
LIBS += \ PRE_TARGETDEPS = \
-L../build-librecoll-Desktop_Qt_5_14_2_MSVC2017_32bit-Release/release \ ../build-librecoll-Desktop_Qt_5_14_2_MSVC2017_32bit-Release/release/librecoll.lib
-llibrecoll \ LIBS += \
$$RECOLLDEPS/libxml2/libxml2-2.9.4+dfsg1/win32/bin.msvc/libxml2.lib \ -L../build-librecoll-Desktop_Qt_5_14_2_MSVC2017_32bit-Release/release -llibrecoll \
$$RECOLLDEPS/libxslt/libxslt-1.1.29/win32/bin.msvc/libxslt.lib \ $$RECOLLDEPS/libxml2/libxml2-2.9.4+dfsg1/win32/bin.msvc/libxml2.lib \
-L../build-libxapian-Desktop_Qt_5_14_2_MSVC2017_32bit-Release/release \ $$RECOLLDEPS/libxslt/libxslt-1.1.29/win32/bin.msvc/libxslt.lib \
-llibxapian \ -L../build-libxapian-Desktop_Qt_5_14_2_MSVC2017_32bit-Release/release -llibxapian \
$$RECOLLDEPS/zlib-1.2.11/zdll.lib \ $$RECOLLDEPS/zlib-1.2.11/zdll.lib \
-L$$RECOLLDEPS/build-libiconv-Desktop_Qt_5_14_2_MSVC2017_32bit-Release/release \ -L$$RECOLLDEPS/build-libiconv-Desktop_Qt_5_14_2_MSVC2017_32bit-Release/release -llibiconv \
-llibiconv -lShell32 \ -lShell32 -lrpcrt4 -lws2_32 -luser32 -lshlwapi -lpsapi -lkernel32
-lrpcrt4 -lws2_32 -luser32 \ }
-lshlwapi -lpsapi -lkernel32
}
INCLUDEPATH += ../../windows INCLUDEPATH += ../../windows
SOURCES += ../../windows/getopt.cc SOURCES += ../../windows/getopt.cc

View File

@ -29,18 +29,16 @@ windows {
contains(QMAKE_CC, cl){ contains(QMAKE_CC, cl){
# Visual Studio # Visual Studio
RECOLLDEPS = ../../../../recolldeps/msvc RECOLLDEPS = ../../../../recolldeps/msvc
PRE_TARGETDEPS = \
../build-librecoll-Desktop_Qt_5_14_2_MSVC2017_32bit-Release/release/librecoll.lib
LIBS += \ LIBS += \
-L../build-librecoll-Desktop_Qt_5_14_2_MSVC2017_32bit-Release/release \ -L../build-librecoll-Desktop_Qt_5_14_2_MSVC2017_32bit-Release/release -llibrecoll \
-llibrecoll \
$$RECOLLDEPS/libxml2/libxml2-2.9.4+dfsg1/win32/bin.msvc/libxml2.lib \ $$RECOLLDEPS/libxml2/libxml2-2.9.4+dfsg1/win32/bin.msvc/libxml2.lib \
$$RECOLLDEPS/libxslt/libxslt-1.1.29/win32/bin.msvc/libxslt.lib \ $$RECOLLDEPS/libxslt/libxslt-1.1.29/win32/bin.msvc/libxslt.lib \
-L../build-libxapian-Desktop_Qt_5_14_2_MSVC2017_32bit-Release/release \ -L../build-libxapian-Desktop_Qt_5_14_2_MSVC2017_32bit-Release/release -llibxapian \
-llibxapian \ -L$$RECOLLDEPS/build-libiconv-Desktop_Qt_5_14_2_MSVC2017_32bit-Release/release/ -llibiconv \
-L$$RECOLLDEPS/build-libiconv-Desktop_Qt_5_14_2_MSVC2017_32bit-Release/release/ \
-llibiconv \
$$RECOLLDEPS/zlib-1.2.11/zdll.lib \ $$RECOLLDEPS/zlib-1.2.11/zdll.lib \
-lrpcrt4 -lws2_32 -luser32 -lshell32 \ -lrpcrt4 -lws2_32 -luser32 -lshell32 -lshlwapi -lpsapi -lkernel32
-lshlwapi -lpsapi -lkernel32
} }
INCLUDEPATH += ../../windows INCLUDEPATH += ../../windows

View File

@ -1 +1 @@
FreqFor maildir_uniquexxx : 692 FreqFor maildir_uniquexxx : 734

View File

@ -8,4 +8,4 @@
# values is identical. # values is identical.
[index] [index]
application/x-tar = execm rcltar application/x-tar = execm rcltar.py

View File

@ -1,2 +1,2 @@
1 results 1 results
application/msword [file:///home/dockes/projets/fulltext/testrecoll/embed/thunderbirdlocalfolders.zip] [xingfx1.doc] 24576 bytes application/msword [file:///home/dockes/projets/fulltext/testrecoll/embed/thunderbirdlocalfolders.zip] [xingfx1.doc (Sending a word document)] 24576 bytes

View File

@ -1,2 +1,2 @@
1 results 1 results
text/plain [file:///home/dockes/projets/fulltext/testrecoll/orgmode/orgmode-example.org] [law and legal code versioned on github] 370 bytes text/x-orgmode-sub [file:///home/dockes/projets/fulltext/testrecoll/orgmode/orgmode-example.org] [law and legal code versioned on github] 487 bytes

View File

@ -2,5 +2,5 @@
0 results 0 results
0 results 0 results
2 results 2 results
application/octet-stream [file:///home/dockes/projets/fulltext/testrecoll/rfc2231/thunder] [épatantuniquefilenameterm.bin (vrai attach)] 5785 bytes
message/rfc822 [file:///home/dockes/projets/fulltext/testrecoll/rfc2231/thunder] [vrai attach] 11208 bytes message/rfc822 [file:///home/dockes/projets/fulltext/testrecoll/rfc2231/thunder] [vrai attach] 11208 bytes
application/octet-stream [file:///home/dockes/projets/fulltext/testrecoll/rfc2231/thunder] [épatantuniquefilenameterm.bin] 5785 bytes