mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-09-02 06:55:16 +00:00
[#3210] refactor string utilities
- Rename util/strutil.h to util/str.h to escape redundancy. - Simplify trim function. - Remove unused functions. - Get rid of the regex conditional compilation that helped Kea build with ancient compilers. Lack of proper regex functionality now results in failure in configure.ac.
This commit is contained in:
@@ -667,8 +667,8 @@ int main() {
|
|||||||
# When cross-compiling we don't have any way to check if regex is
|
# When cross-compiling we don't have any way to check if regex is
|
||||||
# usable or not.
|
# usable or not.
|
||||||
# Let's be optimistic and assume it is by testing only the negative case.
|
# Let's be optimistic and assume it is by testing only the negative case.
|
||||||
if test "x$usable_regex" != "xno" ; then
|
if test "${usable_regex}" = 'no'; then
|
||||||
AC_DEFINE(USE_REGEX, 1, [Define to 1 if C++11 regex is usable])
|
AC_MSG_ERROR([Need proper regex functionality.])]
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check for NETCONF support. If NETCONF was enabled in the build, and this check
|
# Check for NETCONF support. If NETCONF was enabled in the build, and this check
|
||||||
|
@@ -49,7 +49,7 @@
|
|||||||
#include <hooks/hooks_log.h>
|
#include <hooks/hooks_log.h>
|
||||||
#include <hooks/hooks_manager.h>
|
#include <hooks/hooks_manager.h>
|
||||||
#include <stats/stats_mgr.h>
|
#include <stats/stats_mgr.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
#include <log/logger.h>
|
#include <log/logger.h>
|
||||||
#include <cryptolink/cryptolink.h>
|
#include <cryptolink/cryptolink.h>
|
||||||
#include <process/cfgrpt/config_report.h>
|
#include <process/cfgrpt/config_report.h>
|
||||||
|
@@ -42,7 +42,7 @@
|
|||||||
#include <process/config_ctl_parser.h>
|
#include <process/config_ctl_parser.h>
|
||||||
#include <util/encode/encode.h>
|
#include <util/encode/encode.h>
|
||||||
#include <util/multi_threading_mgr.h>
|
#include <util/multi_threading_mgr.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
|
@@ -45,7 +45,7 @@
|
|||||||
#include <process/config_ctl_parser.h>
|
#include <process/config_ctl_parser.h>
|
||||||
#include <util/encode/encode.h>
|
#include <util/encode/encode.h>
|
||||||
#include <util/multi_threading_mgr.h>
|
#include <util/multi_threading_mgr.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
#include <util/triplet.h>
|
#include <util/triplet.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include <flex_option.h>
|
#include <flex_option.h>
|
||||||
#include <flex_option_log.h>
|
#include <flex_option_log.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
#include <cc/simple_parser.h>
|
#include <cc/simple_parser.h>
|
||||||
#include <dhcp/dhcp4.h>
|
#include <dhcp/dhcp4.h>
|
||||||
#include <dhcp/libdhcp++.h>
|
#include <dhcp/libdhcp++.h>
|
||||||
|
@@ -17,7 +17,7 @@
|
|||||||
#include <dhcp/std_option_defs.h>
|
#include <dhcp/std_option_defs.h>
|
||||||
#include <eval/evaluate.h>
|
#include <eval/evaluate.h>
|
||||||
#include <eval/token.h>
|
#include <eval/token.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string/split.hpp>
|
#include <boost/algorithm/string/split.hpp>
|
||||||
#include <boost/algorithm/string/classification.hpp>
|
#include <boost/algorithm/string/classification.hpp>
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
#include <dhcpsrv/network.h>
|
#include <dhcpsrv/network.h>
|
||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
#include <util/multi_threading_mgr.h>
|
#include <util/multi_threading_mgr.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
#include <ha_log.h>
|
#include <ha_log.h>
|
||||||
#include <ha_config.h>
|
#include <ha_config.h>
|
||||||
#include <ha_service_states.h>
|
#include <ha_service_states.h>
|
||||||
|
@@ -29,7 +29,7 @@
|
|||||||
#include <stats/stats_mgr.h>
|
#include <stats/stats_mgr.h>
|
||||||
#include <util/encode/encode.h>
|
#include <util/encode/encode.h>
|
||||||
#include <util/multi_threading_mgr.h>
|
#include <util/multi_threading_mgr.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
|
|
||||||
#include <boost/scoped_ptr.hpp>
|
#include <boost/scoped_ptr.hpp>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include <cc/server_tag.h>
|
#include <cc/server_tag.h>
|
||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
namespace isc {
|
namespace isc {
|
||||||
|
@@ -12,7 +12,7 @@
|
|||||||
#include <database/db_log.h>
|
#include <database/db_log.h>
|
||||||
#include <database/db_messages.h>
|
#include <database/db_messages.h>
|
||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include <cc/data.h>
|
#include <cc/data.h>
|
||||||
#include <dhcp/classify.h>
|
#include <dhcp/classify.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string/classification.hpp>
|
#include <boost/algorithm/string/classification.hpp>
|
||||||
#include <boost/algorithm/string/constants.hpp>
|
#include <boost/algorithm/string/constants.hpp>
|
||||||
|
@@ -8,10 +8,14 @@
|
|||||||
#define DUID_H
|
#define DUID_H
|
||||||
|
|
||||||
#include <asiolink/io_address.h>
|
#include <asiolink/io_address.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
#include <boost/shared_ptr.hpp>
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iomanip>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <stdint.h>
|
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
namespace isc {
|
namespace isc {
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
#include <util/io.h>
|
#include <util/io.h>
|
||||||
#include <util/range_utilities.h>
|
#include <util/range_utilities.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
#include <dhcp/hwaddr.h>
|
#include <dhcp/hwaddr.h>
|
||||||
#include <dhcp/dhcp4.h>
|
#include <dhcp/dhcp4.h>
|
||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
#include <dns/labelsequence.h>
|
#include <dns/labelsequence.h>
|
||||||
#include <util/buffer.h>
|
#include <util/buffer.h>
|
||||||
#include <util/io.h>
|
#include <util/io.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
namespace isc {
|
namespace isc {
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
#include <dhcp/option_data_types.h>
|
#include <dhcp/option_data_types.h>
|
||||||
#include <dns/name.h>
|
#include <dns/name.h>
|
||||||
#include <util/encode/utf8.h>
|
#include <util/encode/utf8.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
#include <dns/labelsequence.h>
|
#include <dns/labelsequence.h>
|
||||||
#include <util/buffer.h>
|
#include <util/buffer.h>
|
||||||
#include <util/io.h>
|
#include <util/io.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
namespace isc {
|
namespace isc {
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
|
||||||
#include <asiolink/io_error.h>
|
#include <asiolink/io_error.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
|
|
||||||
#include <option_classless_static_route.h>
|
#include <option_classless_static_route.h>
|
||||||
|
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
#include <dhcp/option_data_types.h>
|
#include <dhcp/option_data_types.h>
|
||||||
#include <dns/labelsequence.h>
|
#include <dns/labelsequence.h>
|
||||||
#include <dns/name.h>
|
#include <dns/name.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
#include <util/encode/encode.h>
|
#include <util/encode/encode.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
@@ -31,7 +31,7 @@
|
|||||||
#include <util/encode/encode.h>
|
#include <util/encode/encode.h>
|
||||||
#include <dns/labelsequence.h>
|
#include <dns/labelsequence.h>
|
||||||
#include <dns/name.h>
|
#include <dns/name.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
#include <boost/algorithm/string/classification.hpp>
|
#include <boost/algorithm/string/classification.hpp>
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
|
||||||
#include <dhcp/option_string.h>
|
#include <dhcp/option_string.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
namespace isc {
|
namespace isc {
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
|
||||||
#include <dhcp/option_classless_static_route.h>
|
#include <dhcp/option_classless_static_route.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
#include <dhcp/duid_factory.h>
|
#include <dhcp/duid_factory.h>
|
||||||
#include <dhcpsrv/cfg_duid.h>
|
#include <dhcpsrv/cfg_duid.h>
|
||||||
#include <util/encode/encode.h>
|
#include <util/encode/encode.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
#include <dhcpsrv/timer_mgr.h>
|
#include <dhcpsrv/timer_mgr.h>
|
||||||
#include <util/reconnect_ctl.h>
|
#include <util/reconnect_ctl.h>
|
||||||
#include <util/multi_threading_mgr.h>
|
#include <util/multi_threading_mgr.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
|
|
||||||
#include <util/encode/encode.h>
|
#include <util/encode/encode.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
|
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
#include <asiolink/addr_utilities.h>
|
#include <asiolink/addr_utilities.h>
|
||||||
#include <dhcpsrv/lease.h>
|
#include <dhcpsrv/lease.h>
|
||||||
#include <util/pointer_util.h>
|
#include <util/pointer_util.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/scoped_ptr.hpp>
|
#include <boost/scoped_ptr.hpp>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
#include <dhcpsrv/dhcpsrv_log.h>
|
#include <dhcpsrv/dhcpsrv_log.h>
|
||||||
#include <dhcpsrv/parsers/base_network_parser.h>
|
#include <dhcpsrv/parsers/base_network_parser.h>
|
||||||
#include <util/optional.h>
|
#include <util/optional.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
|
|
||||||
using namespace isc::data;
|
using namespace isc::data;
|
||||||
using namespace isc::util;
|
using namespace isc::util;
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
#include <dhcpsrv/parsers/simple_parser6.h>
|
#include <dhcpsrv/parsers/simple_parser6.h>
|
||||||
#include <dhcpsrv/cfg_mac_source.h>
|
#include <dhcpsrv/cfg_mac_source.h>
|
||||||
#include <util/encode/encode.h>
|
#include <util/encode/encode.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
#include <dhcpsrv/parsers/simple_parser4.h>
|
#include <dhcpsrv/parsers/simple_parser4.h>
|
||||||
#include <dhcpsrv/parsers/simple_parser6.h>
|
#include <dhcpsrv/parsers/simple_parser6.h>
|
||||||
#include <util/encode/encode.h>
|
#include <util/encode/encode.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
#include <boost/make_shared.hpp>
|
#include <boost/make_shared.hpp>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@@ -28,7 +28,7 @@
|
|||||||
#include <log/logger_specification.h>
|
#include <log/logger_specification.h>
|
||||||
#include <dhcp/pkt.h>
|
#include <dhcp/pkt.h>
|
||||||
#include <stats/stats_mgr.h>
|
#include <stats/stats_mgr.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
|
|
||||||
#include <boost/make_shared.hpp>
|
#include <boost/make_shared.hpp>
|
||||||
|
|
||||||
|
@@ -31,7 +31,7 @@
|
|||||||
#include <cc/user_context.h>
|
#include <cc/user_context.h>
|
||||||
#include <cc/simple_parser.h>
|
#include <cc/simple_parser.h>
|
||||||
#include <util/optional.h>
|
#include <util/optional.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
|
|
||||||
#include <boost/shared_ptr.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
#include <dhcpsrv/d2_client_mgr.h>
|
#include <dhcpsrv/d2_client_mgr.h>
|
||||||
#include <testutils/test_to_element.h>
|
#include <testutils/test_to_element.h>
|
||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
#include <cc/dhcp_config_error.h>
|
#include <cc/dhcp_config_error.h>
|
||||||
#include <hooks/hooks_parser.h>
|
#include <hooks/hooks_parser.h>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
#include <http/auth_log.h>
|
#include <http/auth_log.h>
|
||||||
#include <http/basic_auth_config.h>
|
#include <http/basic_auth_config.h>
|
||||||
#include <util/filesystem.h>
|
#include <util/filesystem.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
|
|
||||||
using namespace isc;
|
using namespace isc;
|
||||||
using namespace isc::data;
|
using namespace isc::data;
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
#include <http/http_header.h>
|
#include <http/http_header.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
|
|
||||||
namespace isc {
|
namespace isc {
|
||||||
|
@@ -22,7 +22,7 @@
|
|||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
|
|
||||||
#include <util/filesystem.h>
|
#include <util/filesystem.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
|
|
||||||
#include <log/log_messages.h>
|
#include <log/log_messages.h>
|
||||||
#include <log/message_dictionary.h>
|
#include <log/message_dictionary.h>
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
#include <log/message_dictionary.h>
|
#include <log/message_dictionary.h>
|
||||||
#include <log/message_types.h>
|
#include <log/message_types.h>
|
||||||
|
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
@@ -38,7 +38,7 @@
|
|||||||
#include <log/interprocess/interprocess_sync_file.h>
|
#include <log/interprocess/interprocess_sync_file.h>
|
||||||
#include <log/interprocess/interprocess_sync_null.h>
|
#include <log/interprocess/interprocess_sync_null.h>
|
||||||
|
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
|
|
||||||
// Note: as log4cplus and the Kea logger have many concepts in common, and
|
// Note: as log4cplus and the Kea logger have many concepts in common, and
|
||||||
// thus many similar names, to disambiguate types we don't "use" the log4cplus
|
// thus many similar names, to disambiguate types we don't "use" the log4cplus
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
#include <log/log_messages.h>
|
#include <log/log_messages.h>
|
||||||
#include <log/message_exception.h>
|
#include <log/message_exception.h>
|
||||||
#include <log/message_reader.h>
|
#include <log/message_reader.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
@@ -24,7 +24,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
|
|
||||||
#include <log/logger.h>
|
#include <log/logger.h>
|
||||||
#include <log/logger_level.h>
|
#include <log/logger_level.h>
|
||||||
|
@@ -13,7 +13,7 @@
|
|||||||
#include <process/daemon.h>
|
#include <process/daemon.h>
|
||||||
#include <process/redact_config.h>
|
#include <process/redact_config.h>
|
||||||
#include <util/encode/encode.h>
|
#include <util/encode/encode.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
|
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
#include <tcp/tcp_connection_pool.h>
|
#include <tcp/tcp_connection_pool.h>
|
||||||
#include <tcp/tcp_log.h>
|
#include <tcp/tcp_log.h>
|
||||||
#include <tcp/tcp_messages.h>
|
#include <tcp/tcp_messages.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
#include <boost/make_shared.hpp>
|
#include <boost/make_shared.hpp>
|
||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
|
||||||
#include <tcp/tcp_stream_msg.h>
|
#include <tcp/tcp_stream_msg.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
@@ -32,7 +32,7 @@ libkea_util_la_SOURCES += staged_value.h
|
|||||||
libkea_util_la_SOURCES += state_model.cc state_model.h
|
libkea_util_la_SOURCES += state_model.cc state_model.h
|
||||||
libkea_util_la_SOURCES += stopwatch.cc stopwatch.h
|
libkea_util_la_SOURCES += stopwatch.cc stopwatch.h
|
||||||
libkea_util_la_SOURCES += stopwatch_impl.cc stopwatch_impl.h
|
libkea_util_la_SOURCES += stopwatch_impl.cc stopwatch_impl.h
|
||||||
libkea_util_la_SOURCES += strutil.h strutil.cc
|
libkea_util_la_SOURCES += str.h str.cc
|
||||||
libkea_util_la_SOURCES += thread_pool.h
|
libkea_util_la_SOURCES += thread_pool.h
|
||||||
libkea_util_la_SOURCES += triplet.h
|
libkea_util_la_SOURCES += triplet.h
|
||||||
libkea_util_la_SOURCES += unlock_guard.h
|
libkea_util_la_SOURCES += unlock_guard.h
|
||||||
@@ -77,7 +77,7 @@ libkea_util_include_HEADERS = \
|
|||||||
state_model.h \
|
state_model.h \
|
||||||
stopwatch.h \
|
stopwatch.h \
|
||||||
stopwatch_impl.h \
|
stopwatch_impl.h \
|
||||||
strutil.h \
|
str.h \
|
||||||
thread_pool.h \
|
thread_pool.h \
|
||||||
triplet.h \
|
triplet.h \
|
||||||
unlock_guard.h \
|
unlock_guard.h \
|
||||||
|
345
src/lib/util/str.cc
Normal file
345
src/lib/util/str.cc
Normal file
@@ -0,0 +1,345 @@
|
|||||||
|
// Copyright (C) 2011-2024 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <util/encode/encode.h>
|
||||||
|
#include <util/str.h>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <exception>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <regex>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/classification.hpp>
|
||||||
|
#include <boost/algorithm/string/constants.hpp>
|
||||||
|
#include <boost/algorithm/string/split.hpp>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace isc {
|
||||||
|
namespace util {
|
||||||
|
namespace str {
|
||||||
|
|
||||||
|
string
|
||||||
|
trim(const string& input) {
|
||||||
|
if (input.empty()) {
|
||||||
|
return string();
|
||||||
|
}
|
||||||
|
static const char* blanks = " \t\n";
|
||||||
|
|
||||||
|
// Search for first non-blank character in the string.
|
||||||
|
size_t const first(input.find_first_not_of(blanks));
|
||||||
|
if (first == string::npos) {
|
||||||
|
return string();
|
||||||
|
}
|
||||||
|
|
||||||
|
// String not all blanks, so look for last character.
|
||||||
|
size_t const last(input.find_last_not_of(blanks));
|
||||||
|
|
||||||
|
// Extract the trimmed substring.
|
||||||
|
return input.substr(first, (last - first + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<string>
|
||||||
|
tokens(const string& text, const string& delim, bool escape) {
|
||||||
|
vector<string> result;
|
||||||
|
string token;
|
||||||
|
bool in_token = false;
|
||||||
|
bool escaped = false;
|
||||||
|
for (auto const& c : text) {
|
||||||
|
if (delim.find(c) != string::npos) {
|
||||||
|
// Current character is a delimiter
|
||||||
|
if (!in_token) {
|
||||||
|
// Two or more delimiters, eat them
|
||||||
|
} else if (escaped) {
|
||||||
|
// Escaped delimiter in a token: reset escaped and keep it
|
||||||
|
escaped = false;
|
||||||
|
token.push_back(c);
|
||||||
|
} else {
|
||||||
|
// End of the current token: save it if not empty
|
||||||
|
if (!token.empty()) {
|
||||||
|
result.push_back(token);
|
||||||
|
}
|
||||||
|
// Reset state
|
||||||
|
in_token = false;
|
||||||
|
token.clear();
|
||||||
|
}
|
||||||
|
} else if (escape && (c == '\\')) {
|
||||||
|
// Current character is the escape character
|
||||||
|
if (!in_token) {
|
||||||
|
// The escape character is the first character of a new token
|
||||||
|
in_token = true;
|
||||||
|
}
|
||||||
|
if (escaped) {
|
||||||
|
// Escaped escape: reset escaped and keep one character
|
||||||
|
escaped = false;
|
||||||
|
token.push_back(c);
|
||||||
|
} else {
|
||||||
|
// Remember to keep the next character
|
||||||
|
escaped = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Not a delimiter nor an escape
|
||||||
|
if (!in_token) {
|
||||||
|
// First character of a new token
|
||||||
|
in_token = true;
|
||||||
|
}
|
||||||
|
if (escaped) {
|
||||||
|
// Escaped common character: as escape was false
|
||||||
|
escaped = false;
|
||||||
|
token.push_back('\\');
|
||||||
|
token.push_back(c);
|
||||||
|
} else {
|
||||||
|
// The common case: keep it
|
||||||
|
token.push_back(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// End of input: close and save the current token if not empty
|
||||||
|
if (escaped) {
|
||||||
|
// Pending escape
|
||||||
|
token.push_back('\\');
|
||||||
|
}
|
||||||
|
if (!token.empty()) {
|
||||||
|
result.push_back(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (result);
|
||||||
|
}
|
||||||
|
|
||||||
|
char
|
||||||
|
toUpper(char const chr) {
|
||||||
|
return (toupper(chr));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
uppercase(string& text) {
|
||||||
|
transform(text.begin(), text.end(), text.begin(), toUpper);
|
||||||
|
}
|
||||||
|
|
||||||
|
char
|
||||||
|
toLower(char const chr) {
|
||||||
|
return (tolower(static_cast<int>(chr)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
lowercase(string& text) {
|
||||||
|
transform(text.begin(), text.end(), text.begin(), toLower);
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<uint8_t>
|
||||||
|
quotedStringToBinary(const string& quoted_string) {
|
||||||
|
vector<uint8_t> binary;
|
||||||
|
// Remove whitespace before and after the quotes.
|
||||||
|
string trimmed_string = trim(quoted_string);
|
||||||
|
|
||||||
|
// We require two quote characters, so the length of the string must be
|
||||||
|
// equal to 2 at minimum, and it must start and end with quotes.
|
||||||
|
if ((trimmed_string.length() > 1) &&
|
||||||
|
((trimmed_string[0] == '\'') && (trimmed_string[trimmed_string.length() - 1] == '\''))) {
|
||||||
|
// Remove quotes and trim the text inside the quotes.
|
||||||
|
trimmed_string = trim(trimmed_string.substr(1, trimmed_string.length() - 2));
|
||||||
|
// Copy string contents into the vector.
|
||||||
|
binary.assign(trimmed_string.begin(), trimmed_string.end());
|
||||||
|
}
|
||||||
|
// Return resulting vector or empty vector.
|
||||||
|
return (binary);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
decodeColonSeparatedHexString(const string& hex_string, vector<uint8_t>& binary) {
|
||||||
|
decodeSeparatedHexString(hex_string, ":", binary);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
decodeSeparatedHexString(const string& hex_string, const string& sep, vector<uint8_t>& binary) {
|
||||||
|
vector<string> split_text;
|
||||||
|
boost::split(split_text, hex_string, boost::is_any_of(sep),
|
||||||
|
boost::algorithm::token_compress_off);
|
||||||
|
|
||||||
|
vector<uint8_t> binary_vec;
|
||||||
|
for (size_t i = 0; i < split_text.size(); ++i) {
|
||||||
|
// If there are multiple tokens and the current one is empty, it
|
||||||
|
// means that two consecutive colons were specified. This is not
|
||||||
|
// allowed.
|
||||||
|
if ((split_text.size() > 1) && split_text[i].empty()) {
|
||||||
|
isc_throw(BadValue, "two consecutive separators ('"
|
||||||
|
<< sep << "') specified in a decoded string '" << hex_string
|
||||||
|
<< "'");
|
||||||
|
|
||||||
|
// Between a colon we expect at most two characters.
|
||||||
|
} else if (split_text[i].size() > 2) {
|
||||||
|
isc_throw(BadValue, "invalid format of the decoded string"
|
||||||
|
<< " '" << hex_string << "'");
|
||||||
|
|
||||||
|
} else if (!split_text[i].empty()) {
|
||||||
|
stringstream s;
|
||||||
|
s << "0x";
|
||||||
|
|
||||||
|
for (unsigned int j = 0; j < split_text[i].length(); ++j) {
|
||||||
|
// Check if we're dealing with hexadecimal digit.
|
||||||
|
if (!isxdigit(split_text[i][j])) {
|
||||||
|
isc_throw(BadValue, "'" << split_text[i][j]
|
||||||
|
<< "' is not a valid hexadecimal digit in"
|
||||||
|
<< " decoded string '" << hex_string << "'");
|
||||||
|
}
|
||||||
|
s << split_text[i][j];
|
||||||
|
}
|
||||||
|
|
||||||
|
// The stream should now have one or two hexadecimal digits.
|
||||||
|
// Let's convert it to a number and store in a temporary
|
||||||
|
// vector.
|
||||||
|
unsigned int binary_value;
|
||||||
|
s >> hex >> binary_value;
|
||||||
|
|
||||||
|
binary_vec.push_back(static_cast<uint8_t>(binary_value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All ok, replace the data in the output vector with a result.
|
||||||
|
binary.swap(binary_vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
decodeFormattedHexString(const string& hex_string, vector<uint8_t>& binary) {
|
||||||
|
// If there is at least one colon we assume that the string
|
||||||
|
// comprises octets separated by colons (e.g. MAC address notation).
|
||||||
|
if (hex_string.find(':') != string::npos) {
|
||||||
|
decodeSeparatedHexString(hex_string, ":", binary);
|
||||||
|
} else if (hex_string.find(' ') != string::npos) {
|
||||||
|
decodeSeparatedHexString(hex_string, " ", binary);
|
||||||
|
} else {
|
||||||
|
ostringstream s;
|
||||||
|
|
||||||
|
// If we have odd number of digits we'll have to prepend '0'.
|
||||||
|
if (hex_string.length() % 2 != 0) {
|
||||||
|
s << "0";
|
||||||
|
}
|
||||||
|
|
||||||
|
// It is ok to use '0x' prefix in a string.
|
||||||
|
if ((hex_string.length() > 2) && (hex_string.substr(0, 2) == "0x")) {
|
||||||
|
// Exclude '0x' from the decoded string.
|
||||||
|
s << hex_string.substr(2);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// No '0x', so decode the whole string.
|
||||||
|
s << hex_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Decode the hex string.
|
||||||
|
encode::decodeHex(s.str(), binary);
|
||||||
|
|
||||||
|
} catch (...) {
|
||||||
|
isc_throw(BadValue, "'" << hex_string
|
||||||
|
<< "' is not a valid"
|
||||||
|
" string of hexadecimal digits");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StringSanitizerImpl {
|
||||||
|
public:
|
||||||
|
/// @brief Constructor.
|
||||||
|
StringSanitizerImpl(const string& char_set, const string& char_replacement)
|
||||||
|
: char_set_(char_set), char_replacement_(char_replacement) {
|
||||||
|
if (char_set.size() > StringSanitizer::MAX_DATA_SIZE) {
|
||||||
|
isc_throw(BadValue, "char set size: '" << char_set.size() << "' exceeds max size: '"
|
||||||
|
<< StringSanitizer::MAX_DATA_SIZE << "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (char_replacement.size() > StringSanitizer::MAX_DATA_SIZE) {
|
||||||
|
isc_throw(BadValue, "char replacement size: '"
|
||||||
|
<< char_replacement.size() << "' exceeds max size: '"
|
||||||
|
<< StringSanitizer::MAX_DATA_SIZE << "'");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
scrub_exp_ = regex(char_set, regex::extended);
|
||||||
|
} catch (const exception& ex) {
|
||||||
|
isc_throw(BadValue, "invalid regex: '" << char_set_ << "', " << ex.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string scrub(const string& original) {
|
||||||
|
stringstream result;
|
||||||
|
try {
|
||||||
|
regex_replace(ostream_iterator<char>(result), original.begin(), original.end(),
|
||||||
|
scrub_exp_, char_replacement_);
|
||||||
|
} catch (const exception& ex) {
|
||||||
|
isc_throw(BadValue, "replacing '" << char_set_ << "' with '" << char_replacement_
|
||||||
|
<< "' in '" << original << "' failed: ,"
|
||||||
|
<< ex.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
return (result.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// @brief The char set data for regex.
|
||||||
|
string char_set_;
|
||||||
|
|
||||||
|
/// @brief The char replacement data for regex.
|
||||||
|
string char_replacement_;
|
||||||
|
|
||||||
|
regex scrub_exp_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// @note The regex engine is implemented using recursion and can cause
|
||||||
|
// stack overflow if the input data is too large. An arbitrary size of
|
||||||
|
// 4096 should be enough for all cases.
|
||||||
|
const uint32_t StringSanitizer::MAX_DATA_SIZE = 4096;
|
||||||
|
|
||||||
|
StringSanitizer::StringSanitizer(const string& char_set, const string& char_replacement)
|
||||||
|
: impl_(new StringSanitizerImpl(char_set, char_replacement)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
StringSanitizer::scrub(const string& original) {
|
||||||
|
return (impl_->scrub(original));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
isPrintable(const string& content) {
|
||||||
|
for (char const ch : content) {
|
||||||
|
if (isprint(ch) == 0) {
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
isPrintable(const vector<uint8_t>& content) {
|
||||||
|
for (uint8_t const ch : content) {
|
||||||
|
if (isprint(ch) == 0) {
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
dumpAsHex(const uint8_t* data, size_t length) {
|
||||||
|
stringstream output;
|
||||||
|
for (unsigned int i = 0; i < length; i++) {
|
||||||
|
if (i) {
|
||||||
|
output << ":";
|
||||||
|
}
|
||||||
|
|
||||||
|
output << setfill('0') << setw(2) << hex << static_cast<unsigned short>(data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (output.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace str
|
||||||
|
} // namespace util
|
||||||
|
} // namespace isc
|
@@ -4,19 +4,20 @@
|
|||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
#ifndef STRUTIL_H
|
#ifndef KEA_UTIL_STR_H
|
||||||
#define STRUTIL_H
|
#define KEA_UTIL_STR_H
|
||||||
|
|
||||||
|
#include <exceptions/exceptions.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cstddef>
|
||||||
#include <stdint.h>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <memory>
|
||||||
#include <iomanip>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <exceptions/exceptions.h>
|
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
#include <boost/shared_ptr.hpp>
|
|
||||||
|
|
||||||
namespace isc {
|
namespace isc {
|
||||||
namespace util {
|
namespace util {
|
||||||
@@ -27,59 +28,52 @@ namespace str {
|
|||||||
///
|
///
|
||||||
/// @brief A standard string util exception that is thrown if getToken or
|
/// @brief A standard string util exception that is thrown if getToken or
|
||||||
/// numToToken are called with bad input data
|
/// numToToken are called with bad input data
|
||||||
///
|
|
||||||
class StringTokenError : public Exception {
|
class StringTokenError : public Exception {
|
||||||
public:
|
public:
|
||||||
StringTokenError(const char* file, size_t line, const char* what) :
|
StringTokenError(const char* file, size_t line, const char* what)
|
||||||
isc::Exception(file, line, what) {}
|
: isc::Exception(file, line, what) {
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @brief Normalize Backslash
|
/// @brief Trim leading and trailing spaces.
|
||||||
///
|
|
||||||
/// Only relevant to Windows, this replaces all "\" in a string with "/"
|
|
||||||
/// and returns the result. On other systems it is a no-op. Note
|
|
||||||
/// that Windows does recognize file names with the "\" replaced by "/"
|
|
||||||
/// (at least in system calls, if not the command line).
|
|
||||||
///
|
|
||||||
/// @param name Name to be substituted
|
|
||||||
void normalizeSlash(std::string& name);
|
|
||||||
|
|
||||||
/// @brief Trim Leading and Trailing Spaces
|
|
||||||
///
|
///
|
||||||
/// Returns a copy of the input string but with any leading or trailing spaces
|
/// Returns a copy of the input string but with any leading or trailing spaces
|
||||||
/// or tabs removed.
|
/// or tabs removed.
|
||||||
///
|
///
|
||||||
/// @param instring Input string to modify
|
/// @param input Input string to modify.
|
||||||
///
|
///
|
||||||
/// @return String with leading and trailing spaces removed
|
/// @return String with leading and trailing spaces removed.
|
||||||
std::string trim(const std::string& instring);
|
std::string
|
||||||
|
trim(const std::string& input);
|
||||||
|
|
||||||
/// @brief Finds the "trimmed" end of a buffer
|
/// @brief Finds the "trimmed" end of a buffer
|
||||||
///
|
///
|
||||||
/// Works backward from the end of the buffer, looking for the first
|
/// Works backward from the end of the buffer, looking for the first
|
||||||
/// character not equal to the trim value, and returns an iterator
|
/// character not equal to the trim value, and returns an iterator
|
||||||
/// pointing that that position.
|
/// pointing to that position.
|
||||||
///
|
///
|
||||||
/// @param begin - Forward iterator pointing to the beginning of the
|
/// @param begin - Forward iterator pointing to the beginning of the
|
||||||
/// buffer to trim
|
/// buffer to trim.
|
||||||
/// @param end - Forward iterator pointing to the untrimmed end of
|
/// @param end - Forward iterator pointing to the untrimmed end of
|
||||||
/// the buffer to trim
|
/// the buffer to trim.
|
||||||
/// @param trim_val - byte value to trim off
|
/// @param trim_val - byte value to trim off
|
||||||
///
|
///
|
||||||
/// @return Iterator pointing the first character from the end of the
|
/// @return Iterator pointing the first character from the end of the
|
||||||
/// buffer not equal to the trim value
|
/// buffer not equal to the trim value.
|
||||||
template<typename Iterator>
|
template <typename Iterator>
|
||||||
Iterator
|
Iterator
|
||||||
seekTrimmed(Iterator begin, Iterator end, uint8_t trim_val) {
|
seekTrimmed(Iterator const& begin, Iterator end, uint8_t const trim_val) {
|
||||||
for (; end != begin && *(end - 1) == trim_val; --end);
|
while (end != begin && *(end - 1) == trim_val) {
|
||||||
|
--end;
|
||||||
|
}
|
||||||
return (end);
|
return (end);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Split String into Tokens
|
/// @brief Split string into tokens.
|
||||||
///
|
///
|
||||||
/// Splits a string into tokens (the tokens being delimited by one or more of
|
/// Splits a string into tokens (the tokens being delimited by one or more of
|
||||||
/// the delimiter characters) and returns the tokens in a vector array. Note
|
/// the delimiter characters) and returns the tokens in a vector.
|
||||||
/// that adjacent delimiters are considered to be a single delimiter.
|
/// Adjacent delimiters are considered to be a single delimiter.
|
||||||
///
|
///
|
||||||
/// Special cases are:
|
/// Special cases are:
|
||||||
/// -# The empty string is considered to be zero tokens.
|
/// -# The empty string is considered to be zero tokens.
|
||||||
@@ -102,120 +96,46 @@ seekTrimmed(Iterator begin, Iterator end, uint8_t trim_val) {
|
|||||||
/// @param escape Use backslash to escape delimiter characters
|
/// @param escape Use backslash to escape delimiter characters
|
||||||
///
|
///
|
||||||
/// @return Vector of tokens.
|
/// @return Vector of tokens.
|
||||||
std::vector<std::string> tokens(const std::string& text,
|
std::vector<std::string>
|
||||||
const std::string& delim = std::string(" \t\n"),
|
tokens(const std::string& text, const std::string& delim = " \t\n", bool escape = false);
|
||||||
bool escape = false);
|
|
||||||
|
|
||||||
/// @brief Uppercase Character
|
/// @brief Convert character to uppercase.
|
||||||
///
|
///
|
||||||
/// Used in uppercase() to pass as an argument to std::transform(). The
|
/// Used in uppercase() to pass as a parameter to std::transform(). The
|
||||||
/// function std::toupper() can't be used as it takes an "int" as its argument;
|
/// function std::toupper() can't be used as it takes an "int" as its parameter;
|
||||||
/// this confuses the template expansion mechanism because dereferencing a
|
/// this confuses the template expansion mechanism because dereferencing a
|
||||||
/// string::iterator returns a char.
|
/// string::iterator returns a char.
|
||||||
///
|
///
|
||||||
/// @param chr Character to be upper-cased.
|
/// @param chr Character to be upper-cased.
|
||||||
///
|
///
|
||||||
/// @return Uppercase version of the argument
|
/// @return Uppercase version of the input character.
|
||||||
inline char toUpper(char chr) {
|
char
|
||||||
return (static_cast<char>(std::toupper(static_cast<int>(chr))));
|
toUpper(char const chr);
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Uppercase String
|
/// @brief Convert string to uppercase.
|
||||||
///
|
|
||||||
/// A convenience function to uppercase a string.
|
|
||||||
///
|
///
|
||||||
/// @param text String to be upper-cased.
|
/// @param text String to be upper-cased.
|
||||||
inline void uppercase(std::string& text) {
|
void
|
||||||
std::transform(text.begin(), text.end(), text.begin(),
|
uppercase(std::string& text);
|
||||||
isc::util::str::toUpper);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Lowercase Character
|
/// @brief Convert character to lowercase.
|
||||||
///
|
///
|
||||||
/// Used in lowercase() to pass as an argument to std::transform(). The
|
/// Used in lowercase() to pass as a parameter to std::transform(). The
|
||||||
/// function std::tolower() can't be used as it takes an "int" as its argument;
|
/// function std::tolower() can't be used as it takes an "int" as its parameter;
|
||||||
/// this confuses the template expansion mechanism because dereferencing a
|
/// this confuses the template expansion mechanism because dereferencing a
|
||||||
/// string::iterator returns a char.
|
/// string::iterator returns a char.
|
||||||
///
|
///
|
||||||
/// @param chr Character to be lower-cased.
|
/// @param chr Character to be lower-cased.
|
||||||
///
|
///
|
||||||
/// @return Lowercase version of the argument
|
/// @return Lowercase version of the input character.
|
||||||
inline char toLower(char chr) {
|
char
|
||||||
return (static_cast<char>(std::tolower(static_cast<int>(chr))));
|
toLower(char const chr);
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Lowercase String
|
/// @brief Convert string to lowercase.
|
||||||
///
|
|
||||||
/// A convenience function to lowercase a string
|
|
||||||
///
|
///
|
||||||
/// @param text String to be lower-cased.
|
/// @param text String to be lower-cased.
|
||||||
inline void lowercase(std::string& text) {
|
void
|
||||||
std::transform(text.begin(), text.end(), text.begin(),
|
lowercase(std::string& text);
|
||||||
isc::util::str::toLower);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Apply Formatting
|
|
||||||
///
|
|
||||||
/// Given a printf-style format string containing only "%s" place holders
|
|
||||||
/// (others are ignored) and a vector of strings, this produces a single string
|
|
||||||
/// with the placeholders replaced.
|
|
||||||
///
|
|
||||||
/// @param format Format string
|
|
||||||
/// @param args Vector of argument strings
|
|
||||||
///
|
|
||||||
/// @return Resultant string
|
|
||||||
std::string format(const std::string& format,
|
|
||||||
const std::vector<std::string>& args);
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief Returns one token from the given stringstream
|
|
||||||
///
|
|
||||||
/// Using the >> operator, with basic error checking
|
|
||||||
///
|
|
||||||
/// @throw StringTokenError if the token cannot be read from the stream
|
|
||||||
///
|
|
||||||
/// @param iss stringstream to read one token from
|
|
||||||
///
|
|
||||||
/// @return the first token read from the stringstream
|
|
||||||
std::string getToken(std::istringstream& iss);
|
|
||||||
|
|
||||||
/// @brief Converts a string token to an *unsigned* integer.
|
|
||||||
///
|
|
||||||
/// The value is converted using a lexical cast, with error and bounds
|
|
||||||
/// checking.
|
|
||||||
///
|
|
||||||
/// NumType is a *signed* integral type (e.g. int32_t) that is sufficiently
|
|
||||||
/// wide to store resulting integers.
|
|
||||||
///
|
|
||||||
/// BitSize is the maximum number of bits that the resulting integer can take.
|
|
||||||
/// This function first checks whether the given token can be converted to
|
|
||||||
/// an integer of NumType type. It then confirms the conversion result is
|
|
||||||
/// within the valid range, i.e., [0, 2^BitSize - 1]. The second check is
|
|
||||||
/// necessary because lexical_cast<T> where T is an unsigned integer type
|
|
||||||
/// doesn't correctly reject negative numbers when compiled with SunStudio.
|
|
||||||
///
|
|
||||||
/// @throw StringTokenError if the value is out of range, or if it
|
|
||||||
/// could not be converted
|
|
||||||
///
|
|
||||||
/// @param num_token the string token to convert
|
|
||||||
///
|
|
||||||
/// @return the converted value, of type NumType
|
|
||||||
template <typename NumType, int BitSize>
|
|
||||||
NumType
|
|
||||||
tokenToNum(const std::string& num_token) {
|
|
||||||
NumType num;
|
|
||||||
try {
|
|
||||||
num = boost::lexical_cast<NumType>(num_token);
|
|
||||||
} catch (const boost::bad_lexical_cast&) {
|
|
||||||
isc_throw(StringTokenError, "Invalid SRV numeric parameter: " <<
|
|
||||||
num_token);
|
|
||||||
}
|
|
||||||
if (num < 0 || num >= (static_cast<NumType>(1) << BitSize)) {
|
|
||||||
isc_throw(StringTokenError, "Numeric SRV parameter out of range: " <<
|
|
||||||
num);
|
|
||||||
}
|
|
||||||
return (num);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Converts a string in quotes into vector.
|
/// @brief Converts a string in quotes into vector.
|
||||||
///
|
///
|
||||||
@@ -263,16 +183,12 @@ decodeSeparatedHexString(const std::string& hex_string,
|
|||||||
/// @brief Converts a string of hexadecimal digits with colons into
|
/// @brief Converts a string of hexadecimal digits with colons into
|
||||||
/// a vector.
|
/// a vector.
|
||||||
///
|
///
|
||||||
/// Convenience method which calls @c decodeSeparatedHexString() passing
|
|
||||||
/// in a colon for the separator.
|
|
||||||
|
|
||||||
/// @param hex_string Input string.
|
/// @param hex_string Input string.
|
||||||
/// @param binary Vector receiving converted string into binary.
|
/// @param binary Vector receiving converted string into binary.
|
||||||
///
|
///
|
||||||
/// @throw isc::BadValue if the format of the input string is invalid.
|
/// @throw isc::BadValue if the format of the input string is invalid.
|
||||||
void
|
void
|
||||||
decodeColonSeparatedHexString(const std::string& hex_string,
|
decodeColonSeparatedHexString(const std::string& hex_string, std::vector<uint8_t>& binary);
|
||||||
std::vector<uint8_t>& binary);
|
|
||||||
|
|
||||||
/// @brief Converts a formatted string of hexadecimal digits into
|
/// @brief Converts a formatted string of hexadecimal digits into
|
||||||
/// a vector.
|
/// a vector.
|
||||||
@@ -293,24 +209,17 @@ decodeColonSeparatedHexString(const std::string& hex_string,
|
|||||||
///
|
///
|
||||||
/// @throw isc::BadValue if the format of the input string is invalid.
|
/// @throw isc::BadValue if the format of the input string is invalid.
|
||||||
void
|
void
|
||||||
decodeFormattedHexString(const std::string& hex_string,
|
decodeFormattedHexString(const std::string& hex_string, std::vector<uint8_t>& binary);
|
||||||
std::vector<uint8_t>& binary);
|
|
||||||
|
|
||||||
/// @brief Forward declaration to the @c StringSanitizer implementation.
|
/// @brief Forward declaration to the @c StringSanitizer implementation.
|
||||||
class StringSanitizerImpl;
|
class StringSanitizerImpl;
|
||||||
|
|
||||||
/// @brief Type representing the pointer to the @c StringSanitizerImpl.
|
/// @brief Type representing the pointer to the @c StringSanitizerImpl.
|
||||||
typedef boost::shared_ptr<StringSanitizerImpl> StringSanitizerImplPtr;
|
using StringSanitizerImplPtr = std::shared_ptr<StringSanitizerImpl>;
|
||||||
|
|
||||||
/// @brief Implements a regular expression based string scrubber
|
/// @brief Implements a regular expression based string scrubber.
|
||||||
///
|
|
||||||
/// The implementation uses C++11 regex IF the environment supports it
|
|
||||||
/// (tested in configure.ac). If not it falls back to C lib regcomp/regexec.
|
|
||||||
/// Older compilers, such as pre Gnu g++ 4.9.0, provided only experimental
|
|
||||||
/// implementations of regex which are recognized as buggy.
|
|
||||||
class StringSanitizer {
|
class StringSanitizer {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// @brief Constructor.
|
/// @brief Constructor.
|
||||||
///
|
///
|
||||||
/// Compiles the given character set into a regular expression, and
|
/// Compiles the given character set into a regular expression, and
|
||||||
@@ -324,23 +233,17 @@ public:
|
|||||||
/// @param char_replacement string of one or more characters to use as the
|
/// @param char_replacement string of one or more characters to use as the
|
||||||
/// replacement for invalid characters.
|
/// replacement for invalid characters.
|
||||||
///
|
///
|
||||||
/// @throw BadValue if given an invalid regular expression
|
/// @throw BadValue if given an invalid regular expression.
|
||||||
StringSanitizer(const std::string& char_set,
|
StringSanitizer(const std::string& char_set, const std::string& char_replacement);
|
||||||
const std::string& char_replacement);
|
|
||||||
|
|
||||||
/// @brief Destructor.
|
/// @brief Returns a scrubbed copy of a given string.
|
||||||
///
|
|
||||||
/// Destroys the implementation instance.
|
|
||||||
~StringSanitizer();
|
|
||||||
|
|
||||||
/// Returns a scrubbed copy of a given string
|
|
||||||
///
|
///
|
||||||
/// Replaces all occurrences of characters described by the regular
|
/// Replaces all occurrences of characters described by the regular
|
||||||
/// expression with the character replacement.
|
/// expression with the character replacement.
|
||||||
///
|
///
|
||||||
/// @param original the string to scrub
|
/// @param original The string to be scrubbed.
|
||||||
///
|
///
|
||||||
/// @throw Unexpected if an error occurs during scrubbing
|
/// @throw Unexpected if an error occurs during scrubbing.
|
||||||
std::string scrub(const std::string& original);
|
std::string scrub(const std::string& original);
|
||||||
|
|
||||||
/// @brief The maximum size for regex parameters.
|
/// @brief The maximum size for regex parameters.
|
||||||
@@ -356,48 +259,34 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// @brief Type representing the pointer to the @c StringSanitizer.
|
/// @brief Type representing the pointer to the @c StringSanitizer.
|
||||||
typedef boost::shared_ptr<StringSanitizer> StringSanitizerPtr;
|
using StringSanitizerPtr = std::unique_ptr<StringSanitizer>;
|
||||||
|
|
||||||
/// @brief Check if a string is printable
|
/// @brief Check if a string is printable.
|
||||||
///
|
///
|
||||||
/// @param content String to check for printable characters
|
/// @param content String to check for printable characters.
|
||||||
///
|
///
|
||||||
/// @return True if empty or contains only printable characters, False otherwise
|
/// @return True if empty or contains only printable characters, False otherwise.
|
||||||
inline bool
|
bool
|
||||||
isPrintable(const std::string& content) {
|
isPrintable(const std::string& content);
|
||||||
for (auto const& ch : content) {
|
|
||||||
if (isprint(static_cast<int>(ch)) == 0) {
|
|
||||||
return (false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Check if a byte vector is printable
|
/// @brief Check if a byte vector is printable.
|
||||||
///
|
///
|
||||||
/// @param content Vector to check for printable characters
|
/// @param content Vector to check for printable characters.
|
||||||
///
|
///
|
||||||
/// @return True if empty or contains only printable characters, False otherwise
|
/// @return True if empty or contains only printable characters, False otherwise.
|
||||||
inline bool
|
bool
|
||||||
isPrintable(const std::vector<uint8_t>& content) {
|
isPrintable(const std::vector<uint8_t>& content);
|
||||||
for (auto const& ch : content) {
|
|
||||||
if (isprint(static_cast<int>(ch)) == 0) {
|
|
||||||
return (false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// @brief Dumps a buffer of bytes as a string of hexadecimal digits.
|
||||||
/// @brief Dumps a buffer of bytes as a string of hexadecimal digits
|
|
||||||
///
|
///
|
||||||
/// @param data pointer to the data to dump
|
/// @param data Pointer to the data to dump.
|
||||||
/// @param length number of bytes to dump. Caller should ensure the length
|
/// @param length Number of bytes to dump. Caller should ensure the length
|
||||||
/// does not exceed the buffer.
|
/// does not exceed the buffer.
|
||||||
std::string dumpAsHex(const uint8_t* data, size_t length);
|
std::string
|
||||||
|
dumpAsHex(const uint8_t* data, size_t length);
|
||||||
|
|
||||||
} // namespace str
|
} // namespace str
|
||||||
} // namespace util
|
} // namespace util
|
||||||
} // namespace isc
|
} // namespace isc
|
||||||
|
|
||||||
#endif // STRUTIL_H
|
#endif // KEA_UTIL_STR_H
|
@@ -1,467 +0,0 @@
|
|||||||
// Copyright (C) 2011-2024 Internet Systems Consortium, Inc. ("ISC")
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
|
|
||||||
#include <util/encode/encode.h>
|
|
||||||
#include <util/strutil.h>
|
|
||||||
|
|
||||||
#include <boost/algorithm/string/classification.hpp>
|
|
||||||
#include <boost/algorithm/string/constants.hpp>
|
|
||||||
#include <boost/algorithm/string/split.hpp>
|
|
||||||
|
|
||||||
#include <numeric>
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
// Early versions of C++11 regex were buggy, use it if we
|
|
||||||
// can otherwise, we fall back to regcomp/regexec. For more info see:
|
|
||||||
// https://stackoverflow.com/questions/12530406/is-gcc-4-8-or-earlier-buggy-about-regular-expressions
|
|
||||||
#ifdef USE_REGEX
|
|
||||||
#include <regex>
|
|
||||||
#else
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <regex.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace isc {
|
|
||||||
namespace util {
|
|
||||||
namespace str {
|
|
||||||
|
|
||||||
// Normalize slashes
|
|
||||||
|
|
||||||
void
|
|
||||||
normalizeSlash(std::string& name) {
|
|
||||||
if (!name.empty()) {
|
|
||||||
size_t pos = 0;
|
|
||||||
while ((pos = name.find('\\', pos)) != std::string::npos) {
|
|
||||||
name[pos] = '/';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trim String
|
|
||||||
|
|
||||||
string
|
|
||||||
trim(const string& instring) {
|
|
||||||
string retstring = "";
|
|
||||||
if (!instring.empty()) {
|
|
||||||
static const char* blanks = " \t\n";
|
|
||||||
|
|
||||||
// Search for first non-blank character in the string
|
|
||||||
size_t first = instring.find_first_not_of(blanks);
|
|
||||||
if (first != string::npos) {
|
|
||||||
|
|
||||||
// String not all blanks, so look for last character
|
|
||||||
size_t last = instring.find_last_not_of(blanks);
|
|
||||||
|
|
||||||
// Extract the trimmed substring
|
|
||||||
retstring = instring.substr(first, (last - first + 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (retstring);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tokenize string. As noted in the header, this is locally written to avoid
|
|
||||||
// another dependency on a Boost library.
|
|
||||||
|
|
||||||
vector<string>
|
|
||||||
tokens(const std::string& text, const std::string& delim, bool escape) {
|
|
||||||
vector<string> result;
|
|
||||||
string token;
|
|
||||||
bool in_token = false;
|
|
||||||
bool escaped = false;
|
|
||||||
for (auto const& c : text) {
|
|
||||||
if (delim.find(c) != string::npos) {
|
|
||||||
// Current character is a delimiter
|
|
||||||
if (!in_token) {
|
|
||||||
// Two or more delimiters, eat them
|
|
||||||
} else if (escaped) {
|
|
||||||
// Escaped delimiter in a token: reset escaped and keep it
|
|
||||||
escaped = false;
|
|
||||||
token.push_back(c);
|
|
||||||
} else {
|
|
||||||
// End of the current token: save it if not empty
|
|
||||||
if (!token.empty()) {
|
|
||||||
result.push_back(token);
|
|
||||||
}
|
|
||||||
// Reset state
|
|
||||||
in_token = false;
|
|
||||||
token.clear();
|
|
||||||
}
|
|
||||||
} else if (escape && (c == '\\')) {
|
|
||||||
// Current character is the escape character
|
|
||||||
if (!in_token) {
|
|
||||||
// The escape character is the first character of a new token
|
|
||||||
in_token = true;
|
|
||||||
}
|
|
||||||
if (escaped) {
|
|
||||||
// Escaped escape: reset escaped and keep one character
|
|
||||||
escaped = false;
|
|
||||||
token.push_back(c);
|
|
||||||
} else {
|
|
||||||
// Remember to keep the next character
|
|
||||||
escaped = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Not a delimiter nor an escape
|
|
||||||
if (!in_token) {
|
|
||||||
// First character of a new token
|
|
||||||
in_token = true;
|
|
||||||
}
|
|
||||||
if (escaped) {
|
|
||||||
// Escaped common character: as escape was false
|
|
||||||
escaped = false;
|
|
||||||
token.push_back('\\');
|
|
||||||
token.push_back(c);
|
|
||||||
} else {
|
|
||||||
// The common case: keep it
|
|
||||||
token.push_back(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// End of input: close and save the current token if not empty
|
|
||||||
if (escaped) {
|
|
||||||
// Pending escape
|
|
||||||
token.push_back('\\');
|
|
||||||
}
|
|
||||||
if (!token.empty()) {
|
|
||||||
result.push_back(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Local function to pass to accumulate() for summing up string lengths.
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
size_t
|
|
||||||
lengthSum(string::size_type curlen, const string& cur_string) {
|
|
||||||
return (curlen + cur_string.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Provide printf-style formatting.
|
|
||||||
|
|
||||||
std::string
|
|
||||||
format(const std::string& format, const std::vector<std::string>& args) {
|
|
||||||
|
|
||||||
static const string flag = "%s";
|
|
||||||
|
|
||||||
// Initialize return string. To speed things up, we'll reserve an
|
|
||||||
// appropriate amount of space - current string size, plus length of all
|
|
||||||
// the argument strings, less two characters for each argument (the %s in
|
|
||||||
// the format string is being replaced).
|
|
||||||
string result;
|
|
||||||
size_t length = accumulate(args.begin(), args.end(), format.size(),
|
|
||||||
lengthSum) - (args.size() * flag.size());
|
|
||||||
result.reserve(length);
|
|
||||||
|
|
||||||
// Iterate through replacing all tokens
|
|
||||||
result = format;
|
|
||||||
size_t tokenpos = 0; // Position of last token replaced
|
|
||||||
std::vector<std::string>::size_type i = 0; // Index into argument array
|
|
||||||
|
|
||||||
while ((i < args.size()) && (tokenpos != string::npos)) {
|
|
||||||
tokenpos = result.find(flag, tokenpos);
|
|
||||||
if (tokenpos != string::npos) {
|
|
||||||
result.replace(tokenpos, flag.size(), args[i++]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (result);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
getToken(std::istringstream& iss) {
|
|
||||||
string token;
|
|
||||||
iss >> token;
|
|
||||||
if (iss.bad() || iss.fail()) {
|
|
||||||
isc_throw(StringTokenError, "could not read token from string");
|
|
||||||
}
|
|
||||||
return (token);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t>
|
|
||||||
quotedStringToBinary(const std::string& quoted_string) {
|
|
||||||
std::vector<uint8_t> binary;
|
|
||||||
// Remove whitespace before and after the quotes.
|
|
||||||
std::string trimmed_string = trim(quoted_string);
|
|
||||||
|
|
||||||
// We require two quote characters, so the length of the string must be
|
|
||||||
// equal to 2 at minimum, and it must start and end with quotes.
|
|
||||||
if ((trimmed_string.length() > 1) && ((trimmed_string[0] == '\'') &&
|
|
||||||
(trimmed_string[trimmed_string.length()-1] == '\''))) {
|
|
||||||
// Remove quotes and trim the text inside the quotes.
|
|
||||||
trimmed_string = trim(trimmed_string.substr(1, trimmed_string.length() - 2));
|
|
||||||
// Copy string contents into the vector.
|
|
||||||
binary.assign(trimmed_string.begin(), trimmed_string.end());
|
|
||||||
}
|
|
||||||
// Return resulting vector or empty vector.
|
|
||||||
return (binary);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
decodeColonSeparatedHexString(const std::string& hex_string,
|
|
||||||
std::vector<uint8_t>& binary) {
|
|
||||||
decodeSeparatedHexString(hex_string, ":", binary);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
decodeSeparatedHexString(const std::string& hex_string, const std::string& sep,
|
|
||||||
std::vector<uint8_t>& binary) {
|
|
||||||
std::vector<std::string> split_text;
|
|
||||||
boost::split(split_text, hex_string, boost::is_any_of(sep),
|
|
||||||
boost::algorithm::token_compress_off);
|
|
||||||
|
|
||||||
std::vector<uint8_t> binary_vec;
|
|
||||||
for (size_t i = 0; i < split_text.size(); ++i) {
|
|
||||||
|
|
||||||
// If there are multiple tokens and the current one is empty, it
|
|
||||||
// means that two consecutive colons were specified. This is not
|
|
||||||
// allowed.
|
|
||||||
if ((split_text.size() > 1) && split_text[i].empty()) {
|
|
||||||
isc_throw(isc::BadValue, "two consecutive separators ('" << sep << "') specified in"
|
|
||||||
" a decoded string '" << hex_string << "'");
|
|
||||||
|
|
||||||
// Between a colon we expect at most two characters.
|
|
||||||
} else if (split_text[i].size() > 2) {
|
|
||||||
isc_throw(isc::BadValue, "invalid format of the decoded string"
|
|
||||||
<< " '" << hex_string << "'");
|
|
||||||
|
|
||||||
} else if (!split_text[i].empty()) {
|
|
||||||
std::stringstream s;
|
|
||||||
s << "0x";
|
|
||||||
|
|
||||||
for (unsigned int j = 0; j < split_text[i].length(); ++j) {
|
|
||||||
// Check if we're dealing with hexadecimal digit.
|
|
||||||
if (!isxdigit(split_text[i][j])) {
|
|
||||||
isc_throw(isc::BadValue, "'" << split_text[i][j]
|
|
||||||
<< "' is not a valid hexadecimal digit in"
|
|
||||||
<< " decoded string '" << hex_string << "'");
|
|
||||||
}
|
|
||||||
s << split_text[i][j];
|
|
||||||
}
|
|
||||||
|
|
||||||
// The stream should now have one or two hexadecimal digits.
|
|
||||||
// Let's convert it to a number and store in a temporary
|
|
||||||
// vector.
|
|
||||||
unsigned int binary_value;
|
|
||||||
s >> std::hex >> binary_value;
|
|
||||||
|
|
||||||
binary_vec.push_back(static_cast<uint8_t>(binary_value));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// All ok, replace the data in the output vector with a result.
|
|
||||||
binary.swap(binary_vec);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
decodeFormattedHexString(const std::string& hex_string,
|
|
||||||
std::vector<uint8_t>& binary) {
|
|
||||||
// If there is at least one colon we assume that the string
|
|
||||||
// comprises octets separated by colons (e.g. MAC address notation).
|
|
||||||
if (hex_string.find(':') != std::string::npos) {
|
|
||||||
decodeSeparatedHexString(hex_string, ":", binary);
|
|
||||||
} else if (hex_string.find(' ') != std::string::npos) {
|
|
||||||
decodeSeparatedHexString(hex_string, " ", binary);
|
|
||||||
} else {
|
|
||||||
std::ostringstream s;
|
|
||||||
|
|
||||||
// If we have odd number of digits we'll have to prepend '0'.
|
|
||||||
if (hex_string.length() % 2 != 0) {
|
|
||||||
s << "0";
|
|
||||||
}
|
|
||||||
|
|
||||||
// It is ok to use '0x' prefix in a string.
|
|
||||||
if ((hex_string.length() > 2) && (hex_string.substr(0, 2) == "0x")) {
|
|
||||||
// Exclude '0x' from the decoded string.
|
|
||||||
s << hex_string.substr(2);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// No '0x', so decode the whole string.
|
|
||||||
s << hex_string;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Decode the hex string.
|
|
||||||
encode::decodeHex(s.str(), binary);
|
|
||||||
|
|
||||||
} catch (...) {
|
|
||||||
isc_throw(isc::BadValue, "'" << hex_string << "' is not a valid"
|
|
||||||
" string of hexadecimal digits");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StringSanitizerImpl {
|
|
||||||
public:
|
|
||||||
/// @brief Constructor.
|
|
||||||
StringSanitizerImpl(const std::string& char_set, const std::string& char_replacement)
|
|
||||||
: char_set_(char_set), char_replacement_(char_replacement) {
|
|
||||||
if (char_set.size() > StringSanitizer::MAX_DATA_SIZE) {
|
|
||||||
isc_throw(isc::BadValue, "char set size: '" << char_set.size()
|
|
||||||
<< "' exceeds max size: '"
|
|
||||||
<< StringSanitizer::MAX_DATA_SIZE << "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (char_replacement.size() > StringSanitizer::MAX_DATA_SIZE) {
|
|
||||||
isc_throw(isc::BadValue, "char replacement size: '"
|
|
||||||
<< char_replacement.size() << "' exceeds max size: '"
|
|
||||||
<< StringSanitizer::MAX_DATA_SIZE << "'");
|
|
||||||
}
|
|
||||||
#ifdef USE_REGEX
|
|
||||||
try {
|
|
||||||
scrub_exp_ = std::regex(char_set, std::regex::extended);
|
|
||||||
} catch (const std::exception& ex) {
|
|
||||||
isc_throw(isc::BadValue, "invalid regex: '"
|
|
||||||
<< char_set_ << "', " << ex.what());
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
int ec = regcomp(&scrub_exp_, char_set_.c_str(), REG_EXTENDED);
|
|
||||||
if (ec) {
|
|
||||||
char errbuf[512] = "";
|
|
||||||
static_cast<void>(regerror(ec, &scrub_exp_, errbuf, sizeof(errbuf)));
|
|
||||||
regfree(&scrub_exp_);
|
|
||||||
isc_throw(isc::BadValue, "invalid regex: '" << char_set_ << "', " << errbuf);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Destructor.
|
|
||||||
~StringSanitizerImpl() {
|
|
||||||
#ifndef USE_REGEX
|
|
||||||
regfree(&scrub_exp_);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string scrub(const std::string& original) {
|
|
||||||
#ifdef USE_REGEX
|
|
||||||
std::stringstream result;
|
|
||||||
try {
|
|
||||||
std::regex_replace(std::ostream_iterator<char>(result),
|
|
||||||
original.begin(), original.end(),
|
|
||||||
scrub_exp_, char_replacement_);
|
|
||||||
} catch (const std::exception& ex) {
|
|
||||||
isc_throw(isc::BadValue, "replacing '" << char_set_ << "' with '"
|
|
||||||
<< char_replacement_ << "' in '" << original << "' failed: ,"
|
|
||||||
<< ex.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
return (result.str());
|
|
||||||
#else
|
|
||||||
// In order to handle embedded nuls, we have to process in nul-terminated
|
|
||||||
// chunks. We iterate over the original data, doing pattern replacement
|
|
||||||
// on each chunk.
|
|
||||||
const char* orig_data = original.data();
|
|
||||||
const char* dead_end = orig_data + original.size();
|
|
||||||
const char* start_from = orig_data;
|
|
||||||
stringstream result;
|
|
||||||
|
|
||||||
while (start_from < dead_end) {
|
|
||||||
// Iterate over original string, match by match.
|
|
||||||
regmatch_t matches[2]; // n matches + 1
|
|
||||||
const char* end_at = start_from + strlen(start_from);
|
|
||||||
|
|
||||||
while (start_from < end_at) {
|
|
||||||
// Look for the next match
|
|
||||||
if (regexec(&scrub_exp_, start_from, 1, matches, 0) == REG_NOMATCH) {
|
|
||||||
// No matches, so add in the remainder
|
|
||||||
result << start_from;
|
|
||||||
start_from = end_at + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shouldn't happen, but one never knows eh?
|
|
||||||
if (matches[0].rm_so == -1) {
|
|
||||||
isc_throw(isc::Unexpected, "matched but so is -1?");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add everything from starting point up to the current match
|
|
||||||
const char* match_at = start_from + matches[0].rm_so;
|
|
||||||
while (start_from < match_at) {
|
|
||||||
result << *start_from;
|
|
||||||
++start_from;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add in the replacement
|
|
||||||
result << char_replacement_;
|
|
||||||
|
|
||||||
// Move past the match.
|
|
||||||
++start_from;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we have an embedded nul, replace it and continue
|
|
||||||
if (start_from < dead_end) {
|
|
||||||
// Add in the replacement
|
|
||||||
result << char_replacement_;
|
|
||||||
start_from = end_at + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (result.str());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// @brief The char set data for regex.
|
|
||||||
std::string char_set_;
|
|
||||||
|
|
||||||
/// @brief The char replacement data for regex.
|
|
||||||
std::string char_replacement_;
|
|
||||||
|
|
||||||
#ifdef USE_REGEX
|
|
||||||
regex scrub_exp_;
|
|
||||||
#else
|
|
||||||
regex_t scrub_exp_;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
// @note The regex engine is implemented using recursion and can cause
|
|
||||||
// stack overflow if the input data is too large. An arbitrary size of
|
|
||||||
// 4096 should be enough for all cases.
|
|
||||||
const uint32_t StringSanitizer::MAX_DATA_SIZE = 4096;
|
|
||||||
|
|
||||||
StringSanitizer::StringSanitizer(const std::string& char_set,
|
|
||||||
const std::string& char_replacement)
|
|
||||||
: impl_(new StringSanitizerImpl(char_set, char_replacement)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
StringSanitizer::~StringSanitizer() {
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
StringSanitizer::scrub(const std::string& original) {
|
|
||||||
return (impl_->scrub(original));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string dumpAsHex(const uint8_t* data, size_t length) {
|
|
||||||
std::stringstream output;
|
|
||||||
for (unsigned int i = 0; i < length; i++) {
|
|
||||||
if (i) {
|
|
||||||
output << ":";
|
|
||||||
}
|
|
||||||
|
|
||||||
output << std::setfill('0') << std::setw(2) << std::hex
|
|
||||||
<< static_cast<unsigned short>(data[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (output.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace str
|
|
||||||
} // namespace util
|
|
||||||
} // namespace isc
|
|
514
src/lib/util/tests/str_unittests.cc
Normal file
514
src/lib/util/tests/str_unittests.cc
Normal file
@@ -0,0 +1,514 @@
|
|||||||
|
// Copyright (C) 2011-2024 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <testutils/gtest_utils.h>
|
||||||
|
#include <util/encode/encode.h>
|
||||||
|
#include <util/str.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <exception>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
using namespace isc;
|
||||||
|
using namespace isc::util;
|
||||||
|
using namespace isc::util::encode;
|
||||||
|
using namespace isc::util::str;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/// @brief Fixture used to test StringSanitizer.
|
||||||
|
struct StringUtilTest : ::testing::Test {
|
||||||
|
/// @brief Pass string through scrub and check the result.
|
||||||
|
///
|
||||||
|
/// @param original The string to sanitize.
|
||||||
|
/// @param char_set The regular expression string describing invalid characters.
|
||||||
|
/// @param char_replacement - character(s) which replace invalid
|
||||||
|
/// characters
|
||||||
|
/// @param expected - expected sanitized string
|
||||||
|
void checkScrub(const string& original,
|
||||||
|
const string& char_set,
|
||||||
|
const string& char_replacement,
|
||||||
|
const string& expected) {
|
||||||
|
StringSanitizerPtr ss;
|
||||||
|
string sanitized;
|
||||||
|
|
||||||
|
try {
|
||||||
|
ss.reset(new StringSanitizer(char_set, char_replacement));
|
||||||
|
} catch (const exception& ex) {
|
||||||
|
ADD_FAILURE() << "Could not construct sanitizer:" << ex.what();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
sanitized = ss->scrub(original);
|
||||||
|
} catch (const exception& ex) {
|
||||||
|
ADD_FAILURE() << "Could not scrub string:" << ex.what();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(sanitized, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Check that hex strings with colons can be decoded.
|
||||||
|
///
|
||||||
|
/// @param input Input string to be decoded.
|
||||||
|
/// @param reference The expected result.
|
||||||
|
void checkColonSeparated(const string& input, const string& reference) {
|
||||||
|
// Create a reference vector.
|
||||||
|
vector<uint8_t> reference_vector;
|
||||||
|
ASSERT_NO_THROW_LOG(decodeHex(reference, reference_vector));
|
||||||
|
|
||||||
|
// Fill the output vector with some garbage to make sure that
|
||||||
|
// the data is erased when a string is decoded successfully.
|
||||||
|
vector<uint8_t> decoded(1, 10);
|
||||||
|
ASSERT_NO_THROW_LOG(decodeColonSeparatedHexString(input, decoded));
|
||||||
|
|
||||||
|
// Get the string representation of the decoded data for logging
|
||||||
|
// purposes.
|
||||||
|
string encoded;
|
||||||
|
ASSERT_NO_THROW_LOG(encoded = encodeHex(decoded));
|
||||||
|
|
||||||
|
// Check if the decoded data matches the reference.
|
||||||
|
EXPECT_EQ(decoded, reference_vector) << "decoded data don't match the reference, input='"
|
||||||
|
<< input << "', reference='" << reference
|
||||||
|
<< "'"
|
||||||
|
", decoded='"
|
||||||
|
<< encoded << "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Check that formatted hex strings can be decoded.
|
||||||
|
///
|
||||||
|
/// @param input Input string to be decoded.
|
||||||
|
/// @param reference The expected result.
|
||||||
|
void checkFormatted(const string& input, const string& reference) {
|
||||||
|
// Create a reference vector.
|
||||||
|
vector<uint8_t> reference_vector;
|
||||||
|
ASSERT_NO_THROW_LOG(decodeHex(reference, reference_vector));
|
||||||
|
|
||||||
|
// Fill the output vector with some garbage to make sure that
|
||||||
|
// the data is erased when a string is decoded successfully.
|
||||||
|
vector<uint8_t> decoded(1, 10);
|
||||||
|
ASSERT_NO_THROW_LOG(decodeFormattedHexString(input, decoded));
|
||||||
|
|
||||||
|
// Get the string representation of the decoded data for logging
|
||||||
|
// purposes.
|
||||||
|
string encoded;
|
||||||
|
ASSERT_NO_THROW_LOG(encoded = encodeHex(decoded));
|
||||||
|
|
||||||
|
// Check if the decoded data matches the reference.
|
||||||
|
EXPECT_EQ(decoded, reference_vector)
|
||||||
|
<< "decoded data don't match the reference, input='" << input << "', reference='"
|
||||||
|
<< reference << "', decoded='" << encoded << "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Convenience function which calls quotedStringToBinary
|
||||||
|
/// and converts returned vector back to string.
|
||||||
|
///
|
||||||
|
/// @param s Input string.
|
||||||
|
///
|
||||||
|
/// @return String holding a copy of a vector returned by the
|
||||||
|
/// quotedStringToBinary.
|
||||||
|
string checkQuoted(const string& s) {
|
||||||
|
vector<uint8_t> vec = quotedStringToBinary(s);
|
||||||
|
string s2(vec.begin(), vec.end());
|
||||||
|
return (s2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check that leading and trailing space trimming works.
|
||||||
|
TEST_F(StringUtilTest, Trim) {
|
||||||
|
// Empty and full string.
|
||||||
|
EXPECT_EQ("", trim(""));
|
||||||
|
EXPECT_EQ("abcxyz", trim("abcxyz"));
|
||||||
|
|
||||||
|
// Trim right-most blanks
|
||||||
|
EXPECT_EQ("ABC", trim("ABC "));
|
||||||
|
EXPECT_EQ("ABC", trim("ABC\t\t \n\t"));
|
||||||
|
|
||||||
|
// Left-most blank trimming
|
||||||
|
EXPECT_EQ("XYZ", trim(" XYZ"));
|
||||||
|
EXPECT_EQ("XYZ", trim("\t\t \tXYZ"));
|
||||||
|
|
||||||
|
// Right and left, with embedded spaces
|
||||||
|
EXPECT_EQ("MN \t OP", trim("\t\tMN \t OP \t"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check tokenization.
|
||||||
|
TEST_F(StringUtilTest, Tokens) {
|
||||||
|
vector<string> result;
|
||||||
|
|
||||||
|
// Default delimiters
|
||||||
|
|
||||||
|
// Degenerate cases
|
||||||
|
result = tokens(""); // Empty string
|
||||||
|
EXPECT_EQ(0, result.size());
|
||||||
|
|
||||||
|
result = tokens(" \n "); // String is all delimiters
|
||||||
|
EXPECT_EQ(0, result.size());
|
||||||
|
|
||||||
|
result = tokens("abc"); // String has no delimiters
|
||||||
|
ASSERT_EQ(1, result.size());
|
||||||
|
EXPECT_EQ(string("abc"), result[0]);
|
||||||
|
|
||||||
|
// String containing leading and/or trailing delimiters, no embedded ones.
|
||||||
|
result = tokens("\txyz"); // One leading delimiter
|
||||||
|
ASSERT_EQ(1, result.size());
|
||||||
|
EXPECT_EQ(string("xyz"), result[0]);
|
||||||
|
|
||||||
|
result = tokens("\t \nxyz"); // Multiple leading delimiters
|
||||||
|
ASSERT_EQ(1, result.size());
|
||||||
|
EXPECT_EQ(string("xyz"), result[0]);
|
||||||
|
|
||||||
|
result = tokens("xyz\n"); // One trailing delimiter
|
||||||
|
ASSERT_EQ(1, result.size());
|
||||||
|
EXPECT_EQ(string("xyz"), result[0]);
|
||||||
|
|
||||||
|
result = tokens("xyz \t"); // Multiple trailing
|
||||||
|
ASSERT_EQ(1, result.size());
|
||||||
|
EXPECT_EQ(string("xyz"), result[0]);
|
||||||
|
|
||||||
|
result = tokens("\t xyz \n"); // Leading and trailing
|
||||||
|
ASSERT_EQ(1, result.size());
|
||||||
|
EXPECT_EQ(string("xyz"), result[0]);
|
||||||
|
|
||||||
|
// Embedded delimiters
|
||||||
|
result = tokens("abc\ndef"); // 2 tokens, one separator
|
||||||
|
ASSERT_EQ(2, result.size());
|
||||||
|
EXPECT_EQ(string("abc"), result[0]);
|
||||||
|
EXPECT_EQ(string("def"), result[1]);
|
||||||
|
|
||||||
|
result = tokens("abc\t\t\ndef"); // 2 tokens, 3 separators
|
||||||
|
ASSERT_EQ(2, result.size());
|
||||||
|
EXPECT_EQ(string("abc"), result[0]);
|
||||||
|
EXPECT_EQ(string("def"), result[1]);
|
||||||
|
|
||||||
|
result = tokens("abc\n \tdef\t\tghi");
|
||||||
|
ASSERT_EQ(3, result.size()); // Multiple tokens, many delims
|
||||||
|
EXPECT_EQ(string("abc"), result[0]);
|
||||||
|
EXPECT_EQ(string("def"), result[1]);
|
||||||
|
EXPECT_EQ(string("ghi"), result[2]);
|
||||||
|
|
||||||
|
// Embedded and non-embedded delimiters
|
||||||
|
|
||||||
|
result = tokens("\t\t \nabc\n \tdef\t\tghi \n\n");
|
||||||
|
ASSERT_EQ(3, result.size()); // Multiple tokens, many delims
|
||||||
|
EXPECT_EQ(string("abc"), result[0]);
|
||||||
|
EXPECT_EQ(string("def"), result[1]);
|
||||||
|
EXPECT_EQ(string("ghi"), result[2]);
|
||||||
|
|
||||||
|
// Non-default delimiter
|
||||||
|
result = tokens("alpha/beta/ /gamma//delta/epsilon/", "/");
|
||||||
|
ASSERT_EQ(6, result.size());
|
||||||
|
EXPECT_EQ(string("alpha"), result[0]);
|
||||||
|
EXPECT_EQ(string("beta"), result[1]);
|
||||||
|
EXPECT_EQ(string(" "), result[2]);
|
||||||
|
EXPECT_EQ(string("gamma"), result[3]);
|
||||||
|
EXPECT_EQ(string("delta"), result[4]);
|
||||||
|
EXPECT_EQ(string("epsilon"), result[5]);
|
||||||
|
|
||||||
|
// Non-default delimiters (plural)
|
||||||
|
result = tokens("+*--alpha*beta+ -gamma**delta+epsilon-+**", "*+-");
|
||||||
|
ASSERT_EQ(6, result.size());
|
||||||
|
EXPECT_EQ(string("alpha"), result[0]);
|
||||||
|
EXPECT_EQ(string("beta"), result[1]);
|
||||||
|
EXPECT_EQ(string(" "), result[2]);
|
||||||
|
EXPECT_EQ(string("gamma"), result[3]);
|
||||||
|
EXPECT_EQ(string("delta"), result[4]);
|
||||||
|
EXPECT_EQ(string("epsilon"), result[5]);
|
||||||
|
|
||||||
|
// Escaped delimiter
|
||||||
|
result = tokens("foo\\,bar", ",", true);
|
||||||
|
EXPECT_EQ(1, result.size());
|
||||||
|
EXPECT_EQ(string("foo,bar"), result[0]);
|
||||||
|
|
||||||
|
// Escaped escape
|
||||||
|
result = tokens("foo\\\\,bar", ",", true);
|
||||||
|
ASSERT_EQ(2, result.size());
|
||||||
|
EXPECT_EQ(string("foo\\"), result[0]);
|
||||||
|
EXPECT_EQ(string("bar"), result[1]);
|
||||||
|
|
||||||
|
// Double escapes
|
||||||
|
result = tokens("foo\\\\\\\\,\\bar", ",", true);
|
||||||
|
ASSERT_EQ(2, result.size());
|
||||||
|
EXPECT_EQ(string("foo\\\\"), result[0]);
|
||||||
|
EXPECT_EQ(string("\\bar"), result[1]);
|
||||||
|
|
||||||
|
// Escaped standard character
|
||||||
|
result = tokens("fo\\o,bar", ",", true);
|
||||||
|
ASSERT_EQ(2, result.size());
|
||||||
|
EXPECT_EQ(string("fo\\o"), result[0]);
|
||||||
|
EXPECT_EQ(string("bar"), result[1]);
|
||||||
|
|
||||||
|
// Escape at the end
|
||||||
|
result = tokens("foo,bar\\", ",", true);
|
||||||
|
ASSERT_EQ(2, result.size());
|
||||||
|
EXPECT_EQ(string("foo"), result[0]);
|
||||||
|
EXPECT_EQ(string("bar\\"), result[1]);
|
||||||
|
|
||||||
|
// Escape opening a token
|
||||||
|
result = tokens("foo,\\,,bar", ",", true);
|
||||||
|
ASSERT_EQ(3, result.size());
|
||||||
|
EXPECT_EQ(string("foo"), result[0]);
|
||||||
|
EXPECT_EQ(string(","), result[1]);
|
||||||
|
EXPECT_EQ(string("bar"), result[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check changing of case.
|
||||||
|
TEST_F(StringUtilTest, ChangeCase) {
|
||||||
|
string mixed("abcDEFghiJKLmno123[]{=+--+]}");
|
||||||
|
string upper("ABCDEFGHIJKLMNO123[]{=+--+]}");
|
||||||
|
string lower("abcdefghijklmno123[]{=+--+]}");
|
||||||
|
|
||||||
|
string test = mixed;
|
||||||
|
lowercase(test);
|
||||||
|
EXPECT_EQ(lower, test);
|
||||||
|
|
||||||
|
test = mixed;
|
||||||
|
uppercase(test);
|
||||||
|
EXPECT_EQ(upper, test);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(StringUtilTest, quotedStringToBinary) {
|
||||||
|
// No opening or closing quote should result in empty string.
|
||||||
|
EXPECT_TRUE(quotedStringToBinary("'").empty());
|
||||||
|
EXPECT_TRUE(quotedStringToBinary("").empty());
|
||||||
|
EXPECT_TRUE(quotedStringToBinary(" ").empty());
|
||||||
|
EXPECT_TRUE(quotedStringToBinary("'circuit id").empty());
|
||||||
|
EXPECT_TRUE(quotedStringToBinary("circuit id'").empty());
|
||||||
|
|
||||||
|
// If there is only opening and closing quote, an empty
|
||||||
|
// vector should be returned.
|
||||||
|
EXPECT_TRUE(quotedStringToBinary("''").empty());
|
||||||
|
|
||||||
|
// Both opening and ending quote is present.
|
||||||
|
EXPECT_EQ("circuit id", checkQuoted("'circuit id'"));
|
||||||
|
EXPECT_EQ("remote id", checkQuoted(" ' remote id'"));
|
||||||
|
EXPECT_EQ("duid", checkQuoted(" ' duid'"));
|
||||||
|
EXPECT_EQ("duid", checkQuoted("'duid ' "));
|
||||||
|
EXPECT_EQ("remote'id", checkQuoted(" ' remote'id '"));
|
||||||
|
EXPECT_EQ("remote id'", checkQuoted("'remote id''"));
|
||||||
|
EXPECT_EQ("'remote id", checkQuoted("''remote id'"));
|
||||||
|
|
||||||
|
// Multiple quotes.
|
||||||
|
EXPECT_EQ("'", checkQuoted("'''"));
|
||||||
|
EXPECT_EQ("''", checkQuoted("''''"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(StringUtilTest, decodeColonSeparatedHexString) {
|
||||||
|
// Test valid strings.
|
||||||
|
checkColonSeparated("A1:02:C3:d4:e5:F6", "A102C3D4E5F6");
|
||||||
|
checkColonSeparated("A:02:3:d:E5:F6", "0A02030DE5F6");
|
||||||
|
checkColonSeparated("A:B:C:D", "0A0B0C0D");
|
||||||
|
checkColonSeparated("1", "01");
|
||||||
|
checkColonSeparated("1e", "1E");
|
||||||
|
checkColonSeparated("", "");
|
||||||
|
|
||||||
|
// Test invalid strings.
|
||||||
|
vector<uint8_t> decoded;
|
||||||
|
// Whitespaces.
|
||||||
|
EXPECT_THROW_MSG(decodeColonSeparatedHexString(" ", decoded), BadValue,
|
||||||
|
"invalid format of the decoded string ' '");
|
||||||
|
// Whitespace before digits.
|
||||||
|
EXPECT_THROW_MSG(decodeColonSeparatedHexString(" A1", decoded), BadValue,
|
||||||
|
"invalid format of the decoded string ' A1'");
|
||||||
|
// Two consecutive colons.
|
||||||
|
EXPECT_THROW_MSG(decodeColonSeparatedHexString("A::01", decoded), BadValue,
|
||||||
|
"two consecutive separators (':') specified in a decoded string 'A::01'");
|
||||||
|
// Three consecutive colons.
|
||||||
|
EXPECT_THROW_MSG(decodeColonSeparatedHexString("A:::01", decoded), BadValue,
|
||||||
|
"two consecutive separators (':') specified in a decoded string 'A:::01'");
|
||||||
|
// Whitespace within a string.
|
||||||
|
EXPECT_THROW_MSG(decodeColonSeparatedHexString("A :01", decoded), BadValue,
|
||||||
|
"' ' is not a valid hexadecimal digit in decoded string 'A :01'");
|
||||||
|
// Terminating colon.
|
||||||
|
EXPECT_THROW_MSG(decodeColonSeparatedHexString("0A:01:", decoded), BadValue,
|
||||||
|
"two consecutive separators (':') specified in a decoded string '0A:01:'");
|
||||||
|
// Opening colon.
|
||||||
|
EXPECT_THROW_MSG(decodeColonSeparatedHexString(":0A:01", decoded), BadValue,
|
||||||
|
"two consecutive separators (':') specified in a decoded string ':0A:01'");
|
||||||
|
// Three digits before the colon.
|
||||||
|
EXPECT_THROW_MSG(decodeColonSeparatedHexString("0A1:B1", decoded), BadValue,
|
||||||
|
"invalid format of the decoded string '0A1:B1'");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(StringUtilTest, decodeFormattedHexString) {
|
||||||
|
// Colon separated.
|
||||||
|
checkFormatted("1:A7:B5:4:23", "01A7B50423");
|
||||||
|
// Space separated.
|
||||||
|
checkFormatted("1 A7 B5 4 23", "01A7B50423");
|
||||||
|
// No colons, even number of digits.
|
||||||
|
checkFormatted("17a534", "17A534");
|
||||||
|
// Odd number of digits.
|
||||||
|
checkFormatted("A3A6f78", "0A3A6F78");
|
||||||
|
// '0x' prefix.
|
||||||
|
checkFormatted("0xA3A6f78", "0A3A6F78");
|
||||||
|
// '0x' prefix with a special value of 0.
|
||||||
|
checkFormatted("0x0", "00");
|
||||||
|
// Empty string.
|
||||||
|
checkFormatted("", "");
|
||||||
|
|
||||||
|
vector<uint8_t> decoded;
|
||||||
|
// Dangling colon.
|
||||||
|
EXPECT_THROW_MSG(decodeFormattedHexString("0a:", decoded), BadValue,
|
||||||
|
"two consecutive separators (':') specified in a decoded string '0a:'");
|
||||||
|
// Dangling space.
|
||||||
|
EXPECT_THROW_MSG(decodeFormattedHexString("0a ", decoded), BadValue,
|
||||||
|
"two consecutive separators (' ') specified in a decoded string '0a '");
|
||||||
|
// '0x' prefix and spaces.
|
||||||
|
EXPECT_THROW_MSG(decodeFormattedHexString("0x01 02", decoded), BadValue,
|
||||||
|
"invalid format of the decoded string '0x01 02'");
|
||||||
|
// '0x' prefix and colons.
|
||||||
|
EXPECT_THROW_MSG(decodeFormattedHexString("0x01:02", decoded), BadValue,
|
||||||
|
"invalid format of the decoded string '0x01:02'");
|
||||||
|
// colon and spaces mixed
|
||||||
|
EXPECT_THROW_MSG(decodeFormattedHexString("01:02 03", decoded), BadValue,
|
||||||
|
"invalid format of the decoded string '01:02 03'");
|
||||||
|
// Missing colon.
|
||||||
|
EXPECT_THROW_MSG(decodeFormattedHexString("01:0203", decoded), BadValue,
|
||||||
|
"invalid format of the decoded string '01:0203'");
|
||||||
|
// Missing space.
|
||||||
|
EXPECT_THROW_MSG(decodeFormattedHexString("01 0203", decoded), BadValue,
|
||||||
|
"invalid format of the decoded string '01 0203'");
|
||||||
|
// Invalid prefix.
|
||||||
|
EXPECT_THROW_MSG(decodeFormattedHexString("x0102", decoded), BadValue,
|
||||||
|
"'x0102' is not a valid string of hexadecimal digits");
|
||||||
|
// Invalid prefix again.
|
||||||
|
EXPECT_THROW_MSG(decodeFormattedHexString("1x0102", decoded), BadValue,
|
||||||
|
"'1x0102' is not a valid string of hexadecimal digits");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifies StringSantizer class
|
||||||
|
TEST_F(StringUtilTest, stringSanitizer) {
|
||||||
|
// Bad regular expression should throw.
|
||||||
|
StringSanitizerPtr ss;
|
||||||
|
ASSERT_THROW_MSG(ss.reset(new StringSanitizer("[bogus-regex", "")), BadValue,
|
||||||
|
"invalid regex: '[bogus-regex', Invalid range in bracket expression.");
|
||||||
|
|
||||||
|
string good_data(StringSanitizer::MAX_DATA_SIZE, '0');
|
||||||
|
string bad_data(StringSanitizer::MAX_DATA_SIZE + 1, '0');
|
||||||
|
|
||||||
|
ASSERT_NO_THROW_LOG(ss.reset(new StringSanitizer(good_data, good_data)));
|
||||||
|
|
||||||
|
ASSERT_THROW_MSG(ss.reset(new StringSanitizer(bad_data, "")), BadValue,
|
||||||
|
"char set size: '4097' exceeds max size: '4096'");
|
||||||
|
ASSERT_THROW_MSG(ss.reset(new StringSanitizer("", bad_data)), BadValue,
|
||||||
|
"char replacement size: '4097' exceeds max size: '4096'");
|
||||||
|
|
||||||
|
// List of invalid chars should work: (b,c,2 are invalid)
|
||||||
|
checkScrub("abc.123", "[b-c2]", "*", "a**.1*3");
|
||||||
|
// Inverted list of valid chars should work: (b,c,2 are valid)
|
||||||
|
checkScrub("abc.123", "[^b-c2]", "*", "*bc**2*");
|
||||||
|
|
||||||
|
// A string of all valid chars should return an identical string.
|
||||||
|
checkScrub("-_A--B__Cabc34567_-", "[^A-Ca-c3-7_-]", "x", "-_A--B__Cabc34567_-");
|
||||||
|
|
||||||
|
// Replacing with a character should work.
|
||||||
|
checkScrub("A[b]c\12JoE3-_x!B$Y#e", "[^A-Za-z0-9_]", "*", "A*b*c*JoE3*_x*B*Y*e");
|
||||||
|
|
||||||
|
// Removing (i.e.replacing with an "empty" string) should work.
|
||||||
|
checkScrub("A[b]c\12JoE3-_x!B$Y#e", "[^A-Za-z0-9_]", "", "AbcJoE3_xBYe");
|
||||||
|
|
||||||
|
// More than one non-matching in a row should work.
|
||||||
|
checkScrub("%%A%%B%%C%%", "[^A-Za-z0-9_]", "x", "xxAxxBxxCxx");
|
||||||
|
|
||||||
|
// Removing more than one non-matching in a row should work.
|
||||||
|
checkScrub("%%A%%B%%C%%", "[^A-Za-z0-9_]", "", "ABC");
|
||||||
|
|
||||||
|
// Replacing with a string should work.
|
||||||
|
checkScrub("%%A%%B%%C%%", "[^A-Za-z0-9_]", "xyz", "xyzxyzAxyzxyzBxyzxyzCxyzxyz");
|
||||||
|
|
||||||
|
// Dots as valid chars work.
|
||||||
|
checkScrub("abc.123", "[^A-Za-z0-9_.]", "*", "abc.123");
|
||||||
|
|
||||||
|
string withNulls("\000ab\000c.12\0003", 10);
|
||||||
|
checkScrub(withNulls, "[^A-Za-z0-9_.]", "*", "*ab*c.12*3");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifies templated buffer iterator seekTrimmed() function
|
||||||
|
TEST_F(StringUtilTest, seekTrimmed) {
|
||||||
|
// Empty buffer should be fine.
|
||||||
|
vector<uint8_t> buffer;
|
||||||
|
auto begin = buffer.end();
|
||||||
|
auto end = buffer.end();
|
||||||
|
ASSERT_NO_THROW_LOG(end = seekTrimmed(begin, end, 0));
|
||||||
|
EXPECT_EQ(0, distance(begin, end));
|
||||||
|
|
||||||
|
// Buffer of only trim values, should be fine.
|
||||||
|
buffer = {1, 1};
|
||||||
|
begin = buffer.begin();
|
||||||
|
end = buffer.end();
|
||||||
|
ASSERT_NO_THROW_LOG(end = seekTrimmed(begin, end, 1));
|
||||||
|
EXPECT_EQ(0, distance(begin, end));
|
||||||
|
|
||||||
|
// One trailing null should trim off.
|
||||||
|
buffer = {'o', 'n', 'e', 0};
|
||||||
|
begin = buffer.begin();
|
||||||
|
end = buffer.end();
|
||||||
|
ASSERT_NO_THROW_LOG(end = seekTrimmed(begin, end, 0));
|
||||||
|
EXPECT_EQ(3, distance(begin, end));
|
||||||
|
|
||||||
|
// More than one trailing null should trim off.
|
||||||
|
buffer = {'t', 'h', 'r', 'e', 'e', 0, 0, 0};
|
||||||
|
begin = buffer.begin();
|
||||||
|
end = buffer.end();
|
||||||
|
ASSERT_NO_THROW_LOG(end = seekTrimmed(begin, end, 0));
|
||||||
|
EXPECT_EQ(5, distance(begin, end));
|
||||||
|
|
||||||
|
// Embedded null should be left in place.
|
||||||
|
buffer = {'e', 'm', 0, 'b', 'e', 'd'};
|
||||||
|
begin = buffer.begin();
|
||||||
|
end = buffer.end();
|
||||||
|
ASSERT_NO_THROW_LOG(end = seekTrimmed(begin, end, 0));
|
||||||
|
EXPECT_EQ(6, distance(begin, end));
|
||||||
|
|
||||||
|
// Leading null should be left in place.
|
||||||
|
buffer = {0, 'l', 'e', 'a', 'd', 'i', 'n', 'g'};
|
||||||
|
begin = buffer.begin();
|
||||||
|
end = buffer.end();
|
||||||
|
ASSERT_NO_THROW_LOG(end = seekTrimmed(begin, end, 0));
|
||||||
|
EXPECT_EQ(8, distance(begin, end));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifies isPrintable predicate on strings.
|
||||||
|
TEST_F(StringUtilTest, stringIsPrintable) {
|
||||||
|
string content;
|
||||||
|
|
||||||
|
// Empty is printable.
|
||||||
|
EXPECT_TRUE(isPrintable(content));
|
||||||
|
|
||||||
|
// Check Abcd.
|
||||||
|
content = "Abcd";
|
||||||
|
EXPECT_TRUE(isPrintable(content));
|
||||||
|
|
||||||
|
// Add a control character (not printable).
|
||||||
|
content += "\a";
|
||||||
|
EXPECT_FALSE(isPrintable(content));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifies isPrintable predicate on byte vectors.
|
||||||
|
TEST_F(StringUtilTest, vectorIsPrintable) {
|
||||||
|
vector<uint8_t> content;
|
||||||
|
|
||||||
|
// Empty is printable.
|
||||||
|
EXPECT_TRUE(isPrintable(content));
|
||||||
|
|
||||||
|
// Check Abcd.
|
||||||
|
content = {0x41, 0x62, 0x63, 0x64};
|
||||||
|
EXPECT_TRUE(isPrintable(content));
|
||||||
|
|
||||||
|
// Add a control character (not printable).
|
||||||
|
content.push_back('\a');
|
||||||
|
EXPECT_FALSE(isPrintable(content));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
@@ -1,642 +0,0 @@
|
|||||||
// Copyright (C) 2011-2024 Internet Systems Consortium, Inc. ("ISC")
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
|
|
||||||
#include <exceptions/exceptions.h>
|
|
||||||
#include <util/strutil.h>
|
|
||||||
#include <util/encode/encode.h>
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
using namespace isc;
|
|
||||||
using namespace isc::util;
|
|
||||||
using namespace isc::util::str;
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// Check for slash replacement
|
|
||||||
|
|
||||||
TEST(StringUtilTest, Slash) {
|
|
||||||
|
|
||||||
string instring = "";
|
|
||||||
isc::util::str::normalizeSlash(instring);
|
|
||||||
EXPECT_EQ("", instring);
|
|
||||||
|
|
||||||
instring = "C:\\A\\B\\C.D";
|
|
||||||
isc::util::str::normalizeSlash(instring);
|
|
||||||
EXPECT_EQ("C:/A/B/C.D", instring);
|
|
||||||
|
|
||||||
instring = "// \\ //";
|
|
||||||
isc::util::str::normalizeSlash(instring);
|
|
||||||
EXPECT_EQ("// / //", instring);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that leading and trailing space trimming works
|
|
||||||
|
|
||||||
TEST(StringUtilTest, Trim) {
|
|
||||||
|
|
||||||
// Empty and full string.
|
|
||||||
EXPECT_EQ("", isc::util::str::trim(""));
|
|
||||||
EXPECT_EQ("abcxyz", isc::util::str::trim("abcxyz"));
|
|
||||||
|
|
||||||
// Trim right-most blanks
|
|
||||||
EXPECT_EQ("ABC", isc::util::str::trim("ABC "));
|
|
||||||
EXPECT_EQ("ABC", isc::util::str::trim("ABC\t\t \n\t"));
|
|
||||||
|
|
||||||
// Left-most blank trimming
|
|
||||||
EXPECT_EQ("XYZ", isc::util::str::trim(" XYZ"));
|
|
||||||
EXPECT_EQ("XYZ", isc::util::str::trim("\t\t \tXYZ"));
|
|
||||||
|
|
||||||
// Right and left, with embedded spaces
|
|
||||||
EXPECT_EQ("MN \t OP", isc::util::str::trim("\t\tMN \t OP \t"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check tokenization. Note that ASSERT_EQ is used to check the size of the
|
|
||||||
// returned vector; if not as expected, the following references may be invalid
|
|
||||||
// so should not be used.
|
|
||||||
|
|
||||||
TEST(StringUtilTest, Tokens) {
|
|
||||||
vector<string> result;
|
|
||||||
|
|
||||||
// Default delimiters
|
|
||||||
|
|
||||||
// Degenerate cases
|
|
||||||
result = isc::util::str::tokens(""); // Empty string
|
|
||||||
EXPECT_EQ(0, result.size());
|
|
||||||
|
|
||||||
result = isc::util::str::tokens(" \n "); // String is all delimiters
|
|
||||||
EXPECT_EQ(0, result.size());
|
|
||||||
|
|
||||||
result = isc::util::str::tokens("abc"); // String has no delimiters
|
|
||||||
ASSERT_EQ(1, result.size());
|
|
||||||
EXPECT_EQ(string("abc"), result[0]);
|
|
||||||
|
|
||||||
// String containing leading and/or trailing delimiters, no embedded ones.
|
|
||||||
result = isc::util::str::tokens("\txyz"); // One leading delimiter
|
|
||||||
ASSERT_EQ(1, result.size());
|
|
||||||
EXPECT_EQ(string("xyz"), result[0]);
|
|
||||||
|
|
||||||
result = isc::util::str::tokens("\t \nxyz"); // Multiple leading delimiters
|
|
||||||
ASSERT_EQ(1, result.size());
|
|
||||||
EXPECT_EQ(string("xyz"), result[0]);
|
|
||||||
|
|
||||||
result = isc::util::str::tokens("xyz\n"); // One trailing delimiter
|
|
||||||
ASSERT_EQ(1, result.size());
|
|
||||||
EXPECT_EQ(string("xyz"), result[0]);
|
|
||||||
|
|
||||||
result = isc::util::str::tokens("xyz \t"); // Multiple trailing
|
|
||||||
ASSERT_EQ(1, result.size());
|
|
||||||
EXPECT_EQ(string("xyz"), result[0]);
|
|
||||||
|
|
||||||
result = isc::util::str::tokens("\t xyz \n"); // Leading and trailing
|
|
||||||
ASSERT_EQ(1, result.size());
|
|
||||||
EXPECT_EQ(string("xyz"), result[0]);
|
|
||||||
|
|
||||||
// Embedded delimiters
|
|
||||||
result = isc::util::str::tokens("abc\ndef"); // 2 tokens, one separator
|
|
||||||
ASSERT_EQ(2, result.size());
|
|
||||||
EXPECT_EQ(string("abc"), result[0]);
|
|
||||||
EXPECT_EQ(string("def"), result[1]);
|
|
||||||
|
|
||||||
result = isc::util::str::tokens("abc\t\t\ndef"); // 2 tokens, 3 separators
|
|
||||||
ASSERT_EQ(2, result.size());
|
|
||||||
EXPECT_EQ(string("abc"), result[0]);
|
|
||||||
EXPECT_EQ(string("def"), result[1]);
|
|
||||||
|
|
||||||
result = isc::util::str::tokens("abc\n \tdef\t\tghi");
|
|
||||||
ASSERT_EQ(3, result.size()); // Multiple tokens, many delims
|
|
||||||
EXPECT_EQ(string("abc"), result[0]);
|
|
||||||
EXPECT_EQ(string("def"), result[1]);
|
|
||||||
EXPECT_EQ(string("ghi"), result[2]);
|
|
||||||
|
|
||||||
// Embedded and non-embedded delimiters
|
|
||||||
|
|
||||||
result = isc::util::str::tokens("\t\t \nabc\n \tdef\t\tghi \n\n");
|
|
||||||
ASSERT_EQ(3, result.size()); // Multiple tokens, many delims
|
|
||||||
EXPECT_EQ(string("abc"), result[0]);
|
|
||||||
EXPECT_EQ(string("def"), result[1]);
|
|
||||||
EXPECT_EQ(string("ghi"), result[2]);
|
|
||||||
|
|
||||||
// Non-default delimiter
|
|
||||||
result = isc::util::str::tokens("alpha/beta/ /gamma//delta/epsilon/", "/");
|
|
||||||
ASSERT_EQ(6, result.size());
|
|
||||||
EXPECT_EQ(string("alpha"), result[0]);
|
|
||||||
EXPECT_EQ(string("beta"), result[1]);
|
|
||||||
EXPECT_EQ(string(" "), result[2]);
|
|
||||||
EXPECT_EQ(string("gamma"), result[3]);
|
|
||||||
EXPECT_EQ(string("delta"), result[4]);
|
|
||||||
EXPECT_EQ(string("epsilon"), result[5]);
|
|
||||||
|
|
||||||
// Non-default delimiters (plural)
|
|
||||||
result = isc::util::str::tokens("+*--alpha*beta+ -gamma**delta+epsilon-+**",
|
|
||||||
"*+-");
|
|
||||||
ASSERT_EQ(6, result.size());
|
|
||||||
EXPECT_EQ(string("alpha"), result[0]);
|
|
||||||
EXPECT_EQ(string("beta"), result[1]);
|
|
||||||
EXPECT_EQ(string(" "), result[2]);
|
|
||||||
EXPECT_EQ(string("gamma"), result[3]);
|
|
||||||
EXPECT_EQ(string("delta"), result[4]);
|
|
||||||
EXPECT_EQ(string("epsilon"), result[5]);
|
|
||||||
|
|
||||||
// Escaped delimiter
|
|
||||||
result = isc::util::str::tokens("foo\\,bar", ",", true);
|
|
||||||
EXPECT_EQ(1, result.size());
|
|
||||||
EXPECT_EQ(string("foo,bar"), result[0]);
|
|
||||||
|
|
||||||
// Escaped escape
|
|
||||||
result = isc::util::str::tokens("foo\\\\,bar", ",", true);
|
|
||||||
ASSERT_EQ(2, result.size());
|
|
||||||
EXPECT_EQ(string("foo\\"), result[0]);
|
|
||||||
EXPECT_EQ(string("bar"), result[1]);
|
|
||||||
|
|
||||||
// Double escapes
|
|
||||||
result = isc::util::str::tokens("foo\\\\\\\\,\\bar", ",", true);
|
|
||||||
ASSERT_EQ(2, result.size());
|
|
||||||
EXPECT_EQ(string("foo\\\\"), result[0]);
|
|
||||||
EXPECT_EQ(string("\\bar"), result[1]);
|
|
||||||
|
|
||||||
// Escaped standard character
|
|
||||||
result = isc::util::str::tokens("fo\\o,bar", ",", true);
|
|
||||||
ASSERT_EQ(2, result.size());
|
|
||||||
EXPECT_EQ(string("fo\\o"), result[0]);
|
|
||||||
EXPECT_EQ(string("bar"), result[1]);
|
|
||||||
|
|
||||||
// Escape at the end
|
|
||||||
result = isc::util::str::tokens("foo,bar\\", ",", true);
|
|
||||||
ASSERT_EQ(2, result.size());
|
|
||||||
EXPECT_EQ(string("foo"), result[0]);
|
|
||||||
EXPECT_EQ(string("bar\\"), result[1]);
|
|
||||||
|
|
||||||
// Escape opening a token
|
|
||||||
result = isc::util::str::tokens("foo,\\,,bar", ",", true);
|
|
||||||
ASSERT_EQ(3, result.size());
|
|
||||||
EXPECT_EQ(string("foo"), result[0]);
|
|
||||||
EXPECT_EQ(string(","), result[1]);
|
|
||||||
EXPECT_EQ(string("bar"), result[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Changing case
|
|
||||||
|
|
||||||
TEST(StringUtilTest, ChangeCase) {
|
|
||||||
string mixed("abcDEFghiJKLmno123[]{=+--+]}");
|
|
||||||
string upper("ABCDEFGHIJKLMNO123[]{=+--+]}");
|
|
||||||
string lower("abcdefghijklmno123[]{=+--+]}");
|
|
||||||
|
|
||||||
string test = mixed;
|
|
||||||
isc::util::str::lowercase(test);
|
|
||||||
EXPECT_EQ(lower, test);
|
|
||||||
|
|
||||||
test = mixed;
|
|
||||||
isc::util::str::uppercase(test);
|
|
||||||
EXPECT_EQ(upper, test);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Formatting
|
|
||||||
|
|
||||||
TEST(StringUtilTest, Formatting) {
|
|
||||||
|
|
||||||
vector<string> args;
|
|
||||||
args.push_back("arg1");
|
|
||||||
args.push_back("arg2");
|
|
||||||
args.push_back("arg3");
|
|
||||||
|
|
||||||
string format1 = "This is a string with no tokens";
|
|
||||||
EXPECT_EQ(format1, isc::util::str::format(format1, args));
|
|
||||||
|
|
||||||
string format2 = ""; // Empty string
|
|
||||||
EXPECT_EQ(format2, isc::util::str::format(format2, args));
|
|
||||||
|
|
||||||
string format3 = " "; // Empty string
|
|
||||||
EXPECT_EQ(format3, isc::util::str::format(format3, args));
|
|
||||||
|
|
||||||
string format4 = "String with %d non-string tokens %lf";
|
|
||||||
EXPECT_EQ(format4, isc::util::str::format(format4, args));
|
|
||||||
|
|
||||||
string format5 = "String with %s correct %s number of tokens %s";
|
|
||||||
string result5 = "String with arg1 correct arg2 number of tokens arg3";
|
|
||||||
EXPECT_EQ(result5, isc::util::str::format(format5, args));
|
|
||||||
|
|
||||||
string format6 = "String with %s too %s few tokens";
|
|
||||||
string result6 = "String with arg1 too arg2 few tokens";
|
|
||||||
EXPECT_EQ(result6, isc::util::str::format(format6, args));
|
|
||||||
|
|
||||||
string format7 = "String with %s too %s many %s tokens %s !";
|
|
||||||
string result7 = "String with arg1 too arg2 many arg3 tokens %s !";
|
|
||||||
EXPECT_EQ(result7, isc::util::str::format(format7, args));
|
|
||||||
|
|
||||||
string format8 = "String with embedded%s%s%stokens";
|
|
||||||
string result8 = "String with embeddedarg1arg2arg3tokens";
|
|
||||||
EXPECT_EQ(result8, isc::util::str::format(format8, args));
|
|
||||||
|
|
||||||
// Handle an empty vector
|
|
||||||
args.clear();
|
|
||||||
string format9 = "%s %s";
|
|
||||||
EXPECT_EQ(format9, isc::util::str::format(format9, args));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StringUtilTest, getToken) {
|
|
||||||
string s("a b c");
|
|
||||||
istringstream ss(s);
|
|
||||||
EXPECT_EQ("a", isc::util::str::getToken(ss));
|
|
||||||
EXPECT_EQ("b", isc::util::str::getToken(ss));
|
|
||||||
EXPECT_EQ("c", isc::util::str::getToken(ss));
|
|
||||||
EXPECT_THROW(isc::util::str::getToken(ss), isc::util::str::StringTokenError);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t tokenToNumCall_32_16(const string& token) {
|
|
||||||
return isc::util::str::tokenToNum<int32_t, 16>(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t tokenToNumCall_16_8(const string& token) {
|
|
||||||
return isc::util::str::tokenToNum<int16_t, 8>(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StringUtilTest, tokenToNum) {
|
|
||||||
uint32_t num32 = tokenToNumCall_32_16("0");
|
|
||||||
EXPECT_EQ(0, num32);
|
|
||||||
num32 = tokenToNumCall_32_16("123");
|
|
||||||
EXPECT_EQ(123, num32);
|
|
||||||
num32 = tokenToNumCall_32_16("65535");
|
|
||||||
EXPECT_EQ(65535, num32);
|
|
||||||
|
|
||||||
EXPECT_THROW(tokenToNumCall_32_16(""),
|
|
||||||
isc::util::str::StringTokenError);
|
|
||||||
EXPECT_THROW(tokenToNumCall_32_16("a"),
|
|
||||||
isc::util::str::StringTokenError);
|
|
||||||
EXPECT_THROW(tokenToNumCall_32_16("-1"),
|
|
||||||
isc::util::str::StringTokenError);
|
|
||||||
EXPECT_THROW(tokenToNumCall_32_16("65536"),
|
|
||||||
isc::util::str::StringTokenError);
|
|
||||||
EXPECT_THROW(tokenToNumCall_32_16("1234567890"),
|
|
||||||
isc::util::str::StringTokenError);
|
|
||||||
EXPECT_THROW(tokenToNumCall_32_16("-1234567890"),
|
|
||||||
isc::util::str::StringTokenError);
|
|
||||||
|
|
||||||
uint16_t num16 = tokenToNumCall_16_8("123");
|
|
||||||
EXPECT_EQ(123, num16);
|
|
||||||
num16 = tokenToNumCall_16_8("0");
|
|
||||||
EXPECT_EQ(0, num16);
|
|
||||||
num16 = tokenToNumCall_16_8("255");
|
|
||||||
EXPECT_EQ(255, num16);
|
|
||||||
|
|
||||||
EXPECT_THROW(tokenToNumCall_16_8(""),
|
|
||||||
isc::util::str::StringTokenError);
|
|
||||||
EXPECT_THROW(tokenToNumCall_16_8("a"),
|
|
||||||
isc::util::str::StringTokenError);
|
|
||||||
EXPECT_THROW(tokenToNumCall_16_8("-1"),
|
|
||||||
isc::util::str::StringTokenError);
|
|
||||||
EXPECT_THROW(tokenToNumCall_16_8("256"),
|
|
||||||
isc::util::str::StringTokenError);
|
|
||||||
EXPECT_THROW(tokenToNumCall_16_8("1234567890"),
|
|
||||||
isc::util::str::StringTokenError);
|
|
||||||
EXPECT_THROW(tokenToNumCall_16_8("-1234567890"),
|
|
||||||
isc::util::str::StringTokenError);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Convenience function which calls quotedStringToBinary
|
|
||||||
/// and converts returned vector back to string.
|
|
||||||
///
|
|
||||||
/// @param s Input string.
|
|
||||||
/// @return String holding a copy of a vector returned by the
|
|
||||||
/// quotedStringToBinary.
|
|
||||||
std::string testQuoted(const std::string& s) {
|
|
||||||
std::vector<uint8_t> vec = str::quotedStringToBinary(s);
|
|
||||||
std::string s2(vec.begin(), vec.end());
|
|
||||||
return (s2);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StringUtilTest, quotedStringToBinary) {
|
|
||||||
// No opening or closing quote should result in empty string.
|
|
||||||
EXPECT_TRUE(str::quotedStringToBinary("'").empty());
|
|
||||||
EXPECT_TRUE(str::quotedStringToBinary("").empty());
|
|
||||||
EXPECT_TRUE(str::quotedStringToBinary(" ").empty());
|
|
||||||
EXPECT_TRUE(str::quotedStringToBinary("'circuit id").empty());
|
|
||||||
EXPECT_TRUE(str::quotedStringToBinary("circuit id'").empty());
|
|
||||||
|
|
||||||
// If there is only opening and closing quote, an empty
|
|
||||||
// vector should be returned.
|
|
||||||
EXPECT_TRUE(str::quotedStringToBinary("''").empty());
|
|
||||||
|
|
||||||
// Both opening and ending quote is present.
|
|
||||||
EXPECT_EQ("circuit id", testQuoted("'circuit id'"));
|
|
||||||
EXPECT_EQ("remote id", testQuoted(" ' remote id'"));
|
|
||||||
EXPECT_EQ("duid", testQuoted(" ' duid'"));
|
|
||||||
EXPECT_EQ("duid", testQuoted("'duid ' "));
|
|
||||||
EXPECT_EQ("remote'id", testQuoted(" ' remote'id '"));
|
|
||||||
EXPECT_EQ("remote id'", testQuoted("'remote id''"));
|
|
||||||
EXPECT_EQ("'remote id", testQuoted("''remote id'"));
|
|
||||||
|
|
||||||
// Multiple quotes.
|
|
||||||
EXPECT_EQ("'", testQuoted("'''"));
|
|
||||||
EXPECT_EQ("''", testQuoted("''''"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Test that hex string with colons can be decoded.
|
|
||||||
///
|
|
||||||
/// @param input Input string to be decoded.
|
|
||||||
/// @param reference A string without colons representing the
|
|
||||||
/// decoded data.
|
|
||||||
void testColonSeparated(const std::string& input,
|
|
||||||
const std::string& reference) {
|
|
||||||
// Create a reference vector.
|
|
||||||
std::vector<uint8_t> reference_vector;
|
|
||||||
ASSERT_NO_THROW(encode::decodeHex(reference, reference_vector));
|
|
||||||
|
|
||||||
// Fill the output vector with some garbage to make sure that
|
|
||||||
// the data is erased when a string is decoded successfully.
|
|
||||||
std::vector<uint8_t> decoded(1, 10);
|
|
||||||
ASSERT_NO_THROW(decodeColonSeparatedHexString(input, decoded));
|
|
||||||
|
|
||||||
// Get the string representation of the decoded data for logging
|
|
||||||
// purposes.
|
|
||||||
std::string encoded;
|
|
||||||
ASSERT_NO_THROW(encoded = encode::encodeHex(decoded));
|
|
||||||
|
|
||||||
// Check if the decoded data matches the reference.
|
|
||||||
EXPECT_TRUE(decoded == reference_vector)
|
|
||||||
<< "decoded data don't match the reference, input='"
|
|
||||||
<< input << "', reference='" << reference << "'"
|
|
||||||
", decoded='" << encoded << "'";
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StringUtilTest, decodeColonSeparatedHexString) {
|
|
||||||
// Test valid strings.
|
|
||||||
testColonSeparated("A1:02:C3:d4:e5:F6", "A102C3D4E5F6");
|
|
||||||
testColonSeparated("A:02:3:d:E5:F6", "0A02030DE5F6");
|
|
||||||
testColonSeparated("A:B:C:D", "0A0B0C0D");
|
|
||||||
testColonSeparated("1", "01");
|
|
||||||
testColonSeparated("1e", "1E");
|
|
||||||
testColonSeparated("", "");
|
|
||||||
|
|
||||||
// Test invalid strings.
|
|
||||||
std::vector<uint8_t> decoded;
|
|
||||||
// Whitespaces.
|
|
||||||
EXPECT_THROW(decodeColonSeparatedHexString(" ", decoded),
|
|
||||||
isc::BadValue);
|
|
||||||
// Whitespace before digits.
|
|
||||||
EXPECT_THROW(decodeColonSeparatedHexString(" A1", decoded),
|
|
||||||
isc::BadValue);
|
|
||||||
// Two consecutive colons.
|
|
||||||
EXPECT_THROW(decodeColonSeparatedHexString("A::01", decoded),
|
|
||||||
isc::BadValue);
|
|
||||||
// Three consecutive colons.
|
|
||||||
EXPECT_THROW(decodeColonSeparatedHexString("A:::01", decoded),
|
|
||||||
isc::BadValue);
|
|
||||||
// Whitespace within a string.
|
|
||||||
EXPECT_THROW(decodeColonSeparatedHexString("A :01", decoded),
|
|
||||||
isc::BadValue);
|
|
||||||
// Terminating colon.
|
|
||||||
EXPECT_THROW(decodeColonSeparatedHexString("0A:01:", decoded),
|
|
||||||
isc::BadValue);
|
|
||||||
// Opening colon.
|
|
||||||
EXPECT_THROW(decodeColonSeparatedHexString(":0A:01", decoded),
|
|
||||||
isc::BadValue);
|
|
||||||
// Three digits before the colon.
|
|
||||||
EXPECT_THROW(decodeColonSeparatedHexString("0A1:B1", decoded),
|
|
||||||
isc::BadValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
void testFormatted(const std::string& input,
|
|
||||||
const std::string& reference) {
|
|
||||||
// Create a reference vector.
|
|
||||||
std::vector<uint8_t> reference_vector;
|
|
||||||
ASSERT_NO_THROW(encode::decodeHex(reference, reference_vector));
|
|
||||||
|
|
||||||
// Fill the output vector with some garbage to make sure that
|
|
||||||
// the data is erased when a string is decoded successfully.
|
|
||||||
std::vector<uint8_t> decoded(1, 10);
|
|
||||||
ASSERT_NO_THROW(decodeFormattedHexString(input, decoded));
|
|
||||||
|
|
||||||
// Get the string representation of the decoded data for logging
|
|
||||||
// purposes.
|
|
||||||
std::string encoded;
|
|
||||||
ASSERT_NO_THROW(encoded = encode::encodeHex(decoded));
|
|
||||||
|
|
||||||
// Check if the decoded data matches the reference.
|
|
||||||
EXPECT_TRUE(decoded == reference_vector)
|
|
||||||
<< "decoded data don't match the reference, input='"
|
|
||||||
<< input << "', reference='" << reference << "'"
|
|
||||||
", decoded='" << encoded << "'";
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StringUtilTest, decodeFormattedHexString) {
|
|
||||||
// Colon separated.
|
|
||||||
testFormatted("1:A7:B5:4:23", "01A7B50423");
|
|
||||||
// Space separated.
|
|
||||||
testFormatted("1 A7 B5 4 23", "01A7B50423");
|
|
||||||
// No colons, even number of digits.
|
|
||||||
testFormatted("17a534", "17A534");
|
|
||||||
// Odd number of digits.
|
|
||||||
testFormatted("A3A6f78", "0A3A6F78");
|
|
||||||
// '0x' prefix.
|
|
||||||
testFormatted("0xA3A6f78", "0A3A6F78");
|
|
||||||
// '0x' prefix with a special value of 0.
|
|
||||||
testFormatted("0x0", "00");
|
|
||||||
// Empty string.
|
|
||||||
testFormatted("", "");
|
|
||||||
|
|
||||||
std::vector<uint8_t> decoded;
|
|
||||||
// Dangling colon.
|
|
||||||
EXPECT_THROW(decodeFormattedHexString("0a:", decoded),
|
|
||||||
isc::BadValue);
|
|
||||||
// Dangling space.
|
|
||||||
EXPECT_THROW(decodeFormattedHexString("0a ", decoded),
|
|
||||||
isc::BadValue);
|
|
||||||
// '0x' prefix and spaces.
|
|
||||||
EXPECT_THROW(decodeFormattedHexString("0x01 02", decoded),
|
|
||||||
isc::BadValue);
|
|
||||||
// '0x' prefix and colons.
|
|
||||||
EXPECT_THROW(decodeFormattedHexString("0x01:02", decoded),
|
|
||||||
isc::BadValue);
|
|
||||||
// colon and spaces mixed
|
|
||||||
EXPECT_THROW(decodeFormattedHexString("01:02 03", decoded),
|
|
||||||
isc::BadValue);
|
|
||||||
// Missing colon.
|
|
||||||
EXPECT_THROW(decodeFormattedHexString("01:0203", decoded),
|
|
||||||
isc::BadValue);
|
|
||||||
// Missing space.
|
|
||||||
EXPECT_THROW(decodeFormattedHexString("01 0203", decoded),
|
|
||||||
isc::BadValue);
|
|
||||||
// Invalid prefix.
|
|
||||||
EXPECT_THROW(decodeFormattedHexString("x0102", decoded),
|
|
||||||
isc::BadValue);
|
|
||||||
// Invalid prefix again.
|
|
||||||
EXPECT_THROW(decodeFormattedHexString("1x0102", decoded),
|
|
||||||
isc::BadValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Function used to test StringSantitizer
|
|
||||||
/// @param original - string to sanitize
|
|
||||||
/// @param char_set - regular expression string describing invalid
|
|
||||||
/// characters
|
|
||||||
/// @param char_replacement - character(s) which replace invalid
|
|
||||||
/// characters
|
|
||||||
/// @param expected - expected sanitized string
|
|
||||||
void sanitizeStringTest(
|
|
||||||
const std::string& original,
|
|
||||||
const std::string& char_set,
|
|
||||||
const std::string& char_replacement,
|
|
||||||
const std::string& expected) {
|
|
||||||
|
|
||||||
StringSanitizerPtr ss;
|
|
||||||
std::string sanitized;
|
|
||||||
|
|
||||||
try {
|
|
||||||
ss.reset(new StringSanitizer(char_set, char_replacement));
|
|
||||||
} catch (const std::exception& ex) {
|
|
||||||
ADD_FAILURE() << "Could not construct sanitizer:" << ex.what();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
sanitized = ss->scrub(original);
|
|
||||||
} catch (const std::exception& ex) {
|
|
||||||
ADD_FAILURE() << "Could not scrub string:" << ex.what();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT_EQ(sanitized, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verifies StringSantizer class
|
|
||||||
TEST(StringUtilTest, stringSanitizer) {
|
|
||||||
// Bad regular expression should throw.
|
|
||||||
StringSanitizerPtr ss;
|
|
||||||
ASSERT_THROW(ss.reset(new StringSanitizer("[bogus-regex","")), BadValue);
|
|
||||||
|
|
||||||
std::string good_data(StringSanitizer::MAX_DATA_SIZE, '0');
|
|
||||||
std::string bad_data(StringSanitizer::MAX_DATA_SIZE + 1, '0');
|
|
||||||
|
|
||||||
ASSERT_NO_THROW(ss.reset(new StringSanitizer(good_data, good_data)));
|
|
||||||
|
|
||||||
ASSERT_THROW(ss.reset(new StringSanitizer(bad_data, "")), BadValue);
|
|
||||||
ASSERT_THROW(ss.reset(new StringSanitizer("", bad_data)), BadValue);
|
|
||||||
|
|
||||||
// List of invalid chars should work: (b,c,2 are invalid)
|
|
||||||
sanitizeStringTest("abc.123", "[b-c2]", "*",
|
|
||||||
"a**.1*3");
|
|
||||||
// Inverted list of valid chars should work: (b,c,2 are valid)
|
|
||||||
sanitizeStringTest("abc.123", "[^b-c2]", "*",
|
|
||||||
"*bc**2*");
|
|
||||||
|
|
||||||
// A string of all valid chars should return an identical string.
|
|
||||||
sanitizeStringTest("-_A--B__Cabc34567_-", "[^A-Ca-c3-7_-]", "x",
|
|
||||||
"-_A--B__Cabc34567_-");
|
|
||||||
|
|
||||||
// Replacing with a character should work.
|
|
||||||
sanitizeStringTest("A[b]c\12JoE3-_x!B$Y#e", "[^A-Za-z0-9_]", "*",
|
|
||||||
"A*b*c*JoE3*_x*B*Y*e");
|
|
||||||
|
|
||||||
// Removing (i.e.replacing with an "empty" string) should work.
|
|
||||||
sanitizeStringTest("A[b]c\12JoE3-_x!B$Y#e", "[^A-Za-z0-9_]", "",
|
|
||||||
"AbcJoE3_xBYe");
|
|
||||||
|
|
||||||
// More than one non-matching in a row should work.
|
|
||||||
sanitizeStringTest("%%A%%B%%C%%", "[^A-Za-z0-9_]", "x",
|
|
||||||
"xxAxxBxxCxx");
|
|
||||||
|
|
||||||
// Removing more than one non-matching in a row should work.
|
|
||||||
sanitizeStringTest("%%A%%B%%C%%", "[^A-Za-z0-9_]", "",
|
|
||||||
"ABC");
|
|
||||||
|
|
||||||
// Replacing with a string should work.
|
|
||||||
sanitizeStringTest("%%A%%B%%C%%", "[^A-Za-z0-9_]", "xyz",
|
|
||||||
"xyzxyzAxyzxyzBxyzxyzCxyzxyz");
|
|
||||||
|
|
||||||
// Dots as valid chars work.
|
|
||||||
sanitizeStringTest("abc.123", "[^A-Za-z0-9_.]", "*",
|
|
||||||
"abc.123");
|
|
||||||
|
|
||||||
std::string withNulls("\000ab\000c.12\0003",10);
|
|
||||||
sanitizeStringTest(withNulls, "[^A-Za-z0-9_.]", "*",
|
|
||||||
"*ab*c.12*3");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verifies templated buffer iterator seekTrimmed() function
|
|
||||||
TEST(StringUtilTest, seekTrimmed) {
|
|
||||||
|
|
||||||
// Empty buffer should be fine.
|
|
||||||
std::vector<uint8_t> buffer;
|
|
||||||
auto begin = buffer.end();
|
|
||||||
auto end = buffer.end();
|
|
||||||
ASSERT_NO_THROW(end = seekTrimmed(begin, end, 0));
|
|
||||||
EXPECT_EQ(0, std::distance(begin, end));
|
|
||||||
|
|
||||||
// Buffer of only trim values, should be fine.
|
|
||||||
buffer = { 1, 1 };
|
|
||||||
begin = buffer.begin();
|
|
||||||
end = buffer.end();
|
|
||||||
ASSERT_NO_THROW(end = seekTrimmed(begin, end, 1));
|
|
||||||
EXPECT_EQ(0, std::distance(begin, end));
|
|
||||||
|
|
||||||
// One trailing null should trim off.
|
|
||||||
buffer = {'o', 'n', 'e', 0 };
|
|
||||||
begin = buffer.begin();
|
|
||||||
end = buffer.end();
|
|
||||||
ASSERT_NO_THROW(end = seekTrimmed(begin, end, 0));
|
|
||||||
EXPECT_EQ(3, std::distance(begin, end));
|
|
||||||
|
|
||||||
// More than one trailing null should trim off.
|
|
||||||
buffer = { 't', 'h', 'r', 'e', 'e', 0, 0, 0 };
|
|
||||||
begin = buffer.begin();
|
|
||||||
end = buffer.end();
|
|
||||||
ASSERT_NO_THROW(end = seekTrimmed(begin, end, 0));
|
|
||||||
EXPECT_EQ(5, std::distance(begin, end));
|
|
||||||
|
|
||||||
// Embedded null should be left in place.
|
|
||||||
buffer = { 'e', 'm', 0, 'b', 'e', 'd' };
|
|
||||||
begin = buffer.begin();
|
|
||||||
end = buffer.end();
|
|
||||||
ASSERT_NO_THROW(end = seekTrimmed(begin, end, 0));
|
|
||||||
EXPECT_EQ(6, std::distance(begin, end));
|
|
||||||
|
|
||||||
// Leading null should be left in place.
|
|
||||||
buffer = { 0, 'l', 'e', 'a', 'd', 'i', 'n', 'g' };
|
|
||||||
begin = buffer.begin();
|
|
||||||
end = buffer.end();
|
|
||||||
ASSERT_NO_THROW(end = seekTrimmed(begin, end, 0));
|
|
||||||
EXPECT_EQ(8, std::distance(begin, end));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verifies isPrintable predicate on strings.
|
|
||||||
TEST(StringUtilTest, stringIsPrintable) {
|
|
||||||
string content;
|
|
||||||
|
|
||||||
// Empty is printable.
|
|
||||||
EXPECT_TRUE(isPrintable(content));
|
|
||||||
|
|
||||||
// Check Abcd.
|
|
||||||
content = "Abcd";
|
|
||||||
EXPECT_TRUE(isPrintable(content));
|
|
||||||
|
|
||||||
// Add a control character (not printable).
|
|
||||||
content += "\a";
|
|
||||||
EXPECT_FALSE(isPrintable(content));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verifies isPrintable predicate on byte vectors.
|
|
||||||
TEST(StringUtilTest, vectorIsPrintable) {
|
|
||||||
vector<uint8_t> content;
|
|
||||||
|
|
||||||
// Empty is printable.
|
|
||||||
EXPECT_TRUE(isPrintable(content));
|
|
||||||
|
|
||||||
// Check Abcd.
|
|
||||||
content = { 0x41, 0x62, 0x63, 0x64 };
|
|
||||||
EXPECT_TRUE(isPrintable(content));
|
|
||||||
|
|
||||||
// Add a control character (not printable).
|
|
||||||
content.push_back('\a');
|
|
||||||
EXPECT_FALSE(isPrintable(content));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // end of anonymous namespace
|
|
@@ -7,7 +7,7 @@
|
|||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
|
||||||
#include <util/encode/encode.h>
|
#include <util/encode/encode.h>
|
||||||
#include <util/strutil.h>
|
#include <util/str.h>
|
||||||
#include <yang/adaptor_host.h>
|
#include <yang/adaptor_host.h>
|
||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
Reference in New Issue
Block a user