1/* ==========================================================================
2 * errno.c.m4 - Lua Continuation Queues
3 * --------------------------------------------------------------------------
4 * Copyright (c) 2012, 2015  William Ahern
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to permit
11 * persons to whom the Software is furnished to do so, subject to the
12 * following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
20 * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
23 * USE OR OTHER DEALINGS IN THE SOFTWARE.
24 * ==========================================================================
25 */
26#include "config.h"
27
28#include <string.h> /* memcpy(3) strcmp(3) strerror_r(3) strnlen(3) */
29#include <errno.h>
30
31#include <lua.h>
32#include <lauxlib.h>
33
34#include "lib/dns.h"
35#include "lib/socket.h"
36
37#include "cqueues.h"
38
39
40#ifndef STRERROR_R_CHAR_P
41#define STRERROR_R_CHAR_P ((GLIBC_PREREQ(0,0) || UCLIBC_PREREQ(0,0,0)) && (_GNU_SOURCE || !(_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)))
42#endif
43
44cqs_error_t cqs_strerror_r(cqs_error_t error, char *dst, size_t lim) {
45	const char *src;
46
47	if (error >= DNS_EBASE && error < DNS_ELAST) {
48		src = dns_strerror(error);
49	} else if (error >= SO_EBASE && error < SO_ELAST) {
50		src = so_strerror(error);
51	} else {
52#if STRERROR_R_CHAR_P
53		if (!(src = strerror_r(error, dst, lim)))
54			return EINVAL;
55#else
56		/* glibc between 2.3.4 and 2.13 returns -1 on error */
57		if (-1 == (error = strerror_r(error, dst, lim)))
58			return errno;
59
60		return error;
61#endif
62	}
63
64	if (src != dst && lim > 0) {
65		size_t n = strnlen(src, lim - 1);
66		memcpy(dst, src, n);
67		dst[n] = '\0';
68	}
69
70	return 0;
71} /* cqs_strerror_r() */
72
73
74const char *(cqs_strerror)(int error, void *dst, size_t lim) {
75	char *p, *pe, *unknown;
76	char e10[((sizeof error * CHAR_BIT) / 3) + 1], *ep;
77	int n;
78
79	if (!lim)
80		return dst;
81
82	if (0 == cqs_strerror_r(error, dst, lim) && *(char *)dst)
83		return dst;
84
85	p = dst;
86	pe = p + lim;
87
88	unknown = "Unknown error: ";
89	while (*unknown && p < pe)
90		*p++ = *unknown++;
91
92	if (error < 0 && p < pe)
93		*p++ = '-';
94
95	/* translate integer to string in LSB order */
96	for (ep = e10, n = error; n; ep++, n /= 10)
97		*ep = "0123456789"[abs(n % 10)];
98	if (ep == e10)
99		*ep++ = '0';
100
101	/* copy string, flipping from LSB to MSB */
102	while (ep > e10 && p < pe)
103		*p++ = *--ep;
104
105	p[-1] = '\0';
106
107	return dst;
108} /* cqs_strerror() */
109
110
111static const struct {
112	const char *name;
113	int value;
114} errlist[] = {
115changequote(<<<,>>>)dnl
116ifdef(<<<esyscmd>>>,<<<esyscmd>>>,<<<syscmd>>>)(<<<
117../mk/errno.ls | awk '{ print "#ifdef "$1"\n\t{ \""$1"\", "$1" },\n#endif" }'
118>>>)dnl
119};
120
121
122static int le_strerror(lua_State *L) {
123	lua_pushstring(L, cqs_strerror(luaL_checkint(L, 1)));
124
125	return 1;
126} /* le_strerror() */
127
128
129static const luaL_Reg le_globals[] = {
130	{ "strerror", &le_strerror },
131	{ NULL, NULL }
132};
133
134
135int luaopen__cqueues_errno(lua_State *L) {
136	unsigned i;
137
138	luaL_newlib(L, le_globals);
139
140	for (i = 0; i < sizeof errlist / sizeof *errlist; i++) {
141		lua_pushstring(L, errlist[i].name);
142		lua_pushinteger(L, errlist[i].value);
143		lua_settable(L, -3);
144
145#if EAGAIN == EWOULDBLOCK
146		if (!strcmp(errlist[i].name, "EWOULDBLOCK"))
147			continue;
148#endif
149
150		lua_pushinteger(L, errlist[i].value);
151		lua_pushstring(L, errlist[i].name);
152		lua_settable(L, -3);
153	}
154
155	return 1;
156} /* luaopen__cqueues_errno() */
157