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