Skip to content

Commit

Permalink
strerror, strerror_r, gai_strerror, perror: Add full implementation
Browse files Browse the repository at this point in the history
DONE: RTOS-569
  • Loading branch information
agkaminski committed Aug 23, 2023
1 parent d1a034b commit 4dd2ecd
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 86 deletions.
9 changes: 7 additions & 2 deletions stdio/perror.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*
* perror.c
*
* Copyright 2022 Phoenix Systems
* Copyright 2022, 2023 Phoenix Systems
* Author: Aleksander Kaminski
*
* This file is part of Phoenix-RTOS.
Expand All @@ -17,7 +17,12 @@
#include <errno.h>
#include <string.h>


void perror(const char *str)
{
fprintf(stderr, "%s: %s\n", str, strerror(errno));
char errstr[48];

(void)strerror_r(errno, errstr, sizeof(errstr));

fprintf(stderr, "%s: %s\n", str, errstr);
}
32 changes: 1 addition & 31 deletions string/Makefile
Original file line number Diff line number Diff line change
@@ -1,38 +1,8 @@
#
# Makefile for libphoenix/string
#
# Copyright 2017, 2020 Phoenix Systems
# Copyright 2017, 2020, 2023 Phoenix Systems
# Author: Pawel Pisarczyk
#

INCS := errno.str.inc errno.tab.inc gaierr.str.inc gaierr.tab.inc


ERR_EXTRACT := $(SED) -En -e '/^\s*\#\s*define\s+E\w+\s+[[:digit:]]+/{s/^[^d]*define\s+(\w+)\s+([[:digit:]]+).*/\2\t\1/;p;d}' \
-e '/^\s*E\w+\s*=\s*-?[[:digit:]]+/{s/^\s*(\w+)\s*=\s*-?([[:digit:]]+).*/\2\t\1/;p;d}'


%/errno.list: $(SYSROOT)/usr/include/phoenix/errno.h include/errno.h
@(printf "GEN %-24s \n" "$(@F)")
$(SIL)$(ERR_EXTRACT) $^ | sort -n > $@


%/gaierr.list: include/netdb.h Makefile
@(printf "GEN %-24s \n" "$(@F)")
$(SIL)$(ERR_EXTRACT) $^ | sort -n > $@


%.str.inc: %.list Makefile
@(printf "GEN %-24s \n" "$(@F)")
$(SIL)$(SED) -e 's/^.*\t\(.*\)$$/"\1\\0"/' $< > $@


%.tab.inc: %.list Makefile
@(printf "GEN %-24s \n" "$(@F)")
$(SIL)bash -c 'o=0; while read num name; do echo "{ $$num, $$o },"; o=$$((o+$${#name}+1)); done' < $< > $@


$(PREFIX_O)string/strerror.o: $(patsubst %,string/%,$(INCS))


OBJS += $(addprefix $(PREFIX_O)string/, string.o strdup.o strerror.o strsignal.o)
227 changes: 174 additions & 53 deletions string/strerror.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*
* string/strerror (errno names)
*
* Copyright 2018 Phoenix Systems
* Copyright 2018, 2023 Phoenix Systems
* Author: Michal Miroslaw, Aleksander Kaminski
*
* This file is part of Phoenix-RTOS.
Expand All @@ -18,60 +18,162 @@
#include <stdio.h>
#include <string.h>
#include <errno.h>


typedef struct {
uint16_t errnum;
uint16_t offset;
} errinfo_t;


static const char errnames[] = {
#include "errno.str.inc"
};

static const errinfo_t errtab[] = {
#include "errno.tab.inc"
#include <netdb.h>


/* clang-format off */
static const char *__errmsgtab[] = {
/* EOK */ "Operation succeeded",
/* EPERM */ "Operation not permitted",
/* ENOENT */ "No such file or directory",
/* ESRCH */ "No such process",
/* EINTR */ "Interrupted system call",
/* EIO */ "I/O error",
/* ENXIO */ "No such device or address",
/* E2BIG */ "Argument list too long",
/* ENOEXEC */ "Executable format error",
/* EBADF */ "Bad file number",
/* ECHILD */ "No child processes",
/* EAGAIN */ "Try again",
/* ENOMEM */ "Out of memory",
/* EACCES */ "Permission denied",
/* EFAULT */ "Bad address",
/* ENOTBLK */ "Block device required",
/* EBUSY */ "Device or resource busy",
/* EEXIST */ "File exists",
/* EXDEV */ "Cross-device link",
/* ENODEV */ "No such device",
/* ENOTDIR */ "Not a directory",
/* EISDIR */ "Is a directory",
/* EINVAL */ "Invalid argument",
/* ENFILE */ "File table overflow",
/* EMFILE */ "Too many open files",
/* ENOTTY */ "Not a typewriter",
/* ETXTBSY */ "Text file busy",
/* EFBIG */ "File too large",
/* ENOSPC */ "No space left on device",
/* ESPIPE */ "Illegal seek",
/* EROFS */ "Read-only file system",
/* EMLINK */ "Too many links",
/* EPIPE */ "Broken pipe",
/* EDOM */ "Argument out of domain",
/* ERANGE */ "Result not representable",
/* ENOSYS */ "Function not implemented",
/* ENAMETOOLONG */ "Name too long",
/* ETIME */ "Timer expired",
/* EDEADLK */ "Resource deadlock avoided",
/* ENOTEMPTY */ "Directory not empty",
/* ELOOP */ "Too many levels of symbolic links",
NULL,
/* ENOMSG */ "No message of the desired type",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
/* EUNATCH */ "Protocol driver not attached",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
/* ENODATA */ "No message is available on the STREAM head read queue",
NULL,
NULL,
/* ENONET */ "Machine is not on the network.",
NULL,
/* EBADFD */ "File descriptor in bad state",
NULL,
NULL,
NULL,
NULL,
/* EPROTO */ "Protocol error",
NULL,
NULL,
/* EBADMSG */ "Bad message",
/* EOVERFLOW */ "Value too large to be stored in data type",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
/* EILSEQ */ "Invalid or incomplete multibyte or wide character",
NULL,
NULL,
NULL,
/* ENOTSOCK */ "Not a socket",
/* EDESTADDRREQ */ "Destination address required",
/* EMSGSIZE */ "Message too long",
/* EPROTOTYPE */ "Protocol wrong type for socket",
/* ENOPROTOOPT */ "Protocol not available",
/* EPROTONOSUPPORT */ "Protocol not supported",
NULL,
/* EOPNOTSUPP */ "Operation not supported on socket",
/* EPFNOSUPPORT */ "Protocol family not supported",
/* EAFNOSUPPORT */ "Address family not supported",
/* EADDRINUSE */ "Address already in use",
/* EADDRNOTAVAIL */ "Address not available",
/* ENETDOWN */ "Network is down",
/* ENETUNREACH */ "Network unreachable",
/* ENETRESET */ "Connection aborted by network",
/* ECONNABORTED */ "Connection aborted",
/* ECONNRESET */ "Connection reset",
/* ENOBUFS */ "No buffer space available",
/* EISCONN */ "Socket is connected",
/* ENOTCONN */ "The socket is not connected",
NULL,
NULL,
/* ETIMEDOUT */ "Connection timed out",
/* ECONNREFUSED */ "Connection refused",
/* EHOSTDOWN */ "Host is down",
/* EHOSTUNREACH */ "Host is unreachable",
/* EALREADY */ "Connection already in progress",
/* EINPROGRESS */ "Operation in progress",
/* ENOLCK */ "No locks available",
/* EUCLEAN */ "Structure needs cleaning",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
/* ENOTRECOVERABLE */ "State not recoverable"
};

static const char gainames[] = {
#include "gaierr.str.inc"
};

static const errinfo_t gaitab[] = {
#include "gaierr.tab.inc"
};

// static assert: 16-bit offset
static const char assert_errnames_size[-(sizeof(errnames) > 0xFFFF)] __attribute__((unused));
static const char assert_gainames_size[-(sizeof(gainames) > 0xFFFF)] __attribute__((unused));
/* clang-format on */


static int err_cmp(const void *key, const void *elem)
static inline const char *strerror_(int errnum, char *buff, size_t bufflen, int *err)
{
const errinfo_t *p = elem;
long err = (long)key;
const char *str = NULL;

return err - (long)p->errnum;
}


static inline const char *strerror_(const errinfo_t *tab, size_t tabsz, const char *names, int errnum, char *buff, size_t bufflen, int *err)
{
const errinfo_t *e;

if (errnum < 0) {
errnum = -errnum;
if ((errnum >= 0) && (errnum < (int)(sizeof(__errmsgtab) / sizeof(*__errmsgtab)))) {
str = __errmsgtab[errnum];
}

e = bsearch((void *)(long)errnum, tab, tabsz / sizeof(*tab), sizeof(*tab), err_cmp);
if (e == NULL) {
if (str == NULL) {
(void)snprintf(buff, bufflen, "Unknown error %d", errnum);
*err = EINVAL;
return buff;
str = buff;
}

return names + e->offset;
return str;
}


Expand All @@ -81,7 +183,7 @@ char *strerror(int errnum)
const char *str;
int err = 0;

str = strerror_(errtab, sizeof(errtab), errnames, errnum, unknownMsg, sizeof(unknownMsg), &err);
str = strerror_(errnum, unknownMsg, sizeof(unknownMsg), &err);
if (err != 0) {
errno = err;
}
Expand All @@ -97,7 +199,7 @@ int strerror_r(int errnum, char *buf, size_t buflen)
int ret = 0;
size_t len;

str = strerror_(errtab, sizeof(errtab), errnames, errnum, unknownMsg, sizeof(unknownMsg), &ret);
str = strerror_(errnum, unknownMsg, sizeof(unknownMsg), &ret);
len = strlen(str);
if (buflen > len) {
memcpy(buf, str, len + 1);
Expand All @@ -113,13 +215,32 @@ int strerror_r(int errnum, char *buf, size_t buflen)
const char *gai_strerror(int errnum)
{
static char unknownMsg[32];
const char *str;
int err = 0;

str = strerror_(gaitab, sizeof(gaitab), gainames, errnum, unknownMsg, sizeof(unknownMsg), &err);
if (err != 0) {
errno = err;
switch (errnum) {
case EAI_AGAIN:
return "Name could not be resolved at this time";
case EAI_BADFLAGS:
return "Flags parameter had an invalid value";
case EAI_FAIL:
return "Non-recoverable failure in name resolution";
case EAI_FAMILY:
return "Address family was not recognized";
case EAI_MEMORY:
return "Memory allocation failure";
case EAI_NODATA:
return "No address associated with hostname";
case EAI_NONAME:
return "Name does not resolve";
case EAI_OVERFLOW:
return "Argument buffer overflow";
case EAI_SERVICE:
return "Service was not recognized for socket type";
case EAI_SOCKTYPE:
return "Intended socket type was not recognized";
case EAI_SYSTEM:
return "System error returned in errno";
default:
(void)snprintf(unknownMsg, sizeof(unknownMsg), "Unknown error %d", errnum);
return unknownMsg;
}

return (char *)str;
}

0 comments on commit 4dd2ecd

Please sign in to comment.