diff --git a/ChangeLog b/ChangeLog index 5570e04c..5d74f7c3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2018-08-13 Jay Berkenbilt + + * Add new class QPDFSystemError, derived from std::runtime_error, + which is now thrown by QUtil::throw_system_error. This enables the + triggering errno value to be retrieved. Fixes #221. + 2018-08-12 Jay Berkenbilt * qpdf command line: add --no-warn option to suppress issuing diff --git a/include/qpdf/QPDFSystemError.hh b/include/qpdf/QPDFSystemError.hh new file mode 100644 index 00000000..037bbe0a --- /dev/null +++ b/include/qpdf/QPDFSystemError.hh @@ -0,0 +1,58 @@ +// Copyright (c) 2005-2018 Jay Berkenbilt +// +// This file is part of qpdf. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Versions of qpdf prior to version 7 were released under the terms +// of version 2.0 of the Artistic License. At your option, you may +// continue to consider qpdf to be licensed under those terms. Please +// see the manual for additional information. + +#ifndef QPDFSYSTEMERROR_HH +#define QPDFSYSTEMERROR_HH + +#include +#include + +#include +#include +#include + +class QPDFSystemError: public std::runtime_error +{ + public: + QPDF_DLL + QPDFSystemError(std::string const& description, + int system_errno); + QPDF_DLL + virtual ~QPDFSystemError() throw (); + + // To get a complete error string, call what(), provided by + // std::exception. The accessors below return the original values + // used to create the exception. + + QPDF_DLL + std::string const& getDescription() const; + QPDF_DLL + int getErrno() const; + + private: + static std::string createWhat(std::string const& description, + int system_errno); + + std::string description; + int system_errno; +}; + +#endif // QPDFSYSTEMERROR_HH diff --git a/include/qpdf/QUtil.hh b/include/qpdf/QUtil.hh index 68e64370..bcedd4d3 100644 --- a/include/qpdf/QUtil.hh +++ b/include/qpdf/QUtil.hh @@ -61,9 +61,14 @@ namespace QUtil QPDF_DLL unsigned char* unsigned_char_pointer(char const* str); - // Throw std::runtime_error with a string formed by appending to + // Throw QPDFSystemError, which is derived from + // std::runtime_error, with a string formed by appending to // "description: " the standard string corresponding to the - // current value of errno. + // current value of errno. You can retrieve the value of errno by + // calling getErrno() on the QPDFSystemError. Prior to qpdf 8.2.0, + // this method threw system::runtime_error directly, but since + // QPDFSystemError is derived from system::runtime_error, old code + // that specifically catches std::runtime_error will still work. QPDF_DLL void throw_system_error(std::string const& description); diff --git a/libqpdf/QPDFSystemError.cc b/libqpdf/QPDFSystemError.cc new file mode 100644 index 00000000..094341cd --- /dev/null +++ b/libqpdf/QPDFSystemError.cc @@ -0,0 +1,51 @@ +#include +#include +#include + +QPDFSystemError::QPDFSystemError(std::string const& description, + int system_errno) : + std::runtime_error(createWhat(description, system_errno)), + description(description), + system_errno(system_errno) +{ +} + +QPDFSystemError::~QPDFSystemError() throw () +{ +} + +std::string +QPDFSystemError::createWhat(std::string const& description, + int system_errno) +{ + std::string message; +#ifdef _MSC_VER + // "94" is mentioned in the MSVC docs, but it's still safe if the + // message is longer. strerror_s is a templated function that + // knows the size of buf and truncates. + char buf[94]; + if (strerror_s(buf, errno) != 0) + { + message = description + ": failed with an unknown error"; + } + else + { + message = description + ": " + buf; + } +#else + message = description + ": " + strerror(errno); +#endif + return message; +} + +std::string const& +QPDFSystemError::getDescription() const +{ + return this->description; +} + +int +QPDFSystemError::getErrno() const +{ + return this->system_errno; +} diff --git a/libqpdf/QUtil.cc b/libqpdf/QUtil.cc index e2bc0bac..56bac386 100644 --- a/libqpdf/QUtil.cc +++ b/libqpdf/QUtil.cc @@ -7,6 +7,7 @@ # include #endif #include +#include #include #include @@ -132,22 +133,7 @@ QUtil::unsigned_char_pointer(char const* str) void QUtil::throw_system_error(std::string const& description) { -#ifdef _MSC_VER - // "94" is mentioned in the MSVC docs, but it's still safe if the - // message is longer. strerror_s is a templated function that - // knows the size of buf and truncates. - char buf[94]; - if (strerror_s(buf, errno) != 0) - { - throw std::runtime_error(description + ": failed with an unknown error"); - } - else - { - throw std::runtime_error(description + ": " + buf); - } -#else - throw std::runtime_error(description + ": " + strerror(errno)); -#endif + throw QPDFSystemError(description, errno); } int diff --git a/libqpdf/build.mk b/libqpdf/build.mk index 528456f8..61ea4b2d 100644 --- a/libqpdf/build.mk +++ b/libqpdf/build.mk @@ -45,6 +45,7 @@ SRCS_libqpdf = \ libqpdf/QPDFObjectHandle.cc \ libqpdf/QPDFPageDocumentHelper.cc \ libqpdf/QPDFPageObjectHelper.cc \ + libqpdf/QPDFSystemError.cc \ libqpdf/QPDFTokenizer.cc \ libqpdf/QPDFWriter.cc \ libqpdf/QPDFXRefEntry.cc \ diff --git a/libtests/qutil.cc b/libtests/qutil.cc index 78c52156..025f4e43 100644 --- a/libtests/qutil.cc +++ b/libtests/qutil.cc @@ -5,8 +5,10 @@ #include #include #include +#include #include #include +#include #ifdef _WIN32 # include @@ -137,9 +139,10 @@ void fopen_wrapper_test() std::cout << "after fopen" << std::endl; (void) fclose(f); } - catch (std::runtime_error& s) + catch (QPDFSystemError& s) { std::cout << "exception: " << s.what() << std::endl; + assert(s.getErrno() != 0); } }