1 /* sane - Scanner Access Now Easy.
2 
3    Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
4 
5    This file is part of the SANE package.
6 
7    This program is free software; you can redistribute it and/or
8    modify it under the terms of the GNU General Public License as
9    published by the Free Software Foundation; either version 2 of the
10    License, or (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <https://www.gnu.org/licenses/>.
19 
20    As a special exception, the authors of SANE give permission for
21    additional uses of the libraries contained in this release of SANE.
22 
23    The exception is that, if you link a SANE library with other files
24    to produce an executable, this does not by itself cause the
25    resulting executable to be covered by the GNU General Public
26    License.  Your use of that executable is in no way restricted on
27    account of linking the SANE library code into it.
28 
29    This exception does not, however, invalidate any other reasons why
30    the executable file might be covered by the GNU General Public
31    License.
32 
33    If you submit changes to SANE to the maintainers to be included in
34    a subsequent release, you agree by submitting the changes that
35    those changes may be distributed with this exception intact.
36 
37    If you write modifications of your own for SANE, it is your choice
38    whether to permit this exception to apply to your modifications.
39    If you do not wish that, delete this exception notice.
40 */
41 
42 #define DEBUG_DECLARE_ONLY
43 
44 #include "error.h"
45 #include <cstdarg>
46 #include <cstdlib>
47 
48 namespace genesys {
49 
50 extern "C" void sanei_debug_msg(int level, int max_level, const char *be, const char *fmt,
51                                 std::va_list ap);
52 
53 #if (defined(__GNUC__) || defined(__CLANG__)) && (defined(__linux__) || defined(__APPLE__))
54 extern "C" char* __cxa_get_globals();
55 #endif
56 
num_uncaught_exceptions()57 static unsigned num_uncaught_exceptions()
58 {
59 #if __cplusplus >= 201703L
60     int count = std::uncaught_exceptions();
61     return count >= 0 ? count : 0;
62 #elif (defined(__GNUC__) || defined(__CLANG__)) && (defined(__linux__) || defined(__APPLE__))
63     // the format of the __cxa_eh_globals struct is enshrined into the Itanium C++ ABI and it's
64     // very unlikely we'll get issues referencing it directly
65     char* cxa_eh_globals_ptr = __cxa_get_globals();
66     return *reinterpret_cast<unsigned*>(cxa_eh_globals_ptr + sizeof(void*));
67 #else
68     return std::uncaught_exception() ? 1 : 0;
69 #endif
70 }
71 
SaneException(SANE_Status status)72 SaneException::SaneException(SANE_Status status) : status_(status)
73 {
74     set_msg();
75 }
76 
SaneException(SANE_Status status,const char * format,...)77 SaneException::SaneException(SANE_Status status, const char* format, ...) : status_(status)
78 {
79     std::va_list args;
80     va_start(args, format);
81     set_msg(format, args);
82     va_end(args);
83 }
84 
SaneException(const char * format,...)85 SaneException::SaneException(const char* format, ...) : status_(SANE_STATUS_INVAL)
86 {
87     std::va_list args;
88     va_start(args, format);
89     set_msg(format, args);
90     va_end(args);
91 }
92 
status() const93 SANE_Status SaneException::status() const
94 {
95     return status_;
96 }
97 
what() const98 const char* SaneException::what() const noexcept
99 {
100     return msg_.c_str();
101 }
102 
set_msg()103 void SaneException::set_msg()
104 {
105     const char* status_msg = sane_strstatus(status_);
106     std::size_t status_msg_len = std::strlen(status_msg);
107     msg_.reserve(status_msg_len);
108     msg_ = status_msg;
109 }
110 
set_msg(const char * format,std::va_list vlist)111 void SaneException::set_msg(const char* format, std::va_list vlist)
112 {
113     const char* status_msg = sane_strstatus(status_);
114     std::size_t status_msg_len = std::strlen(status_msg);
115 
116     std::va_list vlist2;
117     va_copy(vlist2, vlist);
118     int msg_len = std::vsnprintf(nullptr, 0, format, vlist2);
119     va_end(vlist2);
120 
121     if (msg_len < 0) {
122         const char* formatting_error_msg = "(error formatting arguments)";
123         msg_.reserve(std::strlen(formatting_error_msg) + 3 + status_msg_len);
124         msg_ = formatting_error_msg;
125         msg_ += " : ";
126         msg_ += status_msg;
127         return;
128     }
129 
130     msg_.reserve(msg_len + status_msg_len + 3);
131     msg_.resize(msg_len + 1, ' ');
132     std::vsnprintf(&msg_[0], msg_len + 1, format, vlist);
133     msg_.resize(msg_len, ' ');
134 
135     msg_ += " : ";
136     msg_ += status_msg;
137 }
138 
DebugMessageHelper(const char * func)139 DebugMessageHelper::DebugMessageHelper(const char* func)
140 {
141     func_ = func;
142     num_exceptions_on_enter_ = num_uncaught_exceptions();
143     msg_[0] = '\0';
144     DBG(DBG_proc, "%s: start\n", func_);
145 }
146 
DebugMessageHelper(const char * func,const char * format,...)147 DebugMessageHelper::DebugMessageHelper(const char* func, const char* format, ...)
148 {
149     func_ = func;
150     num_exceptions_on_enter_ = num_uncaught_exceptions();
151     msg_[0] = '\0';
152     DBG(DBG_proc, "%s: start\n", func_);
153     DBG(DBG_proc, "%s: ", func_);
154 
155     std::va_list args;
156     va_start(args, format);
157     sanei_debug_msg(DBG_proc, DBG_LEVEL, STRINGIFY(BACKEND_NAME), format, args);
158     va_end(args);
159     DBG(DBG_proc, "\n");
160 }
161 
162 
~DebugMessageHelper()163 DebugMessageHelper::~DebugMessageHelper()
164 {
165     if (num_exceptions_on_enter_ < num_uncaught_exceptions()) {
166         if (msg_[0] != '\0') {
167             DBG(DBG_error, "%s: failed during %s\n", func_, msg_);
168         } else {
169             DBG(DBG_error, "%s: failed\n", func_);
170         }
171     } else {
172         DBG(DBG_proc, "%s: completed\n", func_);
173     }
174 }
175 
vstatus(const char * format,...)176 void DebugMessageHelper::vstatus(const char* format, ...)
177 {
178     std::va_list args;
179     va_start(args, format);
180     std::vsnprintf(msg_, MAX_BUF_SIZE, format, args);
181     va_end(args);
182 }
183 
log(unsigned level,const char * msg)184 void DebugMessageHelper::log(unsigned level, const char* msg)
185 {
186     DBG(level, "%s: %s\n", func_, msg);
187 }
188 
vlog(unsigned level,const char * format,...)189 void DebugMessageHelper::vlog(unsigned level, const char* format, ...)
190 {
191     std::string msg;
192 
193     std::va_list args;
194 
195     va_start(args, format);
196     int msg_len = std::vsnprintf(nullptr, 0, format, args);
197     va_end(args);
198 
199     if (msg_len < 0) {
200         DBG(level, "%s: error formatting error message: %s\n", func_, format);
201         return;
202     }
203     msg.resize(msg_len + 1, ' ');
204 
205     va_start(args, format);
206     std::vsnprintf(&msg.front(), msg.size(), format, args);
207     va_end(args);
208 
209     msg.resize(msg_len, ' '); // strip the null character
210 
211     DBG(level, "%s: %s\n", func_, msg.c_str());
212 }
213 
214 enum class LogImageDataStatus
215 {
216     NOT_SET,
217     ENABLED,
218     DISABLED
219 };
220 
221 static LogImageDataStatus s_log_image_data_setting = LogImageDataStatus::NOT_SET;
222 
dbg_read_log_image_data_setting()223 LogImageDataStatus dbg_read_log_image_data_setting()
224 {
225     auto* setting = std::getenv("SANE_DEBUG_GENESYS_IMAGE");
226     if (!setting)
227         return LogImageDataStatus::DISABLED;
228     auto setting_int = std::strtol(setting, nullptr, 10);
229     if (setting_int == 0)
230         return LogImageDataStatus::DISABLED;
231     return LogImageDataStatus::ENABLED;
232 }
233 
dbg_log_image_data()234 bool dbg_log_image_data()
235 {
236     if (s_log_image_data_setting == LogImageDataStatus::NOT_SET) {
237         s_log_image_data_setting = dbg_read_log_image_data_setting();
238     }
239     return s_log_image_data_setting == LogImageDataStatus::ENABLED;
240 }
241 
242 } // namespace genesys
243