strerror.cc 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. // Copyright 2020 The Abseil Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // https://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include "y_absl/base/internal/strerror.h"
  15. #include <array>
  16. #include <cerrno>
  17. #include <cstddef>
  18. #include <cstdio>
  19. #include <cstring>
  20. #include <util/generic/string.h>
  21. #include <type_traits>
  22. #include "y_absl/base/internal/errno_saver.h"
  23. namespace y_absl {
  24. Y_ABSL_NAMESPACE_BEGIN
  25. namespace base_internal {
  26. namespace {
  27. const char* StrErrorAdaptor(int errnum, char* buf, size_t buflen) {
  28. #if defined(_WIN32)
  29. int rc = strerror_s(buf, buflen, errnum);
  30. buf[buflen - 1] = '\0'; // guarantee NUL termination
  31. if (rc == 0 && strncmp(buf, "Unknown error", buflen) == 0) *buf = '\0';
  32. return buf;
  33. #else
  34. // The type of `ret` is platform-specific; both of these branches must compile
  35. // either way but only one will execute on any given platform:
  36. auto ret = strerror_r(errnum, buf, buflen);
  37. if (std::is_same<decltype(ret), int>::value) {
  38. // XSI `strerror_r`; `ret` is `int`:
  39. if (ret) *buf = '\0';
  40. return buf;
  41. } else {
  42. // GNU `strerror_r`; `ret` is `char *`:
  43. return reinterpret_cast<const char*>(ret);
  44. }
  45. #endif
  46. }
  47. TString StrErrorInternal(int errnum) {
  48. char buf[100];
  49. const char* str = StrErrorAdaptor(errnum, buf, sizeof buf);
  50. if (*str == '\0') {
  51. snprintf(buf, sizeof buf, "Unknown error %d", errnum);
  52. str = buf;
  53. }
  54. return str;
  55. }
  56. // kSysNerr is the number of errors from a recent glibc. `StrError()` falls back
  57. // to `StrErrorAdaptor()` if the value is larger than this.
  58. constexpr int kSysNerr = 135;
  59. std::array<TString, kSysNerr>* NewStrErrorTable() {
  60. auto* table = new std::array<TString, kSysNerr>;
  61. for (size_t i = 0; i < table->size(); ++i) {
  62. (*table)[i] = StrErrorInternal(static_cast<int>(i));
  63. }
  64. return table;
  65. }
  66. } // namespace
  67. TString StrError(int errnum) {
  68. y_absl::base_internal::ErrnoSaver errno_saver;
  69. static const auto* table = NewStrErrorTable();
  70. if (errnum >= 0 && static_cast<size_t>(errnum) < table->size()) {
  71. return (*table)[static_cast<size_t>(errnum)];
  72. }
  73. return StrErrorInternal(errnum);
  74. }
  75. } // namespace base_internal
  76. Y_ABSL_NAMESPACE_END
  77. } // namespace y_absl