Removed some further Qt dependencies. For example, HTTPS access now is possible without Qt through Curl only.

This commit is contained in:
Matthias Koefferlein 2018-07-14 02:14:06 +02:00
parent 9ea24cb6a0
commit 8f04fc5358
19 changed files with 848 additions and 176 deletions

View File

@ -37,6 +37,7 @@
#include "edtConfig.h"
#include <QObject>
#include <QTimer>
namespace db {
class Manager;

View File

@ -55,7 +55,9 @@ SOURCES = \
tlXMLParser.cc \
tlXMLWriter.cc \
tlThreadedWorkers.cc \
tlThreads.cc
tlThreads.cc \
tlDeferredExecution.cc \
tlUri.cc
HEADERS = \
tlAlgorithm.h \
@ -104,7 +106,9 @@ HEADERS = \
tlXMLParser.h \
tlXMLWriter.h \
tlThreadedWorkers.h \
tlThreads.h
tlThreads.h \
tlDeferredExecution.h \
tlUri.h
equals(HAVE_CURL, "1") {
@ -134,11 +138,11 @@ equals(HAVE_CURL, "1") {
!equals(HAVE_QT, "0") {
HEADERS += \
tlDeferredExecution.h \
tlDeferredExecutionQt.h \
tlFileSystemWatcher.h \
SOURCES += \
tlDeferredExecution.cc \
tlDeferredExecutionQt.cc \
tlFileSystemWatcher.cc \
}

View File

@ -22,31 +22,21 @@
#include "tlDeferredExecution.h"
#include "tlAssert.h"
#include "tlLog.h"
#include <stdio.h>
#include <QApplication>
#if defined(HAVE_QT)
# include "tlDeferredExecutionQt.h"
#endif
namespace tl
{
static DeferredMethodScheduler *s_inst = 0;
DeferredMethodScheduler::DeferredMethodScheduler (QObject *parent)
: QObject (parent),
m_disabled (0), m_scheduled (false)
DeferredMethodScheduler::DeferredMethodScheduler ()
: m_disabled (0), m_scheduled (false)
{
connect (&m_timer, SIGNAL (timeout ()), this, SLOT (timer ()));
m_timer.setInterval (0); // immediately
m_timer.setSingleShot (true); // just once
// set up a fallback timer that cleans up pending execute jobs if something goes wrong
connect (&m_fallback_timer, SIGNAL (timeout ()), this, SLOT (timer ()));
m_fallback_timer.setInterval (500);
m_fallback_timer.setSingleShot (false);
tl_assert (! s_inst);
s_inst = this;
}
@ -59,31 +49,33 @@ DeferredMethodScheduler::~DeferredMethodScheduler ()
DeferredMethodScheduler *
DeferredMethodScheduler::instance ()
{
if (! s_inst && qApp) {
new DeferredMethodScheduler (qApp);
// TODO: provide a way to register non-Qt schedulers
#if defined(HAVE_QT)
if (! s_inst) {
new DeferredMethodSchedulerQt ();
}
#endif
return s_inst;
}
void
DeferredMethodScheduler::schedule (DeferredMethodBase *method)
{
m_lock.lock ();
tl::MutexLocker locker (&m_lock);
if (! method->m_scheduled || ! method->m_compressed) {
m_methods.push_back (method);
if (! m_scheduled) {
qApp->postEvent (this, new QEvent (QEvent::User));
queue_event ();
m_scheduled = true;
}
method->m_scheduled = true;
}
m_lock.unlock ();
}
void
DeferredMethodScheduler::unqueue (DeferredMethodBase *method)
{
m_lock.lock ();
tl::MutexLocker locker (&m_lock);
for (std::list<DeferredMethodBase *>::iterator m = m_methods.begin (); m != m_methods.end (); ) {
std::list<DeferredMethodBase *>::iterator mm = m;
++mm;
@ -93,50 +85,18 @@ DeferredMethodScheduler::unqueue (DeferredMethodBase *method)
}
m = mm;
}
m_lock.unlock ();
}
void
DeferredMethodScheduler::do_enable (bool en)
{
m_lock.lock ();
tl::MutexLocker locker (&m_lock);
if (en) {
tl_assert (m_disabled > 0);
--m_disabled;
} else {
++m_disabled;
}
m_lock.unlock ();
}
bool
DeferredMethodScheduler::event (QEvent *event)
{
if (event->type () == QEvent::User) {
timer ();
return true;
} else {
return QObject::event (event);
}
}
void
DeferredMethodScheduler::timer ()
{
if (m_disabled) {
// start again if disabled
m_timer.start ();
} else {
try {
do_execute ();
} catch (tl::Exception &ex) {
tl::error << tl::to_string (QObject::tr ("Exception caught: ")) << ex.msg ();
} catch (std::exception &ex) {
tl::error << tl::to_string (QObject::tr ("Exception caught: ")) << ex.what ();
} catch (...) {
tl::error << tl::to_string (QObject::tr ("Unspecific exception caught"));
}
}
}
void

View File

@ -23,16 +23,9 @@
#ifndef HDR_tlDeferredExecution
#define HDR_tlDeferredExecution
#if !defined(HAVE_QT)
# error tl::DeferredExecution not available without Qt
#endif
#include "tlCommon.h"
#include "tlObject.h"
#include <QObject>
#include <QTimer>
#include <QMutex>
#include "tlThreads.h"
#include <list>
@ -69,9 +62,7 @@ template <class T> class DeferredMethod;
* @brief The deferred method scheduler
*/
class TL_PUBLIC DeferredMethodScheduler
: public QObject
{
Q_OBJECT
public:
/**
* @brief The singleton instance of the scheduler
@ -117,29 +108,43 @@ public:
}
}
private slots:
void timer ();
/**
* @brief Gets a value indicating whether the scheduler is disabled
*/
bool is_disabled () const
{
return m_disabled;
}
protected:
/**
* @brief Reimplementation of the interface: queue an event
* In effect, the event should later trigger a call to do_execute ().
*/
virtual void queue_event () = 0;
/**
* @brief Executes the pending methods
*/
void do_execute ();
private:
/**
* @brief Constructor
*/
DeferredMethodScheduler (QObject *parent);
DeferredMethodScheduler ();
/**
* @brief Destructor
*/
~DeferredMethodScheduler ();
virtual ~DeferredMethodScheduler ();
private:
int m_disabled;
bool m_scheduled;
std::list<DeferredMethodBase *> m_methods;
QTimer m_timer, m_fallback_timer;
QMutex m_lock;
tl::Mutex m_lock;
virtual bool event (QEvent *event);
void do_enable (bool en);
void do_execute ();
};
/**

View File

@ -0,0 +1,87 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2018 Matthias Koefferlein
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 St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "tlDeferredExecutionQt.h"
#include "tlException.h"
#include "tlLog.h"
#include <QCoreApplication>
namespace tl
{
DeferredMethodSchedulerQt::DeferredMethodSchedulerQt ()
: QObject (qApp), DeferredMethodScheduler ()
{
connect (&m_timer, SIGNAL (timeout ()), this, SLOT (timer ()));
m_timer.setInterval (0); // immediately
m_timer.setSingleShot (true); // just once
// set up a fallback timer that cleans up pending execute jobs if something goes wrong
connect (&m_fallback_timer, SIGNAL (timeout ()), this, SLOT (timer ()));
m_fallback_timer.setInterval (500);
m_fallback_timer.setSingleShot (false);
}
DeferredMethodSchedulerQt::~DeferredMethodSchedulerQt ()
{
// .. nothing yet ..
}
void
DeferredMethodSchedulerQt::queue_event ()
{
qApp->postEvent (this, new QEvent (QEvent::User));
}
bool
DeferredMethodSchedulerQt::event (QEvent *event)
{
if (event->type () == QEvent::User) {
timer ();
return true;
} else {
return QObject::event (event);
}
}
void
DeferredMethodSchedulerQt::timer ()
{
if (is_disabled ()) {
// start again if disabled
m_timer.start ();
} else {
try {
do_execute ();
} catch (tl::Exception &ex) {
tl::error << tl::to_string (QObject::tr ("Exception caught: ")) << ex.msg ();
} catch (std::exception &ex) {
tl::error << tl::to_string (QObject::tr ("Exception caught: ")) << ex.what ();
} catch (...) {
tl::error << tl::to_string (QObject::tr ("Unspecific exception caught"));
}
}
}
}

View File

@ -0,0 +1,75 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2018 Matthias Koefferlein
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 St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_tlDeferredExecutionQt
#define HDR_tlDeferredExecutionQt
#include "tlCommon.h"
#include "tlDeferredExecution.h"
#include <QObject>
#include <QTimer>
#include <QMutex>
#include <list>
namespace tl
{
/**
* @brief The deferred method scheduler
*/
class TL_PUBLIC DeferredMethodSchedulerQt
: public QObject, public DeferredMethodScheduler
{
Q_OBJECT
public:
/**
* @brief Constructor
*/
DeferredMethodSchedulerQt ();
/**
* @brief Destructor
*/
~DeferredMethodSchedulerQt ();
protected:
/**
* @brief Reimplementation of the interface: queue an event
* In effect, the event should later trigger a call to do_execute ().
*/
void queue_event ();
private slots:
void timer ();
private:
QTimer m_timer, m_fallback_timer;
virtual bool event (QEvent *event);
};
}
#endif

View File

@ -139,6 +139,15 @@ std::vector<std::string> split_path (const std::string &p, bool keep_last)
}
parts.push_back (tl::normalized_part (std::string (cp0, 0, cp - cp0)));
} else if ((*cp == '\\' || *cp == '/') && cp[1] && isalpha (cp[1]) && cp[2] == ':') {
// drive name in the form "/c:" or "\c:"
parts.push_back (std::string ());
parts.back () += toupper (cp[1]);
parts.back () += ":";
cp += 3;
}
while (*cp) {
@ -646,6 +655,18 @@ bool file_exists (const std::string &p)
return stat_func (p, st) == 0;
}
bool is_writable (const std::string &p)
{
stat_struct st;
return stat_func (p, st) == 0 && (st.st_mode & S_IWUSR) != 0;
}
bool is_readable (const std::string &p)
{
stat_struct st;
return stat_func (p, st) == 0 && (st.st_mode & S_IRUSR) != 0;
}
bool is_dir (const std::string &p)
{
stat_struct st;

View File

@ -96,6 +96,16 @@ std::string TL_PUBLIC extension (const std::string &s);
*/
bool TL_PUBLIC file_exists (const std::string &s);
/**
* @brief Returns true, if the given path is writable
*/
bool TL_PUBLIC is_writable (const std::string &s);
/**
* @brief Returns true, if the given path is readable
*/
bool TL_PUBLIC is_readable (const std::string &s);
/**
* @brief Returns true, if the given path is a directory
*/

View File

@ -29,11 +29,8 @@
#include "tlAssert.h"
#include "tlStaticObjects.h"
#include "tlProgress.h"
#include "tlDeferredExecution.h"
#include <QUrl>
#include <QFileInfo>
#include <QCoreApplication>
#include "tlFileUtils.h"
#include "tlUri.h"
#include <sys/time.h>
#include <unistd.h>
@ -58,8 +55,8 @@ namespace tl
std::string server_from_url (const std::string &url)
{
QUrl qurl (tl::to_qstring (url));
return tl::to_string (qurl.scheme ()) + ":" + tl::to_string (qurl.host ());
tl::URI uri (url);
return uri.scheme () + "://" + uri.authority ();
}
std::string parse_realm (const std::string &header)
@ -750,7 +747,7 @@ void CurlConnection::check () const
} else if (m_status != 0) {
throw tl::HttpErrorException (tl::to_string (QObject::tr ("Connection error (%1)").arg (QString::fromLatin1 (m_error_msg))), m_status, m_url);
throw tl::HttpErrorException (tl::sprintf (tl::to_string (tr ("Connection error (%s)")), m_error_msg), m_status, m_url);
} else if (m_http_status < 200 || m_http_status >= 300) {
@ -776,7 +773,7 @@ void CurlConnection::check () const
if (error_text) {
throw tl::HttpErrorException (error_text, m_http_status, m_url);
} else {
throw tl::HttpErrorException (tl::to_string (QObject::tr ("HTTP error")), m_http_status, m_url);
throw tl::HttpErrorException (tl::to_string (tr ("HTTP error")), m_http_status, m_url);
}
}
@ -1013,7 +1010,7 @@ int CurlNetworkManager::tick ()
mc = curl_multi_fdset (mp_multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
if (mc != CURLM_OK) {
throw tl::HttpErrorException (tl::to_string (QObject::tr ("Connection error (curl_multi_fdset() failed)")), mc, std::string ());
throw tl::HttpErrorException (tl::to_string (tr ("Connection error (curl_multi_fdset() failed)")), mc, std::string ());
}
/* On success the value of maxfd is guaranteed to be >= -1. We call
@ -1170,7 +1167,7 @@ InputHttpStream::read (char *b, size_t n)
tl::NoDeferredMethods silent;
if (! m_progress.get ()) {
m_progress.reset (new tl::AbsoluteProgress (tl::to_string (QObject::tr ("Downloading")) + " " + m_connection->url (), 1));
m_progress.reset (new tl::AbsoluteProgress (tl::to_string (tr ("Downloading")) + " " + m_connection->url (), 1));
}
while (n > m_connection->read_available () && ! m_connection->finished () && CurlNetworkManager::instance ()->tick ()) {
@ -1200,13 +1197,13 @@ InputHttpStream::close ()
void
InputHttpStream::reset ()
{
throw tl::Exception (tl::to_string (QObject::tr ("'reset' is not supported on HTTP input streams")));
throw tl::Exception (tl::to_string (tr ("'reset' is not supported on HTTP input streams")));
}
std::string
InputHttpStream::filename () const
{
return tl::to_string (QFileInfo (QUrl (tl::to_qstring (m_connection->url ())).path ()).fileName ());
return tl::filename (tl::URI (m_connection->url ()).path ());
}
std::string

View File

@ -41,11 +41,7 @@
#include "tlException.h"
#include "tlString.h"
#if defined(HAVE_QT)
# include <QUrl>
# include <QFileInfo>
#endif
#include "tlUri.h"
namespace tl
{
@ -157,18 +153,13 @@ InputStream::InputStream (const std::string &abstract_path)
mp_delegate = new InputHttpStream (abstract_path);
} else
#endif
#if !defined (_WIN32) // not available on Windows
if (ex.test ("pipe:")) {
mp_delegate = new InputPipe (ex.get ());
} else
#endif
#if defined(HAVE_QT)
// TODO: provide a substitute for QUrl
if (ex.test ("file:")) {
QUrl url (tl::to_qstring (abstract_path));
mp_delegate = new InputZLibFile (tl::to_string (url.toLocalFile ()));
tl::URI uri (abstract_path);
mp_delegate = new InputZLibFile (uri.path ());
} else
#endif
{
mp_delegate = new InputZLibFile (abstract_path);
}
@ -183,16 +174,11 @@ std::string InputStream::absolute_path (const std::string &abstract_path)
tl::Extractor ex (abstract_path.c_str ());
if (ex.test ("http:") || ex.test ("https:")) {
return abstract_path;
#if !defined(_WIN32) // not available on Windows
} else if (ex.test ("pipe:")) {
return abstract_path;
#endif
#if defined(HAVE_QT)
// TODO: provide a substitute for QUrl
} else if (ex.test ("file:")) {
QUrl url (tl::to_qstring (abstract_path));
return tl::to_string (QFileInfo (url.toLocalFile ()).absoluteFilePath ());
#endif
tl::URI uri (abstract_path);
return tl::absolute_path (uri.path ());
} else {
return tl::absolute_file_path (abstract_path);
}

240
src/tl/tl/tlUri.cc Normal file
View File

@ -0,0 +1,240 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2018 Matthias Koefferlein
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 St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "tlUri.h"
#include "tlString.h"
#include <cstring>
#include <cstdio>
namespace tl
{
static bool is_hex (char c)
{
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
}
static char hex2int (char c)
{
if (c >= '0' && c <= '9') {
return c - '0';
} else if (c >= 'A' && c <= 'F') {
return (c - 'A') + 10;
} else if (c >= 'a' || c <= 'f') {
return (c - 'a') + 10;
} else {
return 0;
}
}
static char int2hex (char c)
{
c &= 0xf;
if (c < 10) {
return c + '0';
} else {
return c - 10 + 'A';
}
}
static std::string unescape (const std::string &s)
{
std::string res;
for (const char *cp = s.c_str (); *cp; ++cp) {
if (*cp == '%' && is_hex (cp [1]) && is_hex (cp [2])) {
res += hex2int (cp[1]) * 16 + hex2int (cp[2]);
cp += 2;
} else {
res += *cp;
}
}
return res;
}
static std::string escape (const std::string &s)
{
const char *special = "?#[]$&'()*+,;";
std::string res;
for (const char *cp = s.c_str (); *cp; ++cp) {
if ((unsigned char) *cp <= 32 || (unsigned char) *cp >= 128 || strchr (special, *cp) != 0) {
res += "%";
res += int2hex (*cp >> 4);
res += int2hex (*cp);
} else {
res += *cp;
}
}
return res;
}
URI::URI ()
{
// .. nothing yet ..
}
URI::URI (const std::string &uri)
{
tl::Extractor ex0 (uri.c_str ());
tl::Extractor ex = ex0;
if (ex.try_read_word (m_scheme) && *ex == ':') {
// got scheme
++ex;
} else {
m_scheme.clear ();
ex = ex0;
}
m_scheme = unescape (m_scheme);
bool prefer_authority = true;
if (m_scheme == "file") {
prefer_authority = false;
// other schemes?
} else if (m_scheme.empty ()) {
prefer_authority = false;
}
ex0 = ex;
if (ex.test ("//") || (ex.test ("/") ? prefer_authority : prefer_authority)) {
// definitely an authority
while (! ex.at_end () && *ex != '/') {
m_authority += *ex;
++ex;
}
} else {
ex = ex0;
}
m_authority = unescape (m_authority);
// parse path
while (! ex.at_end () && *ex != '?' && *ex != '#') {
m_path += *ex;
++ex;
}
m_path = unescape (m_path);
// parse parameters
if (*ex == '?') {
++ex;
while (! ex.at_end () && *ex != '#') {
std::string k, v;
while (! ex.at_end () && *ex != '=' && *ex != '&' && *ex != '#') {
k += *ex;
++ex;
}
if (*ex == '=') {
++ex;
while (! ex.at_end () && *ex != '&' && *ex != '#') {
v += *ex;
++ex;
}
}
m_query[unescape (k)] = unescape (v);
if (*ex == '&') {
++ex;
}
}
}
if (*ex == '#') {
++ex;
while (! ex.at_end ()) {
m_fragment += *ex;
++ex;
}
}
m_fragment = unescape (m_fragment);
}
std::string
URI::to_string () const
{
std::string res;
if (! m_scheme.empty ()) {
res += escape (m_scheme);
res += ":";
}
if (! m_authority.empty ()) {
res += "//";
res += escape (m_authority);
}
if (! m_path.empty ()) {
res += escape (m_path);
}
if (! m_query.empty ()) {
for (std::map<std::string, std::string>::const_iterator p = m_query.begin (); p != m_query.end (); ++p) {
res += (p == m_query.begin () ? "?" : "&");
res += escape (p->first);
if (! p->second.empty ()) {
res += "=";
res += escape (p->second);
}
}
}
if (! m_fragment.empty ()) {
res += "#";
res += m_fragment;
}
return res;
}
URI
URI::resolved (const URI &other) const
{
if (! other.scheme ().empty () && other.scheme () != scheme ()) {
return other;
}
if (! other.authority ().empty () && other.authority () != authority ()) {
return other;
}
URI res = *this;
// combine paths
// TODO: normalize? I.e. replace "x/y/../z" by "x/z"?
if (! other.path ().empty ()) {
if (other.path ()[0] == '/') {
res.m_path = other.path ();
} else {
res.m_path += "/";
res.m_path += other.path ();
}
}
res.m_query = other.query ();
res.m_fragment = other.fragment ();
return res;
}
}

123
src/tl/tl/tlUri.h Normal file
View File

@ -0,0 +1,123 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2018 Matthias Koefferlein
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 St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_tlURI
#define HDR_tlURI
#include "tlCommon.h"
#include <string>
#include <map>
namespace tl
{
/**
* @brief A class representing a URI
*
* This class is able to parse the URI and deliver the parts.
*/
class TL_PUBLIC URI
{
public:
/**
* @brief Creates an empty URI
*/
URI ();
/**
* @brief Creates an URI from the given string
*/
URI (const std::string &uri);
/**
* @brief Returns the scheme part or an empty string if there is no scheme
*/
const std::string &scheme () const
{
return m_scheme;
}
/**
* @brief Returns the authority part or an empty string if there is no authority
* The leading slashes and not part of the authority.
* Percent escaping is undone already.
*/
const std::string &authority () const
{
return m_authority;
}
/**
* @brief Returns the path part or an empty string if there is no path
* The path contains the leading slash if there is a path.
* Percent escaping is undone already.
*/
const std::string &path () const
{
return m_path;
}
/**
* @brief Returns the query part or an empty map if there is no query
* The map is a map of keys vs. values. Percent escaping is undone
* in the keys and values.
*/
const std::map<std::string, std::string> &query () const
{
return m_query;
}
/**
* @brief Returns the fragment or an empty string if there is none
* Percent escaping is undone on the fragment already.
*/
const std::string &fragment () const
{
return m_fragment;
}
/**
* @brief Turns the URI into a string
* Percent escaping is employed to escape special characters
*/
std::string to_string () const;
/**
* @brief Resolves an URI relative to this one
*/
URI resolved (const URI &other) const;
private:
std::string m_scheme;
std::string m_authority;
std::string m_path;
std::map<std::string, std::string> m_query;
std::string m_fragment;
};
}
#endif

View File

@ -28,10 +28,10 @@
#include "tlInternational.h"
#include "tlProgress.h"
#include "tlLog.h"
#include "tlUri.h"
#include "tlFileUtils.h"
#include <memory>
#include <QUrl>
#include <QDir>
namespace tl
{
@ -125,15 +125,15 @@ tl::XMLStruct<MultiStatus> xml_struct ("multistatus",
)
);
static std::string item_name (const QString &path1, const QString &path2)
static std::string item_name (const std::string &path1, const std::string &path2)
{
QStringList sl1 = path1.split (QChar ('/'));
if (! sl1.empty () && sl1.back ().isEmpty ()) {
std::vector <std::string> sl1 = tl::split (path1, "/");
if (! sl1.empty () && sl1.back ().empty ()) {
sl1.pop_back ();
}
QStringList sl2 = path2.split (QChar ('/'));
if (! sl2.empty () && sl2.back ().isEmpty ()) {
std::vector <std::string> sl2 = tl::split (path2, "/");
if (! sl2.empty () && sl2.back ().empty ()) {
sl2.pop_back ();
}
@ -141,16 +141,16 @@ static std::string item_name (const QString &path1, const QString &path2)
// This is the top-level item (echoed in the PROPFIND response)
return std::string ();
} else if (! sl2.empty ()) {
return tl::to_string (sl2.back ());
return sl2.back ();
} else {
throw tl::Exception (tl::to_string (QObject::tr ("Invalid WebDAV response: %1 is not a collection sub-item of %2").arg (path2).arg (path1)));
throw tl::Exception (tl::to_string (tr ("Invalid WebDAV response: %s is not a collection sub-item of %s")), path2, path1);
}
}
void
WebDAVObject::read (const std::string &url, int depth)
{
QUrl base_url = QUrl (tl::to_qstring (url));
tl::URI base_uri (url);
tl::InputHttpStream http (url);
http.add_header ("User-Agent", "SVN");
@ -169,10 +169,10 @@ WebDAVObject::read (const std::string &url, int depth)
for (MultiStatus::iterator r = multistatus.begin (); r != multistatus.end (); ++r) {
bool is_collection = r->propstat.prop.resourcetype.is_collection;
QUrl item_url = base_url.resolved (QUrl (tl::to_qstring (r->href)));
tl::URI item_url = base_uri.resolved (tl::URI (r->href));
std::string n = item_name (base_url.path (), item_url.path ());
std::string item_url_string = tl::to_string (item_url.toString ());
std::string n = item_name (base_uri.path (), item_url.path ());
std::string item_url_string = item_url.to_string ();
if (! n.empty ()) {
m_items.push_back (WebDAVItem (is_collection, item_url_string, n));
@ -211,36 +211,35 @@ void fetch_download_items (const std::string &url, const std::string &target, st
if (object.is_collection ()) {
QDir dir (tl::to_qstring (target));
if (! dir.exists ()) {
throw tl::Exception (tl::to_string (QObject::tr ("Download failed: target directory '%1' does not exists").arg (dir.path ())));
if (! tl::file_exists (target)) {
throw tl::Exception (tl::to_string (tr ("Download failed: target directory '%s' does not exists")), target);
}
for (WebDAVObject::iterator i = object.begin (); i != object.end (); ++i) {
QFileInfo new_item (dir.absoluteFilePath (tl::to_qstring (i->name ())));
std::string item_path = tl::absolute_file_path (tl::combine_path (target, i->name ()));
if (i->is_collection ()) {
if (! new_item.exists ()) {
if (! dir.mkdir (tl::to_qstring (i->name ()))) {
throw tl::Exception (tl::to_string (QObject::tr ("Download failed: unable to create subdirectory '%2' in '%1'").arg (dir.path ()).arg (tl::to_qstring (i->name ()))));
if (! tl::file_exists (item_path)) {
if (! tl::mkpath (item_path)) {
throw tl::Exception (tl::to_string (tr ("Download failed: unable to create subdirectory '%s' in '%s'")), i->name (), target);
}
} else if (! new_item.isDir ()) {
throw tl::Exception (tl::to_string (QObject::tr ("Download failed: unable to create subdirectory '%2' in '%1' - is already a file").arg (dir.path ()).arg (tl::to_qstring (i->name ()))));
} else if (! new_item.isWritable ()) {
throw tl::Exception (tl::to_string (QObject::tr ("Download failed: unable to create subdirectory '%2' in '%1' - no write permissions").arg (dir.path ()).arg (tl::to_qstring (i->name ()))));
} else if (! tl::is_dir (item_path)) {
throw tl::Exception (tl::to_string (tr ("Download failed: unable to create subdirectory '%s' in '%s' - is already a file")), i->name (), target);
} else if (! tl::is_writable (item_path)) {
throw tl::Exception (tl::to_string (tr ("Download failed: unable to create subdirectory '%s' in '%s' - no write permissions")), i->name (), target);
}
fetch_download_items (i->url (), tl::to_string (new_item.filePath ()), items, progress);
fetch_download_items (i->url (), item_path, items, progress);
} else {
if (new_item.exists () && ! new_item.isWritable ()) {
throw tl::Exception (tl::to_string (QObject::tr ("Download failed: file is '%2' in '%1' - already exists, but no write permissions").arg (dir.path ()).arg (tl::to_qstring (i->name ()))));
if (tl::file_exists (item_path) && ! tl::is_writable (item_path)) {
throw tl::Exception (tl::to_string (tr ("Download failed: file is '%s' in '%s' - already exists, but no write permissions")), i->name (), target);
}
items.push_back (DownloadItem (i->url (), tl::to_string (dir.absoluteFilePath (tl::to_qstring (i->name ())))));
items.push_back (DownloadItem (i->url (), item_path));
}
}
@ -266,25 +265,25 @@ WebDAVObject::download (const std::string &url, const std::string &target)
try {
tl::info << QObject::tr ("Fetching file structure from ") << url;
tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Fetching directory structure from %1").arg (tl::to_qstring (url))));
tl::info << tr ("Fetching file structure from ") << url;
tl::AbsoluteProgress progress (tl::sprintf (tl::to_string (tr ("Fetching directory structure from %s")), url));
fetch_download_items (url, target, items, progress);
} catch (tl::Exception &ex) {
tl::error << QObject::tr ("Error downloading file structure from '") << url << "':" << tl::endl << ex.msg ();
tl::error << tr ("Error downloading file structure from '") << url << "':" << tl::endl << ex.msg ();
return false;
}
bool has_errors = false;
{
tl::info << tl::to_string (QObject::tr ("Downloading %1 file(s) now ..").arg (items.size ()));
tl::info << tl::sprintf (tl::to_string (tr ("Downloading %d file(s) now ..")), items.size ());
tl::RelativeProgress progress (tl::to_string (QObject::tr ("Downloading file(s) from %1").arg (tl::to_qstring (url))), items.size (), 1);
tl::RelativeProgress progress (tl::sprintf (tl::to_string (tr ("Downloading file(s) from %s")), url), items.size (), 1);
for (std::list<DownloadItem>::const_iterator i = items.begin (); i != items.end (); ++i) {
tl::info << QObject::tr ("Downloading '%1' to '%2' ..").arg (tl::to_qstring (i->url)).arg (tl::to_qstring (i->path));
tl::info << tl::sprintf (tr ("Downloading '%s' to '%s' .."), i->url, i->path);
try {
@ -293,7 +292,7 @@ WebDAVObject::download (const std::string &url, const std::string &target)
is->copy_to (os);
} catch (tl::Exception &ex) {
tl::error << QObject::tr ("Error downloading file from '") << i->url << "':" << tl::endl << ex.msg ();
tl::error << tr ("Error downloading file from '") << i->url << "':" << tl::endl << ex.msg ();
has_errors = true;
}

View File

@ -215,6 +215,16 @@ void XMLCALL start_element_handler (void *user_data, const XML_Char *name, const
void XMLCALL end_element_handler (void *user_data, const XML_Char *name);
void XMLCALL cdata_handler (void *user_data, const XML_Char *s, int len);
static std::string get_lname (const std::string &name)
{
size_t colon = name.find (':');
if (colon != std::string::npos) {
return std::string (name, colon + 1, name.size () - colon - 1);
} else {
return name;
}
}
class XMLParserPrivateData
{
public:
@ -235,8 +245,8 @@ public:
void start_element (const std::string &name)
{
try {
// TODO: separate qname and lname?
mp_struct_handler->start_element (std::string (), name, name);
// TODO: Provide namespace URI?
mp_struct_handler->start_element (std::string (), get_lname (name), name);
} catch (tl::Exception &ex) {
error (ex);
}
@ -245,8 +255,8 @@ public:
void end_element (const std::string &name)
{
try {
// TODO: separate qname and lname?
mp_struct_handler->end_element (std::string (), name, name);
// TODO: Provide namespace URI?
mp_struct_handler->end_element (std::string (), get_lname (name), name);
} catch (tl::Exception &ex) {
error (ex);
}
@ -861,6 +871,10 @@ XMLStructureHandler::start_element (const std::string &uri, const std::string &l
void
XMLStructureHandler::end_element (const std::string &uri, const std::string &lname, const std::string &qname)
{
if (m_stack.empty ()) {
return;
}
const XMLElementBase *element = m_stack.back ();
m_stack.pop_back ();
@ -876,7 +890,7 @@ XMLStructureHandler::end_element (const std::string &uri, const std::string &lna
void
XMLStructureHandler::characters (const std::string &t)
{
if (m_stack.back ()) {
if (! m_stack.empty () && m_stack.back ()) {
m_stack.back ()->cdata (t, *mp_state);
}
}

View File

@ -168,6 +168,8 @@ TEST (3)
}
}
#endif
// Secret mode switchers for testing
namespace tl
{
@ -225,6 +227,8 @@ TEST (10)
EXPECT_EQ (tl::normalize_path ("\\"), "\\");
EXPECT_EQ (tl::normalize_path ("/"), "\\");
EXPECT_EQ (tl::normalize_path ("d:"), "D:");
EXPECT_EQ (tl::normalize_path ("/d:"), "D:");
EXPECT_EQ (tl::normalize_path ("\\d:"), "D:");
EXPECT_EQ (tl::normalize_path ("\\\\"), "\\\\");
EXPECT_EQ (tl::normalize_path ("//"), "\\\\");
EXPECT_EQ (tl::normalize_path ("d:\\"), "D:\\");
@ -370,8 +374,12 @@ TEST(13)
tl::rm_dir_recursive (tt);
EXPECT_EQ (tl::file_exists (tt), false);
EXPECT_EQ (tl::is_readable (tt), false);
EXPECT_EQ (tl::is_writable (tt), false);
EXPECT_EQ (tl::mkpath (tt), true);
EXPECT_EQ (tl::file_exists (tt), true);
EXPECT_EQ (tl::is_readable (tt), true);
EXPECT_EQ (tl::is_writable (tt), true);
tl::rm_dir_recursive (tt);
EXPECT_EQ (tl::file_exists (tt), false);
@ -393,6 +401,8 @@ TEST(13)
EXPECT_EQ (tl::file_exists (tt), false);
}
#if defined(HAVE_QT)
// absolute_path, relative_path and absolute_file_path
TEST(14)
{
@ -412,6 +422,8 @@ TEST(14)
EXPECT_EQ (tl::relative_path (xpath2, tl::combine_path (xpath, "a")), "doesnotexist/a");
}
#endif
// dir_entries
TEST(15)
{
@ -497,4 +509,3 @@ TEST(15)
EXPECT_EQ (tl::rm_dir (tt), false); // not empty
}
#endif

144
src/tl/unit_tests/tlUri.cc Normal file
View File

@ -0,0 +1,144 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2018 Matthias Koefferlein
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 St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "tlUri.h"
#include "tlUnitTest.h"
std::string uri2string (const tl::URI &uri)
{
std::string res;
if (! uri.scheme ().empty ()) {
res += "<" + uri.scheme () + ">";
res += ":";
}
if (! uri.authority ().empty ()) {
res += "//<";
res += uri.authority ();
res += ">";
}
if (! uri.path ().empty ()) {
res += "<" + uri.path () + ">";
}
if (! uri.query ().empty ()) {
for (std::map<std::string, std::string>::const_iterator p = uri.query ().begin (); p != uri.query ().end (); ++p) {
res += (p == uri.query ().begin () ? "?<" : "&<");
res += p->first;
res += ">";
if (! p->second.empty ()) {
res += "=<";
res += p->second;
res += ">";
}
}
}
if (! uri.fragment ().empty ()) {
res += "#<";
res += uri.fragment ();
res += ">";
}
return res;
}
// basic parsing ability
TEST(1)
{
tl::URI uri;
EXPECT_EQ (uri2string (uri), "");
uri = tl::URI ("scheme:");
EXPECT_EQ (uri2string (uri), "<scheme>:");
uri = tl::URI ("http:www.klayout.de/path/to/file");
EXPECT_EQ (uri2string (uri), "<http>://<www.klayout.de></path/to/file>");
uri = tl::URI ("http:/www.klayout.de/path/to/file");
EXPECT_EQ (uri2string (uri), "<http>://<www.klayout.de></path/to/file>");
uri = tl::URI ("http://www.klayout.de/path/to/file");
EXPECT_EQ (uri2string (uri), "<http>://<www.klayout.de></path/to/file>");
EXPECT_EQ (uri.to_string (), "http://www.klayout.de/path/to/file");
uri = tl::URI ("www.klayout.de/path/to/file");
EXPECT_EQ (uri2string (uri), "<www.klayout.de/path/to/file>");
uri = tl::URI ("/www.klayout.de/path/to/file");
EXPECT_EQ (uri2string (uri), "</www.klayout.de/path/to/file>");
uri = tl::URI ("//www.klayout.de/path/to/file");
EXPECT_EQ (uri2string (uri), "//<www.klayout.de></path/to/file>");
EXPECT_EQ (uri.to_string (), "//www.klayout.de/path/to/file");
uri = tl::URI ("file:www.klayout.de/path/to/file");
EXPECT_EQ (uri2string (uri), "<file>:<www.klayout.de/path/to/file>");
uri = tl::URI ("file:/www.klayout.de/path/to/file");
EXPECT_EQ (uri2string (uri), "<file>:</www.klayout.de/path/to/file>");
uri = tl::URI ("file://www.klayout.de/path/to/file");
EXPECT_EQ (uri2string (uri), "<file>://<www.klayout.de></path/to/file>");
uri = tl::URI ("file:///path/to/file");
EXPECT_EQ (uri2string (uri), "<file>:</path/to/file>");
uri = tl::URI ("file:///c:/path/to/file");
EXPECT_EQ (uri2string (uri), "<file>:</c:/path/to/file>");
EXPECT_EQ (uri2string (uri.resolved (tl::URI ("http://www.klayout.de"))), "<http>://<www.klayout.de>");
EXPECT_EQ (uri2string (uri.resolved (tl::URI ("http:///other"))), "<http>:</other>");
EXPECT_EQ (uri2string (uri.resolved (tl::URI ("../other"))), "<file>:</c:/path/to/file/../other>");
EXPECT_EQ (uri2string (uri.resolved (tl::URI ("/other"))), "<file>:</other>");
EXPECT_EQ (uri2string (uri.resolved (tl::URI ("file:../other"))), "<file>:</c:/path/to/file/../other>");
EXPECT_EQ (uri2string (uri.resolved (tl::URI ("file:../other?a=b#frag"))), "<file>:</c:/path/to/file/../other>?<a>=<b>#<frag>");
EXPECT_EQ (uri2string (uri.resolved (tl::URI ("file:/other"))), "<file>:</other>");
EXPECT_EQ (uri2string (uri.resolved (tl::URI ("file:/other?a=b#frag"))), "<file>:</other>?<a>=<b>#<frag>");
uri = tl::URI ("//www.klayout.de/path/to/file?a=b");
EXPECT_EQ (uri2string (uri), "//<www.klayout.de></path/to/file>?<a>=<b>");
uri = tl::URI ("/path/to/file?a=b");
EXPECT_EQ (uri2string (uri), "</path/to/file>?<a>=<b>");
uri = tl::URI ("/path/to/file?a=v1&c=v3&b=v2");
EXPECT_EQ (uri2string (uri), "</path/to/file>?<a>=<v1>&<b>=<v2>&<c>=<v3>");
uri = tl::URI ("/path/to/file?a=v1&c=v3&b=v2#fragment");
EXPECT_EQ (uri2string (uri), "</path/to/file>?<a>=<v1>&<b>=<v2>&<c>=<v3>#<fragment>");
EXPECT_EQ (uri.to_string (), "/path/to/file?a=v1&b=v2&c=v3#fragment");
uri = tl::URI ("/path/to/file#fragment");
EXPECT_EQ (uri2string (uri), "</path/to/file>#<fragment>");
EXPECT_EQ (uri.to_string (), "/path/to/file#fragment");
uri = tl::URI ("/path/to/%2c%2C%20%file#fragment");
EXPECT_EQ (uri2string (uri), "</path/to/,, %file>#<fragment>");
EXPECT_EQ (uri.to_string (), "/path/to/%2C%2C%20%file#fragment");
EXPECT_EQ (tl::URI (uri.to_string ()).to_string (), "/path/to/%2C%2C%20%file#fragment");
uri = tl::URI ("/path/to/file?%61=v%31&%63=v%33&%62=v%32#fragment");
EXPECT_EQ (uri2string (uri), "</path/to/file>?<a>=<v1>&<b>=<v2>&<c>=<v3>#<fragment>");
EXPECT_EQ (uri2string (uri.resolved (tl::URI ("../other"))), "</path/to/file/../other>");
}

View File

@ -23,8 +23,7 @@
#include "tlWebDAV.h"
#include "tlUnitTest.h"
#include <QDir>
#include "tlFileUtils.h"
static std::string test_url1 ("http://www.klayout.org/svn-public/klayout-resources/trunk/testdata");
static std::string test_url2 ("http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/text");
@ -96,34 +95,29 @@ TEST(5)
{
tl::WebDAVObject collection;
QDir tmp_dir (tl::to_qstring (tmp_file ("tmp")));
EXPECT_EQ (tmp_dir.exists (), false);
std::string tmp_dir (tmp_file ("tmp"));
EXPECT_EQ (tl::file_exists (tmp_dir), false);
tmp_dir.cdUp ();
tmp_dir.mkdir (tl::to_qstring ("tmp"));
tmp_dir.cd (tl::to_qstring ("tmp"));
tl::mkpath (tmp_dir);
EXPECT_EQ (tl::file_exists (tmp_dir), true);
bool res = collection.download (test_url1, tl::to_string (tmp_dir.absolutePath ()));
bool res = collection.download (test_url1, tl::absolute_file_path (tmp_dir));
EXPECT_EQ (res, true);
QDir dir1 (tmp_dir.absoluteFilePath (QString::fromUtf8 ("dir1")));
QDir dir2 (tmp_dir.absoluteFilePath (QString::fromUtf8 ("dir2")));
QDir dir21 (dir2.absoluteFilePath (QString::fromUtf8 ("dir21")));
EXPECT_EQ (dir1.exists (), true);
EXPECT_EQ (dir2.exists (), true);
EXPECT_EQ (dir21.exists (), true);
std::string dir1 (tl::absolute_file_path (tl::combine_path (tmp_dir, "dir1")));
std::string dir2 (tl::absolute_file_path (tl::combine_path (tmp_dir, "dir2")));
std::string dir21 (tl::absolute_file_path (tl::combine_path (dir2, "dir21")));
EXPECT_EQ (tl::file_exists (dir1), true);
EXPECT_EQ (tl::file_exists (dir2), true);
EXPECT_EQ (tl::file_exists (dir21), true);
QByteArray ba;
QFile text1 (dir1.absoluteFilePath (QString::fromUtf8 ("text")));
text1.open (QIODevice::ReadOnly);
ba = text1.read (10000);
EXPECT_EQ (ba.constData (), "A text.\n");
tl::InputStream text1 (tl::combine_path (dir1, "text"));
std::string ba1 = text1.read_all ();
EXPECT_EQ (ba1, "A text.\n");
text1.close ();
QFile text21 (dir21.absoluteFilePath (QString::fromUtf8 ("text")));
text21.open (QIODevice::ReadOnly);
ba = text21.read (10000);
EXPECT_EQ (ba.constData (), "A text II.I.\n");
tl::InputStream text21 (tl::combine_path (dir21, "text"));
std::string ba21 = text21.read_all ();
EXPECT_EQ (ba21, "A text II.I.\n");
text21.close ();
}

View File

@ -195,7 +195,7 @@ TEST (5)
#if defined (HAVE_QT)
EXPECT_EQ (error, "XML parser error: tag mismatch in line 2, column 33");
#else
EXPECT_EQ (error, "XML parser error: mismatched tag in line 2, column 29");
EXPECT_EQ (error, "XML parser error: mismatched tag in line 2, column 28");
#endif
}

View File

@ -30,13 +30,14 @@ SOURCES = \
tlVariant.cc \
tlInt128Support.cc \
tlXMLParser.cc \
tlUri.cc \
tlWebDAV.cc \
equals(HAVE_QT, "0") {
# nothing
} else {
SOURCES += \
tlWebDAV.cc \
tlDeferredExecution.cc \
tlFileSystemWatcher.cc \