How can I print the symbolic name of an errno in C?
Asked Answered
F

7

18

I can use perror() or strerror() to print the "human readable" error message belonging to an errno, but what if I also want to print the symbolic name (e.g., "EAGAIN") of the errno?

Any convenient function or macro to do that?

Re-edit from the future: You're better off using the strerrorname_np() recently added to glibc (see accepted answer), or the errnoname library (previously accepted answer), rather than this hacky code I came up with before those options existed:

#include <ctype.h>
#include <errno.h>
#include <stdio.h>

int get_errno_name(char *buf, int buf_size) {
    // Using the linux-only gawk instead of awk, because of the convenient
    // match() functionality. For Posix portability, use a different recipe...
    char cmd[] = "e=       && " // errno to be inserted here (max digits = 6)
                 "echo '#include <errno.h>' | "
                 "gcc -dM -E - | " // optionally, use $CC inead of gcc
                 "gawk \"match(\\$0, /^#[[:space:]]*define[[:space:]]+"
                     "(E[[:alnum:]]+)[[:space:]]+$e($|[^[:alnum:]])/, m) "
                     "{ print m[1] }\"";
    {
        // Insert the errno as the "e" shell variable in the command above.
        int errno_digit_c = snprintf(cmd + 2, 6, "%d", errno);
        if (errno_digit_c < 1) {
            fprintf(stderr, "Failed to stringify an errno "
                            "in get_errno_name().\n");
            return -1;
        }
        // Replace the inserted terminating '\0' with whitespace
        cmd[errno_digit_c + 2] = ' ';
    }
    FILE *f = popen(cmd, "r");
    if (f == NULL) {
        perror("Failed to popen() in get_errno_name()");
        return -1;
    }
    int read_size = 0, c;
    while ((c = getc(f)) != EOF) {
        if (isalnum(c)) {
            buf[read_size++] = c;
            if (read_size + 1 > buf_size) {
                fprintf(stderr, "Read errno name is larger than the character "
                                "buffer supplied to get_errno_name().\n");
                return -1;
            }
        }
    }
    buf[read_size++] = '\0';
    if (pclose(f) == -1) {
        perror("Failed to pclose() in get_errno_name()");
        return -1;
    }
    return read_size;
}
Find answered 10/11, 2013 at 1:48 Comment(2)
Interesting code. I would not deploy a function that ran a C compiler in a production environment - it is both slow and 'unreliable' in that some people don't have compilers at run time. There is ample opportunity for caching results on the assumption that the set of error messages doesn't normally change except (perhaps) when the operating system is upgraded, and even then backwards compatibility means that most error numbers remain unchanged. You could use your code, though, to collect the data for all the system errors and then use that output in function driven from data at compile time.Downcomer
@JonathanLeffler Thanks for your feedback. Agreed on both accounts. I probably should've mentioned that the above is definitely not to be deployed as such (unless you don't mind run-time dependencies on both gcc and gawk...). And yes; code like this would better be run as part of a (c)make script at compile time. My projects tend to be smallish though, so I usually don't bother with such scripts anyway; which is why the above code suits me best for my personal debug builds.Find
N
9

glibc provides a function for that (a GNU extension): strerrorname_np().

It is declared in <string.h>.

It returns the errno name for valid values, or NULL for invalid ones.

man strerrorname_np

It was added in glibc 2.32.

Nl answered 17/2, 2021 at 7:12 Comment(4)
Minor thing, but frustratingly, strerrorname_np does not unambiguously state what happens if the error number passed in is 0: does it return NULL, or is that considered a valid number that returns something like "EOK"? Unclear from the docs, gotta look at the source (this kind of frustration is why I made sure to consciously pick and explicitly document one of those options in errnoname). But it's more than any other libc has done to get us closer to a standard solution. (And to be clear, I am one of the upvoters on this answer.)Depreciatory
Experimentally, it returns the string "0". I'll document that detail in the manual page. The following code errno = 0; printf("<%m> <%#m> <%s> <%s>\n", strerror(0), strerrorname_np(0)); prints <Success> <0> <Success> <0>. I remember this was discussed, and the decission was to not return NULL to avoid making that code trigger Undefined Behavior, since this is usually called for printing. "0" seemed the most reasonable thing to do.Nl
@Depreciatory Sorry for the long delay, I forgot! I've finally documented the result of strerrorname_cp(0).Nl
@Depreciatory git.kernel.org/pub/scm/docs/man-pages/man-pages.git/commit/…Nl
D
8

I recently wrote errnoname to do this.

There is unfortunately still no standard API to do this. (GNU's glibc has strerrorname_np, but no other C library currently provides it, so it's not even available on all Linux distros, let alone anywhere else.)

I try to keep it portable to all systems while covering all errno names on that system. So far that includes Linux, Darwin (macOS, iOS, etc), BSD (FreeBSD, NetBSD, OpenBSD, etc), several closed-source Unixes, Windows, and a few others.

The only "hard" part is getting the list of errno names and values to do the lookups, knowing which errno are even defined on the target system, and exhaustively handling all cases where multiple errno names might map to the same number. (For example, EWOULDBLOCK is equal to EAGAIN on some systems.)

So in my errnoname library you don't have to deal with any of that. I collect the errno names for many operating systems into a list as a separate step, then I generate the actual released C code from that. The C code uses #ifdef for each name, so it can compile on any system regardless of which errno names and values that system has. The compiled result is just simple and efficient code that doesn't have to call any programs or search for errno in library headers.

I also give you the option to compile it as either an array lookup or as a switch, since errno value are not guaranteed to be even close to contiguous, but on many system are contiguous enough to make an array lookup far better. (Although good modern compilers can automatically notice when it would be better to turn a switch into an array lookup.)

Anyway, since it is released under the "Zero-Clause BSD license" (0BSD), which is a permissive license, or more accurately a public-domain-equivalent license, you can do whatever you want with it.

Below is an old copy-paste of the function from my library, so that this answer is useful even if my repo link ever goes dead, but beware:

  1. The copy here only covers all errno names I could find as of start of August 2019 - I have found many others since then.

  2. I will keep updating the library to include errno names as I find them and as systems add them, add other improvements, and add duplicate protections as possible duplicates are discovered. But I will not (and last I checked, cannot, due to size limits on the answer) keep this answer's copy updated.

#include <errno.h>

char const * errnoname(int errno_)
{
    switch(errno_)
    {
#ifdef E2BIG
        case E2BIG: return "E2BIG";
#endif
#ifdef EACCES
        case EACCES: return "EACCES";
#endif
#ifdef EADDRINUSE
        case EADDRINUSE: return "EADDRINUSE";
#endif
#ifdef EADDRNOTAVAIL
        case EADDRNOTAVAIL: return "EADDRNOTAVAIL";
#endif
#ifdef EADI
        case EADI: return "EADI";
#endif
#ifdef EADV
        case EADV: return "EADV";
#endif
#ifdef EAFNOSUPPORT
        case EAFNOSUPPORT: return "EAFNOSUPPORT";
#endif
#ifdef EAGAIN
        case EAGAIN: return "EAGAIN";
#endif
#ifdef EAIO
        case EAIO: return "EAIO";
#endif
#ifdef EALIGN
        case EALIGN: return "EALIGN";
#endif
#ifdef EALREADY
        case EALREADY: return "EALREADY";
#endif
#ifdef EASYNC
        case EASYNC: return "EASYNC";
#endif
#ifdef EAUTH
        case EAUTH: return "EAUTH";
#endif
#ifdef EBADARCH
        case EBADARCH: return "EBADARCH";
#endif
#ifdef EBADE
        case EBADE: return "EBADE";
#endif
#ifdef EBADEXEC
        case EBADEXEC: return "EBADEXEC";
#endif
#ifdef EBADF
        case EBADF: return "EBADF";
#endif
#ifdef EBADFD
        case EBADFD: return "EBADFD";
#endif
#ifdef EBADMACHO
        case EBADMACHO: return "EBADMACHO";
#endif
#ifdef EBADMSG
        case EBADMSG: return "EBADMSG";
#endif
#ifdef EBADR
        case EBADR: return "EBADR";
#endif
#ifdef EBADRPC
        case EBADRPC: return "EBADRPC";
#endif
#ifdef EBADRQC
        case EBADRQC: return "EBADRQC";
#endif
#ifdef EBADSLT
        case EBADSLT: return "EBADSLT";
#endif
#ifdef EBADVER
        case EBADVER: return "EBADVER";
#endif
#ifdef EBFONT
        case EBFONT: return "EBFONT";
#endif
#ifdef EBUSY
        case EBUSY: return "EBUSY";
#endif
#ifdef ECANCELED
        case ECANCELED: return "ECANCELED";
#endif
#if defined(ECANCELLED) && (!defined(ECANCELED) || ECANCELLED != ECANCELED)
        case ECANCELLED: return "ECANCELLED";
#endif
#ifdef ECAPMODE
        case ECAPMODE: return "ECAPMODE";
#endif
#ifdef ECHILD
        case ECHILD: return "ECHILD";
#endif
#ifdef ECHRNG
        case ECHRNG: return "ECHRNG";
#endif
#ifdef ECKPT
        case ECKPT: return "ECKPT";
#endif
#ifdef ECLONEME
        case ECLONEME: return "ECLONEME";
#endif
#ifdef ECOMM
        case ECOMM: return "ECOMM";
#endif
#ifdef ECONFIG
        case ECONFIG: return "ECONFIG";
#endif
#ifdef ECONNABORTED
        case ECONNABORTED: return "ECONNABORTED";
#endif
#ifdef ECONNREFUSED
        case ECONNREFUSED: return "ECONNREFUSED";
#endif
#ifdef ECONNRESET
        case ECONNRESET: return "ECONNRESET";
#endif
#ifdef ECORRUPT
        case ECORRUPT: return "ECORRUPT";
#endif
#ifdef ECVCERORR
        case ECVCERORR: return "ECVCERORR";
#endif
#ifdef ECVPERORR
        case ECVPERORR: return "ECVPERORR";
#endif
#ifdef EDEADLK
        case EDEADLK: return "EDEADLK";
#endif
#if defined(EDEADLOCK) && (!defined(EDEADLK) || EDEADLOCK != EDEADLK)
        case EDEADLOCK: return "EDEADLOCK";
#endif
#ifdef EDESTADDREQ
        case EDESTADDREQ: return "EDESTADDREQ";
#endif
#ifdef EDESTADDRREQ
        case EDESTADDRREQ: return "EDESTADDRREQ";
#endif
#ifdef EDEVERR
        case EDEVERR: return "EDEVERR";
#endif
#ifdef EDIRIOCTL
        case EDIRIOCTL: return "EDIRIOCTL";
#endif
#ifdef EDIRTY
        case EDIRTY: return "EDIRTY";
#endif
#ifdef EDIST
        case EDIST: return "EDIST";
#endif
#ifdef EDOM
        case EDOM: return "EDOM";
#endif
#ifdef EDOOFUS
        case EDOOFUS: return "EDOOFUS";
#endif
#ifdef EDOTDOT
        case EDOTDOT: return "EDOTDOT";
#endif
#ifdef EDQUOT
        case EDQUOT: return "EDQUOT";
#endif
#ifdef EDUPFD
        case EDUPFD: return "EDUPFD";
#endif
#ifdef EDUPPKG
        case EDUPPKG: return "EDUPPKG";
#endif
#ifdef EEXIST
        case EEXIST: return "EEXIST";
#endif
#ifdef EFAIL
        case EFAIL: return "EFAIL";
#endif
#ifdef EFAULT
        case EFAULT: return "EFAULT";
#endif
#ifdef EFBIG
        case EFBIG: return "EFBIG";
#endif
#ifdef EFORMAT
        case EFORMAT: return "EFORMAT";
#endif
#ifdef EFSCORRUPTED
        case EFSCORRUPTED: return "EFSCORRUPTED";
#endif
#ifdef EFTYPE
        case EFTYPE: return "EFTYPE";
#endif
#ifdef EHOSTDOWN
        case EHOSTDOWN: return "EHOSTDOWN";
#endif
#ifdef EHOSTUNREACH
        case EHOSTUNREACH: return "EHOSTUNREACH";
#endif
#ifdef EHWPOISON
        case EHWPOISON: return "EHWPOISON";
#endif
#ifdef EIDRM
        case EIDRM: return "EIDRM";
#endif
#ifdef EILSEQ
        case EILSEQ: return "EILSEQ";
#endif
#ifdef EINIT
        case EINIT: return "EINIT";
#endif
#ifdef EINPROG
        case EINPROG: return "EINPROG";
#endif
#ifdef EINPROGRESS
        case EINPROGRESS: return "EINPROGRESS";
#endif
#ifdef EINTEGRITY
        case EINTEGRITY: return "EINTEGRITY";
#endif
#ifdef EINTR
        case EINTR: return "EINTR";
#endif
#ifdef EINVAL
        case EINVAL: return "EINVAL";
#endif
#ifdef EIO
        case EIO: return "EIO";
#endif
#ifdef EIPSEC
        case EIPSEC: return "EIPSEC";
#endif
#ifdef EISCONN
        case EISCONN: return "EISCONN";
#endif
#ifdef EISDIR
        case EISDIR: return "EISDIR";
#endif
#ifdef EISNAM
        case EISNAM: return "EISNAM";
#endif
#ifdef EJUSTRETURN
        case EJUSTRETURN: return "EJUSTRETURN";
#endif
#ifdef EKEEPLOOKING
        case EKEEPLOOKING: return "EKEEPLOOKING";
#endif
#ifdef EKEYEXPIRED
        case EKEYEXPIRED: return "EKEYEXPIRED";
#endif
#ifdef EKEYREJECTED
        case EKEYREJECTED: return "EKEYREJECTED";
#endif
#ifdef EKEYREVOKED
        case EKEYREVOKED: return "EKEYREVOKED";
#endif
#ifdef EL2HLT
        case EL2HLT: return "EL2HLT";
#endif
#ifdef EL2NSYNC
        case EL2NSYNC: return "EL2NSYNC";
#endif
#ifdef EL3HLT
        case EL3HLT: return "EL3HLT";
#endif
#ifdef EL3RST
        case EL3RST: return "EL3RST";
#endif
#ifdef ELIBACC
        case ELIBACC: return "ELIBACC";
#endif
#ifdef ELIBBAD
        case ELIBBAD: return "ELIBBAD";
#endif
#ifdef ELIBEXEC
        case ELIBEXEC: return "ELIBEXEC";
#endif
#ifdef ELIBMAX
        case ELIBMAX: return "ELIBMAX";
#endif
#ifdef ELIBSCN
        case ELIBSCN: return "ELIBSCN";
#endif
#ifdef ELNRNG
        case ELNRNG: return "ELNRNG";
#endif
#ifdef ELOCKUNMAPPED
        case ELOCKUNMAPPED: return "ELOCKUNMAPPED";
#endif
#ifdef ELOOP
        case ELOOP: return "ELOOP";
#endif
#ifdef EMEDIA
        case EMEDIA: return "EMEDIA";
#endif
#ifdef EMEDIUMTYPE
        case EMEDIUMTYPE: return "EMEDIUMTYPE";
#endif
#ifdef EMFILE
        case EMFILE: return "EMFILE";
#endif
#ifdef EMLINK
        case EMLINK: return "EMLINK";
#endif
#ifdef EMOUNTEXIT
        case EMOUNTEXIT: return "EMOUNTEXIT";
#endif
#ifdef EMOVEFD
        case EMOVEFD: return "EMOVEFD";
#endif
#ifdef EMSGSIZE
        case EMSGSIZE: return "EMSGSIZE";
#endif
#ifdef EMTIMERS
        case EMTIMERS: return "EMTIMERS";
#endif
#ifdef EMULTIHOP
        case EMULTIHOP: return "EMULTIHOP";
#endif
#ifdef ENAMETOOLONG
        case ENAMETOOLONG: return "ENAMETOOLONG";
#endif
#ifdef ENAVAIL
        case ENAVAIL: return "ENAVAIL";
#endif
#ifdef ENEEDAUTH
        case ENEEDAUTH: return "ENEEDAUTH";
#endif
#ifdef ENETDOWN
        case ENETDOWN: return "ENETDOWN";
#endif
#ifdef ENETRESET
        case ENETRESET: return "ENETRESET";
#endif
#ifdef ENETUNREACH
        case ENETUNREACH: return "ENETUNREACH";
#endif
#ifdef ENFILE
        case ENFILE: return "ENFILE";
#endif
#ifdef ENFSREMOTE
        case ENFSREMOTE: return "ENFSREMOTE";
#endif
#ifdef ENOANO
        case ENOANO: return "ENOANO";
#endif
#ifdef ENOATTR
        case ENOATTR: return "ENOATTR";
#endif
#ifdef ENOBUFS
        case ENOBUFS: return "ENOBUFS";
#endif
#ifdef ENOCONNECT
        case ENOCONNECT: return "ENOCONNECT";
#endif
#ifdef ENOCSI
        case ENOCSI: return "ENOCSI";
#endif
#ifdef ENODATA
        case ENODATA: return "ENODATA";
#endif
#ifdef ENODEV
        case ENODEV: return "ENODEV";
#endif
#ifdef ENOENT
        case ENOENT: return "ENOENT";
#endif
#ifdef ENOEXEC
        case ENOEXEC: return "ENOEXEC";
#endif
#ifdef ENOIOCTL
        case ENOIOCTL: return "ENOIOCTL";
#endif
#ifdef ENOKEY
        case ENOKEY: return "ENOKEY";
#endif
#ifdef ENOLCK
        case ENOLCK: return "ENOLCK";
#endif
#ifdef ENOLINK
        case ENOLINK: return "ENOLINK";
#endif
#ifdef ENOLOAD
        case ENOLOAD: return "ENOLOAD";
#endif
#ifdef ENOMATCH
        case ENOMATCH: return "ENOMATCH";
#endif
#ifdef ENOMEDIUM
        case ENOMEDIUM: return "ENOMEDIUM";
#endif
#ifdef ENOMEM
        case ENOMEM: return "ENOMEM";
#endif
#ifdef ENOMSG
        case ENOMSG: return "ENOMSG";
#endif
#ifdef ENONET
        case ENONET: return "ENONET";
#endif
#ifdef ENOPKG
        case ENOPKG: return "ENOPKG";
#endif
#ifdef ENOPOLICY
        case ENOPOLICY: return "ENOPOLICY";
#endif
#ifdef ENOPROTOOPT
        case ENOPROTOOPT: return "ENOPROTOOPT";
#endif
#ifdef ENOREG
        case ENOREG: return "ENOREG";
#endif
#ifdef ENOSPC
        case ENOSPC: return "ENOSPC";
#endif
#ifdef ENOSR
        case ENOSR: return "ENOSR";
#endif
#ifdef ENOSTR
        case ENOSTR: return "ENOSTR";
#endif
#ifdef ENOSYM
        case ENOSYM: return "ENOSYM";
#endif
#ifdef ENOSYS
        case ENOSYS: return "ENOSYS";
#endif
#ifdef ENOTACTIVE
        case ENOTACTIVE: return "ENOTACTIVE";
#endif
#ifdef ENOTBLK
        case ENOTBLK: return "ENOTBLK";
#endif
#ifdef ENOTCAPABLE
        case ENOTCAPABLE: return "ENOTCAPABLE";
#endif
#ifdef ENOTCONN
        case ENOTCONN: return "ENOTCONN";
#endif
#ifdef ENOTDIR
        case ENOTDIR: return "ENOTDIR";
#endif
#ifdef ENOTEMPTY
        case ENOTEMPTY: return "ENOTEMPTY";
#endif
#ifdef ENOTNAM
        case ENOTNAM: return "ENOTNAM";
#endif
#ifdef ENOTREADY
        case ENOTREADY: return "ENOTREADY";
#endif
#ifdef ENOTRECOVERABLE
        case ENOTRECOVERABLE: return "ENOTRECOVERABLE";
#endif
#ifdef ENOTRUST
        case ENOTRUST: return "ENOTRUST";
#endif
#ifdef ENOTSOCK
        case ENOTSOCK: return "ENOTSOCK";
#endif
#ifdef ENOTSUP
        case ENOTSUP: return "ENOTSUP";
#endif
#ifdef ENOTTY
        case ENOTTY: return "ENOTTY";
#endif
#ifdef ENOTUNIQ
        case ENOTUNIQ: return "ENOTUNIQ";
#endif
#ifdef ENOUNLD
        case ENOUNLD: return "ENOUNLD";
#endif
#ifdef ENOUNREG
        case ENOUNREG: return "ENOUNREG";
#endif
#ifdef ENXIO
        case ENXIO: return "ENXIO";
#endif
#ifdef EOPCOMPLETE
        case EOPCOMPLETE: return "EOPCOMPLETE";
#endif
#if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || EOPNOTSUPP != ENOTSUP)
        case EOPNOTSUPP: return "EOPNOTSUPP";
#endif
#ifdef EOVERFLOW
        case EOVERFLOW: return "EOVERFLOW";
#endif
#ifdef EOWNERDEAD
        case EOWNERDEAD: return "EOWNERDEAD";
#endif
#ifdef EPASSTHROUGH
        case EPASSTHROUGH: return "EPASSTHROUGH";
#endif
#ifdef EPATHREMOTE
        case EPATHREMOTE: return "EPATHREMOTE";
#endif
#ifdef EPERM
        case EPERM: return "EPERM";
#endif
#ifdef EPFNOSUPPORT
        case EPFNOSUPPORT: return "EPFNOSUPPORT";
#endif
#ifdef EPIPE
        case EPIPE: return "EPIPE";
#endif
#ifdef EPOWERF
        case EPOWERF: return "EPOWERF";
#endif
#ifdef EPROCLIM
        case EPROCLIM: return "EPROCLIM";
#endif
#ifdef EPROCUNAVAIL
        case EPROCUNAVAIL: return "EPROCUNAVAIL";
#endif
#ifdef EPROGMISMATCH
        case EPROGMISMATCH: return "EPROGMISMATCH";
#endif
#ifdef EPROGUNAVAIL
        case EPROGUNAVAIL: return "EPROGUNAVAIL";
#endif
#ifdef EPROTO
        case EPROTO: return "EPROTO";
#endif
#ifdef EPROTONOSUPPORT
        case EPROTONOSUPPORT: return "EPROTONOSUPPORT";
#endif
#ifdef EPROTOTYPE
        case EPROTOTYPE: return "EPROTOTYPE";
#endif
#ifdef EPWROFF
        case EPWROFF: return "EPWROFF";
#endif
#ifdef EQFULL
        case EQFULL: return "EQFULL";
#endif
#ifdef EQSUSPENDED
        case EQSUSPENDED: return "EQSUSPENDED";
#endif
#ifdef ERANGE
        case ERANGE: return "ERANGE";
#endif
#ifdef ERECYCLE
        case ERECYCLE: return "ERECYCLE";
#endif
#ifdef EREDRIVEOPEN
        case EREDRIVEOPEN: return "EREDRIVEOPEN";
#endif
#ifdef EREFUSED
        case EREFUSED: return "EREFUSED";
#endif
#ifdef ERELOC
        case ERELOC: return "ERELOC";
#endif
#ifdef ERELOCATED
        case ERELOCATED: return "ERELOCATED";
#endif
#ifdef ERELOOKUP
        case ERELOOKUP: return "ERELOOKUP";
#endif
#ifdef EREMCHG
        case EREMCHG: return "EREMCHG";
#endif
#ifdef EREMDEV
        case EREMDEV: return "EREMDEV";
#endif
#ifdef EREMOTE
        case EREMOTE: return "EREMOTE";
#endif
#ifdef EREMOTEIO
        case EREMOTEIO: return "EREMOTEIO";
#endif
#ifdef EREMOTERELEASE
        case EREMOTERELEASE: return "EREMOTERELEASE";
#endif
#ifdef ERESTART
        case ERESTART: return "ERESTART";
#endif
#ifdef ERFKILL
        case ERFKILL: return "ERFKILL";
#endif
#ifdef EROFS
        case EROFS: return "EROFS";
#endif
#ifdef ERPCMISMATCH
        case ERPCMISMATCH: return "ERPCMISMATCH";
#endif
#ifdef ESAD
        case ESAD: return "ESAD";
#endif
#ifdef ESHLIBVERS
        case ESHLIBVERS: return "ESHLIBVERS";
#endif
#ifdef ESHUTDOWN
        case ESHUTDOWN: return "ESHUTDOWN";
#endif
#ifdef ESOCKTNOSUPPORT
        case ESOCKTNOSUPPORT: return "ESOCKTNOSUPPORT";
#endif
#ifdef ESOFT
        case ESOFT: return "ESOFT";
#endif
#ifdef ESPIPE
        case ESPIPE: return "ESPIPE";
#endif
#ifdef ESRCH
        case ESRCH: return "ESRCH";
#endif
#ifdef ESRMNT
        case ESRMNT: return "ESRMNT";
#endif
#ifdef ESTALE
        case ESTALE: return "ESTALE";
#endif
#ifdef ESTART
        case ESTART: return "ESTART";
#endif
#ifdef ESTRPIPE
        case ESTRPIPE: return "ESTRPIPE";
#endif
#ifdef ESYSERROR
        case ESYSERROR: return "ESYSERROR";
#endif
#ifdef ETIME
        case ETIME: return "ETIME";
#endif
#ifdef ETIMEDOUT
        case ETIMEDOUT: return "ETIMEDOUT";
#endif
#ifdef ETOOMANYREFS
        case ETOOMANYREFS: return "ETOOMANYREFS";
#endif
#ifdef ETXTBSY
        case ETXTBSY: return "ETXTBSY";
#endif
#ifdef EUCLEAN
        case EUCLEAN: return "EUCLEAN";
#endif
#ifdef EUNATCH
        case EUNATCH: return "EUNATCH";
#endif
#ifdef EUSERS
        case EUSERS: return "EUSERS";
#endif
#ifdef EVERSION
        case EVERSION: return "EVERSION";
#endif
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || EWOULDBLOCK != EAGAIN)
        case EWOULDBLOCK: return "EWOULDBLOCK";
#endif
#ifdef EWRONGFS
        case EWRONGFS: return "EWRONGFS";
#endif
#ifdef EWRPROTECT
        case EWRPROTECT: return "EWRPROTECT";
#endif
#ifdef EXDEV
        case EXDEV: return "EXDEV";
#endif
#ifdef EXFULL
        case EXFULL: return "EXFULL";
#endif
    }
    return 0;
}
Depreciatory answered 16/8, 2019 at 5:20 Comment(2)
You could have used a macro to avoid duplicating the identifiers.Underclothes
@YvesDaoust The code is machine-generated from a list of errno names. And macros can't contain #ifdef and #if directives, so at best it would be #define X(name) case name: return #name; (and #define X(name) [name] = #name, for the optional source-level array-instead-of-switch optimization). Little value in deferring part of the expansion to the C preprocessor when the rest of the expansion already has to happen in the codegen script.Depreciatory
D
4

There isn't a simple way to do that.

You can create a program — and I have created one, which could be repackaged as a library function — that converts from number to name. But generating the table is moderately hard. I use a Perl script that runs the compiler (GCC or equivalent) with options (-H) to list the headers that are included by including /usr/include/errno.h, and then scans those files, looking for names (#define plus E followed by an upper-case letter or digit), numbers, and comments. This works on Linux, Mac OS X, Solaris, HP-UX, AIX. It isn't particularly trivial.

Beware, the errno.h on Mac OS X includes a name ELAST (a name reserved for the implementation to use) that is a duplicate of the highest number (but the mapping changes from release to release; it was 105 in Mountain Lion, I believe, but it is 106 in Mavericks).

Downcomer answered 10/11, 2013 at 2:5 Comment(9)
echo '#include <errno.h>' | $CC -dM -E - | grep -E '^#[[:space:]]*define[[:space:]]+E'Elbrus
@R..: more or less, yes, but if you're going to compile that usefully, you have to convert (a copy of) the symbolic name into a string, and possibly capture the comment (though you could use strerror() to handle the error message part), etc.Downcomer
That was just a script to get the list of #defines in a clean, mostly-portable manner (better than grepping specific pathnames which will horribly break cross-compiling and other setups). You still need to process the output into a string table.Elbrus
We can quibble about the portability (-dM and - will work with gcc, as will -H, and probably with clang, and maybe icc, but not necessarily other compilers). The output is slightly different, but the net result is much the same. I think we're actually violently in agreement. One advantage of Perl reading the headers is that it doesn't have to pay attention to #ifdefery and can pick up defines that might be missed by what you provide. It isn't clear cut. Some platform could reorganize the layout of the info and screw everything up. It is glitchy more or less however it is done.Downcomer
In the perfect world wouldn't it just be easier for the system to package them with the human readable strings? E.G."Bad file descriptor (EBADF)". They are already mapped after all.Annieannihilate
@Annieannihilate Exactly. If only strerror()/perror() just included the errno and its name in parentheses at the end of its output, everyone would be better off IMO...Find
The fact that Perl doesn't have to pay attention to #ifdefery is also part of the problem. Imagine an errno.h containing #ifdef __mips__ followed by the MIPS values for the errors, etc. The value of my approach is that the script sees exactly what a program including errno.h would see.Elbrus
OK; we're targetting different purposes. What I've got works for me; you may do things your own way.Downcomer
Jonathan, @R.., I updated my question above with an implementation based on your ideas. Thanks again.Find
A
2

To my knowledge you can't. Some integer error constants are mapped to more than one symbolic name anyway e.g. EAGAIN and EWOULDBLOCK. Obviously you can look them up on the man page of the command that set errno.

Annieannihilate answered 10/11, 2013 at 1:52 Comment(4)
EAGAIN and EWOULDBLOCK are the only two that may be the same. All others must have distinct values.Anastasiaanastasie
EDEADLK and EDEADLOCK too on linuxAnnieannihilate
POSIX <errno.h> lists EAGAIN and EWOULDBLOCK as a pair, but also ENOTSUP and EOPNOTSUPP. Platforms may have other duplicate names. Since you only need one name for a given number, you can choose one to be the canonical name.Downcomer
@Annieannihilate EDEADLK and DEADLOCK mean the same thing... (DEADLK being an abbreviated form of DEADLOCK)Ornithology
C
1

I don't know of a function that does this, but it would not be hard to write one if you are familiar with the errors being returned in your program.

You could write strings containing the outputs of strerror() and use a for loop and if statements with strcmp() (which returns a value based on success or failure) to compare the strings you wrote. If they are the same you can output the symbolic name* if you set that to another string.

Coppage answered 10/11, 2013 at 1:56 Comment(2)
Why would you need to strcmp against the output of strerror?Agosto
You could just look them up in errno.h and write something based on that. That will work...until it doesn't because something changed.Annieannihilate
A
1

If you know what errors you're expecting, you could write big switch/case or if/else blocks against errno, in the following style:

if (errno == EAGAIN)
    fprintf(stderr, "EAGAIN");

The obvious problem with this is that if you want the specific errno 'name', you need to write against each possible option, and there's quite a few.

Agosto answered 10/11, 2013 at 2:0 Comment(1)
That's not very general. At least create a function that uses an array of strings, and returns a char const * to the name corresponding to the number.Downcomer
C
-1

Since the symbolic names are stored as enumerations and C treats them as Ints. You will have to create a function similar to this SO question How to convert enum names to string in c. You could probably shorten it to a macro easily enough but creating a case for each error would have to be done.

Compensation answered 10/11, 2013 at 2:0 Comment(2)
The C standard (C99, at least) states that the E* constants are macros, not enumerations.Casandra
Well, the GNU compiler switches on them and treats them as enums. I apologize for misleading info.Compensation

© 2022 - 2024 — McMap. All rights reserved.