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 27namespace genesys { 28 29extern "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__)) 33extern "C" char* __cxa_get_globals(); 34#endif 35 36static 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 51SaneException::SaneException(SANE_Status status) : status_(status) 52{ 53 set_msg(); 54} 55 56SaneException::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 64SaneException::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 72SANE_Status SaneException::status() const 73{ 74 return status_; 75} 76 77const char* SaneException::what() const noexcept 78{ 79 return msg_.c_str(); 80} 81 82void 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 90void 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 118DebugMessageHelper::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 126DebugMessageHelper::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 142DebugMessageHelper::~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 155void 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 163void DebugMessageHelper::log(unsigned level, const char* msg) 164{ 165 DBG(level, "%s: %s\n", func_, msg); 166} 167 168void 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 193enum class LogImageDataStatus 194{ 195 NOT_SET, 196 ENABLED, 197 DISABLED 198}; 199 200static LogImageDataStatus s_log_image_data_setting = LogImageDataStatus::NOT_SET; 201 202LogImageDataStatus 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 213bool 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