#!/usr/bin/env bash export LC_ALL=C KNOWN_VIOLATIONS=( "src/bitcoin-tx.cpp.*stoul" "src/bitcoin-tx.cpp.*std::to_string" "src/bitcoin-tx.cpp.*trim_right" "src/dbwrapper.cpp.*stoul" "src/dbwrapper.cpp:.*vsnprintf" "src/httprpc.cpp.*trim" "src/init.cpp:.*atoi" "src/qt/optionsmodel.cpp.*std::to_string" "src/qt/rpcconsole.cpp:.*atoi" "src/rest.cpp:.*strtol" "src/rpc/net.cpp.*std::to_string" "src/rpc/rawtransaction.cpp.*std::to_string" "src/rpc/util.cpp.*std::to_string" "src/test/addrman_tests.cpp.*std::to_string" "src/test/blockchain_tests.cpp.*std::to_string" "src/test/dbwrapper_tests.cpp:.*snprintf" "src/test/denialofservice_tests.cpp.*std::to_string" "src/test/fuzz/parse_numbers.cpp:.*atoi" "src/test/key_tests.cpp.*std::to_string" "src/test/net_tests.cpp.*std::to_string" "src/test/settings_tests.cpp.*std::to_string" "src/test/timedata_tests.cpp.*std::to_string" "src/test/util/setup_common.cpp.*std::to_string" "src/test/util_tests.cpp.*std::to_string" "src/test/util_threadnames_tests.cpp.*std::to_string" "src/torcontrol.cpp:.*atoi" "src/torcontrol.cpp:.*strtol" "src/util/strencodings.cpp:.*atoi" "src/util/strencodings.cpp:.*strtol" "src/util/strencodings.cpp:.*strtoul" "src/util/strencodings.h:.*atoi" "src/util/system.cpp:.*atoi" "src/wallet/scriptpubkeyman.cpp.*std::to_string" ) REGEXP_IGNORE_EXTERNAL_DEPENDENCIES="^src/(crypto/ctaes/|leveldb/|secp256k1/|tinyformat.h|univalue/)" LOCALE_DEPENDENT_FUNCTIONS=( alphasort # LC_COLLATE (via strcoll) asctime # LC_TIME (directly) asprintf # (via vasprintf) atof # LC_NUMERIC (via strtod) atoi # LC_NUMERIC (via strtol) atol # LC_NUMERIC (via strtol) atoll # (via strtoll) atoq btowc # LC_CTYPE (directly) ctime # (via asctime or localtime) dprintf # (via vdprintf) fgetwc fgetws fold_case # boost::locale::fold_case fprintf # (via vfprintf) fputwc fputws fscanf # (via __vfscanf) fwprintf # (via __vfwprintf) getdate # via __getdate_r => isspace // __localtime_r getwc getwchar is_digit # boost::algorithm::is_digit is_space # boost::algorithm::is_space isalnum # LC_CTYPE isalpha # LC_CTYPE isblank # LC_CTYPE iscntrl # LC_CTYPE isctype # LC_CTYPE isdigit # LC_CTYPE isgraph # LC_CTYPE islower # LC_CTYPE isprint # LC_CTYPE ispunct # LC_CTYPE isspace # LC_CTYPE isupper # LC_CTYPE iswalnum # LC_CTYPE iswalpha # LC_CTYPE iswblank # LC_CTYPE iswcntrl # LC_CTYPE iswctype # LC_CTYPE iswdigit # LC_CTYPE iswgraph # LC_CTYPE iswlower # LC_CTYPE iswprint # LC_CTYPE iswpunct # LC_CTYPE iswspace # LC_CTYPE iswupper # LC_CTYPE iswxdigit # LC_CTYPE isxdigit # LC_CTYPE localeconv # LC_NUMERIC + LC_MONETARY mblen # LC_CTYPE mbrlen mbrtowc mbsinit mbsnrtowcs mbsrtowcs mbstowcs # LC_CTYPE mbtowc # LC_CTYPE mktime normalize # boost::locale::normalize printf # LC_NUMERIC putwc putwchar scanf # LC_NUMERIC setlocale snprintf sprintf sscanf std::to_string stod stof stoi stol stold stoll stoul stoull strcasecmp strcasestr strcoll # LC_COLLATE # strerror strfmon strftime # LC_TIME strncasecmp strptime strtod # LC_NUMERIC strtof strtoimax strtol # LC_NUMERIC strtold strtoll strtoq strtoul # LC_NUMERIC strtoull strtoumax strtouq strxfrm # LC_COLLATE swprintf to_lower # boost::locale::to_lower to_title # boost::locale::to_title to_upper # boost::locale::to_upper tolower # LC_CTYPE toupper # LC_CTYPE towctrans towlower # LC_CTYPE towupper # LC_CTYPE trim # boost::algorithm::trim trim_left # boost::algorithm::trim_left trim_right # boost::algorithm::trim_right ungetwc vasprintf vdprintf versionsort vfprintf vfscanf vfwprintf vprintf vscanf vsnprintf vsprintf vsscanf vswprintf vwprintf wcrtomb wcscasecmp wcscoll # LC_COLLATE wcsftime # LC_TIME wcsncasecmp wcsnrtombs wcsrtombs wcstod # LC_NUMERIC wcstof wcstoimax wcstol # LC_NUMERIC wcstold wcstoll wcstombs # LC_CTYPE wcstoul # LC_NUMERIC wcstoull wcstoumax wcswidth wcsxfrm # LC_COLLATE wctob wctomb # LC_CTYPE wctrans wctype wcwidth wprintf ) function join_array { local IFS="$1" shift echo "$*" } REGEXP_IGNORE_KNOWN_VIOLATIONS=$(join_array "|" "${KNOWN_VIOLATIONS[@]}") # Invoke "git grep" only once in order to minimize run-time REGEXP_LOCALE_DEPENDENT_FUNCTIONS=$(join_array "|" "${LOCALE_DEPENDENT_FUNCTIONS[@]}") GIT_GREP_OUTPUT=$(git grep -E "[^a-zA-Z0-9_\`'\"<>](${REGEXP_LOCALE_DEPENDENT_FUNCTIONS}(_r|_s)?)[^a-zA-Z0-9_\`'\"<>]" -- "*.cpp" "*.h") EXIT_CODE=0 for LOCALE_DEPENDENT_FUNCTION in "${LOCALE_DEPENDENT_FUNCTIONS[@]}"; do MATCHES=$(grep -E "[^a-zA-Z0-9_\`'\"<>]${LOCALE_DEPENDENT_FUNCTION}(_r|_s)?[^a-zA-Z0-9_\`'\"<>]" <<< "${GIT_GREP_OUTPUT}" | \ grep -vE "\.(c|cpp|h):\s*(//|\*|/\*|\").*${LOCALE_DEPENDENT_FUNCTION}") if [[ ${REGEXP_IGNORE_EXTERNAL_DEPENDENCIES} != "" ]]; then MATCHES=$(grep -vE "${REGEXP_IGNORE_EXTERNAL_DEPENDENCIES}" <<< "${MATCHES}") fi if [[ ${REGEXP_IGNORE_KNOWN_VIOLATIONS} != "" ]]; then MATCHES=$(grep -vE "${REGEXP_IGNORE_KNOWN_VIOLATIONS}" <<< "${MATCHES}") fi if [[ ${MATCHES} != "" ]]; then echo "The locale dependent function ${LOCALE_DEPENDENT_FUNCTION}(...) appears to be used:" echo "${MATCHES}" echo EXIT_CODE=1 fi done if [[ ${EXIT_CODE} != 0 ]]; then echo "Unnecessary locale dependence can cause bugs that are very" echo "tricky to isolate and fix. Please avoid using locale dependent" echo "functions if possible." echo echo "Advice not applicable in this specific case? Add an exception" echo "by updating the ignore list in $0" fi exit ${EXIT_CODE}