1 // ***************************************************************** -*- C++ -*-
2 /*
3  * Copyright (C) 2004-2021 Exiv2 authors
4  * This program is part of the Exiv2 distribution.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
19  */
20 /*!
21   @file    error.hpp
22   @brief   Error class for exceptions, log message class
23   @author  Andreas Huggel (ahu)
24            <a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a>
25   @date    15-Jan-04, ahu: created<BR>
26            11-Feb-04, ahu: isolated as a component
27  */
28 #ifndef ERROR_HPP_
29 #define ERROR_HPP_
30 
31 // *****************************************************************************
32 #include "exiv2lib_export.h"
33 
34 // included header files
35 #include "types.hpp"
36 
37 // + standard includes
38 #include <exception>
39 #include <string>
40 
41 // *****************************************************************************
42 // namespace extensions
43 namespace Exiv2 {
44 
45 // *****************************************************************************
46 // class definitions
47 
48     /*!
49       @brief Class for a log message, used by the library. Applications can set
50              the log level and provide a customer log message handler (callback
51              function).
52 
53              This class is meant to be used as a temporary object with the
54              related macro-magic like this:
55 
56              <code>
57              EXV_WARNING << "Warning! Something looks fishy.\n";
58              </code>
59 
60              which translates to
61 
62              <code>
63              if (LogMsg::warn >= LogMsg::level() && LogMsg::handler())
64                  LogMsg(LogMsg::warn).os() << "Warning! Something looks fishy.\n";
65              </code>
66 
67              The macros EXV_DEBUG, EXV_INFO, EXV_WARNING and EXV_ERROR are
68              shorthands and ensure efficient use of the logging facility: If a
69              log message doesn't need to be generated because of the log level
70              setting, the temp object is not even created.
71 
72              Caveat: The entire log message is not processed in this case. So don't
73              make that call any logic that always needs to be executed.
74      */
75     class EXIV2API LogMsg {
76         //! Prevent copy-construction: not implemented.
77         LogMsg(const LogMsg&);
78         //! Prevent assignment: not implemented.
79         LogMsg& operator=(const LogMsg&);
80     public:
81         /*!
82           @brief Defined log levels. To suppress all log messages, either set the
83                  log level to \c mute or set the log message handler to 0.
84          */
85         enum Level { debug = 0, info = 1, warn = 2, error = 3, mute = 4 };
86         /*!
87           @brief Type for a log message handler function. The function receives
88                  the log level and message and can process it in an application
89                  specific way.  The default handler sends the log message to
90                  standard error.
91          */
92         typedef void (*Handler)(int, const char*);
93 
94         //! @name Creators
95         //@{
96         //! Constructor, takes the log message type as an argument
97         explicit LogMsg(Level msgType);
98 
99         //! Destructor, passes the log message to the message handler depending on the log level
100         ~LogMsg();
101         //@}
102 
103         //! @name Manipulators
104         //@{
105         //! Return a reference to the ostringstream which holds the log message
106         std::ostringstream& os();
107         //@}
108 
109         /*!
110           @brief Set the log level. Only log messages with a level greater or
111                  equal \em level are sent to the log message handler. Default
112                  log level is \c warn. To suppress all log messages, set the log
113                  level to \c mute (or set the log message handler to 0).
114         */
115         static void setLevel(Level level);
116         /*!
117           @brief Set the log message handler. The default handler writes log
118                  messages to standard error. To suppress all log messages, set
119                  the log message handler to 0 (or set the log level to \c mute).
120          */
121         static void setHandler(Handler handler);
122         //! Return the current log level
123         static Level level();
124         //! Return the current log message handler
125         static Handler handler();
126         //! The default log handler. Sends the log message to standard error.
127         static void defaultHandler(int level, const char* s);
128 
129     private:
130         // DATA
131         // The output level. Only messages with type >= level_ will be written
132         static Level level_;
133         // The log handler in use
134         static Handler handler_;
135         // The type of this log message
136         const Level msgType_;
137         // Holds the log message until it is passed to the message handler
138         std::ostringstream os_;
139 
140     }; // class LogMsg
141 
142 // Macros for simple access
143 //! Shorthand to create a temp debug log message object and return its ostringstream
144 #define EXV_DEBUG   if (LogMsg::debug >= LogMsg::level() && LogMsg::handler()) LogMsg(LogMsg::debug).os()
145 //! Shorthand for a temp info log message object and return its ostringstream
146 #define EXV_INFO    if (LogMsg::info  >= LogMsg::level() && LogMsg::handler()) LogMsg(LogMsg::info).os()
147 //! Shorthand for a temp warning log message object and return its ostringstream
148 #define EXV_WARNING if (LogMsg::warn  >= LogMsg::level() && LogMsg::handler()) LogMsg(LogMsg::warn).os()
149 //! Shorthand for a temp error log message object and return its ostringstream
150 #define EXV_ERROR   if (LogMsg::error >= LogMsg::level() && LogMsg::handler()) LogMsg(LogMsg::error).os()
151 
152 #ifdef _MSC_VER
153 // Disable MSVC warnings "non - DLL-interface classkey 'identifier' used as base
154 // for DLL-interface classkey 'identifier'"
155 # pragma warning( disable : 4275 )
156 #endif
157 
158     //! Generalised toString function
159     template<typename charT, typename T>
toBasicString(const T & arg)160     std::basic_string<charT> toBasicString(const T& arg)
161     {
162         std::basic_ostringstream<charT> os;
163         os << arg;
164         return os.str();
165     }
166 
167     /*!
168       @brief Error class interface. Allows the definition and use of a hierarchy
169              of error classes which can all be handled in one catch block.
170              Inherits from the standard exception base-class, to make life
171              easier for library users (they have the option of catching most
172              things via std::exception).
173      */
174     class EXIV2API AnyError : public std::exception {
175     public:
176         AnyError();
177         AnyError(const AnyError& o);
178 
179         virtual ~AnyError() throw();
180         ///@brief  Return the error code.
181         virtual int code() const throw() =0;
182     };
183 
184     //! %AnyError output operator
operator <<(std::ostream & os,const AnyError & error)185     inline std::ostream& operator<<(std::ostream& os, const AnyError& error)
186     {
187         return os << error.what();
188     }
189 
190     //! Complete list of all Exiv2 error codes
191     enum ErrorCode {
192         kerGeneralError = -1,
193         kerSuccess = 0,
194         kerErrorMessage,
195         kerCallFailed,
196         kerNotAnImage,
197         kerInvalidDataset,
198         kerInvalidRecord,
199         kerInvalidKey,
200         kerInvalidTag,
201         kerValueNotSet,
202         kerDataSourceOpenFailed,
203         kerFileOpenFailed,
204         kerFileContainsUnknownImageType,
205         kerMemoryContainsUnknownImageType,
206         kerUnsupportedImageType,
207         kerFailedToReadImageData,
208         kerNotAJpeg,
209         kerFailedToMapFileForReadWrite,
210         kerFileRenameFailed,
211         kerTransferFailed,
212         kerMemoryTransferFailed,
213         kerInputDataReadFailed,
214         kerImageWriteFailed,
215         kerNoImageInInputData,
216         kerInvalidIfdId,
217         //! Entry::setValue: Value too large
218         kerValueTooLarge,
219         //! Entry::setDataArea: Value too large
220         kerDataAreaValueTooLarge,
221         kerOffsetOutOfRange,
222         kerUnsupportedDataAreaOffsetType,
223         kerInvalidCharset,
224         kerUnsupportedDateFormat,
225         kerUnsupportedTimeFormat,
226         kerWritingImageFormatUnsupported,
227         kerInvalidSettingForImage,
228         kerNotACrwImage,
229         kerFunctionNotSupported,
230         kerNoNamespaceInfoForXmpPrefix,
231         kerNoPrefixForNamespace,
232         kerTooLargeJpegSegment,
233         kerUnhandledXmpdatum,
234         kerUnhandledXmpNode,
235         kerXMPToolkitError,
236         kerDecodeLangAltPropertyFailed,
237         kerDecodeLangAltQualifierFailed,
238         kerEncodeLangAltPropertyFailed,
239         kerPropertyNameIdentificationFailed,
240         kerSchemaNamespaceNotRegistered,
241         kerNoNamespaceForPrefix,
242         kerAliasesNotSupported,
243         kerInvalidXmpText,
244         kerTooManyTiffDirectoryEntries,
245         kerMultipleTiffArrayElementTagsInDirectory,
246         kerWrongTiffArrayElementTagType,
247         kerInvalidKeyXmpValue,
248         kerInvalidIccProfile,
249         kerInvalidXMP,
250         kerTiffDirectoryTooLarge,
251         kerInvalidTypeValue,
252         kerInvalidLangAltValue,
253         kerInvalidMalloc,
254         kerCorruptedMetadata,
255         kerArithmeticOverflow,
256         kerMallocFailed,
257     };
258 
259     /*!
260       @brief Simple error class used for exceptions. An output operator is
261              provided to print errors to a stream.
262      */
263     template<typename charT>
264     class EXIV2API BasicError : public AnyError {
265     public:
266         //! @name Creators
267         //@{
268         //! Constructor taking only an error code
269         explicit inline BasicError(ErrorCode code);
270 
271         //! Constructor taking an error code and one argument
272         template<typename A>
273         inline BasicError(ErrorCode code, const A& arg1);
274 
275         //! Constructor taking an error code and two arguments
276         template<typename A, typename B>
277         inline BasicError(ErrorCode code, const A& arg1, const B& arg2);
278 
279         //! Constructor taking an error code and three arguments
280         template<typename A, typename B, typename C>
281         inline BasicError(ErrorCode code, const A& arg1, const B& arg2, const C& arg3);
282 
283         //! Virtual destructor. (Needed because of throw())
284         virtual inline ~BasicError() throw();
285         //@}
286 
287         //! @name Accessors
288         //@{
289         virtual inline int code() const throw();
290         /*!
291           @brief Return the error message as a C-string. The pointer returned by what()
292                  is valid only as long as the BasicError object exists.
293          */
294         virtual inline const char* what() const throw();
295 #ifdef EXV_UNICODE_PATH
296         /*!
297           @brief Return the error message as a wchar_t-string. The pointer returned by
298                  wwhat() is valid only as long as the BasicError object exists.
299          */
300         virtual inline const wchar_t* wwhat() const throw();
301 #endif
302         //@}
303 
304     private:
305         //! @name Manipulators
306         //@{
307         //! Assemble the error message from the arguments
308         void setMsg();
309         //@}
310 
311         // DATA
312         ErrorCode code_;                       //!< Error code
313         int count_;                             //!< Number of arguments
314         std::basic_string<charT> arg1_;         //!< First argument
315         std::basic_string<charT> arg2_;         //!< Second argument
316         std::basic_string<charT> arg3_;         //!< Third argument
317         std::string              msg_;          //!< Complete error message
318 #ifdef EXV_UNICODE_PATH
319         std::wstring             wmsg_;         //!< Complete error message as a wide string
320 #endif
321     }; // class BasicError
322 
323     //! Error class used for exceptions (std::string based)
324     typedef BasicError<char> Error;
325 #ifdef EXV_UNICODE_PATH
326     //! Error class used for exceptions (std::wstring based)
327     typedef BasicError<wchar_t> WError;
328 #endif
329 
330 // *****************************************************************************
331 // free functions, template and inline definitions
332 
333     //! Return the error message for the error with code \em code.
334     const char* errMsg(int code);
335 
336     template<typename charT>
BasicError(ErrorCode code)337     BasicError<charT>::BasicError(ErrorCode code)
338         : code_(code), count_(0)
339     {
340         setMsg();
341     }
342 
343     template<typename charT> template<typename A>
BasicError(ErrorCode code,const A & arg1)344     BasicError<charT>::BasicError(ErrorCode code, const A& arg1)
345         : code_(code), count_(1), arg1_(toBasicString<charT>(arg1))
346     {
347         setMsg();
348     }
349 
350     template<typename charT> template<typename A, typename B>
BasicError(ErrorCode code,const A & arg1,const B & arg2)351     BasicError<charT>::BasicError(ErrorCode code, const A& arg1, const B& arg2)
352         : code_(code), count_(2),
353           arg1_(toBasicString<charT>(arg1)),
354           arg2_(toBasicString<charT>(arg2))
355     {
356         setMsg();
357     }
358 
359     template<typename charT> template<typename A, typename B, typename C>
BasicError(ErrorCode code,const A & arg1,const B & arg2,const C & arg3)360     BasicError<charT>::BasicError(ErrorCode code, const A& arg1, const B& arg2, const C& arg3)
361         : code_(code), count_(3),
362           arg1_(toBasicString<charT>(arg1)),
363           arg2_(toBasicString<charT>(arg2)),
364           arg3_(toBasicString<charT>(arg3))
365     {
366         setMsg();
367     }
368 
369     template<typename charT>
~BasicError()370     BasicError<charT>::~BasicError() throw()
371     {
372     }
373 
374     template<typename charT>
code() const375     int BasicError<charT>::code() const throw()
376     {
377         return code_;
378     }
379 
380     template<typename charT>
what() const381     const char* BasicError<charT>::what() const throw()
382     {
383         return msg_.c_str();
384     }
385 
386 #ifdef EXV_UNICODE_PATH
387     template<typename charT>
wwhat() const388     const wchar_t* BasicError<charT>::wwhat() const throw()
389     {
390         return wmsg_.c_str();
391     }
392 #endif
393 
394 #ifdef _MSC_VER
395 # pragma warning( default : 4275 )
396 #endif
397 
398 }                                       // namespace Exiv2
399 #endif                                  // #ifndef ERROR_HPP_
400