Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 76 additions & 79 deletions lib/swoc/src/bw_format.cc
Original file line number Diff line number Diff line change
Expand Up @@ -743,83 +743,79 @@ FixedBufferWriter::operator>>(std::ostream &s) const {
}

namespace {
// Hand rolled, might not be totally compliant everywhere, but probably close
// enough. The long string will be locally accurate. Clang requires the double
// braces. Why, Turing only knows.
static const std::array<std::string_view, 134> ERRNO_SHORT_NAME = {
{
"SUCCESS", "EPERM",
"ENOENT", "ESRCH",
"EINTR", "EIO",
"ENXIO", "E2BIG ",
"ENOEXEC", "EBADF",
"ECHILD", "EAGAIN",
"ENOMEM", "EACCES",
"EFAULT", "ENOTBLK",
"EBUSY", "EEXIST",
"EXDEV", "ENODEV",
"ENOTDIR", "EISDIR",
"EINVAL", "ENFILE",
"EMFILE", "ENOTTY",
"ETXTBSY", "EFBIG",
"ENOSPC", "ESPIPE",
"EROFS", "EMLINK",
"EPIPE", "EDOM",
"ERANGE", "EDEADLK",
"ENAMETOOLONG", "ENOLCK",
"ENOSYS", "ENOTEMPTY",
"ELOOP", "EWOULDBLOCK",
"ENOMSG", "EIDRM",
"ECHRNG", "EL2NSYNC",
"EL3HLT", "EL3RST",
"ELNRNG", "EUNATCH",
"ENOCSI", "EL2HTL",
"EBADE", "EBADR",
"EXFULL", "ENOANO",
"EBADRQC", "EBADSLT",
"EDEADLOCK", "EBFONT",
"ENOSTR", "ENODATA",
"ETIME", "ENOSR",
"ENONET", "ENOPKG",
"EREMOTE", "ENOLINK",
"EADV", "ESRMNT",
"ECOMM", "EPROTO",
"EMULTIHOP", "EDOTDOT",
"EBADMSG", "EOVERFLOW",
"ENOTUNIQ", "EBADFD",
"EREMCHG", "ELIBACC",
"ELIBBAD", "ELIBSCN",
"ELIBMAX", "ELIBEXEC",
"EILSEQ", "ERESTART",
"ESTRPIPE", "EUSERS",
"ENOTSOCK", "EDESTADDRREQ",
"EMSGSIZE", "EPROTOTYPE",
"ENOPROTOOPT", "EPROTONOSUPPORT",
"ESOCKTNOSUPPORT", "EOPNOTSUPP",
"EPFNOSUPPORT", "EAFNOSUPPORT",
"EADDRINUSE", "EADDRNOTAVAIL",
"ENETDOWN", "ENETUNREACH",
"ENETRESET", "ECONNABORTED",
"ECONNRESET", "ENOBUFS",
"EISCONN", "ENOTCONN",
"ESHUTDOWN", "ETOOMANYREFS",
"ETIMEDOUT", "ECONNREFUSED",
"EHOSTDOWN", "EHOSTUNREACH",
"EALREADY", "EINPROGRESS",
"ESTALE", "EUCLEAN",
"ENOTNAM", "ENAVAIL",
"EISNAM", "EREMOTEIO",
"EDQUOT", "ENOMEDIUM",
"EMEDIUMTYPE", "ECANCELED",
"ENOKEY", "EKEYEXPIRED",
"EKEYREVOKED", "EKEYREJECTED",
"EOWNERDEAD", "ENOTRECOVERABLE",
"ERFKILL", "EHWPOISON",
}
};
static constexpr DiscreteRange<unsigned> ERRNO_RANGE{0, ERRNO_SHORT_NAME.size() - 1};
// This provides convenient safe access to the errno short name array.
auto errno_short_name = [](unsigned n) { return ERRNO_RANGE.contains(n) ? ERRNO_SHORT_NAME[n] : "Unknown"sv; };
// errno value -> symbolic name, indexed directly by the errno number (O(1)).
// The numbering differs between Linux and the BSD-derived platforms, and even
// macOS and FreeBSD disagree on a few of their own high-numbered codes, so each
// platform carries a table generated from its real <errno.h>. Gaps in the
// numbering are empty and fall through to "Unknown"; a platform with no table
// is a compile error. The long form (the 'l' spec, below) still uses strerror().
// clang-format off
#if defined(__linux__)
static constexpr std::array<std::string_view, 134> ERRNO_NAMES = {{
/* 0 */ "SUCCESS", "EPERM", "ENOENT", "ESRCH", "EINTR", "EIO", "ENXIO", "E2BIG",
/* 8 */ "ENOEXEC", "EBADF", "ECHILD", "EAGAIN", "ENOMEM", "EACCES", "EFAULT", "ENOTBLK",
/* 16 */ "EBUSY", "EEXIST", "EXDEV", "ENODEV", "ENOTDIR", "EISDIR", "EINVAL", "ENFILE",
/* 24 */ "EMFILE", "ENOTTY", "ETXTBSY", "EFBIG", "ENOSPC", "ESPIPE", "EROFS", "EMLINK",
/* 32 */ "EPIPE", "EDOM", "ERANGE", "EDEADLK", "ENAMETOOLONG", "ENOLCK", "ENOSYS", "ENOTEMPTY",
/* 40 */ "ELOOP", "", "ENOMSG", "EIDRM", "ECHRNG", "EL2NSYNC", "EL3HLT", "EL3RST",
/* 48 */ "ELNRNG", "EUNATCH", "ENOCSI", "EL2HLT", "EBADE", "EBADR", "EXFULL", "ENOANO",
/* 56 */ "EBADRQC", "EBADSLT", "", "EBFONT", "ENOSTR", "ENODATA", "ETIME", "ENOSR",
/* 64 */ "ENONET", "ENOPKG", "EREMOTE", "ENOLINK", "EADV", "ESRMNT", "ECOMM", "EPROTO",
/* 72 */ "EMULTIHOP", "EDOTDOT", "EBADMSG", "EOVERFLOW", "ENOTUNIQ", "EBADFD", "EREMCHG", "ELIBACC",
/* 80 */ "ELIBBAD", "ELIBSCN", "ELIBMAX", "ELIBEXEC", "EILSEQ", "ERESTART", "ESTRPIPE", "EUSERS",
/* 88 */ "ENOTSOCK", "EDESTADDRREQ", "EMSGSIZE", "EPROTOTYPE", "ENOPROTOOPT", "EPROTONOSUPPORT", "ESOCKTNOSUPPORT", "EOPNOTSUPP",
/* 96 */ "EPFNOSUPPORT", "EAFNOSUPPORT", "EADDRINUSE", "EADDRNOTAVAIL", "ENETDOWN", "ENETUNREACH", "ENETRESET", "ECONNABORTED",
/* 104 */ "ECONNRESET", "ENOBUFS", "EISCONN", "ENOTCONN", "ESHUTDOWN", "ETOOMANYREFS", "ETIMEDOUT", "ECONNREFUSED",
/* 112 */ "EHOSTDOWN", "EHOSTUNREACH", "EALREADY", "EINPROGRESS", "ESTALE", "EUCLEAN", "ENOTNAM", "ENAVAIL",
/* 120 */ "EISNAM", "EREMOTEIO", "EDQUOT", "ENOMEDIUM", "EMEDIUMTYPE", "ECANCELED", "ENOKEY", "EKEYEXPIRED",
/* 128 */ "EKEYREVOKED", "EKEYREJECTED", "EOWNERDEAD", "ENOTRECOVERABLE", "ERFKILL", "EHWPOISON",
}};
#elif defined(__APPLE__)
static constexpr std::array<std::string_view, 108> ERRNO_NAMES = {{
/* 0 */ "SUCCESS", "EPERM", "ENOENT", "ESRCH", "EINTR", "EIO", "ENXIO", "E2BIG",
/* 8 */ "ENOEXEC", "EBADF", "ECHILD", "EDEADLK", "ENOMEM", "EACCES", "EFAULT", "ENOTBLK",
/* 16 */ "EBUSY", "EEXIST", "EXDEV", "ENODEV", "ENOTDIR", "EISDIR", "EINVAL", "ENFILE",
/* 24 */ "EMFILE", "ENOTTY", "ETXTBSY", "EFBIG", "ENOSPC", "ESPIPE", "EROFS", "EMLINK",
/* 32 */ "EPIPE", "EDOM", "ERANGE", "EAGAIN", "EINPROGRESS", "EALREADY", "ENOTSOCK", "EDESTADDRREQ",
/* 40 */ "EMSGSIZE", "EPROTOTYPE", "ENOPROTOOPT", "EPROTONOSUPPORT", "ESOCKTNOSUPPORT", "ENOTSUP", "EPFNOSUPPORT", "EAFNOSUPPORT",
/* 48 */ "EADDRINUSE", "EADDRNOTAVAIL", "ENETDOWN", "ENETUNREACH", "ENETRESET", "ECONNABORTED", "ECONNRESET", "ENOBUFS",
/* 56 */ "EISCONN", "ENOTCONN", "ESHUTDOWN", "ETOOMANYREFS", "ETIMEDOUT", "ECONNREFUSED", "ELOOP", "ENAMETOOLONG",
/* 64 */ "EHOSTDOWN", "EHOSTUNREACH", "ENOTEMPTY", "EPROCLIM", "EUSERS", "EDQUOT", "ESTALE", "EREMOTE",
/* 72 */ "EBADRPC", "ERPCMISMATCH", "EPROGUNAVAIL", "EPROGMISMATCH", "EPROCUNAVAIL", "ENOLCK", "ENOSYS", "EFTYPE",
/* 80 */ "EAUTH", "ENEEDAUTH", "EPWROFF", "EDEVERR", "EOVERFLOW", "EBADEXEC", "EBADARCH", "ESHLIBVERS",
/* 88 */ "EBADMACHO", "ECANCELED", "EIDRM", "ENOMSG", "EILSEQ", "ENOATTR", "EBADMSG", "EMULTIHOP",
/* 96 */ "ENODATA", "ENOLINK", "ENOSR", "ENOSTR", "EPROTO", "ETIME", "EOPNOTSUPP", "ENOPOLICY",
/* 104 */ "ENOTRECOVERABLE", "EOWNERDEAD", "EQFULL", "ENOTCAPABLE",
}};
#elif defined(__FreeBSD__)
static constexpr std::array<std::string_view, 98> ERRNO_NAMES = {{
/* 0 */ "SUCCESS", "EPERM", "ENOENT", "ESRCH", "EINTR", "EIO", "ENXIO", "E2BIG",
/* 8 */ "ENOEXEC", "EBADF", "ECHILD", "EDEADLK", "ENOMEM", "EACCES", "EFAULT", "ENOTBLK",
/* 16 */ "EBUSY", "EEXIST", "EXDEV", "ENODEV", "ENOTDIR", "EISDIR", "EINVAL", "ENFILE",
/* 24 */ "EMFILE", "ENOTTY", "ETXTBSY", "EFBIG", "ENOSPC", "ESPIPE", "EROFS", "EMLINK",
/* 32 */ "EPIPE", "EDOM", "ERANGE", "EAGAIN", "EINPROGRESS", "EALREADY", "ENOTSOCK", "EDESTADDRREQ",
/* 40 */ "EMSGSIZE", "EPROTOTYPE", "ENOPROTOOPT", "EPROTONOSUPPORT", "ESOCKTNOSUPPORT", "EOPNOTSUPP", "EPFNOSUPPORT", "EAFNOSUPPORT",
/* 48 */ "EADDRINUSE", "EADDRNOTAVAIL", "ENETDOWN", "ENETUNREACH", "ENETRESET", "ECONNABORTED", "ECONNRESET", "ENOBUFS",
/* 56 */ "EISCONN", "ENOTCONN", "ESHUTDOWN", "ETOOMANYREFS", "ETIMEDOUT", "ECONNREFUSED", "ELOOP", "ENAMETOOLONG",
/* 64 */ "EHOSTDOWN", "EHOSTUNREACH", "ENOTEMPTY", "EPROCLIM", "EUSERS", "EDQUOT", "ESTALE", "EREMOTE",
/* 72 */ "EBADRPC", "ERPCMISMATCH", "EPROGUNAVAIL", "EPROGMISMATCH", "EPROCUNAVAIL", "ENOLCK", "ENOSYS", "EFTYPE",
/* 80 */ "EAUTH", "ENEEDAUTH", "EIDRM", "ENOMSG", "EOVERFLOW", "ECANCELED", "EILSEQ", "ENOATTR",
/* 88 */ "EDOOFUS", "EBADMSG", "EMULTIHOP", "ENOLINK", "EPROTO", "ENOTCAPABLE", "ECAPMODE", "ENOTRECOVERABLE",
/* 96 */ "EOWNERDEAD", "EINTEGRITY",
}};
#else
#error "errno name table not defined for this platform"
#endif
// clang-format on

std::string_view
errno_short_name(int e)
{
if (e >= 0 && e < static_cast<int>(ERRNO_NAMES.size()) && !ERRNO_NAMES[e].empty()) {
return ERRNO_NAMES[e];
}
return "Unknown"sv;
}
} // namespace

BufferWriter &
Expand Down Expand Up @@ -910,8 +906,9 @@ bwformat(BufferWriter &w, bwf::Spec const &spec, std::error_code const &ec) {
if (spec.has_numeric_type()) { // if numeric type, print just the numeric part.
bwformat(w, spec, ec.value());
} else {
if ((&ec.category() == G_CAT || &ec.category() == S_CAT) && swoc::ERRNO_RANGE.contains(ec.value())) {
bwformat(w, spec, swoc::ERRNO_SHORT_NAME[ec.value()]);
auto short_name = (&ec.category() == G_CAT || &ec.category() == S_CAT) ? errno_short_name(ec.value()) : "Unknown"sv;
if (short_name != "Unknown"sv) {
bwformat(w, spec, short_name);
} else {
w.write(ec.message());
}
Expand Down
35 changes: 35 additions & 0 deletions lib/swoc/unit_tests/test_bw_format.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
#include <iostream>
#include <variant>
#include <cmath>
#include <cerrno>
#include <string>
#include <system_error>

#include <netinet/in.h>

Expand Down Expand Up @@ -545,6 +548,38 @@ TEST_CASE("bwstring std formats", "[libswoc][bwprint]") {
w.clear().print("{::l}", swoc::bwf::Errno(13));
REQUIRE(w.view() == "Permission denied [13]"sv);

// The symbolic short name must come from the running platform's <errno.h>,
// not a fixed Linux-numbered table. Regression guard for the FreeBSD/macOS
// mislabel where ETIMEDOUT (errno 60 on those platforms) printed as "ENOSTR"
// (apache/trafficserver#13203). Use the macros so each platform checks its
// own numbering.
w.clear().print("{:s:s}", swoc::bwf::Errno(EPERM));
REQUIRE(w.view() == "EPERM"sv);
w.clear().print("{:s:s}", swoc::bwf::Errno(ETIMEDOUT));
REQUIRE(w.view() == "ETIMEDOUT"sv);
w.clear().print("{:s:s}", swoc::bwf::Errno(ECONNREFUSED));
REQUIRE(w.view() == "ECONNREFUSED"sv);

// Direct guards for the codes that were missing from the tables (each one
// formatted as "Unknown" before this fix). They are platform-specific, so
// each only compiles where it is defined: ENOATTR on macOS/FreeBSD, EL2HLT
// on Linux.
#ifdef ENOATTR
w.clear().print("{:s:s}", swoc::bwf::Errno(ENOATTR));
REQUIRE(w.view() == "ENOATTR"sv);
#endif
#ifdef EL2HLT
w.clear().print("{:s:s}", swoc::bwf::Errno(EL2HLT));
REQUIRE(w.view() == "EL2HLT"sv);
#endif

// The std::error_code formatter routes through the same per-platform table,
// rendering "<name> [<value>]". Build the expectation from the macro so the
// numeric part matches the running platform's numbering.
std::string ec_expected = "ECONNREFUSED [" + std::to_string(ECONNREFUSED) + "]";
w.clear().print("{}", std::error_code(ECONNREFUSED, std::generic_category()));
REQUIRE(w.view() == ec_expected);

time_t t = 1528484137;
// default is GMT
w.clear().print("{} is {}", t, swoc::bwf::Date(t));
Expand Down