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
21 #define DEBUG_DECLARE_ONLY
22
23 #include "error.h"
24 #include <cstdarg>
25 #include <cstdlib>
26
27 namespace genesys {
28
29 extern "C" void sanei_debug_msg(int level, int max_level, const char *be, const char *fmt,
30 std::va_list ap);
31
32 #if (defined(__GNUC__) || defined(__CLANG__)) && (defined(__linux__) || defined(__APPLE__))
33 extern "C" char* __cxa_get_globals();
34 #endif
35
num_uncaught_exceptions()36 static unsigned num_uncaught_exceptions()
37 {
38 #if __cplusplus >= 201703L
39 int count = std::uncaught_exceptions();
40 return count >= 0 ? count : 0;
41 #elif (defined(__GNUC__) || defined(__CLANG__)) && (defined(__linux__) || defined(__APPLE__))
42 // the format of the __cxa_eh_globals struct is enshrined into the Itanium C++ ABI and it's
43 // very unlikely we'll get issues referencing it directly
44 char* cxa_eh_globals_ptr = __cxa_get_globals();
45 return *reinterpret_cast<unsigned*>(cxa_eh_globals_ptr + sizeof(void*));
46 #else
47 return std::uncaught_exception() ? 1 : 0;
48 #endif
49 }
50
SaneException(SANE_Status status)51 SaneException::SaneException(SANE_Status status) : status_(status)
52 {
53 set_msg();
54 }
55
SaneException(SANE_Status status, const char* format, ...)56 SaneException::SaneException(SANE_Status status, const char* format, ...) : status_(status)
57 {
58 std::va_list args;
59 va_start(args, format);
60 set_msg(format, args);
61 va_end(args);
62 }
63
SaneException(const char* format, ...)64 SaneException::SaneException(const char* format, ...) : status_(SANE_STATUS_INVAL)
65 {
66 std::va_list args;
67 va_start(args, format);
68 set_msg(format, args);
69 va_end(args);
70 }
71
status() const72 SANE_Status SaneException::status() const
73 {
74 return status_;
75 }
76
77 const char* SaneException::what() const noexcept
78 {
79 return msg_.c_str();
80 }
81
set_msg()82 void SaneException::set_msg()
83 {
84 const char* status_msg = sane_strstatus(status_);
85 std::size_t status_msg_len = std::strlen(status_msg);
86 msg_.reserve(status_msg_len);
87 msg_ = status_msg;
88 }
89
set_msg(const char* format, std::va_list vlist)90 void SaneException::set_msg(const char* format, std::va_list vlist)
91 {
92 const char* status_msg = sane_strstatus(status_);
93 std::size_t status_msg_len = std::strlen(status_msg);
94
95 std::va_list vlist2;
96 va_copy(vlist2, vlist);
97 int msg_len = std::vsnprintf(nullptr, 0, format, vlist2);
98 va_end(vlist2);
99
100 if (msg_len < 0) {
101 const char* formatting_error_msg = "(error formatting arguments)";
102 msg_.reserve(std::strlen(formatting_error_msg) + 3 + status_msg_len);
103 msg_ = formatting_error_msg;
104 msg_ += " : ";
105 msg_ += status_msg;
106 return;
107 }
108
109 msg_.reserve(msg_len + status_msg_len + 3);
110 msg_.resize(msg_len + 1, ' ');
111 std::vsnprintf(&msg_[0], msg_len + 1, format, vlist);
112 msg_.resize(msg_len, ' ');
113
114 msg_ += " : ";
115 msg_ += status_msg;
116 }
117
DebugMessageHelper(const char* func)118 DebugMessageHelper::DebugMessageHelper(const char* func)
119 {
120 func_ = func;
121 num_exceptions_on_enter_ = num_uncaught_exceptions();
122 msg_[0] = '\0';
123 DBG(DBG_proc, "%s: start\n", func_);
124 }
125
DebugMessageHelper(const char* func, const char* format, ...)126 DebugMessageHelper::DebugMessageHelper(const char* func, const char* format, ...)
127 {
128 func_ = func;
129 num_exceptions_on_enter_ = num_uncaught_exceptions();
130 msg_[0] = '\0';
131 DBG(DBG_proc, "%s: start\n", func_);
132 DBG(DBG_proc, "%s: ", func_);
133
134 std::va_list args;
135 va_start(args, format);
136 sanei_debug_msg(DBG_proc, DBG_LEVEL, STRINGIFY(BACKEND_NAME), format, args);
137 va_end(args);
138 DBG(DBG_proc, "\n");
139 }
140
141
~DebugMessageHelper()142 DebugMessageHelper::~DebugMessageHelper()
143 {
144 if (num_exceptions_on_enter_ < num_uncaught_exceptions()) {
145 if (msg_[0] != '\0') {
146 DBG(DBG_error, "%s: failed during %s\n", func_, msg_);
147 } else {
148 DBG(DBG_error, "%s: failed\n", func_);
149 }
150 } else {
151 DBG(DBG_proc, "%s: completed\n", func_);
152 }
153 }
154
vstatus(const char* format, ...)155 void DebugMessageHelper::vstatus(const char* format, ...)
156 {
157 std::va_list args;
158 va_start(args, format);
159 std::vsnprintf(msg_, MAX_BUF_SIZE, format, args);
160 va_end(args);
161 }
162
log(unsigned level, const char* msg)163 void DebugMessageHelper::log(unsigned level, const char* msg)
164 {
165 DBG(level, "%s: %s\n", func_, msg);
166 }
167
vlog(unsigned level, const char* format, ...)168 void DebugMessageHelper::vlog(unsigned level, const char* format, ...)
169 {
170 std::string msg;
171
172 std::va_list args;
173
174 va_start(args, format);
175 int msg_len = std::vsnprintf(nullptr, 0, format, args);
176 va_end(args);
177
178 if (msg_len < 0) {
179 DBG(level, "%s: error formatting error message: %s\n", func_, format);
180 return;
181 }
182 msg.resize(msg_len + 1, ' ');
183
184 va_start(args, format);
185 std::vsnprintf(&msg.front(), msg.size(), format, args);
186 va_end(args);
187
188 msg.resize(msg_len, ' '); // strip the null character
189
190 DBG(level, "%s: %s\n", func_, msg.c_str());
191 }
192
193 enum class LogImageDataStatus
194 {
195 NOT_SET,
196 ENABLED,
197 DISABLED
198 };
199
200 static LogImageDataStatus s_log_image_data_setting = LogImageDataStatus::NOT_SET;
201
dbg_read_log_image_data_setting()202 LogImageDataStatus dbg_read_log_image_data_setting()
203 {
204 auto* setting = std::getenv("SANE_DEBUG_GENESYS_IMAGE");
205 if (!setting)
206 return LogImageDataStatus::DISABLED;
207 auto setting_int = std::strtol(setting, nullptr, 10);
208 if (setting_int == 0)
209 return LogImageDataStatus::DISABLED;
210 return LogImageDataStatus::ENABLED;
211 }
212
dbg_log_image_data()213 bool dbg_log_image_data()
214 {
215 if (s_log_image_data_setting == LogImageDataStatus::NOT_SET) {
216 s_log_image_data_setting = dbg_read_log_image_data_setting();
217 }
218 return s_log_image_data_setting == LogImageDataStatus::ENABLED;
219 }
220
221 } // namespace genesys
222