/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8                                :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney                              │
│                                                                              │
│ Permission to use, copy, modify, and/or distribute this software for         │
│ any purpose with or without fee is hereby granted, provided that the         │
│ above copyright notice and this permission notice appear in all copies.      │
│                                                                              │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL                │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED                │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE             │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL         │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR        │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER               │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR             │
│ PERFORMANCE OF THIS SOFTWARE.                                                │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/bits/safemacros.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/macros.internal.h"
#include "libc/nt/enum/formatmessageflags.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/str/str.h"

const struct Error {
  const long *x;
  const char *s;
} kErrors[] = {
    {&ENOSYS, "ENOSYS"},
    {&EPERM, "EPERM"},
    {&ENOENT, "ENOENT"},
    {&ESRCH, "ESRCH"},
    {&EINTR, "EINTR"},
    {&EIO, "EIO"},
    {&ENXIO, "ENXIO"},
    {&E2BIG, "E2BIG"},
    {&ENOEXEC, "ENOEXEC"},
    {&EBADF, "EBADF"},
    {&ECHILD, "ECHILD"},
    {&EAGAIN, "EAGAIN"},
    {&ENOMEM, "ENOMEM"},
    {&EACCES, "EACCES"},
    {&EFAULT, "EFAULT"},
    {&ENOTBLK, "ENOTBLK"},
    {&EBUSY, "EBUSY"},
    {&EEXIST, "EEXIST"},
    {&EXDEV, "EXDEV"},
    {&ENODEV, "ENODEV"},
    {&ENOTDIR, "ENOTDIR"},
    {&EISDIR, "EISDIR"},
    {&EINVAL, "EINVAL"},
    {&ENFILE, "ENFILE"},
    {&EMFILE, "EMFILE"},
    {&ENOTTY, "ENOTTY"},
    {&ETXTBSY, "ETXTBSY"},
    {&EFBIG, "EFBIG"},
    {&ENOSPC, "ENOSPC"},
    {&EDQUOT, "EDQUOT"},
    {&ESPIPE, "ESPIPE"},
    {&EROFS, "EROFS"},
    {&EMLINK, "EMLINK"},
    {&EPIPE, "EPIPE"},
    {&EDOM, "EDOM"},
    {&ERANGE, "ERANGE"},
    {&EDEADLK, "EDEADLK"},
    {&ENAMETOOLONG, "ENAMETOOLONG"},
    {&ENOLCK, "ENOLCK"},
    {&ENOTEMPTY, "ENOTEMPTY"},
    {&ELOOP, "ELOOP"},
    {&ENOMSG, "ENOMSG"},
    {&EIDRM, "EIDRM"},
    {&ETIME, "ETIME"},
    {&EPROTO, "EPROTO"},
    {&EOVERFLOW, "EOVERFLOW"},
    {&EILSEQ, "EILSEQ"},
    {&EUSERS, "EUSERS"},
    {&ENOTSOCK, "ENOTSOCK"},
    {&EDESTADDRREQ, "EDESTADDRREQ"},
    {&EMSGSIZE, "EMSGSIZE"},
    {&EPROTOTYPE, "EPROTOTYPE"},
    {&ENOPROTOOPT, "ENOPROTOOPT"},
    {&EPROTONOSUPPORT, "EPROTONOSUPPORT"},
    {&ESOCKTNOSUPPORT, "ESOCKTNOSUPPORT"},
    {&ENOTSUP, "ENOTSUP"},
    {&EOPNOTSUPP, "EOPNOTSUPP"},
    {&EPFNOSUPPORT, "EPFNOSUPPORT"},
    {&EAFNOSUPPORT, "EAFNOSUPPORT"},
    {&EADDRINUSE, "EADDRINUSE"},
    {&EADDRNOTAVAIL, "EADDRNOTAVAIL"},
    {&ENETDOWN, "ENETDOWN"},
    {&ENETUNREACH, "ENETUNREACH"},
    {&ENETRESET, "ENETRESET"},
    {&ECONNABORTED, "ECONNABORTED"},
    {&ECONNRESET, "ECONNRESET"},
    {&ENOBUFS, "ENOBUFS"},
    {&EISCONN, "EISCONN"},
    {&ENOTCONN, "ENOTCONN"},
    {&ESHUTDOWN, "ESHUTDOWN"},
    {&ETOOMANYREFS, "ETOOMANYREFS"},
    {&ETIMEDOUT, "ETIMEDOUT"},
    {&ECONNREFUSED, "ECONNREFUSED"},
    {&EHOSTDOWN, "EHOSTDOWN"},
    {&EHOSTUNREACH, "EHOSTUNREACH"},
    {&EALREADY, "EALREADY"},
    {&EINPROGRESS, "EINPROGRESS"},
    {&ESTALE, "ESTALE"},
    {&EREMOTE, "EREMOTE"},
    {&EBADMSG, "EBADMSG"},
    {&ECANCELED, "ECANCELED"},
    {&EOWNERDEAD, "EOWNERDEAD"},
    {&ENOTRECOVERABLE, "ENOTRECOVERABLE"},
    {&ENONET, "ENONET"},
    {&ERESTART, "ERESTART"},
    {&ECHRNG, "ECHRNG"},
    {&EL2NSYNC, "EL2NSYNC"},
    {&EL3HLT, "EL3HLT"},
    {&EL3RST, "EL3RST"},
    {&ELNRNG, "ELNRNG"},
    {&EUNATCH, "EUNATCH"},
    {&ENOCSI, "ENOCSI"},
    {&EL2HLT, "EL2HLT"},
    {&EBADE, "EBADE"},
    {&EBADR, "EBADR"},
    {&EXFULL, "EXFULL"},
    {&ENOANO, "ENOANO"},
    {&EBADRQC, "EBADRQC"},
    {&EBADSLT, "EBADSLT"},
    {&ENOSTR, "ENOSTR"},
    {&ENODATA, "ENODATA"},
    {&ENOSR, "ENOSR"},
    {&ENOPKG, "ENOPKG"},
    {&ENOLINK, "ENOLINK"},
    {&EADV, "EADV"},
    {&ESRMNT, "ESRMNT"},
    {&ECOMM, "ECOMM"},
    {&EMULTIHOP, "EMULTIHOP"},
    {&EDOTDOT, "EDOTDOT"},
    {&ENOTUNIQ, "ENOTUNIQ"},
    {&EBADFD, "EBADFD"},
    {&EREMCHG, "EREMCHG"},
    {&ELIBACC, "ELIBACC"},
    {&ELIBBAD, "ELIBBAD"},
    {&ELIBSCN, "ELIBSCN"},
    {&ELIBMAX, "ELIBMAX"},
    {&ELIBEXEC, "ELIBEXEC"},
    {&ESTRPIPE, "ESTRPIPE"},
    {&EUCLEAN, "EUCLEAN"},
    {&ENOTNAM, "ENOTNAM"},
    {&ENAVAIL, "ENAVAIL"},
    {&EISNAM, "EISNAM"},
    {&EREMOTEIO, "EREMOTEIO"},
    {&ENOMEDIUM, "ENOMEDIUM"},
    {&EMEDIUMTYPE, "EMEDIUMTYPE"},
    {&ENOKEY, "ENOKEY"},
    {&EKEYEXPIRED, "EKEYEXPIRED"},
    {&EKEYREVOKED, "EKEYREVOKED"},
    {&EKEYREJECTED, "EKEYREJECTED"},
    {&ERFKILL, "ERFKILL"},
    {&EHWPOISON, "EHWPOISON"},
};

static const char *geterrname(long x) {
  int i;
  if (!IsTiny() && x) {
    for (i = 0; i < ARRAYLEN(kErrors); ++i) {
      if (x == *kErrors[i].x) {
        return kErrors[i].s;
      }
    }
  }
  return "EUNKNOWN";
}

/**
 * Converts errno value to string.
 * @return 0 on success, or error code
 */
int strerror_r(int err, char *buf, size_t size) {
  const char *s;
  char16_t buf16[100];
  int winstate, sysvstate;
  s = geterrname(err);
  if (!SupportsWindows()) {
    (snprintf)(buf, size, "%s[%d]", s, err);
  } else {
    winstate = GetLastError();
    sysvstate = errno;
    if (FormatMessage(
            kNtFormatMessageFromSystem | kNtFormatMessageIgnoreInserts, NULL,
            err, 0, buf16, ARRAYLEN(buf16) - 1, 0) > 0) {
      chomp16(buf16);
    } else {
      buf16[0] = u'\0';
    }
    (snprintf)(buf, size, "%s/err=%d/errno:%d/GetLastError:%d%s%hs", s, err,
               sysvstate, winstate, buf16[0] ? " " : "", buf16);
  }
  return 0;
}