Files
free-file-sync-mirror/zen/sys_error.cpp
T
2025-12-10 14:38:26 -08:00

282 lines
13 KiB
C++

// *****************************************************************************
// * This file is part of the FreeFileSync project. It is distributed under *
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
// *****************************************************************************
#include "sys_error.h"
#include <gio/gio.h>
using namespace zen;
namespace
{
std::wstring formatSystemErrorCode(ErrorCode ec)
{
switch (ec) //pretty much all codes currently used on CentOS 7 and macOS 10.15
{
ZEN_CHECK_CASE_FOR_CONSTANT(EPERM);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOENT);
ZEN_CHECK_CASE_FOR_CONSTANT(ESRCH);
ZEN_CHECK_CASE_FOR_CONSTANT(EINTR);
ZEN_CHECK_CASE_FOR_CONSTANT(EIO);
ZEN_CHECK_CASE_FOR_CONSTANT(ENXIO);
ZEN_CHECK_CASE_FOR_CONSTANT(E2BIG);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOEXEC);
ZEN_CHECK_CASE_FOR_CONSTANT(EBADF);
ZEN_CHECK_CASE_FOR_CONSTANT(ECHILD);
ZEN_CHECK_CASE_FOR_CONSTANT(EAGAIN);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOMEM);
ZEN_CHECK_CASE_FOR_CONSTANT(EACCES);
ZEN_CHECK_CASE_FOR_CONSTANT(EFAULT);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOTBLK);
ZEN_CHECK_CASE_FOR_CONSTANT(EBUSY);
ZEN_CHECK_CASE_FOR_CONSTANT(EEXIST);
ZEN_CHECK_CASE_FOR_CONSTANT(EXDEV);
ZEN_CHECK_CASE_FOR_CONSTANT(ENODEV);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOTDIR);
ZEN_CHECK_CASE_FOR_CONSTANT(EISDIR);
ZEN_CHECK_CASE_FOR_CONSTANT(EINVAL);
ZEN_CHECK_CASE_FOR_CONSTANT(ENFILE);
ZEN_CHECK_CASE_FOR_CONSTANT(EMFILE);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOTTY);
ZEN_CHECK_CASE_FOR_CONSTANT(ETXTBSY);
ZEN_CHECK_CASE_FOR_CONSTANT(EFBIG);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOSPC);
ZEN_CHECK_CASE_FOR_CONSTANT(ESPIPE);
ZEN_CHECK_CASE_FOR_CONSTANT(EROFS);
ZEN_CHECK_CASE_FOR_CONSTANT(EMLINK);
ZEN_CHECK_CASE_FOR_CONSTANT(EPIPE);
ZEN_CHECK_CASE_FOR_CONSTANT(EDOM);
ZEN_CHECK_CASE_FOR_CONSTANT(ERANGE);
ZEN_CHECK_CASE_FOR_CONSTANT(EDEADLK);
ZEN_CHECK_CASE_FOR_CONSTANT(ENAMETOOLONG);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOLCK);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOSYS);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOTEMPTY);
ZEN_CHECK_CASE_FOR_CONSTANT(ELOOP);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOMSG);
ZEN_CHECK_CASE_FOR_CONSTANT(EIDRM);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOSTR);
ZEN_CHECK_CASE_FOR_CONSTANT(ENODATA);
ZEN_CHECK_CASE_FOR_CONSTANT(ETIME);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOSR);
ZEN_CHECK_CASE_FOR_CONSTANT(EREMOTE);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOLINK);
ZEN_CHECK_CASE_FOR_CONSTANT(EPROTO);
ZEN_CHECK_CASE_FOR_CONSTANT(EMULTIHOP);
ZEN_CHECK_CASE_FOR_CONSTANT(EBADMSG);
ZEN_CHECK_CASE_FOR_CONSTANT(EOVERFLOW);
ZEN_CHECK_CASE_FOR_CONSTANT(EILSEQ);
ZEN_CHECK_CASE_FOR_CONSTANT(EUSERS);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOTSOCK);
ZEN_CHECK_CASE_FOR_CONSTANT(EDESTADDRREQ);
ZEN_CHECK_CASE_FOR_CONSTANT(EMSGSIZE);
ZEN_CHECK_CASE_FOR_CONSTANT(EPROTOTYPE);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOPROTOOPT);
ZEN_CHECK_CASE_FOR_CONSTANT(EPROTONOSUPPORT);
ZEN_CHECK_CASE_FOR_CONSTANT(ESOCKTNOSUPPORT);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOTSUP);
ZEN_CHECK_CASE_FOR_CONSTANT(EPFNOSUPPORT);
ZEN_CHECK_CASE_FOR_CONSTANT(EAFNOSUPPORT);
ZEN_CHECK_CASE_FOR_CONSTANT(EADDRINUSE);
ZEN_CHECK_CASE_FOR_CONSTANT(EADDRNOTAVAIL);
ZEN_CHECK_CASE_FOR_CONSTANT(ENETDOWN);
ZEN_CHECK_CASE_FOR_CONSTANT(ENETUNREACH);
ZEN_CHECK_CASE_FOR_CONSTANT(ENETRESET);
ZEN_CHECK_CASE_FOR_CONSTANT(ECONNABORTED);
ZEN_CHECK_CASE_FOR_CONSTANT(ECONNRESET);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOBUFS);
ZEN_CHECK_CASE_FOR_CONSTANT(EISCONN);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOTCONN);
ZEN_CHECK_CASE_FOR_CONSTANT(ESHUTDOWN);
ZEN_CHECK_CASE_FOR_CONSTANT(ETOOMANYREFS);
ZEN_CHECK_CASE_FOR_CONSTANT(ETIMEDOUT);
ZEN_CHECK_CASE_FOR_CONSTANT(ECONNREFUSED);
ZEN_CHECK_CASE_FOR_CONSTANT(EHOSTDOWN);
ZEN_CHECK_CASE_FOR_CONSTANT(EHOSTUNREACH);
ZEN_CHECK_CASE_FOR_CONSTANT(EALREADY);
ZEN_CHECK_CASE_FOR_CONSTANT(EINPROGRESS);
ZEN_CHECK_CASE_FOR_CONSTANT(ESTALE);
ZEN_CHECK_CASE_FOR_CONSTANT(EDQUOT);
ZEN_CHECK_CASE_FOR_CONSTANT(ECANCELED);
ZEN_CHECK_CASE_FOR_CONSTANT(EOWNERDEAD);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOTRECOVERABLE);
ZEN_CHECK_CASE_FOR_CONSTANT(ECHRNG);
ZEN_CHECK_CASE_FOR_CONSTANT(EL2NSYNC);
ZEN_CHECK_CASE_FOR_CONSTANT(EL3HLT);
ZEN_CHECK_CASE_FOR_CONSTANT(EL3RST);
ZEN_CHECK_CASE_FOR_CONSTANT(ELNRNG);
ZEN_CHECK_CASE_FOR_CONSTANT(EUNATCH);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOCSI);
ZEN_CHECK_CASE_FOR_CONSTANT(EL2HLT);
ZEN_CHECK_CASE_FOR_CONSTANT(EBADE);
ZEN_CHECK_CASE_FOR_CONSTANT(EBADR);
ZEN_CHECK_CASE_FOR_CONSTANT(EXFULL);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOANO);
ZEN_CHECK_CASE_FOR_CONSTANT(EBADRQC);
ZEN_CHECK_CASE_FOR_CONSTANT(EBADSLT);
ZEN_CHECK_CASE_FOR_CONSTANT(EBFONT);
ZEN_CHECK_CASE_FOR_CONSTANT(ENONET);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOPKG);
ZEN_CHECK_CASE_FOR_CONSTANT(EADV);
ZEN_CHECK_CASE_FOR_CONSTANT(ESRMNT);
ZEN_CHECK_CASE_FOR_CONSTANT(ECOMM);
ZEN_CHECK_CASE_FOR_CONSTANT(EDOTDOT);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOTUNIQ);
ZEN_CHECK_CASE_FOR_CONSTANT(EBADFD);
ZEN_CHECK_CASE_FOR_CONSTANT(EREMCHG);
ZEN_CHECK_CASE_FOR_CONSTANT(ELIBACC);
ZEN_CHECK_CASE_FOR_CONSTANT(ELIBBAD);
ZEN_CHECK_CASE_FOR_CONSTANT(ELIBSCN);
ZEN_CHECK_CASE_FOR_CONSTANT(ELIBMAX);
ZEN_CHECK_CASE_FOR_CONSTANT(ELIBEXEC);
ZEN_CHECK_CASE_FOR_CONSTANT(ERESTART);
ZEN_CHECK_CASE_FOR_CONSTANT(ESTRPIPE);
ZEN_CHECK_CASE_FOR_CONSTANT(EUCLEAN);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOTNAM);
ZEN_CHECK_CASE_FOR_CONSTANT(ENAVAIL);
ZEN_CHECK_CASE_FOR_CONSTANT(EISNAM);
ZEN_CHECK_CASE_FOR_CONSTANT(EREMOTEIO);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOMEDIUM);
ZEN_CHECK_CASE_FOR_CONSTANT(EMEDIUMTYPE);
ZEN_CHECK_CASE_FOR_CONSTANT(ENOKEY);
ZEN_CHECK_CASE_FOR_CONSTANT(EKEYEXPIRED);
ZEN_CHECK_CASE_FOR_CONSTANT(EKEYREVOKED);
ZEN_CHECK_CASE_FOR_CONSTANT(EKEYREJECTED);
ZEN_CHECK_CASE_FOR_CONSTANT(ERFKILL);
ZEN_CHECK_CASE_FOR_CONSTANT(EHWPOISON);
default:
return replaceCpy(_("Error code %x"), L"%x", numberTo<std::wstring>(ec));
}
}
}
std::wstring zen::formatGlibError(const std::string& functionName, GError* error)
{
if (!error)
return formatSystemError(functionName, L"", _("Error description not available.") + L" null GError");
if (error->domain == G_FILE_ERROR) //"values corresponding to errno codes"
return formatSystemError(functionName, error->code);
std::wstring errorCode;
if (error->domain == G_IO_ERROR)
errorCode = [&]() -> std::wstring
{
switch (error->code) //GIOErrorEnum: https://gitlab.gnome.org/GNOME/glib/-/blob/master/gio/gioenums.h#L530
{
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_FAILED);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_NOT_FOUND);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_EXISTS);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_IS_DIRECTORY);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_NOT_DIRECTORY);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_NOT_EMPTY);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_NOT_REGULAR_FILE);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_NOT_SYMBOLIC_LINK);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_NOT_MOUNTABLE_FILE);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_FILENAME_TOO_LONG);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_INVALID_FILENAME);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_TOO_MANY_LINKS);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_NO_SPACE);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_INVALID_ARGUMENT);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_PERMISSION_DENIED);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_NOT_SUPPORTED);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_NOT_MOUNTED);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_ALREADY_MOUNTED);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_CLOSED);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_CANCELLED);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_PENDING);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_READ_ONLY);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_CANT_CREATE_BACKUP);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_WRONG_ETAG);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_TIMED_OUT);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_WOULD_RECURSE);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_BUSY);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_WOULD_BLOCK);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_HOST_NOT_FOUND);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_WOULD_MERGE);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_FAILED_HANDLED);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_TOO_MANY_OPEN_FILES);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_NOT_INITIALIZED);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_ADDRESS_IN_USE);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_PARTIAL_INPUT);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_INVALID_DATA);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_DBUS_ERROR);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_HOST_UNREACHABLE);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_NETWORK_UNREACHABLE);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_CONNECTION_REFUSED);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_PROXY_FAILED);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_PROXY_AUTH_FAILED);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_PROXY_NEED_AUTH);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_PROXY_NOT_ALLOWED);
#ifndef GLIB_CHECK_VERSION //e.g Debian 8 (GLib 2.42) CentOS 7 (GLib 2.56)
#error Where is GLib?
#endif
#if GLIB_CHECK_VERSION(2, 44, 0)
static_assert(G_IO_ERROR_BROKEN_PIPE == G_IO_ERROR_CONNECTION_CLOSED);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_CONNECTION_CLOSED);
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_NOT_CONNECTED);
#endif
#if GLIB_CHECK_VERSION(2, 48, 0)
ZEN_CHECK_CASE_FOR_CONSTANT(G_IO_ERROR_MESSAGE_TOO_LARGE);
#endif
default:
return replaceCpy<std::wstring>(L"GIO error %x", L"%x", numberTo<std::wstring>(error->code));
}
}();
else
{
//g-file-error-quark => g-file-error
//g-io-error-quark => g-io-error
std::wstring domain = utfTo<std::wstring>(::g_quark_to_string(error->domain)); //e.g. "g-io-error-quark"
if (endsWith(domain, L"-quark"))
domain = beforeLast(domain, L"-", IfNotFoundReturn::none);
errorCode = domain + L' ' + numberTo<std::wstring>(error->code); //e.g. "g-io-error 15"
}
const std::wstring errorMsg = utfTo<std::wstring>(error->message); //e.g. "Unable to find or create trash directory for file.txt"
return formatSystemError(functionName, errorCode, errorMsg);
}
std::wstring zen::getSystemErrorDescription(ErrorCode ec) //return empty string on error
{
const ErrorCode ecCurrent = getLastError(); //not necessarily == ec
ZEN_ON_SCOPE_EXIT(errno = ecCurrent);
std::wstring errorMsg = utfTo<std::wstring>(::g_strerror(ec)); //... vs strerror(): "marginally improves thread safety, and marginally improves consistency"
trim(errorMsg); //Windows messages seem to end with a space...
return errorMsg;
}
std::wstring zen::formatSystemError(const std::string& functionName, ErrorCode ec)
{
return formatSystemError(functionName, formatSystemErrorCode(ec), getSystemErrorDescription(ec));
}
std::wstring zen::formatSystemError(const std::string& functionName, const std::wstring& errorCode, const std::wstring& errorMsg)
{
std::wstring output = trimCpy(errorCode);
const std::wstring errorMsgFmt = trimCpy(errorMsg);
if (!output.empty() && !errorMsgFmt.empty())
output += L": ";
output += errorMsgFmt;
if (!functionName.empty())
output += L" [" + utfTo<std::wstring>(functionName) + L']';
return trimCpy(output);
}