diff options
Diffstat (limited to 'common/core')
-rw-r--r-- | common/core/CMakeLists.txt | 32 | ||||
-rw-r--r-- | common/core/Configuration.cxx | 857 | ||||
-rw-r--r-- | common/core/Configuration.h | 348 | ||||
-rw-r--r-- | common/core/Exception.cxx | 134 | ||||
-rw-r--r-- | common/core/Exception.h | 75 | ||||
-rw-r--r-- | common/core/LogWriter.cxx | 135 | ||||
-rw-r--r-- | common/core/LogWriter.h | 114 | ||||
-rw-r--r-- | common/core/Logger.cxx | 95 | ||||
-rw-r--r-- | common/core/Logger.h | 71 | ||||
-rw-r--r-- | common/core/Logger_file.cxx | 123 | ||||
-rw-r--r-- | common/core/Logger_file.h | 53 | ||||
-rw-r--r-- | common/core/Logger_stdio.cxx | 37 | ||||
-rw-r--r-- | common/core/Logger_stdio.h | 39 | ||||
-rw-r--r-- | common/core/Logger_syslog.cxx | 68 | ||||
-rw-r--r-- | common/core/Logger_syslog.h | 41 | ||||
-rw-r--r-- | common/core/Rect.h | 120 | ||||
-rw-r--r-- | common/core/Region.cxx | 203 | ||||
-rw-r--r-- | common/core/Region.h | 85 | ||||
-rw-r--r-- | common/core/Timer.cxx | 155 | ||||
-rw-r--r-- | common/core/Timer.h | 136 | ||||
-rw-r--r-- | common/core/string.cxx | 650 | ||||
-rw-r--r-- | common/core/string.h | 80 | ||||
-rw-r--r-- | common/core/time.cxx | 88 | ||||
-rw-r--r-- | common/core/time.h | 58 | ||||
-rw-r--r-- | common/core/winerrno.h | 91 | ||||
-rw-r--r-- | common/core/xdgdirs.cxx | 162 | ||||
-rw-r--r-- | common/core/xdgdirs.h | 74 |
27 files changed, 4124 insertions, 0 deletions
diff --git a/common/core/CMakeLists.txt b/common/core/CMakeLists.txt new file mode 100644 index 00000000..7e58acc0 --- /dev/null +++ b/common/core/CMakeLists.txt @@ -0,0 +1,32 @@ +add_library(core STATIC + Configuration.cxx + Exception.cxx + Logger.cxx + Logger_file.cxx + Logger_stdio.cxx + LogWriter.cxx + Region.cxx + Timer.cxx + string.cxx + time.cxx + xdgdirs.cxx) + +target_include_directories(core PUBLIC ${CMAKE_SOURCE_DIR}/common) +target_include_directories(core SYSTEM PUBLIC ${PIXMAN_INCLUDE_DIRS}) +target_link_libraries(core ${PIXMAN_LIBRARIES}) + +if(UNIX) + target_sources(core PRIVATE Logger_syslog.cxx) +endif() + +if(WIN32) + target_link_libraries(core ws2_32) +endif() + +if(UNIX) + target_sources(core PRIVATE Logger_syslog.cxx) +endif() + +if(UNIX) + libtool_create_control_file(core) +endif() diff --git a/common/core/Configuration.cxx b/common/core/Configuration.cxx new file mode 100644 index 00000000..e6affb06 --- /dev/null +++ b/common/core/Configuration.cxx @@ -0,0 +1,857 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2004-2005 Cendio AB. + * Copyright 2017 Peter Astrand <astrand@cendio.se> for Cendio AB + * Copyright 2011-2025 Pierre Ossman for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Configuration.cxx + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> + +#include <algorithm> +#include <stdexcept> + +#include <core/Configuration.h> +#include <core/LogWriter.h> +#include <core/string.h> + +#include <rdr/HexOutStream.h> +#include <rdr/HexInStream.h> + +using namespace core; + +static LogWriter vlog("Config"); + + +// -=- The Global Configuration object +Configuration* Configuration::global_ = nullptr; + +Configuration* Configuration::global() { + if (!global_) + global_ = new Configuration(); + return global_; +} + +// -=- Configuration implementation + +bool Configuration::set(const char* paramName, const char* val, + bool immutable) +{ + for (VoidParameter* current: params) { + if (strcasecmp(current->getName(), paramName) == 0) { + bool b = current->setParam(val); + if (b && immutable) + current->setImmutable(); + return b; + } + } + return false; +} + +VoidParameter* Configuration::get(const char* param) +{ + for (VoidParameter* current: params) { + if (strcasecmp(current->getName(), param) == 0) + return current; + } + return nullptr; +} + +void Configuration::list(int width, int nameWidth) { + for (VoidParameter* current: params) { + std::string def_str = current->getDefaultStr(); + std::string desc_str = current->getDescription(); + if (!def_str.empty()) + desc_str += " (default=" + def_str + ")"; + const char* desc = desc_str.c_str(); + fprintf(stderr," %-*s -", nameWidth, current->getName()); + int column = strlen(current->getName()); + if (column < nameWidth) column = nameWidth; + column += 4; + while (true) { + if (desc[0] == '\0') + break; + + int wordLen = strcspn(desc, " \f\n\r\t\v,"); + if (wordLen == 0) { + desc++; + continue; + } + + if (desc[wordLen] == ',') + wordLen++; + + if (column + wordLen + 1 > width) { + fprintf(stderr,"\n%*s",nameWidth+4,""); + column = nameWidth+4; + } + fprintf(stderr, " "); + column++; + + fprintf(stderr, "%.*s", wordLen, desc); + column += wordLen; + desc += wordLen; + } + fprintf(stderr,"\n"); + } +} + + +bool Configuration::remove(const char* param) { + std::list<VoidParameter*>::iterator iter; + + iter = std::find_if(params.begin(), params.end(), + [param](VoidParameter* p) { + return strcasecmp(p->getName(), param) == 0; + }); + if (iter == params.end()) + return false; + + params.erase(iter); + return true; +} + +int Configuration::handleArg(int argc, char* argv[], int index) +{ + std::string param, val; + const char* equal = strchr(argv[index], '='); + + if (equal == argv[index]) + return 0; + + if (equal) { + param.assign(argv[index], equal-argv[index]); + val.assign(equal+1); + } else { + param.assign(argv[index]); + } + + if ((param.length() > 0) && (param[0] == '-')) { + // allow gnu-style --<option> + if ((param.length() > 1) && (param[1] == '-')) + param = param.substr(2); + else + param = param.substr(1); + } else { + // All command line arguments need either an initial '-', or an '=' + if (!equal) + return 0; + } + + if (equal) + return set(param.c_str(), val.c_str()) ? 1 : 0; + + for (VoidParameter* current: params) { + if (strcasecmp(current->getName(), param.c_str()) != 0) + continue; + + // We need to resolve an ambiguity for booleans + if (dynamic_cast<BoolParameter*>(current) != nullptr) { + if (index+1 < argc) { + // FIXME: Should not duplicate the list of values here + if ((strcasecmp(argv[index+1], "0") == 0) || + (strcasecmp(argv[index+1], "1") == 0) || + (strcasecmp(argv[index+1], "on") == 0) || + (strcasecmp(argv[index+1], "off") == 0) || + (strcasecmp(argv[index+1], "true") == 0) || + (strcasecmp(argv[index+1], "false") == 0) || + (strcasecmp(argv[index+1], "yes") == 0) || + (strcasecmp(argv[index+1], "no") == 0)) { + return current->setParam(argv[index+1]) ? 2 : 0; + } + } + } + + if (current->setParam()) + return 1; + + if (index+1 >= argc) + return 0; + + return current->setParam(argv[index+1]) ? 2 : 0; + } + + return 0; +} + + +// -=- VoidParameter + +VoidParameter::VoidParameter(const char* name_, const char* desc_) + : immutable(false), name(name_), description(desc_) +{ + Configuration *conf; + + conf = Configuration::global(); + conf->params.push_back(this); + conf->params.sort([](const VoidParameter* a, const VoidParameter* b) { + return strcasecmp(a->getName(), b->getName()) < 0; + }); +} + +VoidParameter::~VoidParameter() { + Configuration *conf; + + conf = Configuration::global(); + conf->params.remove(this); +} + +const char* +VoidParameter::getName() const { + return name; +} + +const char* +VoidParameter::getDescription() const { + return description; +} + +bool VoidParameter::setParam() { + return false; +} + +bool VoidParameter::isDefault() const { + return getDefaultStr() == getValueStr(); +} + +void +VoidParameter::setImmutable() { + vlog.debug("Set immutable %s", getName()); + immutable = true; +} + +// -=- AliasParameter + +AliasParameter::AliasParameter(const char* name_, const char* desc_, + VoidParameter* param_) + : VoidParameter(name_, desc_), param(param_) { +} + +bool +AliasParameter::setParam(const char* v) { + return param->setParam(v); +} + +bool AliasParameter::setParam() { + return param->setParam(); +} + +std::string AliasParameter::getDefaultStr() const { + return ""; +} + +std::string AliasParameter::getValueStr() const { + return param->getValueStr(); +} + +void +AliasParameter::setImmutable() { + vlog.debug("Set immutable %s (Alias)", getName()); + param->setImmutable(); +} + + +// -=- BoolParameter + +BoolParameter::BoolParameter(const char* name_, const char* desc_, bool v) +: VoidParameter(name_, desc_), value(v), def_value(v) { +} + +bool +BoolParameter::setParam(const char* v) { + if (immutable) return true; + + if (*v == 0 || strcasecmp(v, "1") == 0 || strcasecmp(v, "on") == 0 + || strcasecmp(v, "true") == 0 || strcasecmp(v, "yes") == 0) + setParam(true); + else if (strcasecmp(v, "0") == 0 || strcasecmp(v, "off") == 0 + || strcasecmp(v, "false") == 0 || strcasecmp(v, "no") == 0) + setParam(false); + else { + vlog.error("Bool parameter %s: Invalid value '%s'", getName(), v); + return false; + } + + return true; +} + +bool BoolParameter::setParam() { + setParam(true); + return true; +} + +void BoolParameter::setParam(bool b) { + if (immutable) return; + value = b; + vlog.debug("Set %s(Bool) to %s", getName(), getValueStr().c_str()); +} + +std::string BoolParameter::getDefaultStr() const { + return def_value ? "on" : "off"; +} + +std::string BoolParameter::getValueStr() const { + return value ? "on" : "off"; +} + +BoolParameter::operator bool() const { + return value; +} + +// -=- IntParameter + +IntParameter::IntParameter(const char* name_, const char* desc_, int v, + int minValue_, int maxValue_) + : VoidParameter(name_, desc_), value(v), def_value(v), + minValue(minValue_), maxValue(maxValue_) +{ + if (v < minValue || v > maxValue) { + vlog.error("Invalid default value %d for %s", v, getName()); + throw std::invalid_argument("Invalid default value"); + } +} + +bool +IntParameter::setParam(const char* v) { + char* end; + long n; + if (immutable) return true; + n = strtol(v, &end, 0); + if ((*end != 0) || (n < INT_MIN) || (n > INT_MAX)) { + vlog.error("Int parameter %s: Invalid value '%s'", getName(), v); + return false; + } + return setParam(n); +} + +bool +IntParameter::setParam(int v) { + if (immutable) return true; + if (v < minValue || v > maxValue) { + vlog.error("Int parameter %s: Invalid value '%d'", getName(), v); + return false; + } + vlog.debug("Set %s(Int) to %d", getName(), v); + value = v; + return true; +} + +std::string IntParameter::getDefaultStr() const { + char result[16]; + sprintf(result, "%d", def_value); + return result; +} + +std::string IntParameter::getValueStr() const { + char result[16]; + sprintf(result, "%d", value); + return result; +} + +IntParameter::operator int() const { + return value; +} + +// -=- StringParameter + +StringParameter::StringParameter(const char* name_, const char* desc_, + const char* v) + : VoidParameter(name_, desc_), value(v?v:""), def_value(v?v:"") +{ + if (!v) { + vlog.error("Default value <null> for %s not allowed",name_); + throw std::invalid_argument("Default value <null> not allowed"); + } +} + +bool StringParameter::setParam(const char* v) { + if (immutable) return true; + if (!v) + throw std::invalid_argument("setParam(<null>) not allowed"); + vlog.debug("Set %s(String) to %s", getName(), v); + value = v; + return true; +} + +std::string StringParameter::getDefaultStr() const { + return def_value; +} + +std::string StringParameter::getValueStr() const { + return value; +} + +StringParameter::operator const char *() const { + return value.c_str(); +} + +// -=- EnumParameter + +EnumParameter::EnumParameter(const char* name_, const char* desc_, + const std::set<const char*>& enums_, + const char* v) + : VoidParameter(name_, desc_), value(v?v:""), def_value(v?v:"") +{ + if (!v) { + vlog.error("Default value <null> for %s not allowed", name_); + throw std::invalid_argument("Default value <null> not allowed"); + } + + for (const char* e: enums_) { + if (!e) { + vlog.error("Enumeration <null> for %s not allowed", name_); + throw std::invalid_argument("Enumeration <null> not allowed"); + } + enums.insert(e); + } + + if (std::find(enums.begin(), enums.end(), def_value) == enums.end()) { + vlog.error("Default value %s for %s is not in list of valid values", + def_value.c_str(), name_); + throw std::invalid_argument("Default value is not in list of valid values"); + } +} + +bool EnumParameter::setParam(const char* v) +{ + std::set<std::string>::const_iterator iter; + if (immutable) return true; + if (!v) + throw std::invalid_argument("setParam(<null>) not allowed"); + iter = std::find_if(enums.begin(), enums.end(), + [v](const std::string& e) { + return strcasecmp(e.c_str(), v) == 0; + }); + if (iter == enums.end()) { + vlog.error("Enum parameter %s: Invalid value '%s'", getName(), v); + return false; + } + vlog.debug("Set %s(Enum) to %s", getName(), iter->c_str()); + value = *iter; + return true; +} + +std::string EnumParameter::getDefaultStr() const +{ + return def_value; +} + +std::string EnumParameter::getValueStr() const +{ + return value; +} + +bool EnumParameter::operator==(const char* other) const +{ + return strcasecmp(value.c_str(), other) == 0; +} + +bool EnumParameter::operator==(const std::string& other) const +{ + return *this == other.c_str(); +} + +bool EnumParameter::operator!=(const char* other) const +{ + return strcasecmp(value.c_str(), other) != 0; +} + +bool EnumParameter::operator!=(const std::string& other) const +{ + return *this != other.c_str(); +} + +// -=- BinaryParameter + +BinaryParameter::BinaryParameter(const char* name_, const char* desc_, + const uint8_t* v, size_t l) +: VoidParameter(name_, desc_), + value(nullptr), length(0), def_value(nullptr), def_length(0) { + if (l) { + assert(v); + value = new uint8_t[l]; + length = l; + memcpy(value, v, l); + def_value = new uint8_t[l]; + def_length = l; + memcpy(def_value, v, l); + } +} +BinaryParameter::~BinaryParameter() { + delete [] value; + delete [] def_value; +} + +bool BinaryParameter::setParam(const char* v) { + if (immutable) return true; + std::vector<uint8_t> newValue = hexToBin(v, strlen(v)); + if (newValue.empty() && strlen(v) > 0) + return false; + setParam(newValue.data(), newValue.size()); + return true; +} + +void BinaryParameter::setParam(const uint8_t* v, size_t len) { + if (immutable) return; + vlog.debug("Set %s(Binary)", getName()); + delete [] value; + value = nullptr; + length = 0; + if (len) { + assert(v); + value = new uint8_t[len]; + length = len; + memcpy(value, v, len); + } +} + +std::string BinaryParameter::getDefaultStr() const { + return binToHex(def_value, def_length); +} + +std::string BinaryParameter::getValueStr() const { + return binToHex(value, length); +} + +std::vector<uint8_t> BinaryParameter::getData() const { + std::vector<uint8_t> out(length); + memcpy(out.data(), value, length); + return out; +} + +// -=- ListParameter template + +template<typename ValueType> +ListParameter<ValueType>::ListParameter(const char* name_, + const char* desc_, + const ListType& v) + : VoidParameter(name_, desc_), value(v), def_value(v) +{ +} + +template<typename ValueType> +bool ListParameter<ValueType>::setParam(const char* v) +{ + std::vector<std::string> entries; + ListType new_value; + + if (immutable) + return true; + + // setParam({}) ends up as setParam(nullptr) + if (v != nullptr) + entries = split(v, ','); + + for (std::string& entry : entries) { + ValueType e; + + entry.erase(0, entry.find_first_not_of(" \f\n\r\t\v")); + entry.erase(entry.find_last_not_of(" \f\n\r\t\v")+1); + + // Special case, entire v was just whitespace + if (entry.empty() && (entries.size() == 1)) + break; + + if (!decodeEntry(entry.c_str(), &e)) { + vlog.error("List parameter %s: Invalid value '%s'", + getName(), entry.c_str()); + return false; + } + + new_value.push_back(e); + } + + return setParam(new_value); +} + +template<typename ValueType> +bool ListParameter<ValueType>::setParam(const ListType& v) +{ + ListType vnorm; + if (immutable) + return true; + for (const ValueType& entry : v) { + if (!validateEntry(entry)) { + vlog.error("List parameter %s: Invalid value '%s'", getName(), + encodeEntry(entry).c_str()); + return false; + } + vnorm.push_back(normaliseEntry(entry)); + } + value = vnorm; + vlog.debug("set %s(List) to %s", getName(), getValueStr().c_str()); + return true; +} + +template<typename ValueType> +std::string ListParameter<ValueType>::getDefaultStr() const +{ + std::string result; + + for (ValueType entry : def_value) { + // FIXME: Might want to add a space here as well for readability, + // but this would sacrifice backward compatibility + if (!result.empty()) + result += ','; + result += encodeEntry(entry); + } + + return result; +} + +template<typename ValueType> +std::string ListParameter<ValueType>::getValueStr() const +{ + std::string result; + + for (ValueType entry : value) { + // FIXME: Might want to add a space here as well for readability, + // but this would sacrifice backward compatibility + if (!result.empty()) + result += ','; + result += encodeEntry(entry); + } + + return result; +} + +template<typename ValueType> +typename ListParameter<ValueType>::const_iterator ListParameter<ValueType>::begin() const +{ + return value.begin(); +} + +template<typename ValueType> +typename ListParameter<ValueType>::const_iterator ListParameter<ValueType>::end() const +{ + return value.end(); +} + +template<typename ValueType> +bool ListParameter<ValueType>::validateEntry(const ValueType& /*entry*/) const +{ + return true; +} + +template<typename ValueType> +ValueType ListParameter<ValueType>::normaliseEntry(const ValueType& entry) const +{ + return entry; +} + +// -=- IntListParameter + +template class core::ListParameter<int>; + +IntListParameter::IntListParameter(const char* name_, const char* desc_, + const ListType& v, + int minValue_, int maxValue_) + : ListParameter<int>(name_, desc_, v), + minValue(minValue_), maxValue(maxValue_) +{ + for (int entry : v) { + if (!validateEntry(entry)) { + vlog.error("Invalid default value %d for %s", entry, getName()); + throw std::invalid_argument("Invalid default value"); + } + } +} + +bool IntListParameter::decodeEntry(const char* entry, int* out) const +{ + long n; + char *end; + + assert(entry); + assert(out); + + if (entry[0] == '\0') + return false; + + n = strtol(entry, &end, 0); + if ((*end != 0) || (n < INT_MIN) || (n > INT_MAX)) + return false; + + *out = n; + + return true; +} + +std::string IntListParameter::encodeEntry(const int& entry) const +{ + char valstr[16]; + sprintf(valstr, "%d", entry); + return valstr; +} + +bool IntListParameter::validateEntry(const int& entry) const +{ + return (entry >= minValue) && (entry <= maxValue); +} + +// -=- StringListParameter + +template class core::ListParameter<std::string>; + +StringListParameter::StringListParameter(const char* name_, + const char* desc_, + const std::list<const char*>& v_) + : ListParameter<std::string>(name_, desc_, {}) +{ + for (const char* v: v_) { + if (!v) { + vlog.error("Default value <null> for %s not allowed", name_); + throw std::invalid_argument("Default value <null> not allowed"); + } + value.push_back(v); + def_value.push_back(v); + } +} + +StringListParameter::const_iterator StringListParameter::begin() const +{ + return ListParameter<std::string>::begin(); +} + +StringListParameter::const_iterator StringListParameter::end() const +{ + return ListParameter<std::string>::end(); +} + +bool StringListParameter::decodeEntry(const char* entry, std::string* out) const +{ + *out = entry; + return true; +} + +std::string StringListParameter::encodeEntry(const std::string& entry) const +{ + return entry; +} + +// -=- EnumListEntry + +EnumListEntry::EnumListEntry(const std::string& v) + : value(v) +{ +} + +std::string EnumListEntry::getValueStr() const +{ + return value; +} + +bool EnumListEntry::operator==(const char* other) const +{ + return strcasecmp(value.c_str(), other) == 0; +} + +bool EnumListEntry::operator==(const std::string& other) const +{ + return *this == other.c_str(); +} + +bool EnumListEntry::operator!=(const char* other) const +{ + return strcasecmp(value.c_str(), other) != 0; +} + +bool EnumListEntry::operator!=(const std::string& other) const +{ + return *this != other.c_str(); +} + +// -=- EnumListParameter + +EnumListParameter::EnumListParameter(const char* name_, + const char* desc_, + const std::set<const char*>& enums_, + const std::list<const char*>& v_) + : ListParameter<std::string>(name_, desc_, {}) +{ + for (const char* v: v_) { + if (!v) { + vlog.error("Default value <null> for %s not allowed", name_); + throw std::invalid_argument("Default value <null> not allowed"); + } + value.push_back(v); + def_value.push_back(v); + } + + for (const char* e: enums_) { + if (!e) { + vlog.error("Enumeration <null> for %s not allowed", name_); + throw std::invalid_argument("Enumeration <null> not allowed"); + } + enums.insert(e); + } + + for (const std::string& def_entry : def_value) { + if (std::find(enums.begin(), enums.end(), def_entry) == enums.end()) { + vlog.error("Default value %s for %s is not in list of valid values", + def_entry.c_str(), name_); + throw std::invalid_argument("Default value is not in list of valid values"); + } + } +} + +EnumListParameter::const_iterator EnumListParameter::begin() const +{ + return ListParameter<std::string>::begin(); +} + +EnumListParameter::const_iterator EnumListParameter::end() const +{ + return ListParameter<std::string>::end(); +} + +bool EnumListParameter::decodeEntry(const char* entry, std::string* out) const +{ + *out = entry; + return true; +} + +std::string EnumListParameter::encodeEntry(const std::string& entry) const +{ + return entry; +} + +bool EnumListParameter::validateEntry(const std::string& entry) const +{ + for (const std::string& e : enums) { + if (strcasecmp(e.c_str(), entry.c_str()) == 0) + return true; + } + return false; +} + +std::string EnumListParameter::normaliseEntry(const std::string& entry) const +{ + for (const std::string& e : enums) { + if (strcasecmp(e.c_str(), entry.c_str()) == 0) + return e; + } + throw std::logic_error("Entry is not in list of valid values"); +} diff --git a/common/core/Configuration.h b/common/core/Configuration.h new file mode 100644 index 00000000..431dd0c5 --- /dev/null +++ b/common/core/Configuration.h @@ -0,0 +1,348 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011-2025 Pierre Ossman for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Configuration.h +// +// This header defines a set of classes used to represent configuration +// parameters of different types. Instances of the different parameter +// types are associated with instances of the Configuration class, and +// are each given a unique name. The Configuration class provides a +// generic API through which parameters may be located by name and their +// value set, thus removing the need to write platform-specific code. +// Simply defining a new parameter and associating it with a Configuration +// will allow it to be configured by the user. +// +// If no Configuration is specified when creating a Parameter, then the +// global Configuration will be assumed. +// +// Configurations can be "chained" into groups. Each group has a root +// Configuration, a pointer to which should be passed to the constructors +// of the other group members. set() and get() operations called on the +// root will iterate through all of the group's members. +// +// NB: On platforms that support Threading, locking is performed to protect +// complex parameter types from concurrent access (e.g. strings). +// NB: NO LOCKING is performed when linking Configurations to groups +// or when adding Parameters to Configurations. + +#ifndef __CORE_CONFIGURATION_H__ +#define __CORE_CONFIGURATION_H__ + +#include <limits.h> +#include <stdint.h> + +#include <list> +#include <set> +#include <string> +#include <vector> + +namespace core { + + class VoidParameter; + + // -=- Configuration + // Class used to access parameters. + + class Configuration { + public: + // - Create a new Configuration object + Configuration() {} + + // - Set named parameter to value + bool set(const char* param, const char* value, bool immutable=false); + + // - Get named parameter + VoidParameter* get(const char* param); + + // - List the parameters of this Configuration group + void list(int width=79, int nameWidth=10); + + // - Remove a parameter from this Configuration group + bool remove(const char* param); + + // - handleArg + // Parse a command line argument into a parameter, returning how + // many arguments were consumed + int handleArg(int argc, char* argv[], int index); + + + // - Iterate over all parameters + std::list<VoidParameter*>::iterator begin() { return params.begin(); } + std::list<VoidParameter*>::iterator end() { return params.end(); } + + // - Returns the number of parameters + int size() { return params.size(); } + + // - Get the Global Configuration object + // NB: This call does NOT lock the Configuration system. + // ALWAYS ensure that if you have ANY global Parameters, + // then they are defined as global objects, to ensure that + // global() is called when only the main thread is running. + static Configuration* global(); + + // - Container for process-wide Global parameters + static bool setParam(const char* param, const char* value, bool immutable=false) { + return global()->set(param, value, immutable); + } + static VoidParameter* getParam(const char* param) { return global()->get(param); } + static void listParams(int width=79, int nameWidth=10) { + global()->list(width, nameWidth); + } + static bool removeParam(const char* param) { + return global()->remove(param); + } + static int handleParamArg(int argc, char* argv[], int index) { + return global()->handleArg(argc, argv, index); + } + + private: + friend class VoidParameter; + + // - List of Parameters + std::list<VoidParameter*> params; + + // The process-wide, Global Configuration object + static Configuration* global_; + }; + + // -=- VoidParameter + // Configuration parameter base-class. + + class VoidParameter { + public: + VoidParameter(const char* name_, const char* desc_); + virtual ~VoidParameter(); + const char* getName() const; + const char* getDescription() const; + + virtual bool setParam(const char* value) = 0; + virtual bool setParam(); + virtual std::string getDefaultStr() const = 0; + virtual std::string getValueStr() const = 0; + + virtual bool isDefault() const; + + virtual void setImmutable(); + + protected: + friend class Configuration; + + VoidParameter* _next; + bool immutable; + const char* name; + const char* description; + }; + + class AliasParameter : public VoidParameter { + public: + AliasParameter(const char* name_, const char* desc_,VoidParameter* param_); + bool setParam(const char* value) override; + bool setParam() override; + std::string getDefaultStr() const override; + std::string getValueStr() const override; + void setImmutable() override; + private: + VoidParameter* param; + }; + + class BoolParameter : public VoidParameter { + public: + BoolParameter(const char* name_, const char* desc_, bool v); + bool setParam(const char* value) override; + bool setParam() override; + virtual void setParam(bool b); + std::string getDefaultStr() const override; + std::string getValueStr() const override; + operator bool() const; + protected: + bool value; + bool def_value; + }; + + class IntParameter : public VoidParameter { + public: + IntParameter(const char* name_, const char* desc_, int v, + int minValue=INT_MIN, int maxValue=INT_MAX); + using VoidParameter::setParam; + bool setParam(const char* value) override; + virtual bool setParam(int v); + std::string getDefaultStr() const override; + std::string getValueStr() const override; + operator int() const; + protected: + int value; + int def_value; + int minValue, maxValue; + }; + + class StringParameter : public VoidParameter { + public: + StringParameter(const char* name_, const char* desc_, const char* v); + bool setParam(const char* value) override; + std::string getDefaultStr() const override; + std::string getValueStr() const override; + operator const char*() const; + protected: + std::string value; + std::string def_value; + }; + + class EnumParameter : public VoidParameter { + public: + EnumParameter(const char* name_, const char* desc_, + const std::set<const char*>& enums, const char* v); + bool setParam(const char* value) override; + std::string getDefaultStr() const override; + std::string getValueStr() const override; + bool operator==(const char* other) const; + bool operator==(const std::string& other) const; + bool operator!=(const char* other) const; + bool operator!=(const std::string& other) const; + // operator const char*() omitted on purpose to force usage of above + // comparison operators + protected: + std::string value; + std::string def_value; + std::set<std::string> enums; + }; + + class BinaryParameter : public VoidParameter { + public: + BinaryParameter(const char* name_, const char* desc_, + const uint8_t* v, size_t l); + using VoidParameter::setParam; + ~BinaryParameter() override; + bool setParam(const char* value) override; + virtual void setParam(const uint8_t* v, size_t l); + std::string getDefaultStr() const override; + std::string getValueStr() const override; + + std::vector<uint8_t> getData() const; + + protected: + uint8_t* value; + size_t length; + uint8_t* def_value; + size_t def_length; + }; + + template<typename ValueType> + class ListParameter : public VoidParameter { + public: + typedef std::list<ValueType> ListType; + typedef typename ListType::const_iterator const_iterator; + + ListParameter(const char* name_, const char* desc_, + const ListType& v); + using VoidParameter::setParam; + bool setParam(const char* value) override; + virtual bool setParam(const ListType& v); + std::string getDefaultStr() const override; + std::string getValueStr() const override; + + const_iterator begin() const; + const_iterator end() const; + + protected: + virtual bool decodeEntry(const char* entry, ValueType* out) const = 0; + virtual std::string encodeEntry(const ValueType& entry) const = 0; + virtual bool validateEntry(const ValueType& entry) const; + virtual ValueType normaliseEntry(const ValueType& entry) const; + + protected: + ListType value; + ListType def_value; + }; + + class IntListParameter : public ListParameter<int> { + public: + IntListParameter(const char* name_, const char* desc_, + const ListType& v, + int minValue=INT_MIN, int maxValue=INT_MAX); + protected: + bool decodeEntry(const char* entry, int* out) const override; + std::string encodeEntry(const int& entry) const override; + bool validateEntry(const int& entry) const override; + + protected: + int minValue, maxValue; + }; + + class StringListParameter : public ListParameter<std::string> { + public: + StringListParameter(const char* name_, const char* desc_, + const std::list<const char*>& v); + + class const_iterator : public ListType::const_iterator { + public: + const_iterator(const ListType::const_iterator& it) : ListType::const_iterator(it) {} + const char* operator*() const { return (ListType::const_iterator::operator*()).c_str(); } + }; + + const_iterator begin() const; + const_iterator end() const; + + protected: + bool decodeEntry(const char* entry, std::string* out) const override; + std::string encodeEntry(const std::string& entry) const override; + }; + + class EnumListEntry { + public: + EnumListEntry(const std::string& v); + std::string getValueStr() const; + bool operator==(const char* other) const; + bool operator==(const std::string& other) const; + bool operator!=(const char* other) const; + bool operator!=(const std::string& other) const; + // operator const char*() omitted on purpose to force usage of above + // comparison operators + protected: + std::string value; + }; + + class EnumListParameter : public ListParameter<std::string> { + public: + EnumListParameter(const char* name_, const char* desc_, + const std::set<const char*>& enums, + const std::list<const char*>& v); + + class const_iterator : public ListType::const_iterator { + public: + const_iterator(const ListType::const_iterator& it) : ListType::const_iterator(it) {} + const EnumListEntry operator*() const { return EnumListEntry(ListType::const_iterator::operator*()); } + const EnumListEntry operator->() const { return EnumListEntry(*ListType::const_iterator::operator->()); } + }; + + const_iterator begin() const; + const_iterator end() const; + + protected: + bool decodeEntry(const char* entry, std::string* out) const override; + std::string encodeEntry(const std::string& entry) const override; + bool validateEntry(const std::string& entry) const override; + std::string normaliseEntry(const std::string& entry) const override; + + protected: + std::set<std::string> enums; + }; + +}; + +#endif // __CORE_CONFIGURATION_H__ diff --git a/common/core/Exception.cxx b/common/core/Exception.cxx new file mode 100644 index 00000000..5b4f0599 --- /dev/null +++ b/common/core/Exception.cxx @@ -0,0 +1,134 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2004 Red Hat Inc. + * Copyright (C) 2010 TigerVNC Team + * Copyright 2014-2024 Pierre Ossman for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdarg.h> + +#include <core/Exception.h> +#include <core/string.h> + +#ifdef _WIN32 +#include <winsock2.h> +#include <windows.h> +#include <ws2tcpip.h> +#else +#include <netdb.h> +#endif + +#include <string.h> + +using namespace core; + + +getaddrinfo_error::getaddrinfo_error(const char* s, int err_) noexcept + : std::runtime_error(core::format("%s: %s (%d)", s, + strerror(err_).c_str(), err_)), + err(err_) +{ +} + +getaddrinfo_error::getaddrinfo_error(const std::string& s, + int err_) noexcept + : std::runtime_error(core::format("%s: %s (%d)", s.c_str(), + strerror(err_).c_str(), err_)), + err(err_) +{ +} + +std::string getaddrinfo_error::strerror(int err_) const noexcept +{ +#ifdef _WIN32 + char str[256]; + + WideCharToMultiByte(CP_UTF8, 0, gai_strerrorW(err_), -1, str, + sizeof(str), nullptr, nullptr); + + return str; +#else + return gai_strerror(err_); +#endif +} + +posix_error::posix_error(const char* what_arg, int err_) noexcept + : std::runtime_error(core::format("%s: %s (%d)", what_arg, + strerror(err_).c_str(), err_)), + err(err_) +{ +} + +posix_error::posix_error(const std::string& what_arg, int err_) noexcept + : std::runtime_error(core::format("%s: %s (%d)", what_arg.c_str(), + strerror(err_).c_str(), err_)), + err(err_) +{ +} + +std::string posix_error::strerror(int err_) const noexcept +{ +#ifdef _WIN32 + char str[256]; + + WideCharToMultiByte(CP_UTF8, 0, _wcserror(err_), -1, str, + sizeof(str), nullptr, nullptr); + + return str; +#else + return ::strerror(err_); +#endif +} + +#ifdef WIN32 +win32_error::win32_error(const char* what_arg, unsigned err_) noexcept + : std::runtime_error(core::format("%s: %s (%d)", what_arg, + strerror(err_).c_str(), err_)), + err(err_) +{ +} + +win32_error::win32_error(const std::string& what_arg, + unsigned err_) noexcept + : std::runtime_error(core::format("%s: %s (%d)", what_arg.c_str(), + strerror(err_).c_str(), err_)), + err(err_) +{ +} + +std::string win32_error::strerror(unsigned err_) const noexcept +{ + wchar_t wstr[256]; + char str[256]; + + FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, err_, 0, wstr, sizeof(wstr), nullptr); + WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, + sizeof(str), nullptr, nullptr); + + int l = strlen(str); + if ((l >= 2) && (str[l-2] == '\r') && (str[l-1] == '\n')) + str[l-2] = 0; + + return str; +} +#endif diff --git a/common/core/Exception.h b/common/core/Exception.h new file mode 100644 index 00000000..04463a17 --- /dev/null +++ b/common/core/Exception.h @@ -0,0 +1,75 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2004 Red Hat Inc. + * Copyright (C) 2010 TigerVNC Team + * Copyright 2015-2024 Pierre Ossman for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __CORE_EXCEPTION_H__ +#define __CORE_EXCEPTION_H__ + +#include <stdexcept> +#include <string> + +namespace core { + + class posix_error : public std::runtime_error { + public: + int err; + posix_error(const char* what_arg, int err_) noexcept; + posix_error(const std::string& what_arg, int err_) noexcept; + private: + std::string strerror(int err_) const noexcept; + }; + +#ifdef WIN32 + class win32_error : public std::runtime_error { + public: + unsigned err; + win32_error(const char* what_arg, unsigned err_) noexcept; + win32_error(const std::string& what_arg, unsigned err_) noexcept; + private: + std::string strerror(unsigned err_) const noexcept; + }; +#endif + +#ifdef WIN32 + class socket_error : public win32_error { + public: + socket_error(const char* what_arg, unsigned err_) noexcept : win32_error(what_arg, err_) {} + socket_error(const std::string& what_arg, unsigned err_) noexcept : win32_error(what_arg, err_) {} + }; +#else + class socket_error : public posix_error { + public: + socket_error(const char* what_arg, unsigned err_) noexcept : posix_error(what_arg, err_) {} + socket_error(const std::string& what_arg, unsigned err_) noexcept : posix_error(what_arg, err_) {} + }; +#endif + + class getaddrinfo_error : public std::runtime_error { + public: + int err; + getaddrinfo_error(const char* s, int err_) noexcept; + getaddrinfo_error(const std::string& s, int err_) noexcept; + private: + std::string strerror(int err_) const noexcept; + }; + +} + +#endif diff --git a/common/core/LogWriter.cxx b/common/core/LogWriter.cxx new file mode 100644 index 00000000..98e5201c --- /dev/null +++ b/common/core/LogWriter.cxx @@ -0,0 +1,135 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- LogWriter.cxx - client-side logging interface + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <string.h> + +#include <core/Configuration.h> +#include <core/LogWriter.h> +#include <core/string.h> + +using namespace core; + +LogParameter core::logParams; + +LogWriter::LogWriter(const char* name) + : m_name(name), m_level(0), m_log(nullptr), m_next(log_writers) { + log_writers = this; +} + +LogWriter::~LogWriter() { + // *** Should remove this logger here! +} + +void LogWriter::setLog(Logger *logger) { + m_log = logger; +} + +void LogWriter::setLevel(int level) { + m_level = level; +} + +void +LogWriter::listLogWriters(int /*width*/) { + // *** make this respect width... + LogWriter* current = log_writers; + fprintf(stderr, " "); + while (current) { + fprintf(stderr, "%s", current->m_name); + current = current->m_next; + if (current) fprintf(stderr, ", "); + } + fprintf(stderr, "\n"); +} + +LogWriter* LogWriter::log_writers; + +LogWriter* +LogWriter::getLogWriter(const char* name) { + LogWriter* current = log_writers; + while (current) { + if (strcasecmp(name, current->m_name) == 0) return current; + current = current->m_next; + } + return nullptr; +} + +bool LogWriter::setLogParams(const char* params) { + std::vector<std::string> parts; + parts = split(params, ':'); + if (parts.size() != 3) { + fprintf(stderr, "Failed to parse log params:%s\n",params); + return false; + } + int level = atoi(parts[2].c_str()); + Logger* logger = nullptr; + if (!parts[1].empty()) { + logger = Logger::getLogger(parts[1].c_str()); + if (!logger) + fprintf(stderr, "No logger found! %s\n", parts[1].c_str()); + } + if (parts[0] == "*") { + LogWriter* current = log_writers; + while (current) { + current->setLog(logger); + current->setLevel(level); + current = current->m_next; + } + return true; + } else { + LogWriter* logwriter = getLogWriter(parts[0].c_str()); + if (!logwriter) { + fprintf(stderr, "No logwriter found! %s\n", parts[0].c_str()); + } else { + logwriter->setLog(logger); + logwriter->setLevel(level); + return true; + } + } + return false; +} + + +LogParameter::LogParameter() + : StringListParameter("Log", + "Specifies which log output should be directed to " + "which target logger, and the level of output to log. " + "Format is <log>:<target>:<level>[, ...].", + {}) +{ +} + +bool LogParameter::setParam(const char* v) { + if (immutable) return true; + LogWriter::setLogParams("*::0"); + if (!StringListParameter::setParam(v)) + return false; + for (const char* part : *this) { + if (part[0] == '\0') + continue; + if (!LogWriter::setLogParams(part)) + return false; + } + return true; +} diff --git a/common/core/LogWriter.h b/common/core/LogWriter.h new file mode 100644 index 00000000..58dcc063 --- /dev/null +++ b/common/core/LogWriter.h @@ -0,0 +1,114 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- LogWriter.h - The Log writer class. + +#ifndef __CORE_LOG_WRITER_H__ +#define __CORE_LOG_WRITER_H__ + +#include <stdarg.h> + +#include <core/Configuration.h> +#include <core/Logger.h> + +// Each log writer instance has a unique textual name, +// and is attached to a particular Log instance and +// is assigned a particular log level. + +#define DEF_LOGFUNCTION(name, level) \ + inline void v##name(const char* fmt, va_list ap) \ + __attribute__((__format__ (__printf__, 2, 0))) \ + { \ + if (m_log && (level <= m_level)) \ + m_log->write(level, m_name, fmt, ap); \ + } \ + inline void name(const char* fmt, ...) \ + __attribute__((__format__ (__printf__, 2, 3))) \ + { \ + if (m_log && (level <= m_level)) { \ + va_list ap; va_start(ap, fmt); \ + m_log->write(level, m_name, fmt, ap);\ + va_end(ap); \ + } \ + } + +namespace core { + + class LogWriter { + public: + LogWriter(const char* name); + ~LogWriter(); + + const char *getName() {return m_name;} + + void setLog(Logger *logger); + void setLevel(int level); + int getLevel(void) { return m_level; } + + inline void write(int level, const char* format, ...) + __attribute__((__format__ (__printf__, 3, 4))) + { + if (m_log && (level <= m_level)) { + va_list ap; + va_start(ap, format); + m_log->write(level, m_name, format, ap); + va_end(ap); + } + } + + static const int LEVEL_ERROR = 0; + static const int LEVEL_STATUS = 10; + static const int LEVEL_INFO = 30; + static const int LEVEL_DEBUG = 100; + + DEF_LOGFUNCTION(error, LEVEL_ERROR) + DEF_LOGFUNCTION(status, LEVEL_STATUS) + DEF_LOGFUNCTION(info, LEVEL_INFO) + DEF_LOGFUNCTION(debug, LEVEL_DEBUG) + + // -=- DIAGNOSTIC & HELPER ROUTINES + + static void listLogWriters(int width=79); + + // -=- CLASS FIELDS & FUNCTIONS + + static LogWriter* log_writers; + + static LogWriter* getLogWriter(const char* name); + + static bool setLogParams(const char* params); + + private: + const char* m_name; + int m_level; + Logger* m_log; + LogWriter* m_next; + }; + + class LogParameter : public StringListParameter { + public: + LogParameter(); + bool setParam(const char* v) override; + // We explicitly don't inherit setParam(std::list) as we want to + // force callers to use the above method to parse the values + }; + extern LogParameter logParams; + +}; + +#endif // __CORE_LOG_WRITER_H__ diff --git a/common/core/Logger.cxx b/common/core/Logger.cxx new file mode 100644 index 00000000..ad9a3d63 --- /dev/null +++ b/common/core/Logger.cxx @@ -0,0 +1,95 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Logger.cxx - support for the Logger and LogWriter classes + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +#include <core/Logger.h> +#include <core/LogWriter.h> + +using namespace core; + +Logger* Logger::loggers = nullptr; + +Logger::Logger(const char* name) + : registered(false), m_name(name), m_next(nullptr) +{ +} + +Logger::~Logger() { + // *** Should remove this logger here! +} + +void Logger::write(int level, const char *logname, const char* format, + va_list ap) +{ + // - Format the supplied data, and pass it to the + // actual log_message function + // The log level is included as a hint for loggers capable of representing + // different log levels in some way. + char buf1[4096]; + vsnprintf(buf1, sizeof(buf1)-1, format, ap); + buf1[sizeof(buf1)-1] = 0; + char *buf = buf1; + while (true) { + char *end = strchr(buf, '\n'); + if (end) + *end = '\0'; + write(level, logname, buf); + if (!end) + break; + buf = end + 1; + } +} + +void +Logger::registerLogger() { + if (!registered) { + registered = true; + m_next = loggers; + loggers=this; + } +} + +Logger* +Logger::getLogger(const char* name) { + Logger* current = loggers; + while (current) { + if (strcasecmp(name, current->m_name) == 0) return current; + current = current->m_next; + } + return nullptr; +} + +void +Logger::listLoggers() { + Logger* current = loggers; + while (current) { + printf(" %s\n", current->m_name); + current = current->m_next; + } +} + + diff --git a/common/core/Logger.h b/common/core/Logger.h new file mode 100644 index 00000000..5291915f --- /dev/null +++ b/common/core/Logger.h @@ -0,0 +1,71 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Logger.h - The Logger class. + +#ifndef __CORE_LOGGER_H__ +#define __CORE_LOGGER_H__ + +#include <stdarg.h> +#include <stdio.h> + +// Each log writer instance has a unique textual name, +// and is attached to a particular Logger instance and +// is assigned a particular log level. + +namespace core { + + class Logger { + public: + + // -=- Create / Destroy a logger + + Logger(const char* name); + virtual ~Logger(); + + // -=- Get the name of a logger + + const char *getName() {return m_name;} + + // -=- Write data to a log + + virtual void write(int level, const char *logname, const char *text) = 0; + void write(int level, const char *logname, const char* format, va_list ap) + __attribute__((__format__ (__printf__, 4, 0))); + + // -=- Register a logger + + void registerLogger(); + + // -=- CLASS FIELDS & FUNCTIONS + + static Logger* loggers; + + static Logger* getLogger(const char* name); + + static void listLoggers(); + + private: + bool registered; + const char *m_name; + Logger *m_next; + }; + +}; + +#endif // __CORE_LOGGER_H__ diff --git a/common/core/Logger_file.cxx b/common/core/Logger_file.cxx new file mode 100644 index 00000000..b8c72270 --- /dev/null +++ b/common/core/Logger_file.cxx @@ -0,0 +1,123 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Logger_file.cxx - Logger instance for a file + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <limits.h> +#include <stdlib.h> +#include <string.h> + +#include <core/Logger_file.h> + +using namespace core; + +Logger_File::Logger_File(const char* loggerName) + : Logger(loggerName), indent(13), width(79), m_file(nullptr), + m_lastLogTime(0) +{ + m_filename[0] = '\0'; +} + +Logger_File::~Logger_File() +{ + closeFile(); +} + +void Logger_File::write(int /*level*/, const char *logname, const char *message) +{ + if (!m_file) { + if (m_filename[0] == '\0') + return; + char bakFilename[PATH_MAX]; + if (snprintf(bakFilename, sizeof(bakFilename), + "%s.bak", m_filename) >= (int)sizeof(bakFilename)) { + remove(m_filename); + } else { + remove(bakFilename); + rename(m_filename, bakFilename); + } + m_file = fopen(m_filename, "w+"); + if (!m_file) return; + } + + time_t current = time(nullptr); + if (current != m_lastLogTime) { + m_lastLogTime = current; + fprintf(m_file, "\n%s", ctime(&m_lastLogTime)); + } + + fprintf(m_file," %s:", logname); + int column = strlen(logname) + 2; + if (column < indent) { + fprintf(m_file,"%*s",indent-column,""); + column = indent; + } + while (true) { + const char* s = strchr(message, ' '); + int wordLen; + if (s) wordLen = s-message; + else wordLen = strlen(message); + + if (column + wordLen + 1 > width) { + fprintf(m_file,"\n%*s",indent,""); + column = indent; + } + fprintf(m_file," %.*s",wordLen,message); + column += wordLen + 1; + message += wordLen + 1; + if (!s) break; + } + fprintf(m_file,"\n"); + fflush(m_file); +} + +void Logger_File::setFilename(const char* filename) +{ + closeFile(); + m_filename[0] = '\0'; + if (strlen(filename) >= sizeof(m_filename)) + return; + strcpy(m_filename, filename); +} + +void Logger_File::setFile(FILE* file) +{ + closeFile(); + m_file = file; +} + +void Logger_File::closeFile() +{ + if (m_file) { + fclose(m_file); + m_file = nullptr; + } +} + +static Logger_File logger("file"); + +bool core::initFileLogger(const char* filename) +{ + logger.setFilename(filename); + logger.registerLogger(); + return true; +} diff --git a/common/core/Logger_file.h b/common/core/Logger_file.h new file mode 100644 index 00000000..6bce3e2d --- /dev/null +++ b/common/core/Logger_file.h @@ -0,0 +1,53 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Logger_file - log to a file + +#ifndef __CORE_LOGGER_FILE_H__ +#define __CORE_LOGGER_FILE_H__ + +#include <time.h> +#include <limits.h> + +#include <core/Logger.h> + +namespace core { + + class Logger_File : public Logger { + public: + Logger_File(const char* loggerName); + ~Logger_File(); + + void write(int level, const char *logname, const char *message) override; + void setFilename(const char* filename); + void setFile(FILE* file); + + int indent; + int width; + + protected: + void closeFile(); + char m_filename[PATH_MAX]; + FILE* m_file; + time_t m_lastLogTime; + }; + + bool initFileLogger(const char* filename); +}; + +#endif diff --git a/common/core/Logger_stdio.cxx b/common/core/Logger_stdio.cxx new file mode 100644 index 00000000..f27fc35d --- /dev/null +++ b/common/core/Logger_stdio.cxx @@ -0,0 +1,37 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Logger_stdio.cxx - Logger instances for stderr and stdout + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <core/Logger_stdio.h> + +using namespace core; + +static Logger_StdIO logStdErr("stderr", stderr); +static Logger_StdIO logStdOut("stdout", stdout); + +bool core::initStdIOLoggers() +{ + logStdErr.registerLogger(); + logStdOut.registerLogger(); + return true; +} diff --git a/common/core/Logger_stdio.h b/common/core/Logger_stdio.h new file mode 100644 index 00000000..7613a20b --- /dev/null +++ b/common/core/Logger_stdio.h @@ -0,0 +1,39 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Logger_stdio - standard output logger instances + +#ifndef __CORE_LOGGER_STDIO_H__ +#define __CORE_LOGGER_STDIO_H__ + +#include <core/Logger_file.h> + +namespace core { + + class Logger_StdIO : public Logger_File { + public: + Logger_StdIO(const char *name, FILE* file) : Logger_File(name) { + setFile(file); + } + }; + + bool initStdIOLoggers(); + +}; + +#endif diff --git a/common/core/Logger_syslog.cxx b/common/core/Logger_syslog.cxx new file mode 100644 index 00000000..cf7d065d --- /dev/null +++ b/common/core/Logger_syslog.cxx @@ -0,0 +1,68 @@ +/* Copyright (C) 2015 TigerVNC + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Logger_syslog.cxx - Logger instance for a syslog + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include <syslog.h> + +#include <core/Logger_syslog.h> +#include <core/LogWriter.h> + +using namespace core; + + +Logger_Syslog::Logger_Syslog(const char* loggerName) + : Logger(loggerName) +{ + openlog(nullptr, LOG_CONS | LOG_PID, LOG_USER); +} + +Logger_Syslog::~Logger_Syslog() +{ + closelog(); +} + +void Logger_Syslog::write(int level, const char *logname, const char *message) +{ + // Convert our priority level into syslog level + int priority; + if (level >= LogWriter::LEVEL_DEBUG) { + priority = LOG_DEBUG; + } else if (level >= LogWriter::LEVEL_INFO) { + priority = LOG_INFO; + } else if (level >= LogWriter::LEVEL_STATUS) { + priority = LOG_NOTICE; + } else { + priority = LOG_ERR; + } + + syslog(priority, "%s: %s", logname, message); +} + +static Logger_Syslog logger("syslog"); + +void core::initSyslogLogger() +{ + logger.registerLogger(); +} diff --git a/common/core/Logger_syslog.h b/common/core/Logger_syslog.h new file mode 100644 index 00000000..46adf932 --- /dev/null +++ b/common/core/Logger_syslog.h @@ -0,0 +1,41 @@ +/* Copyright (C) 2015 TigerVNC + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Logger_syslog - log to syslog + +#ifndef __CORE_LOGGER_SYSLOG_H__ +#define __CORE_LOGGER_SYSLOG_H__ + +#include <time.h> + +#include <core/Logger.h> + +namespace core { + + class Logger_Syslog : public Logger { + public: + Logger_Syslog(const char* loggerName); + virtual ~Logger_Syslog(); + + void write(int level, const char *logname, const char *message) override; + }; + + void initSyslogLogger(); +}; + +#endif diff --git a/common/core/Rect.h b/common/core/Rect.h new file mode 100644 index 00000000..e4cb1634 --- /dev/null +++ b/common/core/Rect.h @@ -0,0 +1,120 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// core::Rect and core::Point structures + +#ifndef __CORE_RECT_INCLUDED__ +#define __CORE_RECT_INCLUDED__ + +#include <algorithm> + +namespace core { + + // core::Point + // + // Represents a point in 2D space, by X and Y coordinates. + // Can also be used to represent a delta, or offset, between + // two Points. + // Functions are provided to allow Points to be compared for + // equality and translated by a supplied offset. + // Functions are also provided to negate offset Points. + + struct Point { + Point() : x(0), y(0) {} + Point(int x_, int y_) : x(x_), y(y_) {} + inline Point negate() const + __attribute__ ((warn_unused_result)) + {return Point(-x, -y);} + inline bool operator==(const Point &p) const {return x==p.x && y==p.y;} + inline bool operator!=(const Point &p) const {return x!=p.x || y!=p.y;} + inline Point translate(const Point &p) const + __attribute__ ((warn_unused_result)) + {return Point(x+p.x, y+p.y);} + inline Point subtract(const Point &p) const + __attribute__ ((warn_unused_result)) + {return Point(x-p.x, y-p.y);} + int x, y; + }; + + // core::Rect + // + // Represents a rectangular region defined by its top-left (tl) + // and bottom-right (br) Points. + // Rects may be compared for equality, checked to determine whether + // or not they are empty, cleared (made empty), or intersected with + // one another. The bounding rectangle of two existing Rects + // may be calculated, as may the area of a Rect. + // Rects may also be translated, in the same way as Points, by + // an offset specified in a Point structure. + + struct Rect { + Rect() {} + Rect(Point tl_, Point br_) : tl(tl_), br(br_) {} + Rect(int x1, int y1, int x2, int y2) : tl(x1, y1), br(x2, y2) {} + inline void setXYWH(int x, int y, int w, int h) { + tl.x = x; tl.y = y; br.x = x+w; br.y = y+h; + } + inline Rect intersect(const Rect &r) const + __attribute__ ((warn_unused_result)) + { + Rect result; + result.tl.x = std::max(tl.x, r.tl.x); + result.tl.y = std::max(tl.y, r.tl.y); + result.br.x = std::max(std::min(br.x, r.br.x), result.tl.x); + result.br.y = std::max(std::min(br.y, r.br.y), result.tl.y); + return result; + } + inline Rect union_boundary(const Rect &r) const + __attribute__ ((warn_unused_result)) + { + if (r.is_empty()) return *this; + if (is_empty()) return r; + Rect result; + result.tl.x = std::min(tl.x, r.tl.x); + result.tl.y = std::min(tl.y, r.tl.y); + result.br.x = std::max(br.x, r.br.x); + result.br.y = std::max(br.y, r.br.y); + return result; + } + inline Rect translate(const Point &p) const + __attribute__ ((warn_unused_result)) + { + return Rect(tl.translate(p), br.translate(p)); + } + inline bool operator==(const Rect &r) const {return r.tl == tl && r.br == br;} + inline bool operator!=(const Rect &r) const {return r.tl != tl || r.br != br;} + inline bool is_empty() const {return (tl.x >= br.x) || (tl.y >= br.y);} + inline void clear() {tl = Point(); br = Point();} + inline bool enclosed_by(const Rect &r) const { + return (tl.x>=r.tl.x) && (tl.y>=r.tl.y) && (br.x<=r.br.x) && (br.y<=r.br.y); + } + inline bool overlaps(const Rect &r) const { + return tl.x < r.br.x && tl.y < r.br.y && br.x > r.tl.x && br.y > r.tl.y; + } + inline int area() const {return is_empty() ? 0 : (br.x-tl.x)*(br.y-tl.y);} + inline Point dimensions() const {return Point(width(), height());} + inline int width() const {return br.x-tl.x;} + inline int height() const {return br.y-tl.y;} + inline bool contains(const Point &p) const { + return (tl.x<=p.x) && (tl.y<=p.y) && (br.x>p.x) && (br.y>p.y); + } + Point tl; + Point br; + }; +} +#endif // __CORE_RECT_INCLUDED__ diff --git a/common/core/Region.cxx b/common/core/Region.cxx new file mode 100644 index 00000000..03d788a9 --- /dev/null +++ b/common/core/Region.cxx @@ -0,0 +1,203 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2016-2020 Pierre Ossman for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <core/LogWriter.h> +#include <core/Region.h> + +extern "C" { +#include <pixman.h> +} + +using namespace core; + +static LogWriter vlog("Region"); + +Region::Region() +{ + rgn = new struct pixman_region16; + pixman_region_init(rgn); +} + +Region::Region(const Rect& r) +{ + rgn = new struct pixman_region16; + pixman_region_init_rect(rgn, r.tl.x, r.tl.y, r.width(), r.height()); +} + +Region::Region(const Region& r) +{ + rgn = new struct pixman_region16; + pixman_region_init(rgn); + pixman_region_copy(rgn, r.rgn); +} + +Region::~Region() +{ + pixman_region_fini(rgn); + delete rgn; +} + +Region& Region::operator=(const Region& r) +{ + pixman_region_copy(rgn, r.rgn); + return *this; +} + +void Region::clear() +{ + // pixman_region_clear() isn't available on some older systems + pixman_region_fini(rgn); + pixman_region_init(rgn); +} + +void Region::reset(const Rect& r) +{ + pixman_region_fini(rgn); + pixman_region_init_rect(rgn, r.tl.x, r.tl.y, r.width(), r.height()); +} + +void Region::translate(const Point& delta) +{ + pixman_region_translate(rgn, delta.x, delta.y); +} + +void Region::assign_intersect(const Region& r) +{ + pixman_region_intersect(rgn, rgn, r.rgn); +} + +void Region::assign_union(const Region& r) +{ + pixman_region_union(rgn, rgn, r.rgn); +} + +void Region::assign_subtract(const Region& r) +{ + pixman_region_subtract(rgn, rgn, r.rgn); +} + +Region Region::intersect(const Region& r) const +{ + Region ret; + pixman_region_intersect(ret.rgn, rgn, r.rgn); + return ret; +} + +Region Region::union_(const Region& r) const +{ + Region ret; + pixman_region_union(ret.rgn, rgn, r.rgn); + return ret; +} + +Region Region::subtract(const Region& r) const +{ + Region ret; + pixman_region_subtract(ret.rgn, rgn, r.rgn); + return ret; +} + +bool Region::operator==(const Region& r) const +{ + return pixman_region_equal(rgn, r.rgn); +} + +bool Region::operator!=(const Region& r) const +{ + return !pixman_region_equal(rgn, r.rgn); +} + +int Region::numRects() const +{ + return pixman_region_n_rects(rgn); +} + +bool Region::get_rects(std::vector<Rect>* rects, + bool left2right, bool topdown) const +{ + int nRects; + const pixman_box16_t* boxes; + int xInc, yInc, i; + + boxes = pixman_region_rectangles(rgn, &nRects); + + rects->clear(); + rects->reserve(nRects); + + xInc = left2right ? 1 : -1; + yInc = topdown ? 1 : -1; + i = topdown ? 0 : nRects-1; + + while (nRects > 0) { + int firstInNextBand = i; + int nRectsInBand = 0; + + while (nRects > 0 && boxes[firstInNextBand].y1 == boxes[i].y1) + { + firstInNextBand += yInc; + nRects--; + nRectsInBand++; + } + + if (xInc != yInc) + i = firstInNextBand - yInc; + + while (nRectsInBand > 0) { + Rect r(boxes[i].x1, boxes[i].y1, boxes[i].x2, boxes[i].y2); + rects->push_back(r); + i += xInc; + nRectsInBand--; + } + + i = firstInNextBand; + } + + return !rects->empty(); +} + +Rect Region::get_bounding_rect() const +{ + const pixman_box16_t* extents; + extents = pixman_region_extents(rgn); + return Rect(extents->x1, extents->y1, extents->x2, extents->y2); +} + + +void Region::debug_print(const char* prefix) const +{ + Rect extents; + std::vector<Rect> rects; + std::vector<Rect>::const_iterator iter; + + extents = get_bounding_rect(); + get_rects(&rects); + + vlog.debug("%s num rects %3ld extents %3d,%3d %3dx%3d", + prefix, (long)rects.size(), extents.tl.x, extents.tl.y, + extents.width(), extents.height()); + + for (iter = rects.begin(); iter != rects.end(); ++iter) { + vlog.debug(" rect %3d,%3d %3dx%3d", + iter->tl.x, iter->tl.y, iter->width(), iter->height()); + } +} diff --git a/common/core/Region.h b/common/core/Region.h new file mode 100644 index 00000000..729f1475 --- /dev/null +++ b/common/core/Region.h @@ -0,0 +1,85 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2016-2020 Pierre Ossman for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// Region class wrapper around pixman's region operations + +#ifndef __CORE_REGION_INCLUDED__ +#define __CORE_REGION_INCLUDED__ + +#include <vector> + +#include <core/Rect.h> + +struct pixman_region16; + +namespace core { + + struct Point; + struct Rect; + + class Region { + public: + // Create an empty region + Region(); + // Create a rectangular region + Region(const Rect& r); + + Region(const Region& r); + Region &operator=(const Region& src); + + ~Region(); + + // the following methods alter the region in place: + + void clear(); + void reset(const Rect& r); + void translate(const Point& delta); + + void assign_intersect(const Region& r); + void assign_union(const Region& r); + void assign_subtract(const Region& r); + + // the following three operations return a new region: + + Region intersect(const Region& r) const + __attribute__ ((warn_unused_result)); + Region union_(const Region& r) const + __attribute__ ((warn_unused_result)); + Region subtract(const Region& r) const + __attribute__ ((warn_unused_result)); + + bool operator==(const Region& b) const; + bool operator!=(const Region& b) const; + int numRects() const; + bool is_empty() const { return numRects() == 0; } + + bool get_rects(std::vector<Rect>* rects, bool left2right=true, + bool topdown=true) const; + Rect get_bounding_rect() const; + + void debug_print(const char *prefix) const; + + protected: + + struct pixman_region16* rgn; + }; + +}; + +#endif // __CORE_REGION_INCLUDED__ diff --git a/common/core/Timer.cxx b/common/core/Timer.cxx new file mode 100644 index 00000000..77e98daf --- /dev/null +++ b/common/core/Timer.cxx @@ -0,0 +1,155 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2016-2024 Pierre Ossman for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- Timer.cxx + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <sys/time.h> + +#include <algorithm> + +#include <core/LogWriter.h> +#include <core/Timer.h> +#include <core/time.h> + +using namespace core; + +#ifndef __NO_DEFINE_VLOG__ +static LogWriter vlog("Timer"); +#endif + +std::list<Timer*> Timer::pending; + +int Timer::checkTimeouts() { + timeval start; + + if (pending.empty()) + return -1; + + gettimeofday(&start, nullptr); + while (pending.front()->isBefore(start)) { + Timer* timer; + + timer = pending.front(); + pending.pop_front(); + + timer->lastDueTime = timer->dueTime; + timer->cb->handleTimeout(timer); + + if (pending.empty()) + return -1; + } + return getNextTimeout(); +} + +int Timer::getNextTimeout() { + timeval now; + gettimeofday(&now, nullptr); + + if (pending.empty()) + return -1; + + int toWait = pending.front()->getRemainingMs(); + + if (toWait > pending.front()->timeoutMs) { + if (toWait - pending.front()->timeoutMs < 1000) { + vlog.info("gettimeofday is broken..."); + return toWait; + } + // Time has jumped backwards! + vlog.info("Time has moved backwards!"); + pending.front()->dueTime = now; + toWait = 0; + } + + return toWait; +} + +void Timer::insertTimer(Timer* t) { + std::list<Timer*>::iterator i; + for (i=pending.begin(); i!=pending.end(); i++) { + if (t->isBefore((*i)->dueTime)) { + pending.insert(i, t); + return; + } + } + pending.push_back(t); +} + +void Timer::start(int timeoutMs_) { + timeval now; + gettimeofday(&now, nullptr); + stop(); + timeoutMs = timeoutMs_; + dueTime = addMillis(now, timeoutMs); + insertTimer(this); +} + +void Timer::repeat(int timeoutMs_) { + timeval now; + + gettimeofday(&now, nullptr); + + if (isStarted()) { + vlog.error("Incorrectly repeating already running timer"); + stop(); + } + + if (msBetween(&lastDueTime, &dueTime) != 0) + vlog.error("Timer incorrectly modified whilst repeating"); + + if (timeoutMs_ != -1) + timeoutMs = timeoutMs_; + + dueTime = addMillis(lastDueTime, timeoutMs); + if (isBefore(now)) { + // Time has jumped forwards, or we're not getting enough + // CPU time for the timers + dueTime = now; + } + + insertTimer(this); +} + +void Timer::stop() { + pending.remove(this); +} + +bool Timer::isStarted() { + return std::find(pending.begin(), pending.end(), + this) != pending.end(); +} + +int Timer::getTimeoutMs() { + return timeoutMs; +} + +int Timer::getRemainingMs() { + return msUntil(&dueTime); +} + +bool Timer::isBefore(timeval other) { + return (dueTime.tv_sec < other.tv_sec) || + ((dueTime.tv_sec == other.tv_sec) && + (dueTime.tv_usec < other.tv_usec)); +} diff --git a/common/core/Timer.h b/common/core/Timer.h new file mode 100644 index 00000000..cde672b2 --- /dev/null +++ b/common/core/Timer.h @@ -0,0 +1,136 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2018-2024 Pierre Ossman for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __CORE_TIMER_H__ +#define __CORE_TIMER_H__ + +#include <list> +#include <sys/time.h> + +namespace core { + + /* Timer + + Cross-platform timeout handling. The caller creates instances of + Timer and passes a Callback implementation to each. The Callback + will then be called with a pointer to the Timer instance that + timed-out when the timeout occurs. + + The static methods of Timer are used by the main loop of the + application both to dispatch elapsed Timer callbacks and to + determine how long to wait in select() for the next timeout to + occur. + + For classes that can be derived it's best to use MethodTimer which + can call a specific method on the class, thus avoiding conflicts + when subclassing. + */ + + struct Timer { + + struct Callback { + // handleTimeout + // Passed a pointer to the Timer that has timed out. If the + // handler returns true then the Timer is reset and left + // running, causing another timeout after the appropriate + // interval. + // If the handler returns false then the Timer is cancelled. + virtual void handleTimeout(Timer* t) = 0; + + virtual ~Callback() {} + }; + + // checkTimeouts() + // Dispatches any elapsed Timers, and returns the number of + // milliseconds until the next Timer will timeout. + static int checkTimeouts(); + + // getNextTimeout() + // Returns the number of milliseconds until the next timeout, + // without dispatching any elapsed Timers. + static int getNextTimeout(); + + // Create a Timer with the specified callback handler + Timer(Callback* cb_) {cb = cb_;} + ~Timer() {stop();} + + // start() + // Starts the timer, causing a timeout after the specified number + // of milliseconds. If the timer is already active then it will + // be implicitly cancelled and re-started. + void start(int timeoutMs_); + + // repeat() + // Restarts the timer in a way that repeats that last timeout. + // This allows you to have a periodic timer without the risk of + // accumulating drift caused by processing delays. + // A new interval can be specified, otherwise the previous + // interval is reused. + void repeat(int timeoutMs_=-1); + + // stop() + // Cancels the timer. + void stop(); + + // isStarted() + // Determines whether the timer is started. + bool isStarted(); + + // getTimeoutMs() + // Determines the previously used timeout value, if any. + // Usually used with isStarted() to get the _current_ timeout. + int getTimeoutMs(); + + // getRemainingMs() + // Determines how many milliseconds are left before the Timer + // will timeout. Only valid for an active timer. + int getRemainingMs(); + + // isBefore() + // Determine whether the Timer will timeout before the specified + // time. + bool isBefore(timeval other); + + protected: + timeval dueTime, lastDueTime; + int timeoutMs; + Callback* cb; + + static void insertTimer(Timer* t); + // The list of currently active Timers, ordered by time left until + // timeout. + static std::list<Timer*> pending; + }; + + template<class T> class MethodTimer + : public Timer, public Timer::Callback { + public: + MethodTimer(T* obj_, void (T::*cb_)(Timer*)) + : Timer(this), obj(obj_), cb(cb_) {} + + void handleTimeout(Timer* t) override { return (obj->*cb)(t); } + + private: + T* obj; + void (T::*cb)(Timer*); + }; + +}; + +#endif diff --git a/common/core/string.cxx b/common/core/string.cxx new file mode 100644 index 00000000..091836db --- /dev/null +++ b/common/core/string.cxx @@ -0,0 +1,650 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011-2023 Pierre Ossman for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +#include <core/string.h> + +namespace core { + + std::string format(const char *fmt, ...) + { + va_list ap; + int len; + char *buf; + std::string out; + + va_start(ap, fmt); + len = vsnprintf(nullptr, 0, fmt, ap); + va_end(ap); + + if (len < 0) + return ""; + + buf = new char[len+1]; + + va_start(ap, fmt); + vsnprintf(buf, len+1, fmt, ap); + va_end(ap); + + out = buf; + + delete [] buf; + + return out; + } + + std::vector<std::string> split(const char* src, + const char delimiter) + { + std::vector<std::string> out; + const char *start, *stop; + + if (src[0] == '\0') + return out; + + start = src; + do { + stop = strchr(start, delimiter); + if (stop == nullptr) { + out.push_back(start); + } else { + out.push_back(std::string(start, stop-start)); + start = stop + 1; + } + } while (stop != nullptr); + + return out; + } + + static char intToHex(uint8_t i) { + if (i<=9) + return '0'+i; + else if ((i>=10) && (i<=15)) + return 'a'+(i-10); + assert(false); + return '\0'; + } + + void binToHex(const uint8_t* in, size_t inlen, + char* out, size_t outlen) { + if (inlen > outlen/2) + inlen = outlen/2; + + if (inlen > 0) { + assert(in); + assert(out); + } + + for (size_t i=0; i<inlen; i++) { + out[i*2] = intToHex((in[i] >> 4) & 15); + out[i*2+1] = intToHex((in[i] & 15)); + } + } + + std::string binToHex(const uint8_t* in, size_t inlen) { + char* buffer = new char[inlen*2+1](); + std::string out; + binToHex(in, inlen, buffer, inlen*2); + out = buffer; + delete [] buffer; + return out; + } + + static bool readHexAndShift(char c, uint8_t* v) { + c=tolower(c); + if ((c >= '0') && (c <= '9')) + *v = (*v << 4) + (c - '0'); + else if ((c >= 'a') && (c <= 'f')) + *v = (*v << 4) + (c - 'a' + 10); + else + return false; + return true; + } + + bool hexToBin(const char* in, size_t inlen, + uint8_t* out, size_t outlen) { + assert(in || inlen == 0); + assert(out || outlen == 0); + + if (inlen & 1) + return false; + + if (inlen > outlen*2) + inlen = outlen*2; + + for(size_t i=0; i<inlen; i+=2) { + uint8_t byte = 0; + if (!readHexAndShift(in[i], &byte) || + !readHexAndShift(in[i+1], &byte)) + return false; + out[i/2] = byte; + } + + return true; + } + + std::vector<uint8_t> hexToBin(const char* in, size_t inlen) { + std::vector<uint8_t> out(inlen/2); + if (!hexToBin(in, inlen, out.data(), inlen/2)) + return std::vector<uint8_t>(); + return out; + } + + std::string convertLF(const char* src, size_t bytes) + { + size_t sz; + std::string out; + + const char* in; + size_t in_len; + + // Compute output size + sz = 0; + in = src; + in_len = bytes; + while ((in_len > 0) && (*in != '\0')) { + if (*in != '\r') { + sz++; + in++; + in_len--; + continue; + } + + if ((in_len < 2) || (*(in+1) != '\n')) + sz++; + + in++; + in_len--; + } + + // Reserve space + out.reserve(sz); + + // And convert + in = src; + in_len = bytes; + while ((in_len > 0) && (*in != '\0')) { + if (*in != '\r') { + out += *in++; + in_len--; + continue; + } + + if ((in_len < 2) || (*(in+1) != '\n')) + out += '\n'; + + in++; + in_len--; + } + + return out; + } + + std::string convertCRLF(const char* src, size_t bytes) + { + std::string out; + size_t sz; + + const char* in; + size_t in_len; + + // Compute output size + sz = 0; + in = src; + in_len = bytes; + while ((in_len > 0) && (*in != '\0')) { + sz++; + + if (*in == '\r') { + if ((in_len < 2) || (*(in+1) != '\n')) + sz++; + } else if (*in == '\n') { + if ((in == src) || (*(in-1) != '\r')) + sz++; + } + + in++; + in_len--; + } + + // Reserve space + out.reserve(sz); + + // And convert + in = src; + in_len = bytes; + while ((in_len > 0) && (*in != '\0')) { + if (*in == '\n') { + if ((in == src) || (*(in-1) != '\r')) + out += '\r'; + } + + out += *in; + + if (*in == '\r') { + if ((in_len < 2) || (*(in+1) != '\n')) + out += '\n'; + } + + in++; + in_len--; + } + + return out; + } + + size_t ucs4ToUTF8(unsigned src, char dst[5]) { + if (src < 0x80) { + *dst++ = src; + *dst++ = '\0'; + return 1; + } else if (src < 0x800) { + *dst++ = 0xc0 | (src >> 6); + *dst++ = 0x80 | (src & 0x3f); + *dst++ = '\0'; + return 2; + } else if ((src >= 0xd800) && (src < 0xe000)) { + return ucs4ToUTF8(0xfffd, dst); + } else if (src < 0x10000) { + *dst++ = 0xe0 | (src >> 12); + *dst++ = 0x80 | ((src >> 6) & 0x3f); + *dst++ = 0x80 | (src & 0x3f); + *dst++ = '\0'; + return 3; + } else if (src < 0x110000) { + *dst++ = 0xf0 | (src >> 18); + *dst++ = 0x80 | ((src >> 12) & 0x3f); + *dst++ = 0x80 | ((src >> 6) & 0x3f); + *dst++ = 0x80 | (src & 0x3f); + *dst++ = '\0'; + return 4; + } else { + return ucs4ToUTF8(0xfffd, dst); + } + } + + size_t utf8ToUCS4(const char* src, size_t max, unsigned* dst) { + size_t count, consumed; + + *dst = 0xfffd; + + if (max == 0) + return 0; + + consumed = 1; + + if ((*src & 0x80) == 0) { + *dst = *src; + count = 0; + } else if ((*src & 0xe0) == 0xc0) { + *dst = *src & 0x1f; + count = 1; + } else if ((*src & 0xf0) == 0xe0) { + *dst = *src & 0x0f; + count = 2; + } else if ((*src & 0xf8) == 0xf0) { + *dst = *src & 0x07; + count = 3; + } else { + // Invalid sequence, consume all continuation characters + src++; + max--; + while ((max-- > 0) && ((*src++ & 0xc0) == 0x80)) + consumed++; + return consumed; + } + + src++; + max--; + + while (count--) { + consumed++; + + // Invalid or truncated sequence? + if ((max == 0) || ((*src & 0xc0) != 0x80)) { + *dst = 0xfffd; + return consumed; + } + + *dst <<= 6; + *dst |= *src & 0x3f; + + src++; + max--; + } + + // UTF-16 surrogate code point? + if ((*dst >= 0xd800) && (*dst < 0xe000)) + *dst = 0xfffd; + + return consumed; + } + + size_t ucs4ToUTF16(unsigned src, wchar_t dst[3]) { + if ((src < 0xd800) || ((src >= 0xe000) && (src < 0x10000))) { + *dst++ = src; + *dst++ = L'\0'; + return 1; + } else if ((src >= 0x10000) && (src < 0x110000)) { + src -= 0x10000; + *dst++ = 0xd800 | ((src >> 10) & 0x03ff); + *dst++ = 0xdc00 | (src & 0x03ff); + *dst++ = L'\0'; + return 2; + } else { + return ucs4ToUTF16(0xfffd, dst); + } + } + + size_t utf16ToUCS4(const wchar_t* src, size_t max, unsigned* dst) { + *dst = 0xfffd; + + if (max == 0) + return 0; + + if ((*src < 0xd800) || (*src >= 0xe000)) { + *dst = *src; + return 1; + } + + if (*src & 0x0400) { + size_t consumed; + + // Invalid sequence, consume all continuation characters + consumed = 0; + while ((max > 0) && (*src & 0x0400)) { + src++; + max--; + consumed++; + } + + return consumed; + } + + *dst = *src++; + max--; + + // Invalid or truncated sequence? + if ((max == 0) || ((*src & 0xfc00) != 0xdc00)) { + *dst = 0xfffd; + return 1; + } + + *dst = 0x10000 + ((*dst & 0x03ff) << 10); + *dst |= *src & 0x3ff; + + return 2; + } + + std::string latin1ToUTF8(const char* src, size_t bytes) { + std::string out; + size_t sz; + + const char* in; + size_t in_len; + + // Compute output size + sz = 0; + in = src; + in_len = bytes; + while ((in_len > 0) && (*in != '\0')) { + char buf[5]; + sz += ucs4ToUTF8(*(const unsigned char*)in, buf); + in++; + in_len--; + } + + // Reserve space + out.reserve(sz); + + // And convert + in = src; + in_len = bytes; + while ((in_len > 0) && (*in != '\0')) { + char buf[5]; + ucs4ToUTF8(*(const unsigned char*)in, buf); + out += buf; + in++; + in_len--; + } + + return out; + } + + std::string utf8ToLatin1(const char* src, size_t bytes) { + std::string out; + size_t sz; + + const char* in; + size_t in_len; + + // Compute output size + sz = 0; + in = src; + in_len = bytes; + while ((in_len > 0) && (*in != '\0')) { + size_t len; + unsigned ucs; + + len = utf8ToUCS4(in, in_len, &ucs); + in += len; + in_len -= len; + sz++; + } + + // Reserve space + out.reserve(sz); + + // And convert + in = src; + in_len = bytes; + while ((in_len > 0) && (*in != '\0')) { + size_t len; + unsigned ucs; + + len = utf8ToUCS4(in, in_len, &ucs); + in += len; + in_len -= len; + + if (ucs > 0xff) + out += '?'; + else + out += (unsigned char)ucs; + } + + return out; + } + + std::string utf16ToUTF8(const wchar_t* src, size_t units) + { + std::string out; + size_t sz; + + const wchar_t* in; + size_t in_len; + + // Compute output size + sz = 0; + in = src; + in_len = units; + while ((in_len > 0) && (*in != '\0')) { + size_t len; + unsigned ucs; + char buf[5]; + + len = utf16ToUCS4(in, in_len, &ucs); + in += len; + in_len -= len; + + sz += ucs4ToUTF8(ucs, buf); + } + + // Reserve space + out.reserve(sz); + + // And convert + in = src; + in_len = units; + while ((in_len > 0) && (*in != '\0')) { + size_t len; + unsigned ucs; + char buf[5]; + + len = utf16ToUCS4(in, in_len, &ucs); + in += len; + in_len -= len; + + ucs4ToUTF8(ucs, buf); + out += buf; + } + + return out; + } + + std::wstring utf8ToUTF16(const char* src, size_t bytes) + { + std::wstring out; + size_t sz; + + const char* in; + size_t in_len; + + // Compute output size + sz = 0; + in = src; + in_len = bytes; + while ((in_len > 0) && (*in != '\0')) { + size_t len; + unsigned ucs; + wchar_t buf[3]; + + len = utf8ToUCS4(in, in_len, &ucs); + in += len; + in_len -= len; + + sz += ucs4ToUTF16(ucs, buf); + } + + // Reserve space + out.reserve(sz); + + // And convert + in = src; + in_len = bytes; + while ((in_len > 0) && (*in != '\0')) { + size_t len; + unsigned ucs; + wchar_t buf[3]; + + len = utf8ToUCS4(in, in_len, &ucs); + in += len; + in_len -= len; + + ucs4ToUTF16(ucs, buf); + out += buf; + } + + return out; + } + + bool isValidUTF8(const char* str, size_t bytes) + { + while ((bytes > 0) && (*str != '\0')) { + size_t len; + unsigned ucs; + + len = utf8ToUCS4(str, bytes, &ucs); + str += len; + bytes -= len; + + if (ucs == 0xfffd) + return false; + } + + return true; + } + + bool isValidUTF16(const wchar_t* wstr, size_t units) + { + while ((units > 0) && (*wstr != '\0')) { + size_t len; + unsigned ucs; + + len = utf16ToUCS4(wstr, units, &ucs); + wstr += len; + units -= len; + + if (ucs == 0xfffd) + return false; + } + + return true; + } + + static std::string doPrefix(long long value, const char *unit, + unsigned divisor, const char **prefixes, + size_t prefixCount, int precision) { + char buffer[256]; + double newValue; + size_t prefix; + + newValue = value; + prefix = 0; + while (newValue >= divisor) { + if (prefix >= prefixCount) + break; + newValue /= divisor; + prefix++; + } + + snprintf(buffer, sizeof(buffer), "%.*g %s%s", precision, newValue, + (prefix == 0) ? "" : prefixes[prefix-1], unit); + buffer[sizeof(buffer)-1] = '\0'; + + return buffer; + } + + static const char *siPrefixes[] = + { "k", "M", "G", "T", "P", "E", "Z", "Y" }; + static const char *iecPrefixes[] = + { "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi" }; + + std::string siPrefix(long long value, const char *unit, + int precision) { + return doPrefix(value, unit, 1000, siPrefixes, + sizeof(siPrefixes)/sizeof(*siPrefixes), + precision); + } + + std::string iecPrefix(long long value, const char *unit, + int precision) { + return doPrefix(value, unit, 1024, iecPrefixes, + sizeof(iecPrefixes)/sizeof(*iecPrefixes), + precision); + } +}; diff --git a/common/core/string.h b/common/core/string.h new file mode 100644 index 00000000..1465484d --- /dev/null +++ b/common/core/string.h @@ -0,0 +1,80 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011-2023 Pierre Ossman for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// +// string.h - string utility functions +// + +#ifndef __CORE_STRING_H__ +#define __CORE_STRING_H__ + +#include <stdint.h> + +#include <string> +#include <vector> + +namespace core { + + // Formats according to printf(), with a dynamic allocation + std::string format(const char *fmt, ...) + __attribute__((__format__ (__printf__, 1, 2))); + + // Splits a string with the specified delimiter + std::vector<std::string> split(const char* src, + const char delimiter); + + // Conversion to and from a hex string + + void binToHex(const uint8_t* in, size_t inlen, char* out, size_t outlen); + std::string binToHex(const uint8_t* in, size_t inlen); + bool hexToBin(const char* in, size_t inlen, uint8_t* out, size_t outlen); + std::vector<uint8_t> hexToBin(const char* in, size_t inlen); + + // Makes sure line endings are in a certain format + + std::string convertLF(const char* src, size_t bytes = (size_t)-1); + std::string convertCRLF(const char* src, size_t bytes = (size_t)-1); + + // Convertions between various Unicode formats + + size_t ucs4ToUTF8(unsigned src, char dst[5]); + size_t utf8ToUCS4(const char* src, size_t max, unsigned* dst); + + size_t ucs4ToUTF16(unsigned src, wchar_t dst[3]); + size_t utf16ToUCS4(const wchar_t* src, size_t max, unsigned* dst); + + std::string latin1ToUTF8(const char* src, size_t bytes = (size_t)-1); + std::string utf8ToLatin1(const char* src, size_t bytes = (size_t)-1); + + std::string utf16ToUTF8(const wchar_t* src, size_t units = (size_t)-1); + std::wstring utf8ToUTF16(const char* src, size_t bytes = (size_t)-1); + + bool isValidUTF8(const char* str, size_t bytes = (size_t)-1); + bool isValidUTF16(const wchar_t* wstr, size_t units = (size_t)-1); + + // Convert a value to a string using the correct prefix to reduce + // the length of the string + + std::string siPrefix(long long value, const char *unit, + int precision=6); + std::string iecPrefix(long long value, const char *unit, + int precision=6); +} + +#endif diff --git a/common/core/time.cxx b/common/core/time.cxx new file mode 100644 index 00000000..47a0ff8a --- /dev/null +++ b/common/core/time.cxx @@ -0,0 +1,88 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011-2019 Pierre Ossman for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stddef.h> +#include <sys/time.h> + +#include <core/time.h> + +namespace core { + + unsigned msBetween(const struct timeval *first, + const struct timeval *second) + { + unsigned udiff; + + if (isBefore(second, first)) + return 0; + + udiff = (second->tv_sec - first->tv_sec) * 1000000 + + (second->tv_usec - first->tv_usec); + + return (udiff + 999) / 1000; + } + + unsigned msSince(const struct timeval *then) + { + struct timeval now; + + gettimeofday(&now, nullptr); + + return msBetween(then, &now); + } + + unsigned msUntil(const struct timeval *then) + { + struct timeval now; + + gettimeofday(&now, nullptr); + + return msBetween(&now, then); + } + + bool isBefore(const struct timeval *first, + const struct timeval *second) + { + if (first->tv_sec < second->tv_sec) + return true; + if (first->tv_sec > second->tv_sec) + return false; + if (first->tv_usec < second->tv_usec) + return true; + return false; + } + + struct timeval addMillis(struct timeval inTime, int millis) + { + int secs = millis / 1000; + millis = millis % 1000; + inTime.tv_sec += secs; + inTime.tv_usec += millis * 1000; + if (inTime.tv_usec >= 1000000) { + inTime.tv_sec++; + inTime.tv_usec -= 1000000; + } + return inTime; + } + +} diff --git a/common/core/time.h b/common/core/time.h new file mode 100644 index 00000000..319f336f --- /dev/null +++ b/common/core/time.h @@ -0,0 +1,58 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011-2019 Pierre Ossman for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// +// time.h - time helper functions +// + +#ifndef __CORE_TIME_H__ +#define __CORE_TIME_H__ + +#include <limits.h> + +struct timeval; + +namespace core { + + // secsToMillis() turns seconds into milliseconds, capping the value so it + // can't wrap round and become -ve + inline int secsToMillis(int secs) { + return (secs < 0 || secs > (INT_MAX/1000) ? INT_MAX : secs * 1000); + } + + // Returns time elapsed between two moments in milliseconds. + unsigned msBetween(const struct timeval *first, + const struct timeval *second); + + // Returns time elapsed since given moment in milliseconds. + unsigned msSince(const struct timeval *then); + + // Returns time until the given moment in milliseconds. + unsigned msUntil(const struct timeval *then); + + // Returns true if first happened before seconds + bool isBefore(const struct timeval *first, + const struct timeval *second); + + // Returns a new timeval a specified number of milliseconds later than + // the given timeval + struct timeval addMillis(struct timeval inTime, int millis); +} + +#endif diff --git a/common/core/winerrno.h b/common/core/winerrno.h new file mode 100644 index 00000000..a81fdc94 --- /dev/null +++ b/common/core/winerrno.h @@ -0,0 +1,91 @@ + +/* Generated with: +cat /usr/i686-pc-mingw32/sys-root/mingw/include/winerror.h \ + | awk '/#define WSAE.*WSABASE/{gsub("WSA", ""); print "#undef " $2 "\n#define " $2 " WSA" $2}' \ + | egrep -v 'EINTR|EBADF|EACCES|EFAULT|EINVAL|EMFILE|_QOS|PROVIDER|PROCTABLE' +*/ + +#include <winsock2.h> + +#undef EWOULDBLOCK +#define EWOULDBLOCK WSAEWOULDBLOCK +#undef EINPROGRESS +#define EINPROGRESS WSAEINPROGRESS +#undef EALREADY +#define EALREADY WSAEALREADY +#undef ENOTSOCK +#define ENOTSOCK WSAENOTSOCK +#undef EDESTADDRREQ +#define EDESTADDRREQ WSAEDESTADDRREQ +#undef EMSGSIZE +#define EMSGSIZE WSAEMSGSIZE +#undef EPROTOTYPE +#define EPROTOTYPE WSAEPROTOTYPE +#undef ENOPROTOOPT +#define ENOPROTOOPT WSAENOPROTOOPT +#undef EPROTONOSUPPORT +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#undef ESOCKTNOSUPPORT +#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT +#undef EOPNOTSUPP +#define EOPNOTSUPP WSAEOPNOTSUPP +#undef EPFNOSUPPORT +#define EPFNOSUPPORT WSAEPFNOSUPPORT +#undef EAFNOSUPPORT +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#undef EADDRINUSE +#define EADDRINUSE WSAEADDRINUSE +#undef EADDRNOTAVAIL +#define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#undef ENETDOWN +#define ENETDOWN WSAENETDOWN +#undef ENETUNREACH +#define ENETUNREACH WSAENETUNREACH +#undef ENETRESET +#define ENETRESET WSAENETRESET +#undef ECONNABORTED +#define ECONNABORTED WSAECONNABORTED +#undef ECONNRESET +#define ECONNRESET WSAECONNRESET +#undef ENOBUFS +#define ENOBUFS WSAENOBUFS +#undef EISCONN +#define EISCONN WSAEISCONN +#undef ENOTCONN +#define ENOTCONN WSAENOTCONN +#undef ESHUTDOWN +#define ESHUTDOWN WSAESHUTDOWN +#undef ETOOMANYREFS +#define ETOOMANYREFS WSAETOOMANYREFS +#undef ETIMEDOUT +#define ETIMEDOUT WSAETIMEDOUT +#undef ECONNREFUSED +#define ECONNREFUSED WSAECONNREFUSED +#undef ELOOP +#define ELOOP WSAELOOP +#undef ENAMETOOLONG +#define ENAMETOOLONG WSAENAMETOOLONG +#undef EHOSTDOWN +#define EHOSTDOWN WSAEHOSTDOWN +#undef EHOSTUNREACH +#define EHOSTUNREACH WSAEHOSTUNREACH +#undef ENOTEMPTY +#define ENOTEMPTY WSAENOTEMPTY +#undef EPROCLIM +#define EPROCLIM WSAEPROCLIM +#undef EUSERS +#define EUSERS WSAEUSERS +#undef EDQUOT +#define EDQUOT WSAEDQUOT +#undef ESTALE +#define ESTALE WSAESTALE +#undef EREMOTE +#define EREMOTE WSAEREMOTE +#undef EDISCON +#define EDISCON WSAEDISCON +#undef ENOMORE +#define ENOMORE WSAENOMORE +#undef ECANCELLED +#define ECANCELLED WSAECANCELLED +#undef EREFUSED +#define EREFUSED WSAEREFUSED diff --git a/common/core/xdgdirs.cxx b/common/core/xdgdirs.cxx new file mode 100644 index 00000000..2628f317 --- /dev/null +++ b/common/core/xdgdirs.cxx @@ -0,0 +1,162 @@ +/* Copyright (C) 2010 TightVNC Team. All Rights Reserved. + * Copyright 2021-2023 Pierre Ossman for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> + +#ifndef WIN32 +#include <pwd.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#else +#include <windows.h> +#include <wininet.h> /* MinGW needs it */ +#include <shlobj.h> +#define stat _stat +#define mkdir(path, mode) mkdir(path) +#endif + +#include <core/xdgdirs.h> + +static const char* getvncdir(bool userDir, const char *xdg_env, const char *xdg_def) +{ + static char dir[PATH_MAX], legacy[PATH_MAX]; + struct stat st; + +#ifndef WIN32 + char *homedir, *xdgdir; + uid_t uid; + struct passwd *passwd; +#else + BOOL ret; +#endif + +#ifndef WIN32 + homedir = getenv("HOME"); + if (homedir == nullptr) { + uid = getuid(); + passwd = getpwuid(uid); + if (passwd == nullptr) { + /* Do we want emit error msg here? */ + return nullptr; + } + homedir = passwd->pw_dir; + } + + if (userDir) + return homedir; + + xdgdir = getenv(xdg_env); + if (xdgdir != nullptr && xdgdir[0] == '/') + snprintf(dir, sizeof(dir), "%s/tigervnc", xdgdir); + else + snprintf(dir, sizeof(dir), "%s/%s/tigervnc", homedir, xdg_def); + + snprintf(legacy, sizeof(legacy), "%s/.vnc", homedir); +#else + (void) xdg_def; + (void) xdg_env; + + if (userDir) + ret = SHGetSpecialFolderPath(nullptr, dir, CSIDL_PROFILE, FALSE); + else + ret = SHGetSpecialFolderPath(nullptr, dir, CSIDL_APPDATA, FALSE); + + if (ret == FALSE) + return nullptr; + + if (userDir) + return dir; + + ret = SHGetSpecialFolderPath(nullptr, legacy, CSIDL_APPDATA, FALSE); + + if (ret == FALSE) + return nullptr; + + if (strlen(dir) + strlen("\\TigerVNC") >= sizeof(dir)) + return nullptr; + if (strlen(legacy) + strlen("\\vnc") >= sizeof(legacy)) + return nullptr; + + strcat(dir, "\\TigerVNC"); + strcat(legacy, "\\vnc"); +#endif + return (stat(dir, &st) != 0 && stat(legacy, &st) == 0) ? legacy : dir; +} + +const char* core::getuserhomedir() +{ + return getvncdir(true, nullptr, nullptr); +} + +const char* core::getvncconfigdir() +{ + return getvncdir(false, "XDG_CONFIG_HOME", ".config"); +} + +const char* core::getvncdatadir() +{ + return getvncdir(false, "XDG_DATA_HOME", ".local/share"); +} + +const char* core::getvncstatedir() +{ + return getvncdir(false, "XDG_STATE_HOME", ".local/state"); +} + +int core::mkdir_p(const char *path_, mode_t mode) +{ + char *path = strdup(path_); + char *p; + +#ifdef WIN32 + (void)mode; +#endif + + for (p = path + 1; *p; p++) { + if (*p == '/') { + *p = '\0'; + if (mkdir(path, mode) == -1) { + if (errno != EEXIST) { + free(path); + return -1; + } + } + *p = '/'; + } + } + + if (mkdir(path, mode) == -1) { + free(path); + return -1; + } + + free(path); + + return 0; +} diff --git a/common/core/xdgdirs.h b/common/core/xdgdirs.h new file mode 100644 index 00000000..0769ba8b --- /dev/null +++ b/common/core/xdgdirs.h @@ -0,0 +1,74 @@ +/* Copyright (C) 2010 TightVNC Team. All Rights Reserved. + * Copyright 2021-2023 Pierre Ossman for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef CORE_XDGDIRS_H +#define CORE_XDGDIRS_H + +#include <sys/stat.h> + +namespace core { + + /* + * Get user home directory. + * If HOME environment variable is set then it is used. + * Otherwise home directory is obtained via getpwuid function. + * + * Returns NULL on failure. + */ + const char* getuserhomedir(); + + /* + * Get VNC config directory. On Unix-like systems, this is either: + * - $XDG_CONFIG_HOME/tigervnc + * - $HOME/.config/tigervnc + * On Windows, this is simply %APPDATA%/TigerVNC/. + * + * Returns NULL on failure. + */ + const char* getvncconfigdir(); + + /* + * Get VNC data directory used for X.509 known hosts. + * On Unix-like systems, this is either: + * - $XDG_DATA_HOME/tigervnc + * - $HOME/.local/share/tigervnc + * On Windows, this is simply %APPDATA%/TigerVNC/. + * + * Returns NULL on failure. + */ + const char* getvncdatadir(); + + /* + * Get VNC state (logs) directory. On Unix-like systems, this is either: + * - $XDG_STATE_HOME/tigervnc + * - $HOME/.local/state/tigervnc + * On Windows, this is simply %APPDATA%/TigerVNC/. + * + * Returns NULL on failure. + */ + const char* getvncstatedir(); + + /* + * Create directory recursively. Useful to create the nested directory + * structures needed for the above directories. + */ + int mkdir_p(const char *path, mode_t mode); +} + +#endif /* CORE_XDGDIRS_H */ |