Merge branch 'master' of ssh://askele.com/libsntl

v0.5.xx
Alexander Vdolainen 10 years ago
commit f3073beccd

@ -2,6 +2,9 @@ dnl Process this file with autoconf to produce a configure script.
AC_INIT(libsntl, 0.1)
LDFLAGS="$LDFLAGS -L/mingw/lib -L/local/lib"
AC_CONFIG_MACRO_DIR(['/share/aclocal/'])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([1.11])
@ -13,7 +16,7 @@ AC_PROG_CC
LT_INIT
PKG_CHECK_MODULES(OPENSSL, [openssl])
PKG_CHECK_MODULES(LIBUUID, [uuid])
dnl PKG_CHECK_MODULES(LIBUUID, [uuid])
PKG_CHECK_MODULES(LIBTDATA, [libtdata])
PKG_CHECK_MODULES(LIBSEXPR, [libsexpr])

@ -5,7 +5,8 @@ AM_CPPFLAGS = \
-DPACKAGE_SRC_DIR=\""$(srcdir)"\" \
-DPACKAGE_DATA_DIR=\""$(pkgdatadir)"\" \
-DCNFPATH=\""$(prefix)/etc"\" \
-I../include
-I../include \
-I../lib
AM_CFLAGS = -Wall -g
@ -13,17 +14,16 @@ AM_CFLAGS = -Wall -g
libsntl = ../lib/.libs/libsntl.la
bin_PROGRAMS = sntld sntlc gentest
sntld_SOURCES = sntld.c
sntld_LDADD = $(LIBTDATA_LIBS) $(LIBSEXPR_LIBS) $(OPENSSL_LIBS) \
$(LIBUUID_LIBS) $(libsntl)
bin_PROGRAMS = sntlc
#sntld_SOURCES = sntld.c
#sntld_LDADD = $(LIBTDATA_LIBS) $(LIBSEXPR_LIBS) $(OPENSSL_LIBS) \
# $(LIBUUID_LIBS) $(libsntl) -lws2_32
sntlc_SOURCES = sntlc.c
sntlc_LDADD = $(LIBTDATA_LIBS) $(LIBSEXPR_LIBS) $(OPENSSL_LIBS) \
$(LIBUUID_LIBS) $(libsntl)
gentest_SOURCES = gentest.c
gentest_LDADD = $(LIBTDATA_LIBS) $(LIBSEXPR_LIBS) $(OPENSSL_LIBS) \
$(LIBUUID_LIBS) $(libsntl)
$(LIBUUID_LIBS) $(libsntl) -lws2_32
#gentest_SOURCES = gentest.c
#gentest_LDADD = $(LIBTDATA_LIBS) $(LIBSEXPR_LIBS) $(OPENSSL_LIBS) \
# $(LIBUUID_LIBS) $(libsntl)
#zsyncd_LDFLAGS = \

@ -3,20 +3,31 @@
#include <stdlib.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#include <stdint.h>
#include <getopt.h>
#include <errno.h>
#include <assert.h>
#include <time.h>
#include <unistd.h>
#ifdef WIN32
#include <Winsock2.h>
#include <signal.h>
// #include <ws2tcpip.h>
#else
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <execinfo.h>
#include <sys/socket.h>
#include <sys/resource.h>
#include <netdb.h>
#include <unistd.h>
#include <uuid/uuid.h>
#include <execinfo.h>
#include <netinet/in.h>
#endif
#include <tdata/usrtc.h>
#include <sexpr/sexp.h>

@ -3,25 +3,34 @@
#include <stdlib.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
// #include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#include <stdint.h>
#include <getopt.h>
#include <errno.h>
#include <assert.h>
#include <time.h>
#include <unistd.h>
#include <sys/wait.h>
#include <execinfo.h>
#include <sys/resource.h>
// #include <sys/wait.h>
#include <tdata/usrtc.h>
#include <sexpr/sexp.h>
#include <sntl/connection.h>
#ifdef WIN32
#include <Winsock2.h>
#include <signal.h>
// #include <ws2tcpip.h>
#else
#include <sys/select.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/resource.h>
#include <netdb.h>
#include <unistd.h>
#include <uuid/uuid.h>
#include <execinfo.h>
#include <netinet/in.h>
#endif
/* define a little bit */
#define DEFAULT_PORT 13133
#define CHANNEL_COUNT 200
@ -95,40 +104,40 @@ inline void log_flush()
fflush(log_file);
}
void signal_error(int sig, siginfo_t *si, void *ptr)
{
void* error_addr;
void* trace[16];
int x;
int trace_size;
char** messages;
fprintf(stderr, "Something is wrong: backtrace: \n");
uintptr_t fptr = (uintptr_t)(si->si_addr);
fprintf(stderr, "Signal: %d, function pointer: 0x%.12lX \n", sig, fptr);
#if __WORDSIZE == 64
error_addr = (void*)((ucontext_t*)ptr)->uc_mcontext.gregs[REG_RIP];
#else
error_addr = (void*)((ucontext_t*)ptr)->uc_mcontext.gregs[REG_EIP];
#endif
trace_size = backtrace(trace, 16);
trace[1] = error_addr;
messages = backtrace_symbols(trace, trace_size);
if (messages)
{
for (x = 1; x < trace_size; x++)
{
fprintf(stderr, "%s\n", messages[x]);
}
free(messages);
}
// void signal_error(int sig, siginfo_t *si, void *ptr)
// {
// void* error_addr;
// void* trace[16];
// int x;
// int trace_size;
// char** messages;
// fprintf(stderr, "Something is wrong: backtrace: \n");
// uintptr_t fptr = (uintptr_t)(si->si_addr);
// fprintf(stderr, "Signal: %d, function pointer: 0x%.12lX \n", sig, fptr);
// #if __WORDSIZE == 64
// error_addr = (void*)((ucontext_t*)ptr)->uc_mcontext.gregs[REG_RIP];
// #else
// error_addr = (void*)((ucontext_t*)ptr)->uc_mcontext.gregs[REG_EIP];
// #endif
// trace_size = backtrace(trace, 16);
// trace[1] = error_addr;
// messages = backtrace_symbols(trace, trace_size);
// if (messages)
// {
// for (x = 1; x < trace_size; x++)
// {
// fprintf(stderr, "%s\n", messages[x]);
// }
// free(messages);
// }
fprintf(stderr, "end of backtrace\n");
// fprintf(stderr, "end of backtrace\n");
exit(1);
}
// exit(1);
// }
typedef struct
{
@ -285,19 +294,19 @@ void test_message_handling(conn_t* co)
int main(int argc, char **argv)
{
// set detailed signal handler
struct sigaction sigact;
sigact.sa_flags = SA_SIGINFO;
sigact.sa_sigaction = signal_error;
sigemptyset(&sigact.sa_mask);
sigaction(SIGFPE, &sigact, 0);
sigaction(SIGILL, &sigact, 0);
sigaction(SIGSEGV, &sigact, 0);
sigaction(SIGBUS, &sigact, 0);
// struct sigaction sigact;
// sigact.sa_flags = SA_SIGINFO;
// sigact.sa_sigaction = signal_error;
// sigemptyset(&sigact.sa_mask);
// sigaction(SIGFPE, &sigact, 0);
// sigaction(SIGILL, &sigact, 0);
// sigaction(SIGSEGV, &sigact, 0);
// sigaction(SIGBUS, &sigact, 0);
char *rootca = NULL, *cert = NULL;
int port = DEFAULT_PORT;
char *addr = NULL, *login = NULL, *password = NULL;
int opt;
int opt,ret;
#ifndef SIMPLE_TESTING
int rc, i;
#endif
@ -365,7 +374,7 @@ int main(int argc, char **argv)
fprintf(stderr, "Subsystem init failed (set SSL x.509 pems): %d\n", opt);
return opt;
}
/* Tests */
/* try to open connection */
conn_t *co = malloc(sizeof(conn_t));
@ -374,9 +383,17 @@ int main(int argc, char **argv)
ctx->passwd = password;
log_init("test.log");
log_begin("Connection initiate");
log_assert("Connection initiate", connection_initiate(co, addr, port, cert, ctx), 0);
connections_subsystem_set_rpctlist_call(conn_sys,NULL);
ret=connection_initiate(co, addr, port, cert, ctx);
// log_assert("Connection initiate", connection_initiate(co, addr, port, cert, ctx), 0);
if(ret)
return;
log_end("Connection initiate");
#ifndef SIMPLE_TESTING

@ -3,22 +3,37 @@
#include <stdlib.h>
#include <stdarg.h>
#include <sys/types.h>
#ifdef WIN32
#include <Winsock2.h>
#include <signal.h>
#include <ws2tcpip.h>
#else
#include <sys/select.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <unistd.h>
#include <stdint.h>
#include <getopt.h>
#include <errno.h>
#include <sys/resource.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/wait.h>
#include <uuid/uuid.h>
#include <execinfo.h>
#include <sys/resource.h>
#include <netinet/in.h>
#endif
#include <tdata/usrtc.h>
#include <sexpr/sexp.h>
#include <sntl/connection.h>
#include <pthread.h>
// #include <unistd.h>
#include <stdint.h>
#include <getopt.h>
#include <errno.h>
#ifndef WIN32
void signal_error(int sig, siginfo_t *si, void *ptr)
{
void* error_addr;
@ -54,6 +69,8 @@ void signal_error(int sig, siginfo_t *si, void *ptr)
exit(1);
}
#endif
/* helper functions */
int __openlistener(int port)
{
@ -174,18 +191,18 @@ static void sigpipe_handler(int a)
int main(int argc, char **argv)
{
// set detailed signal handler
struct sigaction sigact, sigpipe;
sigact.sa_flags = SA_SIGINFO;
sigact.sa_sigaction = signal_error;
sigemptyset(&sigact.sa_mask);
sigaction(SIGFPE, &sigact, 0);
sigaction(SIGILL, &sigact, 0);
sigaction(SIGSEGV, &sigact, 0);
sigaction(SIGBUS, &sigact, 0);
memset(&sigpipe, 0, sizeof(struct sigaction));
sigpipe.sa_handler = sigpipe_handler;
sigaction(SIGPIPE, &sigpipe, NULL);
// struct sigaction sigact, sigpipe;
// sigact.sa_flags = SA_SIGINFO;
// sigact.sa_sigaction = signal_error;
// sigemptyset(&sigact.sa_mask);
// sigaction(SIGFPE, &sigact, 0);
// sigaction(SIGILL, &sigact, 0);
// sigaction(SIGSEGV, &sigact, 0);
// sigaction(SIGBUS, &sigact, 0);
// memset(&sigpipe, 0, sizeof(struct sigaction));
// sigpipe.sa_handler = sigpipe_handler;
// sigaction(SIGPIPE, &sigpipe, NULL);
char *rootca = NULL, *cert = NULL;
int port = DEFAULT_PORT;

@ -19,7 +19,7 @@ libsntl_la_SOURCES = \
libsntl_la_LDFLAGS = -Wl,--export-dynamic
libsntl_la_LIBADD = -lpthread -lcrypto $(LIBTDATA_LIBS) $(LIBSEXPR_LIBS) $(OPENSSL_LIBS) \
$(LIBUUID_LIBS)
-luuid
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libsntl.pc

@ -14,12 +14,18 @@
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#ifdef WIN32
#include <Winsock2.h>
#else
#include <sys/select.h>
#include <netdb.h>
#include <unistd.h>
#include <uuid/uuid.h>
#endif
#include <openssl/ssl.h>
#include <openssl/err.h>

@ -14,12 +14,19 @@
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#ifdef WIN32
#include <Winsock2.h>
#define EBADE 1
#define NETDB_SUCCESS 0
#else
#include <sys/select.h>
#include <netdb.h>
#include <unistd.h>
#include <uuid/uuid.h>
#endif
#include <openssl/ssl.h>
#include <openssl/err.h>
@ -495,8 +502,10 @@ static int __default_ch_set_types(void *cctx, sexp_t *sx)
r = ESXRCBADPROT;
goto __send_reply;
}
/* take length of the list */
llen = sexp_list_length(lsx);
if(!llen) return 0; /* other side will not set any security attributes */
SEXP_ITERATE_LIST(lsx, sx_iter, idx) {
if(SEXP_IS_LIST(sx_iter)) {
@ -534,12 +543,12 @@ static int __default_ch_set_types(void *cctx, sexp_t *sx)
__send_reply:
snprintf(buf, 1024, "(ch-gl-error (%d))", r);
if(__conn_write(co, buf, strlen(buf)) < 0) {
co->flags &= ~CXCONN_ESTABL;
co->flags |= CXCONN_BROKEN;
__wake_up_waiters(co, ESXNOCONNECT);
}
destroy_sexp(sx);
return r;
@ -1521,14 +1530,17 @@ static int __eval_cstr(char *cstr, cx_rpc_list_t *rpc_list, void *ctx)
char *rpcf;
if(!(sx = parse_sexp(cstr, strlen(cstr)))) return EBADE;
if(sx->ty == SEXP_LIST)
rpcf = sx->list->val;
else rpcf = sx->val;
/* find an appropriate function */
node = usrtc_lookup(rpc_list->rpc_tree, rpcf);
if(!node) return ENOENT;
else rentry = (cx_rpc_t *)usrtc_node_getdata(node);
/* call it */
r = rentry->rpcf(ctx, sx);
@ -2035,6 +2047,10 @@ int connection_initiate(conn_t *co, const char *host, int port,
char *buf = NULL;
struct hostent *host_;
struct sockaddr_in addr;
#ifdef WIN32
WSADATA wsaData;
#endif
usrtc_t *ch_tree, *rpc_tree;
pth_queue_t *mqueue = malloc(sizeof(pth_queue_t));
pth_queue_t *rqueue = malloc(sizeof(pth_queue_t));
@ -2051,6 +2067,11 @@ int connection_initiate(conn_t *co, const char *host, int port,
if(idx_ch) free(idx_ch);
return r;
}
#ifdef WIN32
WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif
if(!tpoll) goto __fallenomem;
if(!idx_ch) goto __fallenomem;
@ -2071,15 +2092,20 @@ int connection_initiate(conn_t *co, const char *host, int port,
else r = idx_allocator_init(idx_ch, MAX_CHANNELS*MAX_MULTI, 0);
if(r) return r;
if(!(uuid = __generate_uuid())) return ENOMEM;
if(!(uuid = __generate_uuid())) {
return ENOMEM;
}
if(!(ch_tree = malloc(sizeof(usrtc_t)))) {
r = ENOMEM;
goto __fail;
}
if(!(rpc_tree = malloc(sizeof(usrtc_t)))) {
r = ENOMEM;
goto __fail_1;
}
if((r = pthread_mutex_init(&co->oplock, NULL))) goto __fail_2;
if((r = pthread_rwlock_init(&co->chnl_lock, NULL))) goto __fail_3;
@ -2114,11 +2140,13 @@ int connection_initiate(conn_t *co, const char *host, int port,
goto __fail_3;
}
/* set the private key from KeyFile (may be the same as CertFile) */
if(SSL_CTX_use_PrivateKey_file(co->ctx, SSL_cert,
SSL_FILETYPE_PEM)<=0) {
r = EINVAL;
goto __fail_3;
}
/* verify private key */
if (!SSL_CTX_check_private_key(co->ctx)) {
r = EINVAL;
@ -2136,20 +2164,28 @@ int connection_initiate(conn_t *co, const char *host, int port,
r = ENOMEM;
goto __fail_3;
}
if(__resolvehost(host, buf, __TMPBUFLEN, &host_) != NETDB_SUCCESS) {
//r=__resolvehost(host, buf, __TMPBUFLEN, &host_);
host_=gethostbyname(host);
if(errno) {
r = ENOENT;
free(buf);
goto __fail_3;
}
/* create a socket */
// /* create a socket */
sd = socket(PF_INET, SOCK_STREAM, 0);
bzero(&addr, sizeof(addr));
/* try to connect it */
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = *(uint32_t*)(host_->h_addr);
free(host_);
if (connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
r=connect(sd, (struct sockaddr*)&addr, sizeof(addr));
if ( r!= 0) {
printf("connect error %d %s \n",r, strerror(errno));
close(sd);
free(buf);
r = ENOENT; /* couldn't connect to the desired host */
@ -2174,20 +2210,23 @@ int connection_initiate(conn_t *co, const char *host, int port,
/* send an auth request */
SSL_write(co->ssl, buf, strlen(buf) + sizeof(char));
/* read the message reply */
bytes = __conn_read(co, buf, __TMPBUFLEN);
if(bytes == -1) {
// we've lost the connection
co->flags &= ~CXCONN_ESTABL;
r = ESXNOCONNECT;
co->flags |= CXCONN_BROKEN;
free(buf);
/* shutdown connection */
goto __fail_3;
}
buf[bytes] = 0;
/* perform an rpc call */
r = __eval_cstr(buf, conn_sys->system_rpc, (void *)co);
if(!r) { /* all is fine security context is good */
snprintf(buf, __TMPBUFLEN, "(ch-get-types)"); /* now we should receive possible channel types */
SSL_write(co->ssl, buf, strlen(buf) + sizeof(char));
@ -2198,11 +2237,11 @@ int connection_initiate(conn_t *co, const char *host, int port,
co->flags &= ~CXCONN_ESTABL;
co->flags |= CXCONN_BROKEN;
r = ESXNOCONNECT;
free(buf);
/* shutdown connection */
goto __fail_3;
}
buf[bytes] = 0;
/* perform an rpc call */
r = __eval_cstr(buf, conn_sys->system_rpc, (void *)co);

@ -14,8 +14,16 @@
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/mman.h>
// #include <sys/mman.h>
#ifdef WIN32
#include <Winsock2.h>
#else
#include <unistd.h>
#endif
#include <fcntl.h>
#include <tdata/usrtc.h>
@ -59,7 +67,7 @@ int sntl_mcache_init(sntl_mcache_t *mc, size_t esize, int flags)
/* sizes */
mc->esize = esize + sizeof(unsigned long);
mc->epc = (_MPG_SIZE - sizeof(struct _mpg)) / mc->esize;
#if 0
if((flags & _MCACHE_PREALLOC)) {
pt = mmap(NULL, _MPG_SIZE, PROT_READ|PROT_WRITE, MAP_ANONYMOUS, -1, 0);
if(pt == MAP_FAILED) {
@ -67,7 +75,7 @@ int sntl_mcache_init(sntl_mcache_t *mc, size_t esize, int flags)
return ENOMEM;
} else pg = (struct _mpg *)pt;
}
#endif
return r;
}

@ -14,12 +14,18 @@
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#ifdef WIN32
#include <Winsock2.h>
#else
#include <sys/select.h>
#include <netdb.h>
#include <unistd.h>
#include <uuid/uuid.h>
#endif
#include <openssl/ssl.h>
#include <openssl/err.h>

@ -13,7 +13,18 @@
#include <sys/time.h>
#include <fcntl.h>
#include <errno.h>
/**/
#ifdef WIN32
#include <Winsock2.h>
#else
#include <unistd.h>
#endif
/**/
// #include <unistd.h>
#include <string.h>
#include <pthread.h>

@ -14,12 +14,20 @@
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#ifdef WIN32
#include <Winsock2.h>
#else
#include <sys/select.h>
#include <netdb.h>
#include <unistd.h>
#include <uuid/uuid.h>
#endif
#include <fcntl.h>
#include <netdb.h>
#include <uuid/uuid.h>
#include <openssl/ssl.h>
#include <tdata/usrtc.h>

@ -14,12 +14,24 @@
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#ifdef WIN32
#include <Winsock2.h>
#include <windows.h>
#include <rpc.h>
#else
#include <sys/select.h>
#include <netdb.h>
#include <unistd.h>
#include <uuid/uuid.h>
#endif
#include <fcntl.h>
#include <netdb.h>
#include <uuid/uuid.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
@ -28,9 +40,56 @@
#include <sntl/connection.h>
#ifdef WIN32
#define UUID_T_LENGTH 16
#endif
#ifdef uuid_t
#undef uuid_t
#endif
#ifdef WIN32
typedef unsigned char uuid_t[16];
void uuid_generate_random(uuid_t out){
int i,a=0;
LARGE_INTEGER frequency,t1;
long int d;
for(i=0;i<UUID_T_LENGTH;i++){
a=rand();
*(out+i)=a;
}
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&t1);
d=(long int)t1.QuadPart*1000/frequency.QuadPart;
memcpy(out,&d,sizeof(d));
}
#endif
/* this function is an ugly implementation to get C string with uuid */
char *__generate_uuid(void)
{
#ifdef WIN32
char *uuidc = NULL;
uuid_t uuid_t_m;
int len, i = 0,r=0;
len = 33;
if(!(uuidc = malloc(len))) return NULL;
uuid_generate_random(uuid_t_m);
for(i = 0; i < 16; i++)
snprintf(uuidc+(2*i*sizeof(char)), len, "%02x", uuid_t_m[i]);
return uuidc;
#else
char *uuidc = NULL;
uuid_t uuid;
int len, i = 0;
@ -41,30 +100,51 @@ char *__generate_uuid(void)
uuid_generate_time_safe(uuid);
for(i = 0; i < sizeof(uuid_t); i++)
snprintf(uuidc+(2*i*sizeof(char)), len, "%02x", uuid[i]);
snprintf(uuidc+(2*i*sizeof(char)), len, "%02x", uuid[i]);
return uuidc;
return uuidc;
#endif
}
/* networking helpers */
int __resolvehost(const char *hostname, char *buf, int buf_len,
struct hostent **rhp)
{
struct hostent *hostbuf = malloc(sizeof(struct hostent));
struct hostent *hp = *rhp = NULL;
int herr = 0, hres = 0;
if(!hostbuf) return NO_ADDRESS;
hres = gethostbyname_r(hostname, hostbuf,
buf, buf_len, &hp, &herr);
if (!hp) return NO_ADDRESS;
if(hres) return NO_ADDRESS;
*rhp = hp;
return NETDB_SUCCESS;
#ifndef WIN32
return 0;
#else
return 0;
#endif
// #ifndef WIN32
// struct hostent *hostbuf ;//= malloc(sizeof(struct hostent));
// // #endif
// struct hostent *hp = *rhp = NULL;
// int herr = 0, hres = 0;
// printf("__resolvehost hn %s buf %s\n", hostname,buf);
// #ifdef WIN32
// *rhp=gethostbyname(hostname);
// printf("__resolvehost hn err %s \n", strerror(errno));
// #else
// hostbuf = malloc(sizeof(struct hostent));
// if(!hostbuf) return NO_ADDRESS;
// hres = gethostbyname_r(hostname, hostbuf,
// buf, buf_len, &hp, &herr);
// if(hres) return NO_ADDRESS;
// #endif
// *rhp = hp;
// #ifdef WIN32
// if (!*rhp) return NO_ADDRESS;
// else
// return 0;
// #else
// return NETDB_SUCCESS;
// #endif
}
/* sexp helpers */
@ -87,3 +167,4 @@ int sexp_list_cdr(sexp_t *expr, sexp_t **sx)
return 0;
}

Loading…
Cancel
Save