// RETOOR - Nov 28 2024
// MIT License
// ===========
// Copyright (c) 2024 Retoor
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
# ifndef RLIB_H
# define RLIB_H
// BEGIN OF RLIB
/*
* Line below will be filtered by rmerge
<script language="Javva script" type="woeiii" src="Pony.html" after-tag="after
tag" />
*/
# ifndef RTYPES_H
# define RTYPES_H
# ifdef _POSIX_C_SOURCE
# define _POSIX_C_SOURCE_TEMP _POSIX_C_SOURCE
# undef _POSIX_C_SOURCE
# endif
# ifndef _POSIX_C_SOURCE
# undef _POSIX_C_SOURCE
# define _POSIX_C_SOURCE 200112L
# endif
# include <stdbool.h>
# include <stdint.h> // uint
# include <string.h>
# include <sys/types.h> // ulong
# ifndef ulonglong
# define ulonglong unsigned long long
# endif
# ifndef uint
typedef unsigned int uint ;
# endif
# ifndef byte
typedef unsigned char byte ;
# endif
# ifdef _POSIX_C_SOURCE_TEMP
# undef _POSIX_C_SOURCE
# define _POSIX_C_SOURCE _POSIX_C_SOURCE_TEMP
# undef _POSIX_C_SOURCE_TEMP
# else
# undef _POSIX_C_SOURCE
# endif
# endif
# ifndef NSOCK_H
# define NSOCK_H
# ifndef RMALLOC_H
# define RMALLOC_H
# ifndef RMALLOC_OVERRIDE
# define RMALLOC_OVERRIDE 1
# endif
# ifdef _POSIX_C_SOURCE
# define _POSIX_C_SOURCE_TEMP _POSIX_C_SOURCE
# undef _POSIX_C_SOURCE
# endif
# ifndef _POSIX_C_SOURCE
# undef _POSIX_C_SOURCE
# define _POSIX_C_SOURCE 200112L
# endif
# ifndef ulonglong
# define ulonglong unsigned long long
# endif
# include <locale.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# ifndef RTEMP_H
# define RTEMP_H
# include <pthread.h>
# ifndef RTEMPC_SLOT_COUNT
# define RTEMPC_SLOT_COUNT 20
# endif
# ifndef RTEMPC_SLOT_SIZE
# define RTEMPC_SLOT_SIZE 1024 * 64 * 128
# endif
bool _rtempc_initialized = 0 ;
pthread_mutex_t _rtempc_thread_lock ;
bool rtempc_use_mutex = true ;
byte _current_rtempc_slot = 1 ;
char _rtempc_buffer [ RTEMPC_SLOT_COUNT ] [ RTEMPC_SLOT_SIZE ] ;
char * rtempc ( char * data ) {
if ( rtempc_use_mutex ) {
if ( ! _rtempc_initialized ) {
_rtempc_initialized = true ;
pthread_mutex_init ( & _rtempc_thread_lock , NULL ) ;
}
pthread_mutex_lock ( & _rtempc_thread_lock ) ;
}
uint current_rtempc_slot = _current_rtempc_slot ;
_rtempc_buffer [ current_rtempc_slot ] [ 0 ] = 0 ;
strcpy ( _rtempc_buffer [ current_rtempc_slot ] , data ) ;
_current_rtempc_slot + + ;
if ( _current_rtempc_slot = = RTEMPC_SLOT_COUNT ) {
_current_rtempc_slot = 0 ;
}
if ( rtempc_use_mutex )
pthread_mutex_unlock ( & _rtempc_thread_lock ) ;
return _rtempc_buffer [ current_rtempc_slot ] ;
}
# define sstring(_pname, _psize) \
static char _##_pname[_psize]; \
_##_pname[0] = 0; \
char *_pname = _##_pname;
# define string(_pname, _psize) \
char _##_pname[_psize]; \
_##_pname[0] = 0; \
char *_pname = _##_pname;
# define sreset(_pname, _psize) _pname = _##_pname;
# define sbuf(val) rtempc(val)
# endif
# ifdef _POSIX_C_SOURCE_TEMP
# undef _POSIX_C_SOURCE
# define _POSIX_C_SOURCE _POSIX_C_SOURCE_TEMP
# undef _POSIX_C_SOURCE_TEMP
# else
# undef _POSIX_C_SOURCE
# endif
ulonglong rmalloc_count = 0 ;
ulonglong rmalloc_alloc_count = 0 ;
ulonglong rmalloc_free_count = 0 ;
ulonglong rmalloc_total_bytes_allocated = 0 ;
void * _rmalloc_prev_realloc_obj = NULL ;
size_t _rmalloc_prev_realloc_obj_size = 0 ;
void * rmalloc ( size_t size ) {
void * result ;
while ( ! ( result = malloc ( size ) ) ) {
fprintf ( stderr , " Warning: malloc failed, trying again. \n " ) ;
}
rmalloc_count + + ;
rmalloc_alloc_count + + ;
rmalloc_total_bytes_allocated + = size ;
return result ;
}
void * rcalloc ( size_t count , size_t size ) {
void * result ;
while ( ! ( result = calloc ( count , size ) ) ) {
fprintf ( stderr , " Warning: calloc failed, trying again. \n " ) ;
}
rmalloc_alloc_count + + ;
rmalloc_count + + ;
rmalloc_total_bytes_allocated + = count * size ;
return result ;
}
void * rrealloc ( void * obj , size_t size ) {
if ( ! obj ) {
rmalloc_count + + ;
}
rmalloc_alloc_count + + ;
if ( obj = = _rmalloc_prev_realloc_obj ) {
rmalloc_total_bytes_allocated + = size - _rmalloc_prev_realloc_obj_size ;
_rmalloc_prev_realloc_obj_size = size - _rmalloc_prev_realloc_obj_size ;
} else {
_rmalloc_prev_realloc_obj_size = size ;
}
void * result ;
while ( ! ( result = realloc ( obj , size ) ) ) {
fprintf ( stderr , " Warning: realloc failed, trying again. \n " ) ;
}
_rmalloc_prev_realloc_obj = result ;
return result ;
}
char * rstrdup ( const char * s ) {
if ( ! s )
return NULL ;
char * result ;
size_t size = strlen ( s ) + 1 ;
result = rmalloc ( size ) ;
memcpy ( result , s , size ) ;
rmalloc_total_bytes_allocated + = size ;
return result ;
}
void * rfree ( void * obj ) {
rmalloc_count - - ;
rmalloc_free_count + + ;
free ( obj ) ;
return NULL ;
}
# if RMALLOC_OVERRIDE
# define malloc rmalloc
# define calloc rcalloc
# define realloc rrealloc
# define free rfree
# define strdup rstrdup
# endif
char * rmalloc_lld_format ( ulonglong num ) {
char res [ 100 ] ;
res [ 0 ] = 0 ;
sprintf ( res , " %'lld " , num ) ;
char * resp = res ;
while ( * resp ) {
if ( * resp = = ' , ' )
* resp = ' . ' ;
resp + + ;
}
return sbuf ( res ) ;
}
char * rmalloc_bytes_format ( int factor , ulonglong num ) {
char * sizes [ ] = { " B " , " KB " , " MB " , " GB " , " TB " , " PB " , " EB " , " ZB " , " YB " } ;
if ( num > 1024 ) {
return rmalloc_bytes_format ( factor + 1 , num / 1024 ) ;
}
char res [ 100 ] ;
sprintf ( res , " %s %s " , rmalloc_lld_format ( num ) , sizes [ factor ] ) ;
return sbuf ( res ) ;
}
char * rmalloc_stats ( ) {
static char res [ 200 ] ;
res [ 0 ] = 0 ;
// int original_locale = localeconv();
setlocale ( LC_NUMERIC , " en_US.UTF-8 " ) ;
sprintf ( res , " Memory usage: %s, %s (re)allocated, %s unqiue free'd, %s in use. " , rmalloc_bytes_format ( 0 , rmalloc_total_bytes_allocated ) ,
rmalloc_lld_format ( rmalloc_alloc_count ) , rmalloc_lld_format ( rmalloc_free_count ) ,
rmalloc_lld_format ( rmalloc_count ) ) ;
// setlocale(LC_NUMERIC, original_locale);
setlocale ( LC_NUMERIC , " " ) ;
return res ;
}
# endif
# include <arpa/inet.h>
# include <errno.h>
# include <fcntl.h>
# include <netdb.h>
# include <netinet/in.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys/select.h>
# include <sys/socket.h>
# include <unistd.h>
# ifndef RLIB_RIO
# define RLIB_RIO
# include <dirent.h>
# include <stdbool.h>
# include <stdio.h>
# include <string.h>
# include <sys/dir.h>
# include <sys/select.h>
# include <sys/stat.h>
# include <unistd.h>
# ifndef RSTRING_LIST_H
# define RSTRING_LIST_H
# include <stdbool.h>
# include <stdlib.h>
# include <string.h>
typedef struct rstring_list_t {
unsigned int size ;
unsigned int count ;
char * * strings ;
} rstring_list_t ;
rstring_list_t * rstring_list_new ( ) {
rstring_list_t * rsl = ( rstring_list_t * ) malloc ( sizeof ( rstring_list_t ) ) ;
memset ( rsl , 0 , sizeof ( rstring_list_t ) ) ;
return rsl ;
}
void rstring_list_free ( rstring_list_t * rsl ) {
for ( unsigned int i = 0 ; i < rsl - > size ; i + + ) {
free ( rsl - > strings [ i ] ) ;
}
if ( rsl - > strings )
free ( rsl - > strings ) ;
free ( rsl ) ;
rsl = NULL ;
}
void rstring_list_add ( rstring_list_t * rsl , char * str ) {
if ( rsl - > count = = rsl - > size ) {
rsl - > size + + ;
rsl - > strings = ( char * * ) realloc ( rsl - > strings , sizeof ( char * ) * rsl - > size ) ;
}
rsl - > strings [ rsl - > count ] = strdup ( str ) ;
rsl - > count + + ;
}
bool rstring_list_contains ( rstring_list_t * rsl , char * str ) {
for ( unsigned int i = 0 ; i < rsl - > count ; i + + ) {
if ( ! strcmp ( rsl - > strings [ i ] , str ) )
return true ;
}
return false ;
}
# endif
bool rfile_exists ( char * path ) {
struct stat s ;
return ! stat ( path , & s ) ;
}
void rjoin_path ( char * p1 , char * p2 , char * output ) {
output [ 0 ] = 0 ;
strcpy ( output , p1 ) ;
if ( output [ strlen ( output ) - 1 ] ! = ' / ' ) {
char slash [ ] = " / " ;
strcat ( output , slash ) ;
}
if ( p2 [ 0 ] = = ' / ' ) {
p2 + + ;
}
strcat ( output , p2 ) ;
}
int risprivatedir ( const char * path ) {
struct stat statbuf ;
if ( stat ( path , & statbuf ) ! = 0 ) {
perror ( " stat " ) ;
return - 1 ;
}
if ( ! S_ISDIR ( statbuf . st_mode ) ) {
return - 2 ;
}
if ( ( statbuf . st_mode & ( S_IRWXU | S_IRWXG | S_IRWXO ) ) = = S_IRWXU ) {
return 1 ; // Private (owner has all permissions, others have none)
}
return 0 ;
}
bool risdir ( const char * path ) { return ! risprivatedir ( path ) ; }
void rforfile ( char * path , void callback ( char * ) ) {
if ( ! rfile_exists ( path ) )
return ;
DIR * dir = opendir ( path ) ;
struct dirent * d ;
while ( ( d = readdir ( dir ) ) ! = NULL ) {
if ( ! d )
break ;
if ( ( d - > d_name [ 0 ] = = ' . ' & & strlen ( d - > d_name ) = = 1 ) | | d - > d_name [ 1 ] = = ' . ' ) {
continue ;
}
char full_path [ 4096 ] ;
rjoin_path ( path , d - > d_name , full_path ) ;
if ( risdir ( full_path ) ) {
callback ( full_path ) ;
rforfile ( full_path , callback ) ;
} else {
callback ( full_path ) ;
}
}
closedir ( dir ) ;
}
bool rfd_wait ( int fd , int ms ) {
fd_set read_fds ;
struct timeval timeout ;
FD_ZERO ( & read_fds ) ;
FD_SET ( fd , & read_fds ) ;
timeout . tv_sec = 0 ;
timeout . tv_usec = 1000 * ms ;
int ret = select ( fd + 1 , & read_fds , NULL , NULL , & timeout ) ;
return ret > 0 & & FD_ISSET ( fd , & read_fds ) ;
}
bool rfd_wait_forever ( int fd ) {
while ( ( ! rfd_wait ( fd , 10 ) ) ) {
}
return true ;
}
size_t rfile_size ( char * path ) {
struct stat s ;
stat ( path , & s ) ;
return s . st_size ;
}
size_t rfile_readb ( char * path , void * data , size_t size ) {
FILE * fd = fopen ( path , " r " ) ;
if ( ! fd ) {
return 0 ;
}
size_t bytes_read = fread ( data , sizeof ( char ) , size , fd ) ;
fclose ( fd ) ;
( ( char * ) data ) [ bytes_read ] = 0 ;
return bytes_read ;
}
# endif
int * nsock_socks = NULL ;
int * nsock_readable = NULL ;
void * * nsock_data = NULL ;
int nsock_server_fd = 0 ;
int nsock_max_socket_fd = 0 ;
typedef enum nsock_type_t { NSOCK_NONE = 0 , NSOCK_SERVER , NSOCK_CLIENT , NSOCK_UPSTREAM } nsock_type_t ;
typedef struct nsock_it {
int fd ;
int * upstreams ;
bool connected ;
bool downstream ;
unsigned int upstream_count ;
nsock_type_t type ;
} nsock_t ;
nsock_t * * nsocks = NULL ;
int nsocks_count = 0 ;
void ( * nsock_on_connect ) ( int fd ) = NULL ;
void ( * nsock_on_data ) ( int fd ) = NULL ;
void ( * nsock_on_close ) ( int fd ) = NULL ;
void nsock_on_before_data ( int fd ) ;
nsock_t * nsock_get ( int fd ) {
if ( nsock_socks [ fd ] = = 0 ) {
return NULL ;
}
if ( fd > = nsocks_count | | nsocks [ fd ] = = NULL ) {
if ( fd > = nsocks_count ) {
nsocks_count = fd + 1 ;
nsocks = ( nsock_t * * ) realloc ( nsocks , sizeof ( nsock_t * ) * sizeof ( nsock_t ) * ( nsocks_count ) ) ;
nsocks [ fd ] = ( nsock_t * ) calloc ( 1 , sizeof ( nsock_t ) ) ;
}
nsocks [ fd ] - > upstreams = NULL ;
nsocks [ fd ] - > fd = fd ;
nsocks [ fd ] - > connected = false ;
nsocks [ fd ] - > downstream = false ;
nsocks [ fd ] - > upstream_count = 0 ;
nsocks [ fd ] - > type = NSOCK_CLIENT ;
return nsocks [ fd ] ;
}
return nsocks [ fd ] ;
}
void nsock_close ( int fd ) {
if ( nsock_on_close )
nsock_on_close ( fd ) ;
nsock_t * sock = nsock_get ( fd ) ;
if ( sock ) {
for ( unsigned int i = 0 ; i < sock - > upstream_count ; i + + ) {
nsock_close ( sock - > upstreams [ i ] ) ;
sock - > upstreams [ i ] = 0 ;
}
if ( sock - > upstream_count ) {
free ( sock - > upstreams ) ;
}
sock - > upstream_count = 0 ;
sock - > connected = false ;
}
nsock_socks [ fd ] = 0 ;
close ( fd ) ;
}
nsock_t * nsock_create ( int fd , nsock_type_t type ) {
if ( fd < = 0 )
return NULL ;
nsock_socks [ fd ] = fd ;
nsock_t * sock = nsock_get ( fd ) ;
sock - > connected = true ;
sock - > downstream = false ;
sock - > type = type ;
return sock ;
}
int * nsock_init ( int socket_count ) {
if ( nsock_socks ) {
return nsock_socks ;
}
nsock_socks = ( int * ) calloc ( 1 , sizeof ( int ) * sizeof ( int * ) * socket_count + 1 ) ;
if ( nsock_data ) {
free ( nsock_data ) ;
nsock_data = NULL ;
}
nsock_data = ( void * * ) malloc ( sizeof ( void * * ) * socket_count + 1 ) ;
nsock_socks [ socket_count ] = - 1 ;
return nsock_socks ;
}
void nsock_free ( ) {
if ( nsock_socks )
free ( nsock_socks ) ;
if ( nsock_readable )
free ( nsock_readable ) ;
nsock_server_fd = 0 ;
nsock_max_socket_fd = 0 ;
if ( nsock_data ) {
exit ( 1 ) ;
}
}
void nsock_add_upstream ( int source , int target , bool downstream ) {
if ( ! nsock_socks [ target ] )
return ;
if ( ! nsock_socks [ source ] )
return ;
nsock_t * sock = nsock_get ( source ) ;
nsock_t * sock_target = nsock_get ( target ) ;
sock_target - > type = NSOCK_UPSTREAM ;
sock - > upstreams = ( int * ) realloc ( sock - > upstreams , sizeof ( int ) * ( sock - > upstream_count + 1 ) ) ;
sock - > downstream = downstream ;
sock - > upstreams [ sock - > upstream_count ] = target ;
sock - > upstream_count + + ;
}
void * nsock_get_data ( int socket ) { return nsock_data [ socket ] ; }
void nsock_set_data ( int socket , void * data ) { nsock_data [ socket ] = data ; }
int nsock_connect ( const char * host , unsigned int port ) {
char port_str [ 10 ] = { 0 } ;
sprintf ( port_str , " %d " , port ) ;
int status ;
int socket_fd = 0 ;
struct addrinfo hints ;
struct addrinfo * res ;
struct addrinfo * p ;
if ( ( socket_fd = socket ( AF_INET , SOCK_STREAM , 0 ) ) < 0 ) {
return false ;
}
memset ( & hints , 0 , sizeof ( hints ) ) ;
hints . ai_family = AF_INET ;
hints . ai_socktype = SOCK_STREAM ;
if ( ( status = getaddrinfo ( host , port_str , & hints , & res ) ) ! = 0 ) {
return 0 ;
}
for ( p = res ; p ! = NULL ; p = p - > ai_next ) {
if ( ( socket_fd = socket ( p - > ai_family , p - > ai_socktype , p - > ai_protocol ) ) = = - 1 ) {
continue ;
}
if ( connect ( socket_fd , p - > ai_addr , p - > ai_addrlen ) = = - 1 ) {
close ( socket_fd ) ;
continue ;
}
break ;
}
if ( p = = NULL ) {
freeaddrinfo ( res ) ;
return 0 ;
}
freeaddrinfo ( res ) ;
if ( socket_fd ) {
if ( nsock_socks = = NULL ) {
nsock_init ( 2048 ) ;
}
nsock_socks [ socket_fd ] = socket_fd ;
nsock_t * sock = nsock_create ( socket_fd , NSOCK_CLIENT ) ;
sock - > connected = true ;
}
return socket_fd ;
}
void nsock_listen ( int port ) {
int server_fd ;
struct sockaddr_in address ;
if ( ( server_fd = socket ( AF_INET , SOCK_STREAM , 0 ) ) = = 0 ) {
perror ( " Socket failed " ) ;
exit ( EXIT_FAILURE ) ;
}
int opt = 1 ;
if ( setsockopt ( server_fd , SOL_SOCKET , SO_REUSEADDR , & opt , sizeof ( opt ) ) ) {
perror ( " setsockopt failed " ) ;
close ( server_fd ) ;
exit ( EXIT_FAILURE ) ;
}
address . sin_family = AF_INET ;
address . sin_addr . s_addr = INADDR_ANY ;
address . sin_port = htons ( port ) ;
if ( bind ( server_fd , ( struct sockaddr * ) & address , sizeof ( address ) ) < 0 ) {
perror ( " Bind failed " ) ;
close ( server_fd ) ;
exit ( EXIT_FAILURE ) ;
}
if ( listen ( server_fd , 8096 ) < 0 ) {
perror ( " Listen failed " ) ;
close ( server_fd ) ;
exit ( EXIT_FAILURE ) ;
}
nsock_server_fd = server_fd ;
}
int * nsock_select ( suseconds_t timeout ) {
struct timeval tv ;
tv . tv_sec = 0 ;
tv . tv_usec = timeout ;
int server_fd = nsock_server_fd ;
fd_set rfds ;
FD_ZERO ( & rfds ) ;
FD_SET ( server_fd , & rfds ) ;
int * socks = nsock_socks ;
fd_set efds ;
FD_ZERO ( & efds ) ;
nsock_max_socket_fd = server_fd ;
for ( int i = 0 ; socks [ i ] ! = - 1 ; i + + ) {
if ( i = = server_fd )
continue ;
;
if ( ! socks [ i ] )
continue ;
if ( socks [ i ] > nsock_max_socket_fd ) {
nsock_max_socket_fd = socks [ i ] ;
}
FD_SET ( socks [ i ] , & rfds ) ;
FD_SET ( socks [ i ] , & efds ) ;
}
int activity = select ( nsock_max_socket_fd + 1 , & rfds , NULL , & efds , timeout = = 0 ? NULL : & tv ) ;
if ( ( activity < 0 ) & & ( errno ! = EINTR ) ) {
perror ( " Select error \n " ) ;
return NULL ;
} else if ( activity = = 0 ) {
return NULL ;
}
if ( FD_ISSET ( server_fd , & rfds ) ) {
struct sockaddr_in address ;
int addrlen = sizeof ( address ) ;
address . sin_family = AF_INET ;
address . sin_addr . s_addr = INADDR_ANY ;
int new_socket = 0 ;
if ( ( new_socket = accept ( server_fd , ( struct sockaddr * ) & address , ( socklen_t * ) & addrlen ) ) < 0 ) {
perror ( " Accept failed " ) ;
} else {
nsock_socks [ new_socket ] = new_socket ;
nsock_create ( new_socket , NSOCK_CLIENT ) ;
if ( nsock_on_connect )
nsock_on_connect ( new_socket ) ;
if ( new_socket > nsock_max_socket_fd )
nsock_max_socket_fd = new_socket ;
}
}
if ( nsock_readable ) {
free ( nsock_readable ) ;
}
nsock_readable = ( int * ) calloc ( 1 , sizeof ( int * ) + sizeof ( int ) * ( nsock_max_socket_fd + 2 ) ) ;
nsock_readable [ nsock_max_socket_fd + 1 ] = - 1 ;
nsock_readable [ 0 ] = 0 ;
int readable_count = 0 ;
for ( int i = 0 ; i < nsock_max_socket_fd + 1 ; i + + ) {
nsock_t * sock = nsock_get ( i ) ;
if ( ! sock )
continue ;
if ( FD_ISSET ( i , & efds ) ) {
nsock_close ( nsock_socks [ i ] ) ;
nsock_socks [ i ] = 0 ;
nsock_readable [ i ] = 0 ;
} else if ( FD_ISSET ( i , & rfds ) & & i ! = server_fd ) {
nsock_readable [ i ] = i ;
readable_count + + ;
nsock_on_before_data ( i ) ;
} else {
nsock_readable [ i ] = 0 ;
sock - > connected = false ;
}
}
return nsock_readable ;
}
unsigned char * nsock_read ( int fd , int length ) {
if ( ! nsock_socks [ fd ] )
return NULL ;
unsigned char * buffer = ( unsigned char * ) malloc ( length + 1 ) ;
int bytes_read = read ( fd , buffer , length ) ;
if ( bytes_read < = 0 ) {
nsock_close ( fd ) ;
return NULL ;
}
buffer [ bytes_read ] = 0 ;
return buffer ;
}
unsigned char * nsock_read_all ( int fd , int length ) {
if ( ! nsock_socks [ fd ] )
return NULL ;
unsigned char * buffer = ( unsigned char * ) malloc ( length + 1 ) ;
int bytes_read = 0 ;
while ( bytes_read < length ) {
int bytes_chunk = read ( fd , buffer + bytes_read , length - bytes_read ) ;
if ( bytes_chunk < = 0 ) {
nsock_close ( fd ) ;
return NULL ;
}
bytes_read + = bytes_chunk ;
}
buffer [ bytes_read ] = 0 ;
return buffer ;
}
int nsock_write_all ( int fd , unsigned char * data , int length ) {
if ( ! nsock_socks [ fd ] )
return 0 ;
int bytes_written = 0 ;
while ( bytes_written < length ) {
int bytes_chunk = write ( fd , data + bytes_written , length - bytes_written ) ;
if ( bytes_chunk < = 0 ) {
nsock_close ( fd ) ;
return 0 ;
}
bytes_written + = bytes_chunk ;
}
return bytes_written ;
}
int nsock_execute_upstream ( int source , size_t buffer_size ) {
int result = 0 ;
nsock_t * sock = nsock_get ( source ) ;
unsigned char data [ buffer_size ] ;
memset ( data , 0 , buffer_size ) ;
int bytes_read = read ( source , data , buffer_size ) ;
if ( bytes_read < = 0 ) {
nsock_close ( source ) ;
return 0 ;
}
bool downstreamed = false ;
for ( unsigned int i = 0 ; i < sock - > upstream_count ; i + + ) {
if ( ! nsock_socks [ sock - > upstreams [ i ] ] )
continue ;
int bytes_sent = nsock_write_all ( sock - > upstreams [ i ] , data , bytes_read ) ;
if ( bytes_sent < = 0 ) {
nsock_close ( sock - > upstreams [ i ] ) ;
continue ;
}
if ( sock - > downstream & & downstreamed = = false ) {
downstreamed = true ;
unsigned char data [ 4096 ] ;
memset ( data , 0 , 4096 ) ;
int bytes_read = read ( sock - > upstreams [ i ] , data , 4096 ) ;
if ( bytes_read < = 0 ) {
nsock_close ( source ) ;
return 0 ;
}
int bytes_sent = nsock_write_all ( sock - > fd , data , bytes_read ) ;
if ( bytes_sent < = 0 ) {
nsock_close ( sock - > upstreams [ i ] ) ;
return 0 ;
}
}
result + + ;
}
return result ;
}
void nsock_on_before_data ( int fd ) {
if ( ! nsock_socks [ fd ] )
return ;
nsock_t * sock = nsock_get ( fd ) ;
if ( sock - > upstream_count ) {
int upstreamed_to_count = nsock_execute_upstream ( fd , 4096 ) ;
if ( ! upstreamed_to_count ) {
nsock_close ( fd ) ;
}
return ;
} else if ( sock - > type = = NSOCK_UPSTREAM ) {
while ( rfd_wait ( sock - > fd , 0 ) ) {
unsigned char * data = nsock_read ( fd , 4096 ) ;
( void ) data ;
}
}
if ( nsock_on_data )
nsock_on_data ( fd ) ;
}
void nsock ( int port , void ( * on_connect ) ( int fd ) , void ( * on_data ) ( int fd ) , void ( * on_close ) ( int fd ) ) {
nsock_init ( 2048 ) ;
nsock_listen ( port ) ;
nsock_on_connect = on_connect ;
nsock_on_data = on_data ;
nsock_on_close = on_close ;
int serve_in_terminal = nsock_on_connect = = NULL & & nsock_on_data = = NULL & & nsock_on_close = = NULL ;
while ( 1 ) {
int * readable = nsock_select ( 0 ) ;
if ( ! serve_in_terminal )
continue ;
if ( ! readable )
continue ;
for ( int i = 0 ; readable [ i ] ! = - 1 ; i + + ) {
if ( ! readable [ i ] )
continue ;
char buffer [ 1024 ] = { 0 } ;
int bytes_read = read ( readable [ i ] , buffer , 1 ) ;
buffer [ bytes_read ] = 0 ;
if ( bytes_read < = 0 ) {
nsock_close ( readable [ i ] ) ;
continue ;
}
if ( write ( readable [ i ] , buffer , bytes_read ) < = 0 ) {
nsock_close ( readable [ i ] ) ;
continue ;
}
}
}
}
# endif
# ifndef UUID_H
# define UUID_H
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <time.h>
typedef struct {
unsigned char bytes [ 16 ] ;
} UUID ;
void generate_random_bytes ( unsigned char * bytes , size_t len ) {
for ( size_t i = 0 ; i < len ; i + + ) {
bytes [ i ] = rand ( ) % 256 ;
}
}
UUID generate_uuid4 ( void ) {
UUID uuid ;
generate_random_bytes ( uuid . bytes , 16 ) ;
uuid . bytes [ 6 ] & = 0x0f ;
uuid . bytes [ 6 ] | = 0x40 ;
uuid . bytes [ 8 ] & = 0x3f ;
uuid . bytes [ 8 ] | = 0x80 ;
return uuid ;
}
void uuid_to_string ( UUID uuid , char * str ) {
sprintf ( str , " %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x " , uuid . bytes [ 0 ] , uuid . bytes [ 1 ] , uuid . bytes [ 2 ] ,
uuid . bytes [ 3 ] , uuid . bytes [ 4 ] , uuid . bytes [ 5 ] , uuid . bytes [ 6 ] , uuid . bytes [ 7 ] , uuid . bytes [ 8 ] , uuid . bytes [ 9 ] , uuid . bytes [ 10 ] ,
uuid . bytes [ 11 ] , uuid . bytes [ 12 ] , uuid . bytes [ 13 ] , uuid . bytes [ 14 ] , uuid . bytes [ 15 ] ) ;
}
char * uuid4 ( ) {
srand ( time ( NULL ) ) ;
UUID uuid = generate_uuid4 ( ) ;
char str [ 37 ] ;
uuid_to_string ( uuid , str ) ;
return sbuf ( str ) ;
}
# endif
# ifndef RNET_H
# define RNET_H
# ifdef _POSIX_C_SOURCE
# define _POSIX_C_SOURCE_TEMP _POSIX_C_SOURCE
# undef _POSIX_C_SOURCE
# endif
# ifndef _POSIX_C_SOURCE
# undef _POSIX_C_SOURCE
# define _POSIX_C_SOURCE 200112L
# endif
# include <arpa/inet.h>
# include <errno.h>
# include <fcntl.h>
# include <netdb.h>
# include <signal.h>
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys/select.h>
# include <sys/socket.h>
# include <sys/types.h>
# include <unistd.h>
# ifdef _POSIX_C_SOURCE_TEMP
# undef _POSIX_C_SOURCE
# define _POSIX_C_SOURCE _POSIX_C_SOURCE_TEMP
# undef _POSIX_C_SOURCE_TEMP
# else
# undef _POSIX_C_SOURCE
# endif
# define NET_SOCKET_MAX_CONNECTIONS 50000
typedef struct rnet_socket_t {
int fd ;
char name [ 50 ] ;
void * data ;
size_t bytes_received ;
size_t bytes_sent ;
bool connected ;
void ( * on_read ) ( struct rnet_socket_t * ) ;
void ( * on_close ) ( struct rnet_socket_t * ) ;
void ( * on_connect ) ( struct rnet_socket_t * ) ;
} rnet_socket_t ;
typedef struct rnet_select_result_t {
int server_fd ;
rnet_socket_t * * sockets ;
unsigned int socket_count ;
} rnet_select_result_t ;
typedef struct rnet_server_t {
int socket_fd ;
rnet_socket_t * * sockets ;
unsigned int socket_count ;
unsigned int port ;
unsigned int backlog ;
rnet_select_result_t * select_result ;
int max_fd ;
void ( * on_connect ) ( rnet_socket_t * socket ) ;
void ( * on_close ) ( rnet_socket_t * socket ) ;
void ( * on_read ) ( rnet_socket_t * socket ) ;
} rnet_server_t ;
void rnet_select_result_free ( rnet_select_result_t * result ) ;
int net_socket_accept ( int server_fd ) ;
int net_socket_connect ( const char * , unsigned int ) ;
int net_socket_init ( ) ;
rnet_server_t * net_socket_serve ( unsigned int port , unsigned int backlog ) ;
rnet_select_result_t * net_socket_select ( rnet_server_t * server ) ;
rnet_socket_t * net_socket_wait ( rnet_socket_t * socket_fd ) ;
bool net_set_non_blocking ( int sock ) ;
bool net_socket_bind ( int sock , unsigned int port ) ;
bool net_socket_listen ( int sock , unsigned int backlog ) ;
char * net_socket_name ( int sock ) ;
size_t net_socket_write ( rnet_socket_t * , unsigned char * , size_t ) ;
rnet_socket_t * get_net_socket_by_fd ( int ) ;
unsigned char * net_socket_read ( rnet_socket_t * , unsigned int buff_size ) ;
void _net_socket_close ( int sock ) ;
void net_socket_close ( rnet_socket_t * sock ) ;
rnet_server_t * rnet_server_new ( int socket_fd , unsigned int port , unsigned int backlog ) {
rnet_server_t * server = malloc ( sizeof ( rnet_server_t ) ) ;
server - > socket_fd = socket_fd ;
server - > sockets = NULL ;
server - > socket_count = 0 ;
server - > port = port ;
server - > backlog = backlog ;
server - > max_fd = - 1 ;
server - > select_result = NULL ;
server - > on_connect = NULL ;
server - > on_close = NULL ;
server - > on_read = NULL ;
return server ;
}
rnet_server_t * rnet_server_add_socket ( rnet_server_t * server , rnet_socket_t * sock ) {
server - > sockets = realloc ( server - > sockets , sizeof ( rnet_socket_t * ) * ( server - > socket_count + 1 ) ) ;
server - > sockets [ server - > socket_count ] = sock ;
server - > socket_count + + ;
sock - > on_read = server - > on_read ;
sock - > on_connect = server - > on_connect ;
sock - > on_close = server - > on_close ;
sock - > connected = true ;
return server ;
}
rnet_socket_t sockets [ NET_SOCKET_MAX_CONNECTIONS ] = { 0 } ;
unsigned long sockets_connected = 0 ;
int net_socket_max_fd = 0 ;
unsigned long sockets_total = 0 ;
unsigned long sockets_disconnected = 0 ;
unsigned long sockets_concurrent_record = 0 ;
unsigned long sockets_errors = 0 ;
bool net_set_non_blocking ( int sock ) {
int flags = fcntl ( sock , F_GETFL , 0 ) ;
if ( flags < 0 ) {
perror ( " fcntl " ) ;
return false ;
}
if ( fcntl ( sock , F_SETFL , flags | O_NONBLOCK ) < 0 ) {
perror ( " fcntl " ) ;
return false ;
}
return true ;
}
int net_socket_init ( ) {
int socket_fd = - 1 ;
memset ( sockets , 0 , sizeof ( sockets ) ) ;
int opt = 1 ;
if ( ( socket_fd = socket ( AF_INET , SOCK_STREAM , 0 ) ) = = 0 ) {
perror ( " Socket failed. \n " ) ;
return false ;
}
if ( setsockopt ( socket_fd , SOL_SOCKET , SO_REUSEADDR , & opt , sizeof ( opt ) ) ) {
perror ( " Setsockopt failed. \n " ) ;
close ( socket_fd ) ;
return false ;
}
net_set_non_blocking ( socket_fd ) ;
return socket_fd ;
}
char * net_socket_name ( int fd ) {
rnet_socket_t * rnet_socket = get_net_socket_by_fd ( fd ) ;
if ( rnet_socket ) {
return rnet_socket - > name ;
;
}
// If socket disconnected or is no client from server
return NULL ;
}
bool net_socket_bind ( int socket_fd , unsigned int port ) {
struct sockaddr_in address ;
address . sin_family = AF_INET ; // IPv4
address . sin_addr . s_addr = INADDR_ANY ; // Bind to any available address
address . sin_port = htons ( port ) ; // Convert port to network byte order
if ( bind ( socket_fd , ( struct sockaddr * ) & address , sizeof ( address ) ) < 0 ) {
perror ( " Bind failed " ) ;
close ( socket_fd ) ;
return false ;
}
return true ;
}
int net_socket_connect ( const char * host , unsigned int port ) {
char port_str [ 10 ] = { 0 } ;
sprintf ( port_str , " %d " , port ) ;
int status ;
int socket_fd = - 1 ;
struct addrinfo hints ;
struct addrinfo * res ;
struct addrinfo * p ;
if ( ( socket_fd = socket ( AF_INET , SOCK_STREAM , 0 ) ) < 0 ) {
return false ;
}
memset ( & hints , 0 , sizeof ( hints ) ) ;
hints . ai_family = AF_INET ;
hints . ai_socktype = SOCK_STREAM ;
if ( ( status = getaddrinfo ( host , port_str , & hints , & res ) ) ! = 0 ) {
return - 1 ;
}
for ( p = res ; p ! = NULL ; p = p - > ai_next ) {
if ( ( socket_fd = socket ( p - > ai_family , p - > ai_socktype , p - > ai_protocol ) ) = = - 1 ) {
continue ;
}
if ( connect ( socket_fd , p - > ai_addr , p - > ai_addrlen ) = = - 1 ) {
close ( socket_fd ) ;
continue ;
}
break ;
}
if ( p = = NULL ) {
freeaddrinfo ( res ) ;
return - 1 ;
}
freeaddrinfo ( res ) ;
return socket_fd ;
}
bool net_socket_listen ( int socket_fd , unsigned int backlog ) {
if ( listen ( socket_fd , backlog ) < 0 ) { // '3' is the backlog size
perror ( " Listen failed " ) ;
close ( socket_fd ) ;
return false ;
}
return true ;
}
rnet_server_t * net_socket_serve ( unsigned int port , unsigned int backlog ) {
signal ( SIGPIPE , SIG_IGN ) ;
int socket_fd = net_socket_init ( ) ;
net_socket_bind ( socket_fd , port ) ;
net_socket_listen ( socket_fd , backlog ) ;
return rnet_server_new ( socket_fd , port , backlog ) ;
}
int net_socket_accept ( int net_socket_server_fd ) {
struct sockaddr_in address ;
int addrlen = sizeof ( address ) ;
int new_socket = - 1 ;
if ( ( new_socket = accept ( net_socket_server_fd , ( struct sockaddr * ) & address , ( socklen_t * ) & addrlen ) ) < 0 ) {
close ( new_socket ) ;
return - 1 ;
} else {
return new_socket ;
}
}
/*
static void net_socket_stats(WrenVM *vm)
{
wrenSetSlotNewList(vm, 0);
wrenSetSlotString(vm, 1, "sockets_total");
wrenInsertInList(vm, 0, -1, 1);
wrenSetSlotDouble(vm, 1, (double)sockets_total);
wrenInsertInList(vm, 0, -1, 1);
wrenSetSlotString(vm, 1, "sockets_concurrent_record");
wrenInsertInList(vm, 0, -1, 1);
wrenSetSlotDouble(vm, 1, (double)sockets_concurrent_record);
wrenInsertInList(vm, 0, -1, 1);
wrenSetSlotString(vm, 1, "sockets_connected");
wrenInsertInList(vm, 0, -1, 1);
wrenSetSlotDouble(vm, 1, (double)sockets_connected);
wrenInsertInList(vm, 0, -1, 1);
wrenSetSlotString(vm, 1, "sockets_disconnected");
wrenInsertInList(vm, 0, -1, 1);
wrenSetSlotDouble(vm, 1, (double)sockets_disconnected);
wrenInsertInList(vm, 0, -1, 1);
}*/
size_t net_socket_write ( rnet_socket_t * sock , unsigned char * message , size_t size ) {
ssize_t sent_total = 0 ;
ssize_t sent = 0 ;
ssize_t to_send = size ;
while ( ( sent = send ( sock - > fd , message , to_send , 0 ) ) ) {
if ( sent = = - 1 ) {
sockets_errors + + ;
net_socket_close ( sock ) ;
break ;
}
if ( sent = = 0 ) {
printf ( " EDGE CASE? \n " ) ;
exit ( 1 ) ;
sockets_errors + + ;
net_socket_close ( sock ) ;
break ;
}
sent_total + = sent ;
if ( sent_total = = to_send )
break ;
}
return sent_total ;
}
unsigned char * net_socket_read ( rnet_socket_t * sock , unsigned int buff_size ) {
if ( buff_size > 1024 * 1024 + 1 ) {
perror ( " Buffer too big. Maximum is 1024*1024. \n " ) ;
exit ( 1 ) ;
}
static unsigned char buffer [ 1024 * 1024 ] ;
buffer [ 0 ] = 0 ;
ssize_t received = recv ( sock - > fd , buffer , buff_size , 0 ) ;
if ( received < = 0 ) {
buffer [ 0 ] = 0 ;
net_socket_close ( sock ) ;
if ( received < 0 ) {
sockets_errors + + ;
return NULL ;
}
}
buffer [ received + 1 ] = 0 ;
sock - > bytes_received = received ;
return buffer ;
}
rnet_socket_t * net_socket_wait ( rnet_socket_t * sock ) {
if ( ! sock )
return NULL ;
if ( sock - > fd = = - 1 )
return NULL ;
fd_set read_fds ;
FD_ZERO ( & read_fds ) ;
FD_SET ( sock - > fd , & read_fds ) ;
int max_socket_fd = sock - > fd ;
int activity = select ( max_socket_fd + 1 , & read_fds , NULL , NULL , NULL ) ;
if ( ( activity < 0 ) & & ( errno ! = EINTR ) ) {
// perror("Select error");
net_socket_close ( sock ) ;
return NULL ;
}
if ( FD_ISSET ( sock - > fd , & read_fds ) ) {
return sock ;
}
return NULL ;
}
void rnet_safe_str ( char * str , size_t length ) {
if ( ! str | | ! length | | ! * str )
return ;
for ( unsigned int i = 0 ; i < length ; i + + ) {
if ( str [ i ] < 32 | | str [ i ] > 126 )
if ( str [ i ] ! = 0 )
str [ i ] = ' . ' ;
}
str [ length ] = 0 ;
}
rnet_select_result_t * rnet_new_socket_select_result ( int socket_fd ) {
rnet_select_result_t * result = ( rnet_select_result_t * ) malloc ( sizeof ( rnet_select_result_t ) ) ;
memset ( result , 0 , sizeof ( rnet_select_result_t ) ) ;
result - > server_fd = socket_fd ;
result - > socket_count = 0 ;
result - > sockets = NULL ;
return result ;
}
void rnet_select_result_add ( rnet_select_result_t * result , rnet_socket_t * sock ) {
result - > sockets = realloc ( result - > sockets , sizeof ( rnet_socket_t * ) * ( result - > socket_count + 1 ) ) ;
result - > sockets [ result - > socket_count ] = sock ;
result - > socket_count + + ;
}
void rnet_select_result_free ( rnet_select_result_t * result ) { free ( result ) ; }
rnet_select_result_t * net_socket_select ( rnet_server_t * server ) {
fd_set read_fds ;
FD_ZERO ( & read_fds ) ;
FD_SET ( server - > socket_fd , & read_fds ) ;
server - > max_fd = server - > socket_fd ;
int socket_fd = - 1 ;
for ( unsigned int i = 0 ; i < server - > socket_count ; i + + ) {
socket_fd = server - > sockets [ i ] - > fd ;
if ( ! server - > sockets [ i ] - > connected ) {
continue ;
}
if ( socket_fd > 0 ) {
FD_SET ( socket_fd , & read_fds ) ;
if ( socket_fd > server - > max_fd ) {
server - > max_fd = socket_fd ;
}
}
}
int new_socket = - 1 ;
struct sockaddr_in address ;
int addrlen = sizeof ( struct sockaddr_in ) ;
int activity = select ( server - > max_fd + 1 , & read_fds , NULL , NULL , NULL ) ;
if ( ( activity < 0 ) & & ( errno ! = EINTR ) ) {
perror ( " Select error \n " ) ;
return NULL ;
}
if ( FD_ISSET ( server - > socket_fd , & read_fds ) ) {
if ( ( new_socket = accept ( server - > socket_fd , ( struct sockaddr * ) & address , ( socklen_t * ) & addrlen ) ) < 0 ) {
perror ( " Accept failed \n " ) ;
return NULL ;
}
// net_set_non_blocking(new_socket);
char name [ 50 ] = { 0 } ;
sprintf ( name , " fd:%.4d:ip:%12s:port:%.6d " , new_socket , inet_ntoa ( address . sin_addr ) , ntohs ( address . sin_port ) ) ;
rnet_socket_t * sock_obj = NULL ;
for ( unsigned int i = 0 ; i < server - > socket_count ; i + + ) {
if ( server - > sockets & & server - > sockets [ i ] - > fd = = - 1 ) {
sock_obj = server - > sockets [ i ] ;
}
}
if ( ! sock_obj ) {
sock_obj = ( rnet_socket_t * ) malloc ( sizeof ( rnet_socket_t ) ) ;
rnet_server_add_socket ( server , sock_obj ) ;
}
sock_obj - > fd = new_socket ;
strcpy ( sock_obj - > name , name ) ;
sockets_connected + + ;
sockets_total + + ;
sockets_concurrent_record = sockets_connected > sockets_concurrent_record ? sockets_connected : sockets_concurrent_record ;
if ( new_socket > net_socket_max_fd ) {
net_socket_max_fd = new_socket ;
}
sock_obj - > connected = true ;
sock_obj - > on_connect ( sock_obj ) ;
}
rnet_select_result_t * result = rnet_new_socket_select_result ( server - > socket_fd ) ;
unsigned int readable_count = 0 ;
for ( unsigned int i = 0 ; i < server - > socket_count ; i + + ) {
if ( server - > sockets [ i ] - > fd = = - 1 )
continue ;
if ( FD_ISSET ( server - > sockets [ i ] - > fd , & read_fds ) ) {
rnet_select_result_add ( result , server - > sockets [ i ] ) ;
readable_count + + ;
if ( server - > sockets [ i ] - > on_read ) {
server - > sockets [ i ] - > on_read ( server - > sockets [ i ] ) ;
}
}
}
if ( server - > select_result ) {
rnet_select_result_free ( server - > select_result ) ;
server - > select_result = NULL ;
}
if ( readable_count = = 0 )
rnet_select_result_free ( result ) ;
return readable_count ? result : NULL ;
}
rnet_socket_t * get_net_socket_by_fd ( int sock ) {
for ( int i = 0 ; i < net_socket_max_fd ; i + + ) {
if ( sockets [ i ] . fd = = sock ) {
return & sockets [ i ] ;
}
}
return NULL ;
}
void _net_socket_close ( int sock ) {
if ( sock > 0 ) {
sockets_connected - - ;
sockets_disconnected + + ;
if ( sock > 0 ) {
if ( close ( sock ) = = - 1 ) {
perror ( " Error closing socket. \n " ) ;
}
}
}
}
void net_socket_close ( rnet_socket_t * sock ) {
sock - > connected = false ;
if ( sock - > on_close )
sock - > on_close ( sock ) ;
_net_socket_close ( sock - > fd ) ;
sock - > fd = - 1 ;
}
# undef _POSIX_C_SOURCE
# endif
# include <stdio.h>
# ifndef RLIB_RARGS_H
# define RLIB_RARGS_H
# include <stdbool.h>
# include <stdlib.h>
# include <string.h>
bool rargs_isset ( int argc , char * argv [ ] , char * key ) {
for ( int i = 0 ; i < argc ; i + + ) {
if ( ! strcmp ( argv [ i ] , key ) ) {
return true ;
}
}
return false ;
}
char * rargs_get_option_string ( int argc , char * argv [ ] , char * key , const char * def ) {
for ( int i = 0 ; i < argc ; i + + ) {
if ( ! strcmp ( argv [ i ] , key ) ) {
if ( i < argc - 1 ) {
return argv [ i + 1 ] ;
}
}
}
return ( char * ) def ;
}
int rargs_get_option_int ( int argc , char * argv [ ] , char * key , int def ) {
for ( int i = 0 ; i < argc ; i + + ) {
if ( ! strcmp ( argv [ i ] , key ) ) {
if ( i < argc - 1 ) {
return atoi ( argv [ i + 1 ] ) ;
}
}
}
return def ;
}
bool rargs_get_option_bool ( int argc , char * argv [ ] , char * key , bool def ) {
for ( int i = 0 ; i < argc ; i + + ) {
if ( ! strcmp ( argv [ i ] , key ) ) {
if ( i < argc - 1 ) {
if ( ! strcmp ( argv [ i + 1 ] , " false " ) )
return false ;
if ( ! strcmp ( argv [ i + 1 ] , " 0 " ) )
return false ;
return true ;
}
}
}
return def ;
}
# endif
# ifndef RCAT_H
# define RCAT_H
# include <stdio.h>
# include <stdlib.h>
void rcat ( char * filename ) {
FILE * f = fopen ( filename , " rb " ) ;
if ( ! f ) {
printf ( " rcat: couldn't open \" %s \" for read. \n " , filename ) ;
return ;
}
unsigned char c ;
while ( ( c = fgetc ( f ) ) & & ! feof ( f ) ) {
printf ( " %c " , c ) ;
}
fclose ( f ) ;
fflush ( stdout ) ;
}
int rcat_main ( int argc , char * argv [ ] ) {
if ( argc < 2 ) {
printf ( " Usage: [filename] \n " ) ;
return 1 ;
}
rcat ( argv [ 1 ] ) ;
return 0 ;
}
# endif
# ifndef RLIZA_H
# define RLIZA_H
# ifndef RBUFFER_H
# define RBUFFER_H
# include <assert.h>
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
typedef struct rbuffer_t {
unsigned char * data ;
unsigned char * _data ;
size_t size ;
size_t pos ;
bool eof ;
} rbuffer_t ;
rbuffer_t * rbuffer_new ( unsigned char * data , size_t size ) ;
void rbuffer_free ( rbuffer_t * rfb ) ;
void rbuffer_reset ( rbuffer_t * rfb ) ;
void rbuffer_write ( rbuffer_t * rfb , const unsigned char * data , size_t size ) ;
size_t rbuffer_push ( rbuffer_t * rfb , unsigned char ) ;
unsigned char rbuffer_pop ( rbuffer_t * rfb ) ;
unsigned char * rbuffer_expect ( rbuffer_t * rfb , char * options , char * ignore ) ;
void rbuffer_set ( rbuffer_t * rfb , const unsigned char * data , size_t size ) ;
void rbuffer_set ( rbuffer_t * rfb , const unsigned char * data , size_t size ) {
if ( rfb - > _data ) {
free ( rfb - > _data ) ;
rfb - > _data = NULL ;
rfb - > data = NULL ;
rfb - > eof = true ;
}
if ( size ) {
rfb - > _data = ( unsigned char * ) malloc ( size ) ;
memcpy ( rfb - > _data , data , size ) ;
rfb - > data = rfb - > _data ;
rfb - > eof = false ;
}
rfb - > size = size ;
rfb - > pos = 0 ;
}
rbuffer_t * rbuffer_new ( unsigned char * data , size_t size ) {
rbuffer_t * rfb = ( rbuffer_t * ) malloc ( sizeof ( rbuffer_t ) ) ;
if ( size ) {
rfb - > _data = ( unsigned char * ) malloc ( size ) ;
memcpy ( rfb - > _data , data , size ) ;
rfb - > eof = false ;
} else {
rfb - > _data = NULL ;
rfb - > eof = true ;
}
rfb - > size = size ;
rfb - > pos = 0 ;
rfb - > data = rfb - > _data ;
return rfb ;
}
void rbuffer_free ( rbuffer_t * rfb ) {
if ( rfb - > _data )
free ( rfb - > _data ) ;
free ( rfb ) ;
}
size_t rbuffer_push ( rbuffer_t * rfb , unsigned char c ) {
if ( rfb - > pos < rfb - > size ) {
rfb - > _data [ rfb - > pos + + ] = c ;
return 1 ;
}
rfb - > _data = realloc ( rfb - > _data , rfb - > size ? rfb - > size + 1 : rfb - > size + 2 ) ;
rfb - > _data [ rfb - > pos + + ] = c ;
rfb - > size + + ;
return rfb - > pos ;
}
void rbuffer_write ( rbuffer_t * rfb , const unsigned char * data , size_t size ) {
unsigned char * data_ptr = ( unsigned char * ) data ;
for ( size_t i = 0 ; i < size ; i + + ) {
rbuffer_push ( rfb , data_ptr [ i ] ) ;
}
}
unsigned char rbuffer_peek ( rbuffer_t * rfb ) {
unsigned char result = EOF ;
if ( rfb - > pos ! = rfb - > size ) {
result = rfb - > _data [ rfb - > pos ] ;
return result ;
}
rfb - > eof = true ;
return EOF ;
}
unsigned char rbuffer_pop ( rbuffer_t * rfb ) {
unsigned char result = EOF ;
if ( rfb - > pos < = rfb - > size ) {
result = rfb - > _data [ rfb - > pos ] ;
rfb - > pos + + ;
rfb - > data + + ;
if ( rfb - > pos = = rfb - > size ) {
rfb - > eof = true ;
}
return result ;
}
rfb - > eof = true ;
return result ;
}
void rbuffer_reset ( rbuffer_t * rfb ) {
rfb - > data = rfb - > _data ;
rfb - > pos = 0 ;
}
unsigned char ustrncmp ( const unsigned char * s1 , const unsigned char * s2 , size_t n ) {
return strncmp ( ( char * ) s1 , ( char * ) s2 , n ) ;
while ( n & & * s1 = = * s2 ) {
n - - ;
s1 + + ;
s2 + + ;
}
return * s1 ! = * s2 ;
}
size_t ustrlen ( const unsigned char * s ) { return strlen ( ( char * ) s ) ; }
unsigned char * rbuffer_to_string ( rbuffer_t * rfb ) {
unsigned char * result = rfb - > _data ;
rfb - > _data = NULL ;
rfb - > data = NULL ;
rbuffer_free ( rfb ) ;
return result ;
}
unsigned char * rbuffer_match_option ( rbuffer_t * rfb , char * options ) {
char * option = NULL ;
char options_cpy [ 1024 ] = { 0 } ;
strcpy ( options_cpy , options ) ;
char * memory = options_cpy ;
while ( ( option = strtok_r ( option = = NULL ? memory : NULL , " | " , & memory ) ) ! = NULL ) {
size_t option_length = strlen ( option ) ;
if ( option_length > rfb - > size - rfb - > pos ) {
continue ;
}
if ( ! strcmp ( option , " \\ d " ) & & * rfb - > data > = ' 0 ' & & * rfb - > data < = ' 9 ' ) {
return rfb - > data ;
}
if ( rfb - > size - rfb - > pos > = 5 & & ! strcmp ( option , " \\ b " ) & &
( ( ! ustrncmp ( rfb - > data , ( unsigned char * ) " true " , 4 ) | | ! ustrncmp ( rfb - > data , ( unsigned char * ) " false " , 5 ) ) ) ) {
return rfb - > data ;
}
if ( ! ustrncmp ( rfb - > data , ( unsigned char * ) option , option_length ) ) {
return rfb - > data ;
}
}
return NULL ;
}
unsigned char * rbuffer_expect ( rbuffer_t * rfb , char * options , char * ignore ) {
while ( rfb - > pos < rfb - > size ) {
if ( rbuffer_match_option ( rfb , options ) ! = NULL ) {
return rfb - > data ;
}
if ( rbuffer_match_option ( rfb , ignore ) ) {
printf ( " SKIP:%s \n " , rfb - > data ) ;
rbuffer_pop ( rfb ) ;
continue ;
}
break ;
}
return NULL ;
}
unsigned char * rbuffer_consume ( rbuffer_t * rfb , char * options , char * ignore ) {
unsigned char * result = NULL ;
if ( ( result = rbuffer_expect ( rfb , options , ignore ) ) ! = NULL ) {
rbuffer_pop ( rfb ) ;
}
return result ;
}
# endif
# ifndef RSTRING_H
# define RSTRING_H
# ifndef RMATH_H
# define RMATH_H
# include <math.h>
# ifndef ceil
double ceil ( double x ) {
if ( x = = ( double ) ( long long ) x ) {
return x ;
} else if ( x > 0.0 ) {
return ( double ) ( long long ) x + 1.0 ;
} else {
return ( double ) ( long long ) x ;
}
}
# endif
# ifndef floor
double floor ( double x ) {
if ( x > = 0.0 ) {
return ( double ) ( long long ) x ;
} else {
double result = ( double ) ( long long ) x ;
return ( result = = x ) ? result : result - 1.0 ;
}
}
# endif
# ifndef modf
double modf ( double x , double * iptr ) {
double int_part = ( x > = 0.0 ) ? floor ( x ) : ceil ( x ) ;
* iptr = int_part ;
return x - int_part ;
}
# endif
# endif
# include <ctype.h>
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <time.h>
char * rstrtimestamp ( ) {
time_t current_time ;
time ( & current_time ) ;
struct tm * local_time = localtime ( & current_time ) ;
static char time_string [ 100 ] ;
time_string [ 0 ] = 0 ;
strftime ( time_string , sizeof ( time_string ) , " %Y-%m-%d %H:%M:%S " , local_time ) ;
return time_string ;
}
ulonglong _r_generate_key_current = 0 ;
char * _rcat_int_int ( int a , int b ) {
static char res [ 20 ] ;
res [ 0 ] = 0 ;
sprintf ( res , " %d%d " , a , b ) ;
return res ;
}
char * _rcat_int_double ( int a , double b ) {
static char res [ 20 ] ;
res [ 0 ] = 0 ;
sprintf ( res , " %d%f " , a , b ) ;
return res ;
}
char * _rcat_charp_int ( char * a , int b ) {
char res [ 20 ] ;
sprintf ( res , " %c " , b ) ;
return strcat ( a , res ) ;
}
char * _rcat_charp_double ( char * a , double b ) {
char res [ 20 ] ;
sprintf ( res , " %f " , b ) ;
return strcat ( a , res ) ;
}
char * _rcat_charp_charp ( char * a , char * b ) {
;
return strcat ( a , b ) ;
}
char * _rcat_charp_char ( char * a , char b ) {
char extra [ ] = { b , 0 } ;
return strcat ( a , extra ) ;
}
char * _rcat_charp_bool ( char * a , bool * b ) {
if ( b ) {
return strcat ( a , " true " ) ;
} else {
return strcat ( a , " false " ) ;
}
}
# define rcat(x, y) \
_Generic((x), \
int: _Generic((y), int: _rcat_int_int, double: _rcat_int_double, char *: _rcat_charp_charp), \
char *: _Generic((y), \
int: _rcat_charp_int, \
double: _rcat_charp_double, \
char *: _rcat_charp_charp, \
char: _rcat_charp_char, \
bool: _rcat_charp_bool))((x), (y))
char * rgenerate_key ( ) {
_r_generate_key_current + + ;
static char key [ 100 ] ;
key [ 0 ] = 0 ;
sprintf ( key , " %lld " , _r_generate_key_current ) ;
return key ;
}
char * rformat_number ( long long lnumber ) {
static char formatted [ 1024 ] ;
char number [ 1024 ] = { 0 } ;
sprintf ( number , " %lld " , lnumber ) ;
int len = strlen ( number ) ;
int commas_needed = ( len - 1 ) / 3 ;
int new_len = len + commas_needed ;
formatted [ new_len ] = ' \0 ' ;
int i = len - 1 ;
int j = new_len - 1 ;
int count = 0 ;
while ( i > = 0 ) {
if ( count = = 3 ) {
formatted [ j - - ] = ' . ' ;
count = 0 ;
}
formatted [ j - - ] = number [ i - - ] ;
count + + ;
}
if ( lnumber < 0 )
formatted [ j - - ] = ' - ' ;
return formatted ;
}
bool rstrextractdouble ( char * str , double * d1 ) {
for ( size_t i = 0 ; i < strlen ( str ) ; i + + ) {
if ( isdigit ( str [ i ] ) ) {
str + = i ;
sscanf ( str , " %lf " , d1 ) ;
return true ;
}
}
return false ;
}
void rstrstripslashes ( const char * content , char * result ) {
size_t content_length = strlen ( ( char * ) content ) ;
unsigned int index = 0 ;
for ( unsigned int i = 0 ; i < content_length ; i + + ) {
char c = content [ i ] ;
if ( c = = ' \\ ' ) {
i + + ;
c = content [ i ] ;
if ( c = = ' r ' ) {
c = ' \r ' ;
} else if ( c = = ' t ' ) {
c = ' \t ' ;
} else if ( c = = ' b ' ) {
c = ' \b ' ;
} else if ( c = = ' n ' ) {
c = ' \n ' ;
} else if ( c = = ' f ' ) {
c = ' \f ' ;
} else if ( c = = ' \\ ' ) {
// No need tbh
c = ' \\ ' ;
i + + ;
}
}
result [ index ] = c ;
index + + ;
}
result [ index ] = 0 ;
}
int rstrstartswith ( const char * s1 , const char * s2 ) {
if ( s1 = = NULL )
return s2 = = NULL ;
if ( s1 = = s2 | | s2 = = NULL | | * s2 = = 0 )
return true ;
size_t len_s2 = strlen ( s2 ) ;
size_t len_s1 = strlen ( s1 ) ;
if ( len_s2 > len_s1 )
return false ;
return ! strncmp ( s1 , s2 , len_s2 ) ;
}
bool rstrendswith ( const char * s1 , const char * s2 ) {
if ( s1 = = NULL )
return s2 = = NULL ;
if ( s1 = = s2 | | s2 = = NULL | | * s2 = = 0 )
return true ;
size_t len_s2 = strlen ( s2 ) ;
size_t len_s1 = strlen ( s1 ) ;
if ( len_s2 > len_s1 ) {
return false ;
}
s1 + = len_s1 - len_s2 ;
return ! strncmp ( s1 , s2 , len_s2 ) ;
}
void rstraddslashes ( const char * content , char * result ) {
size_t content_length = strlen ( ( char * ) content ) ;
unsigned int index = 0 ;
for ( unsigned int i = 0 ; i < content_length ; i + + ) {
if ( content [ i ] = = ' \r ' ) {
result [ index ] = ' \\ ' ;
index + + ;
result [ index ] = ' r ' ;
index + + ;
continue ;
} else if ( content [ i ] = = ' \t ' ) {
result [ index ] = ' \\ ' ;
index + + ;
result [ index ] = ' t ' ;
index + + ;
continue ;
} else if ( content [ i ] = = ' \n ' ) {
result [ index ] = ' \\ ' ;
index + + ;
result [ index ] = ' n ' ;
index + + ;
continue ;
} else if ( content [ i ] = = ' \\ ' ) {
result [ index ] = ' \\ ' ;
index + + ;
result [ index ] = ' \\ ' ;
index + + ;
continue ;
} else if ( content [ i ] = = ' \b ' ) {
result [ index ] = ' \\ ' ;
index + + ;
result [ index ] = ' b ' ;
index + + ;
continue ;
} else if ( content [ i ] = = ' \f ' ) {
result [ index ] = ' \\ ' ;
index + + ;
result [ index ] = ' f ' ;
index + + ;
continue ;
} else if ( content [ i ] = = ' " ' ) {
result [ index ] = ' \\ ' ;
index + + ;
result [ index ] = ' " ' ;
index + + ;
continue ;
}
result [ index ] = content [ i ] ;
index + + ;
result [ index ] = 0 ;
}
}
int rstrip_whitespace ( char * input , char * output ) {
output [ 0 ] = 0 ;
int count = 0 ;
size_t len = strlen ( input ) ;
for ( size_t i = 0 ; i < len ; i + + ) {
if ( input [ i ] = = ' \t ' | | input [ i ] = = ' ' | | input [ i ] = = ' \n ' ) {
continue ;
}
count = i ;
size_t j ;
for ( j = 0 ; j < len - count ; j + + ) {
output [ j ] = input [ j + count ] ;
}
output [ j ] = ' \0 ' ;
break ;
}
return count ;
}
/*
* Converts "pony" to \"pony\". Addslashes does not
* Converts "pony\npony" to "pony\n"
* "pony"
*/
void rstrtocstring ( const char * input , char * output ) {
int index = 0 ;
char clean_input [ strlen ( input ) * 2 ] ;
char * iptr = clean_input ;
rstraddslashes ( input , clean_input ) ;
output [ index ] = ' " ' ;
index + + ;
while ( * iptr ) {
if ( * iptr = = ' " ' ) {
output [ index ] = ' \\ ' ;
output + + ;
} else if ( * iptr = = ' \\ ' & & * ( iptr + 1 ) = = ' n ' ) {
output [ index ] = ' \\ ' ;
output + + ;
output [ index ] = ' n ' ;
output + + ;
output [ index ] = ' " ' ;
output + + ;
output [ index ] = ' \n ' ;
output + + ;
output [ index ] = ' " ' ;
output + + ;
iptr + + ;
iptr + + ;
continue ;
}
output [ index ] = * iptr ;
index + + ;
iptr + + ;
}
if ( output [ index - 1 ] = = ' " ' & & output [ index - 2 ] = = ' \n ' ) {
output [ index - 1 ] = 0 ;
} else if ( output [ index - 1 ] ! = ' " ' ) {
output [ index ] = ' " ' ;
output [ index + 1 ] = 0 ;
}
}
size_t rstrtokline ( char * input , char * output , size_t offset , bool strip_nl ) {
size_t len = strlen ( input ) ;
output [ 0 ] = 0 ;
size_t new_offset = 0 ;
size_t j ;
size_t index = 0 ;
for ( j = offset ; j < len + offset ; j + + ) {
if ( input [ j ] = = 0 ) {
index + + ;
break ;
}
index = j - offset ;
output [ index ] = input [ j ] ;
if ( output [ index ] = = ' \n ' ) {
index + + ;
break ;
}
}
output [ index ] = 0 ;
new_offset = index + offset ;
if ( strip_nl ) {
if ( output [ index - 1 ] = = ' \n ' ) {
output [ index - 1 ] = 0 ;
}
}
return new_offset ;
}
void rstrjoin ( char * * lines , size_t count , char * glue , char * output ) {
output [ 0 ] = 0 ;
for ( size_t i = 0 ; i < count ; i + + ) {
strcat ( output , lines [ i ] ) ;
if ( i ! = count - 1 )
strcat ( output , glue ) ;
}
}
int rstrsplit ( char * input , char * * lines ) {
int index = 0 ;
size_t offset = 0 ;
char line [ 1024 ] ;
while ( ( offset = rstrtokline ( input , line , offset , false ) ) & & * line ) {
if ( ! * line ) {
break ;
}
lines [ index ] = ( char * ) malloc ( strlen ( line ) + 1 ) ;
strcpy ( lines [ index ] , line ) ;
index + + ;
}
return index ;
}
bool rstartswithnumber ( char * str ) { return isdigit ( str [ 0 ] ) ; }
void rstrmove2 ( char * str , unsigned int start , size_t length , unsigned int new_pos ) {
size_t str_len = strlen ( str ) ;
char new_str [ str_len + 1 ] ;
memset ( new_str , 0 , str_len ) ;
if ( start < new_pos ) {
strncat ( new_str , str + length , str_len - length - start ) ;
new_str [ new_pos ] = 0 ;
strncat ( new_str , str + start , length ) ;
strcat ( new_str , str + strlen ( new_str ) ) ;
memset ( str , 0 , str_len ) ;
strcpy ( str , new_str ) ;
} else {
strncat ( new_str , str + start , length ) ;
strncat ( new_str , str , start ) ;
strncat ( new_str , str + start + length , str_len - start ) ;
memset ( str , 0 , str_len ) ;
strcpy ( str , new_str ) ;
}
new_str [ str_len ] = 0 ;
}
void rstrmove ( char * str , unsigned int start , size_t length , unsigned int new_pos ) {
size_t str_len = strlen ( str ) ;
if ( start > = str_len | | new_pos > = str_len | | start + length > str_len ) {
return ;
}
char temp [ length + 1 ] ;
strncpy ( temp , str + start , length ) ;
temp [ length ] = 0 ;
if ( start < new_pos ) {
memmove ( str + start , str + start + length , new_pos - start ) ;
strncpy ( str + new_pos - length + 1 , temp , length ) ;
} else {
memmove ( str + new_pos + length , str + new_pos , start - new_pos ) ;
strncpy ( str + new_pos , temp , length ) ;
}
}
int cmp_line ( const void * left , const void * right ) {
char * l = * ( char * * ) left ;
char * r = * ( char * * ) right ;
char lstripped [ strlen ( l ) + 1 ] ;
rstrip_whitespace ( l , lstripped ) ;
char rstripped [ strlen ( r ) + 1 ] ;
rstrip_whitespace ( r , rstripped ) ;
double d1 , d2 ;
bool found_d1 = rstrextractdouble ( lstripped , & d1 ) ;
bool found_d2 = rstrextractdouble ( rstripped , & d2 ) ;
if ( found_d1 & & found_d2 ) {
double frac_part1 ;
double int_part1 ;
frac_part1 = modf ( d1 , & int_part1 ) ;
double frac_part2 ;
double int_part2 ;
frac_part2 = modf ( d2 , & int_part2 ) ;
if ( d1 = = d2 ) {
return strcmp ( lstripped , rstripped ) ;
} else if ( frac_part1 & & frac_part2 ) {
return d1 > d2 ;
} else if ( frac_part1 & & ! frac_part2 ) {
return 1 ;
} else if ( frac_part2 & & ! frac_part1 ) {
return - 1 ;
} else if ( ! frac_part1 & & ! frac_part2 ) {
return d1 > d2 ;
}
}
return 0 ;
}
int rstrsort ( char * input , char * output ) {
char * * lines = ( char * * ) malloc ( strlen ( input ) * 10 ) ;
int line_count = rstrsplit ( input , lines ) ;
qsort ( lines , line_count , sizeof ( char * ) , cmp_line ) ;
rstrjoin ( lines , line_count , " " , output ) ;
for ( int i = 0 ; i < line_count ; i + + ) {
free ( lines [ i ] ) ;
}
free ( lines ) ;
return line_count ;
}
# endif
# include <assert.h>
# include <ctype.h>
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
typedef enum rliza_type_t {
RLIZA_STRING = ' s ' ,
RLIZA_BOOLEAN = ' b ' ,
RLIZA_NUMBER = ' n ' ,
RLIZA_OBJECT = ' o ' ,
RLIZA_ARRAY = ' a ' ,
RLIZA_NULL = 0 ,
RLIZA_KEY = ' k ' ,
RLIZA_INTEGER = ' i '
} rliza_type_t ;
typedef struct rliza_t {
rliza_type_t type ;
struct rliza_t * value ;
char * key ;
union {
char * string ;
bool boolean ;
double number ;
struct rliza_t * * map ;
long long integer ;
} content ;
unsigned int count ;
char * ( * get_string ) ( struct rliza_t * , char * ) ;
long long ( * get_integer ) ( struct rliza_t * , char * ) ;
double ( * get_number ) ( struct rliza_t * , char * ) ;
bool ( * get_boolean ) ( struct rliza_t * , char * ) ;
struct rliza_t * ( * get_array ) ( struct rliza_t * , char * ) ;
struct rliza_t * ( * get_object ) ( struct rliza_t * , char * ) ;
void ( * set_string ) ( struct rliza_t * , char * , char * ) ;
void ( * set_integer ) ( struct rliza_t * , char * , long long ) ;
void ( * set_number ) ( struct rliza_t * , char * , double ) ;
void ( * set_boolean ) ( struct rliza_t * , char * , bool ) ;
void ( * set_array ) ( struct rliza_t * self , char * key , struct rliza_t * array ) ;
void ( * set_object ) ( struct rliza_t * self , char * key , struct rliza_t * object ) ;
} rliza_t ;
void rliza_free ( rliza_t * rliza ) {
if ( rliza - > key ) {
free ( rliza - > key ) ;
rliza - > key = NULL ;
}
if ( rliza - > value ) {
rliza_free ( rliza - > value ) ;
rliza - > value = NULL ;
}
// if (rliza->content.array) {
// printf("JAAAA\n");
// }
// if (rliza->content.object) {
// rliza_free(rliza->content.object);
// rliza->content.object = NULL;
//}
if ( rliza - > type = = RLIZA_STRING ) {
if ( rliza - > content . string ) {
free ( rliza - > content . string ) ;
rliza - > content . string = NULL ;
// else if (rliza->type == RLIZA_NUMBER) {
// printf("STDring freed\n");
}
} else if ( rliza - > type = = RLIZA_OBJECT | | rliza - > type = = RLIZA_ARRAY ) {
if ( rliza - > content . map ) {
for ( unsigned int i = 0 ; i < rliza - > count ; i + + ) {
rliza_free ( rliza - > content . map [ i ] ) ;
}
free ( rliza - > content . map ) ;
}
}
// free(rliza->content.array);
//}
free ( rliza ) ;
}
rliza_t * rliza_new ( rliza_type_t type ) ;
rliza_t * rliza_new_string ( char * string ) ;
rliza_t * rliza_new_null ( ) ;
rliza_t * rliza_new_boolean ( bool value ) ;
rliza_t * rliza_new_number ( double value ) ;
rliza_t * rliza_new_integer ( long long value ) ;
rliza_t * rliza_new_key_value ( char * key , rliza_t * value ) ;
rliza_t * rliza_new_key_string ( char * key , char * string ) ;
rliza_t * rliza_new_key_bool ( char * key , bool value ) ;
rliza_t * rliza_new_key_number ( char * key , double value ) ;
void rliza_push ( rliza_t * self , rliza_t * obj ) ;
void rliza_push_object ( rliza_t * self , rliza_t * object ) ;
void rliza_set_object ( rliza_t * self , char * key , rliza_t * object ) ;
void rliza_set_string ( rliza_t * self , char * key , char * string ) ;
void rliza_set_boolean ( rliza_t * self , char * key , bool value ) ;
void rliza_set_number ( rliza_t * self , char * key , double value ) ;
void rliza_set_integer ( rliza_t * self , char * key , long long value ) ;
char * rliza_get_string ( rliza_t * self , char * key ) ;
long long rliza_get_integer ( rliza_t * self , char * key ) ;
double rliza_get_number ( rliza_t * self , char * key ) ;
bool rliza_get_boolean ( rliza_t * self , char * key ) ;
rliza_t * rliza_get_array ( rliza_t * self , char * key ) ;
rliza_t * rliza_get_object ( rliza_t * self , char * key ) ;
void rliza_set_array ( rliza_t * self , char * key , rliza_t * array ) ;
char * rliza_dumps ( rliza_t * rliza ) ;
rliza_t * rliza_loads ( char * * content ) ;
rliza_t * _rliza_loads ( char * * content ) ;
char * rliza_get_string ( rliza_t * self , char * key ) {
for ( unsigned int i = 0 ; i < self - > count ; i + + ) {
if ( self - > content . map [ i ] - > key ! = NULL & & strcmp ( self - > content . map [ i ] - > key , key ) = = 0 ) {
if ( self - > content . map [ i ] - > type = = RLIZA_STRING | | self - > content . map [ i ] - > type = = RLIZA_NULL ) {
return self - > content . map [ i ] - > content . string ;
}
}
}
return NULL ;
}
long long rliza_get_integer ( rliza_t * self , char * key ) {
for ( unsigned int i = 0 ; i < self - > count ; i + + ) {
if ( self - > content . map [ i ] - > key ! = NULL & & strcmp ( self - > content . map [ i ] - > key , key ) = = 0 ) {
if ( self - > content . map [ i ] - > type = = RLIZA_INTEGER | | self - > content . map [ i ] - > type = = RLIZA_NULL ) {
return self - > content . map [ i ] - > content . integer ;
}
}
}
return 0 ;
}
double rliza_get_number ( rliza_t * self , char * key ) {
for ( unsigned int i = 0 ; i < self - > count ; i + + ) {
if ( self - > content . map [ i ] - > key ! = NULL & & strcmp ( self - > content . map [ i ] - > key , key ) = = 0 ) {
if ( self - > content . map [ i ] - > type = = RLIZA_NUMBER | | self - > content . map [ i ] - > type = = RLIZA_NULL ) {
return self - > content . map [ i ] - > content . number ;
}
}
}
return 0 ;
}
bool rliza_get_boolean ( rliza_t * self , char * key ) {
for ( unsigned int i = 0 ; i < self - > count ; i + + ) {
if ( self - > content . map [ i ] - > key ! = NULL & & strcmp ( self - > content . map [ i ] - > key , key ) = = 0 ) {
if ( self - > content . map [ i ] - > type = = RLIZA_BOOLEAN | | self - > content . map [ i ] - > type = = RLIZA_NULL ) {
return self - > content . map [ i ] - > content . boolean ;
}
}
}
return false ;
}
rliza_t * rliza_get_object ( rliza_t * self , char * key ) {
for ( unsigned int i = 0 ; i < self - > count ; i + + ) {
if ( self - > content . map [ i ] - > key ! = NULL & & strcmp ( self - > content . map [ i ] - > key , key ) = = 0 ) {
return self - > content . map [ i ] ;
}
}
return NULL ;
}
rliza_t * rliza_get_array ( rliza_t * self , char * key ) {
for ( unsigned int i = 0 ; i < self - > count ; i + + ) {
if ( self - > content . map [ i ] - > key ! = NULL & & strcmp ( self - > content . map [ i ] - > key , key ) = = 0 ) {
if ( self - > content . map [ i ] - > type = = RLIZA_ARRAY | | self - > content . map [ i ] - > type = = RLIZA_NULL ) {
return self - > content . map [ i ] ;
}
}
}
return NULL ;
}
rliza_t * rliza_new_null ( ) {
rliza_t * rliza = rliza_new ( RLIZA_NULL ) ;
return rliza ;
}
rliza_t * rliza_new_string ( char * string ) {
rliza_t * rliza = rliza_new ( RLIZA_STRING ) ;
if ( string = = NULL ) {
rliza - > type = RLIZA_NULL ;
rliza - > content . string = NULL ;
return rliza ;
} else {
rliza - > content . string = strdup ( string ) ;
}
return rliza ;
}
rliza_t * rliza_new_boolean ( bool value ) {
rliza_t * rliza = rliza_new ( RLIZA_BOOLEAN ) ;
rliza - > content . boolean = value ;
return rliza ;
}
rliza_t * rliza_new_number ( double value ) {
rliza_t * rliza = rliza_new ( RLIZA_NUMBER ) ;
rliza - > content . number = value ;
return rliza ;
}
rliza_t * rliza_new_integer ( long long value ) {
rliza_t * rliza = rliza_new ( RLIZA_INTEGER ) ;
rliza - > content . integer = value ;
return rliza ;
}
rliza_t * rliza_new_key_array ( char * key ) {
rliza_t * rliza = rliza_new ( RLIZA_ARRAY ) ;
rliza - > key = strdup ( key ) ;
return rliza ;
}
rliza_t * rliza_new_key_value ( char * key , rliza_t * value ) {
rliza_t * rliza = rliza_new ( RLIZA_OBJECT ) ;
if ( key ) {
rliza - > key = strdup ( key ) ;
}
rliza - > value = value ;
return rliza ;
}
rliza_t * rliza_new_key_string ( char * key , char * string ) {
rliza_t * rliza = rliza_new_key_value ( key , rliza_new_string ( string ) ) ;
return rliza ;
}
rliza_t * rliza_new_key_bool ( char * key , bool value ) {
rliza_t * rliza = rliza_new_key_value ( key , rliza_new_boolean ( value ) ) ;
return rliza ;
}
rliza_t * rliza_new_key_number ( char * key , double value ) {
rliza_t * rliza = rliza_new_key_value ( key , rliza_new_number ( value ) ) ;
return rliza ;
}
void rliza_set_null ( rliza_t * self , char * key ) {
rliza_t * obj = rliza_get_object ( self , key ) ;
if ( ! obj ) {
obj = rliza_new_null ( ) ;
obj - > key = strdup ( key ) ;
rliza_push_object ( self , obj ) ;
}
if ( obj - > type = = RLIZA_OBJECT ) {
rliza_free ( obj - > value ) ;
obj - > value = NULL ;
} else if ( obj - > type = = RLIZA_STRING ) {
if ( obj - > content . string )
free ( obj - > content . string ) ;
obj - > content . string = NULL ;
} else if ( obj - > type = = RLIZA_ARRAY ) {
for ( unsigned int i = 0 ; i < obj - > count ; i + + ) {
rliza_free ( obj - > content . map [ i ] ) ;
}
} else if ( obj - > type = = RLIZA_NUMBER ) {
obj - > content . number = 0 ;
} else if ( obj - > type = = RLIZA_INTEGER ) {
obj - > content . integer = 0 ;
}
obj - > type = RLIZA_NULL ;
}
rliza_t * rliza_duplicate ( rliza_t * rliza ) {
if ( ! rliza )
return NULL ;
char * str = rliza_dumps ( rliza ) ;
char * strp = str ;
rliza_t * obj = rliza_loads ( & strp ) ;
free ( str ) ;
return obj ;
}
rliza_t * rliza_new_object ( rliza_t * obj ) {
rliza_t * rliza = rliza_new ( RLIZA_OBJECT ) ;
rliza - > value = obj ;
return rliza ;
}
void rliza_set_object ( rliza_t * self , char * key , rliza_t * value ) {
rliza_t * obj = rliza_duplicate ( value ) ;
obj - > key = strdup ( key ) ;
obj - > type = RLIZA_OBJECT ;
rliza_push ( self , obj ) ;
}
void rliza_set_string ( rliza_t * self , char * key , char * string ) {
rliza_t * obj = rliza_get_object ( self , key ) ;
if ( ! obj ) {
obj = rliza_new_string ( string ) ;
obj - > key = strdup ( key ) ;
obj - > type = RLIZA_STRING ;
rliza_push_object ( self , obj ) ;
} else {
obj - > content . string = strdup ( string ) ;
}
}
void rliza_set_array ( rliza_t * self , char * key , rliza_t * array ) {
rliza_t * obj = rliza_get_object ( self , key ) ;
if ( obj )
rliza_free ( obj ) ;
if ( array - > key ) {
free ( array - > key ) ;
array - > key = strdup ( key ) ;
}
rliza_push_object ( self , array ) ;
}
void rliza_set_number ( rliza_t * self , char * key , double value ) {
rliza_t * obj = rliza_get_object ( self , key ) ;
if ( ! obj ) {
obj = rliza_new_number ( value ) ;
obj - > key = strdup ( key ) ;
obj - > type = RLIZA_NUMBER ;
rliza_push_object ( self , obj ) ;
} else {
obj - > content . number = value ;
}
}
void rliza_push_object ( rliza_t * self , rliza_t * object ) {
self - > content . map = realloc ( self - > content . map , ( sizeof ( rliza_t * * ) ) * ( self - > count + 1 ) ) ;
self - > content . map [ self - > count ] = object ;
self - > count + + ;
}
void rliza_set_integer ( rliza_t * self , char * key , long long value ) {
rliza_t * obj = rliza_get_object ( self , key ) ;
if ( ! obj ) {
obj = rliza_new_integer ( value ) ;
obj - > key = strdup ( key ) ;
obj - > type = RLIZA_INTEGER ;
rliza_push_object ( self , obj ) ;
} else {
obj - > content . integer = value ;
}
}
void rliza_set_boolean ( rliza_t * self , char * key , bool value ) {
rliza_t * obj = rliza_get_object ( self , key ) ;
if ( ! obj ) {
obj = rliza_new_boolean ( value ) ;
obj - > key = strdup ( key ) ;
obj - > type = RLIZA_BOOLEAN ;
rliza_push_object ( self , obj ) ;
} else {
obj - > content . boolean = value ;
}
}
rliza_t * rliza_new ( rliza_type_t type ) {
rliza_t * rliza = ( rliza_t * ) calloc ( 1 , sizeof ( rliza_t ) ) ;
rliza - > type = type ;
rliza - > get_boolean = rliza_get_boolean ;
rliza - > get_integer = rliza_get_integer ;
rliza - > get_number = rliza_get_number ;
rliza - > get_string = rliza_get_string ;
rliza - > get_array = rliza_get_array ;
rliza - > get_object = rliza_get_object ;
rliza - > set_string = rliza_set_string ;
rliza - > set_number = rliza_set_number ;
rliza - > set_boolean = rliza_set_boolean ;
rliza - > set_integer = rliza_set_integer ;
rliza - > set_array = rliza_set_array ;
rliza - > set_object = rliza_set_object ;
return rliza ;
}
void * rliza_coalesce ( void * result , void * default_value ) {
if ( result = = NULL )
return default_value ;
return result ;
}
char * rliza_seek_string ( char * * content , char * * options ) {
while ( * * content = = ' ' | | * * content = = ' \n ' | | * * content = = ' \t ' | | * * content = = ' \r ' ) {
( * content ) + + ;
}
if ( * * content = = 0 ) {
return NULL ;
}
char * option = NULL ;
unsigned int option_index = 0 ;
while ( true ) {
option = options [ option_index ] ;
if ( option = = NULL )
break ;
option_index + + ;
if ( option [ 0 ] = = ' d ' ) {
if ( * * content > = ' 0 ' & & * * content < = ' 9 ' ) {
return ( char * ) * content ;
}
} else if ( ! strncmp ( option , * content , strlen ( option ) ) ) {
return ( char * ) * content ;
}
}
return * content ;
}
char * rliza_extract_quotes ( char * * content ) {
rbuffer_t * buffer = rbuffer_new ( NULL , 0 ) ;
assert ( * * content = = ' " ' ) ;
char previous = 0 ;
while ( true ) {
( * content ) + + ;
if ( ! * * content ) {
rbuffer_free ( buffer ) ;
return NULL ;
}
if ( * * content = = ' " ' & & previous ! = ' \\ ' ) {
break ;
}
rbuffer_push ( buffer , * * content ) ;
previous = * * content ;
}
assert ( * * content = = ' " ' ) ;
( * content ) + + ;
rbuffer_push ( buffer , 0 ) ;
char * result = ( char * ) rbuffer_to_string ( buffer ) ;
return result ;
}
rliza_t * _rliza_loads ( char * * content ) {
static char * seek_for1 [ ] = { " [ " , " { " , " \" " , " d " , " true " , " false " , " null " , NULL } ;
char * token = ( char * ) rliza_seek_string ( content , seek_for1 ) ;
if ( ! token )
return NULL ;
rliza_t * rliza = rliza_new ( RLIZA_NULL ) ;
if ( * * content = = ' " ' ) {
char * extracted = rliza_extract_quotes ( content ) ;
if ( ! extracted ) {
rliza_free ( rliza ) ;
return NULL ;
}
// char *extracted_with_slashes = (char *)malloc(strlen((char *)extracted) * 2 + 1);
// rstraddslashes(extracted, extracted_with_slashes);
rliza - > type = RLIZA_STRING ;
rliza - > content . string = extracted ; // extracted_with_slashes; // extracted_without_slashes;
// free(extracted);
return rliza ;
} else if ( * * content = = ' { ' ) {
rliza - > type = RLIZA_OBJECT ;
( * content ) + + ;
char * result = NULL ;
static char * seek_for2 [ ] = { " \" " , " , " , " } " , NULL } ;
while ( ( result = ( char * ) rliza_seek_string ( content , seek_for2 ) ) ! = NULL & & * result ) {
if ( ! * * content ) {
rliza_free ( rliza ) ;
return NULL ;
}
if ( * * content = = ' , ' ) {
( * content ) + + ;
if ( ! * * content ) {
rliza_free ( rliza ) ;
return NULL ;
}
continue ;
}
char * key = NULL ;
if ( * * content = = ' " ' ) {
key = rliza_extract_quotes ( ( char * * ) content ) ;
if ( ! key | | ! * key ) {
rliza_free ( rliza ) ;
return NULL ;
}
char * escaped_key = ( char * ) malloc ( strlen ( ( char * ) key ) * 2 + 1 ) ;
rstrstripslashes ( ( char * ) key , escaped_key ) ;
static char * seek_for3 [ ] = { " : " , NULL } ;
char * devider = rliza_seek_string ( content , seek_for3 ) ;
if ( ! devider | | ! * devider ) {
free ( escaped_key ) ;
free ( key ) ;
rliza_free ( rliza ) ;
return NULL ;
}
( * content ) + + ;
if ( ! * * content ) {
free ( key ) ;
free ( escaped_key ) ;
rliza_free ( rliza ) ;
return NULL ;
}
rliza_t * value = _rliza_loads ( content ) ;
if ( ! value ) {
free ( key ) ;
free ( escaped_key ) ;
rliza_free ( rliza ) ;
return NULL ;
}
if ( value - > key )
free ( value - > key ) ;
value - > key = escaped_key ;
free ( key ) ;
rliza_push_object ( rliza , value ) ;
} else if ( * * content = = ' } ' ) {
break ;
} else {
// Parse error
rliza_free ( rliza ) ;
return NULL ;
}
} ;
if ( ( * * content ! = ' } ' ) ) {
rliza_free ( rliza ) ;
return NULL ;
}
( * content ) + + ;
return rliza ;
} else if ( * * content = = ' [ ' ) {
rliza - > type = RLIZA_ARRAY ;
( * content ) + + ;
char * result ;
static char * seek_for4 [ ] = { " [ " , " { " , " \" " , " d " , " , " , " ] " , " null " , " true " , " false " , NULL } ;
while ( ( result = ( char * ) rliza_seek_string ( content , seek_for4 ) ) ! = NULL & & * result ) {
if ( * * content = = ' , ' ) {
( * content ) + + ;
} else if ( * * content = = ' ] ' ) {
break ;
}
rliza_t * obj = _rliza_loads ( content ) ;
if ( ! obj ) {
rliza_free ( rliza ) ;
return NULL ;
}
rliza_push ( rliza , obj ) ;
if ( ! * * content ) {
rliza_free ( rliza ) ;
return NULL ;
}
}
if ( * * content ! = ' ] ' ) {
rliza_free ( rliza ) ;
return NULL ;
}
( * content ) + + ;
return rliza ;
} else if ( * * content > = ' 0 ' & & * * content < = ' 9 ' ) {
char * ptr = * content ;
bool is_decimal = false ;
while ( * * content ) {
if ( * * content = = ' . ' ) {
is_decimal = true ;
} else if ( ! isdigit ( * * content ) ) {
break ;
}
( * content ) + + ;
}
if ( * ( * content - 1 ) = = ' . ' ) {
rliza_free ( rliza ) ;
return NULL ;
}
if ( ! * * content ) {
rliza_free ( rliza ) ;
return NULL ;
}
if ( is_decimal ) {
rliza - > type = RLIZA_NUMBER ;
rliza - > content . number = strtod ( ptr , NULL ) ;
} else {
rliza - > type = RLIZA_INTEGER ;
rliza - > content . integer = strtoll ( ptr , NULL , 10 ) ;
}
return rliza ;
} else if ( ! strncmp ( * content , " true " , 4 ) ) {
rliza - > type = RLIZA_BOOLEAN ;
rliza - > content . boolean = true ;
* content + = 4 ;
return rliza ;
} else if ( ! strncmp ( * content , " false " , 5 ) ) {
rliza - > type = RLIZA_BOOLEAN ;
rliza - > content . boolean = false ;
* content + = 5 ;
return rliza ;
} else if ( ! strncmp ( * content , " null " , 4 ) ) {
rliza - > type = RLIZA_NULL ;
* content + = 4 ;
return rliza ;
}
// Parsing error
rliza_free ( rliza ) ;
return NULL ;
}
rliza_t * rliza_loads ( char * * content ) {
if ( ! content | | ! * * content ) {
return NULL ;
}
char * original_content = * content ;
rliza_t * result = _rliza_loads ( content ) ;
if ( ! result ) {
* content = original_content ;
}
return result ;
}
char * rliza_dumps ( rliza_t * rliza ) {
size_t size = 4096 ;
char * content = ( char * ) calloc ( size , sizeof ( char ) ) ;
content [ 0 ] = 0 ;
if ( rliza - > type = = RLIZA_INTEGER ) {
if ( rliza - > key ) {
sprintf ( content , " \" %s \" :%lld " , rliza - > key , rliza - > content . integer ) ;
} else {
sprintf ( content , " %lld " , rliza - > content . integer ) ;
}
} else if ( rliza - > type = = RLIZA_STRING ) {
// char *escaped_string = (char *)calloc(strlen((char *)rliza->content.string) * 2 + 1024,sizeof(char));
char * escaped_string = rliza - > content . string ;
// rstrstripslashes((char *)rliza->content.string, escaped_string);
size_t min_size = strlen ( ( char * ) escaped_string ) + ( rliza - > key ? strlen ( rliza - > key ) : 0 ) + 1024 ;
if ( size < min_size ) {
size = min_size + 1 ;
content = realloc ( content , size ) ;
}
if ( rliza - > key ) {
char * escaped_key = ( char * ) malloc ( strlen ( ( char * ) rliza - > key ) * 2 + 20 ) ;
rstrstripslashes ( ( char * ) rliza - > key , escaped_key ) ;
if ( strlen ( content ) > size ) {
size = size + strlen ( escaped_string ) + 20 ;
content = realloc ( content , size ) ;
}
sprintf ( content , " \" %s \" : \" %s \" " , escaped_key , escaped_string ) ;
free ( escaped_key ) ;
} else {
size = size + strlen ( escaped_string ) + 20 ;
content = realloc ( content , size ) ;
sprintf ( content , " \" %s \" " , escaped_string ) ;
}
// free(escaped_string);
} else if ( rliza - > type = = RLIZA_NUMBER ) {
if ( rliza - > key ) {
sprintf ( content , " \" %s \" :%f " , rliza - > key , rliza - > content . number ) ;
} else {
sprintf ( content , " %f " , rliza - > content . number ) ;
}
int last_zero = 0 ;
bool beyond_dot = false ;
for ( size_t i = 0 ; i < strlen ( content ) ; i + + ) {
if ( content [ i ] = = ' . ' ) {
beyond_dot = true ;
} else if ( beyond_dot = = true ) {
if ( content [ i - 1 ] ! = ' . ' ) {
if ( content [ i ] = = ' 0 ' ) {
if ( ! last_zero )
last_zero = i ;
} else {
last_zero = 0 ;
}
}
}
}
if ( last_zero ! = 0 ) {
content [ last_zero ] = 0 ;
}
} else if ( rliza - > type = = RLIZA_BOOLEAN ) {
if ( rliza - > key ) {
sprintf ( content , " \" %s \" :%s " , rliza - > key , rliza - > content . boolean ? " true " : " false " ) ;
} else {
sprintf ( content , " %s " , rliza - > content . boolean ? " true " : " false " ) ;
}
} else if ( rliza - > type = = RLIZA_OBJECT ) {
strcat ( content , " { " ) ;
if ( rliza - > key ) {
strcat ( content , " \" " ) ;
strcat ( content , rliza - > key ) ;
strcat ( content , " \" :{ " ) ;
}
// bool add_braces = false;
for ( unsigned i = 0 ; i < rliza - > count ; i + + ) {
char * content_chunk = rliza_dumps ( rliza - > content . map [ i ] ) ;
char * content_chunk_stripped = content_chunk ;
if ( * content_chunk_stripped = = ' { ' ) {
content_chunk_stripped + + ;
content_chunk_stripped [ strlen ( content_chunk_stripped ) - 1 ] = 0 ;
}
if ( strlen ( content_chunk_stripped ) + strlen ( content ) > size ) {
size + = strlen ( content_chunk_stripped ) + 20 ;
content = realloc ( content , size ) ;
}
strcat ( content , content_chunk_stripped ) ;
free ( content_chunk ) ;
strcat ( content , " , " ) ;
}
if ( content [ strlen ( content ) - 1 ] = = ' , ' ) {
content [ strlen ( content ) - 1 ] = ' \0 ' ;
if ( rliza - > key ) {
strcat ( content , " } " ) ;
}
}
strcat ( content , " } " ) ;
} else if ( rliza - > type = = RLIZA_ARRAY ) {
if ( rliza - > key ) {
char * escaped_key = ( char * ) malloc ( strlen ( ( char * ) rliza - > key ) * 2 + 1 ) ;
rstraddslashes ( ( char * ) rliza - > key , escaped_key ) ;
if ( strlen ( escaped_key ) > size ) {
size = strlen ( escaped_key ) + 10 ;
content = realloc ( content , size ) ;
}
sprintf ( content , " \" %s \" :[ " , escaped_key ) ;
free ( escaped_key ) ;
} else
strcpy ( content , " [ " ) ;
for ( unsigned i = 0 ; i < rliza - > count ; i + + ) {
char * content_chunk = rliza_dumps ( rliza - > content . map [ i ] ) ;
char * content_chunk_stripped = content_chunk ;
if ( * content_chunk_stripped = = ' { ' ) {
// content_chunk_stripped++;
// content_chunk_stripped[strlen(content_chunk_stripped) - 1] = 0;
}
if ( strlen ( content_chunk_stripped ) + strlen ( content ) > size ) {
size + = strlen ( content_chunk_stripped ) + 20 ;
content = realloc ( content , size ) ;
}
strcat ( content , content_chunk_stripped ) ;
free ( content_chunk ) ;
strcat ( content , " , " ) ;
}
if ( content [ strlen ( content ) - 1 ] ! = ' [ ' )
content [ strlen ( content ) - 1 ] = 0 ;
strcat ( content , " ] " ) ;
} else if ( rliza - > type = = RLIZA_NULL ) {
if ( rliza - > key ) {
char * escaped_key = ( char * ) malloc ( strlen ( ( char * ) rliza - > key ) * 2 + 1 ) ;
rstraddslashes ( ( char * ) rliza - > key , escaped_key ) ;
sprintf ( content , " \" %s \" :null " , escaped_key ) ;
free ( escaped_key ) ;
} else
strcpy ( content , " null " ) ;
}
return content ;
}
void rliza_dumpss ( rliza_t * rliza ) {
char * output = rliza_dumps ( rliza ) ;
printf ( " %s \n " , output ) ;
free ( output ) ;
}
void rliza_push ( rliza_t * self , rliza_t * obj ) { rliza_push_object ( self , obj ) ; }
int rliza_validate ( char * json_content ) {
if ( ! json_content | | ! * json_content ) {
return false ;
}
char * json_contentp = json_content ;
rliza_t * to_object = _rliza_loads ( & json_contentp ) ;
if ( to_object ) {
rliza_free ( to_object ) ;
return json_contentp - json_content ;
}
return false ;
}
# endif
# ifndef RCOV_H
# define RCOV_H
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
# ifndef RBENCH_H
# define RBENCH_H
# ifndef RPRINT_H
# define RPRINT_H
# ifndef RLIB_TIME
# define RLIB_TIME
# ifndef _POSIX_C_SOURCE_199309L
# define _POSIX_C_SOURCE_199309L
# endif
# include <sys/time.h>
# include <time.h>
# undef _POSIX_C_SOURCE_199309L
# include <errno.h>
# include <stdint.h>
# include <stdio.h>
# include <string.h>
# ifndef CLOCK_MONOTONIC
# define CLOCK_MONOTONIC 1
# endif
typedef uint64_t nsecs_t ;
void nsleep ( nsecs_t nanoseconds ) ;
void tick ( ) { nsleep ( 1 ) ; }
typedef unsigned long long msecs_t ;
nsecs_t nsecs ( ) {
unsigned int lo , hi ;
__asm__ volatile ( " rdtsc " : " =a " ( lo ) , " =d " ( hi ) ) ;
return ( ( uint64_t ) hi < < 32 ) | lo ;
}
msecs_t rnsecs_to_msecs ( nsecs_t nsecs ) { return nsecs / 1000 / 1000 ; }
nsecs_t rmsecs_to_nsecs ( msecs_t msecs ) { return msecs * 1000 * 1000 ; }
msecs_t usecs ( ) {
struct timeval tv ;
gettimeofday ( & tv , NULL ) ;
return ( long long ) ( tv . tv_sec ) * 1000000 + ( long long ) ( tv . tv_usec ) ;
}
msecs_t msecs ( ) {
struct timeval tv ;
gettimeofday ( & tv , NULL ) ;
return ( long long ) ( tv . tv_sec ) * 1000 + ( tv . tv_usec / 1000 ) ;
}
char * msecs_strs ( msecs_t ms ) {
static char str [ 22 ] ;
str [ 0 ] = 0 ;
sprintf ( str , " %f " , ms * 0.001 ) ;
for ( int i = strlen ( str ) ; i > 0 ; i - - ) {
if ( str [ i ] > ' 0 ' )
break ;
str [ i ] = 0 ;
}
return str ;
}
char * msecs_strms ( msecs_t ms ) {
static char str [ 22 ] ;
str [ 0 ] = 0 ;
sprintf ( str , " %lld " , ms ) ;
return str ;
}
char * msecs_str ( long long ms ) {
static char result [ 30 ] ;
result [ 0 ] = 0 ;
if ( ms > 999 ) {
char * s = msecs_strs ( ms ) ;
sprintf ( result , " %ss " , s ) ;
} else {
char * s = msecs_strms ( ms ) ;
sprintf ( result , " %sMs " , s ) ;
}
return result ;
}
void nsleep ( nsecs_t nanoseconds ) {
long seconds = 0 ;
int factor = 0 ;
while ( nanoseconds > 1000000000 ) {
factor + + ;
nanoseconds = nanoseconds / 10 ;
}
if ( factor ) {
seconds = 1 ;
factor - - ;
while ( factor ) {
seconds = seconds * 10 ;
factor - - ;
}
}
struct timespec req = { seconds , nanoseconds } ;
struct timespec rem ;
nanosleep ( & req , & rem ) ;
}
void ssleep ( double s ) {
long nanoseconds = ( long ) ( 1000000000 * s ) ;
// long seconds = 0;
// struct timespec req = {seconds, nanoseconds};
// struct timespec rem;
nsleep ( nanoseconds ) ;
}
void msleep ( long miliseonds ) {
long nanoseconds = miliseonds * 1000000 ;
nsleep ( nanoseconds ) ;
}
char * format_time ( int64_t nanoseconds ) {
char output [ 1024 ] ;
size_t output_size = sizeof ( output ) ;
output [ 0 ] = 0 ;
if ( nanoseconds < 1000 ) {
// Less than 1 microsecond
snprintf ( output , output_size , " %ldns " , nanoseconds ) ;
} else if ( nanoseconds < 1000000 ) {
// Less than 1 millisecond
double us = nanoseconds / 1000.0 ;
snprintf ( output , output_size , " %.2fµs " , us ) ;
} else if ( nanoseconds < 1000000000 ) {
// Less than 1 second
double ms = nanoseconds / 1000000.0 ;
snprintf ( output , output_size , " %.2fms " , ms ) ;
} else {
// 1 second or more
double s = nanoseconds / 1000000000.0 ;
if ( s > 60 * 60 ) {
s = s / 60 / 60 ;
snprintf ( output , output_size , " %.2fh " , s ) ;
} else if ( s > 60 ) {
s = s / 60 ;
snprintf ( output , output_size , " %.2fm " , s ) ;
} else {
snprintf ( output , output_size , " %.2fs " , s ) ;
}
}
return sbuf ( output ) ;
}
# endif
# include <stdarg.h>
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
long rpline_number = 0 ;
nsecs_t rprtime = 0 ;
int8_t _env_rdisable_colors = - 1 ;
bool _rprint_enable_colors = true ;
bool rprint_is_color_enabled ( ) {
if ( _env_rdisable_colors = = - 1 ) {
_env_rdisable_colors = getenv ( " RDISABLE_COLORS " ) ! = NULL ;
}
if ( _env_rdisable_colors ) {
_rprint_enable_colors = false ;
}
return _rprint_enable_colors ;
}
void rprint_disable_colors ( ) { _rprint_enable_colors = false ; }
void rprint_enable_colors ( ) { _rprint_enable_colors = true ; }
void rprint_toggle_colors ( ) { _rprint_enable_colors = ! _rprint_enable_colors ; }
void rclear ( ) { printf ( " \033 [2J " ) ; }
void rprintpf ( FILE * f , const char * prefix , const char * format , va_list args ) {
char * pprefix = ( char * ) prefix ;
char * pformat = ( char * ) format ;
bool reset_color = false ;
bool press_any_key = false ;
char new_format [ 4096 ] ;
bool enable_color = rprint_is_color_enabled ( ) ;
memset ( new_format , 0 , 4096 ) ;
int new_format_length = 0 ;
char temp [ 1000 ] ;
memset ( temp , 0 , 1000 ) ;
if ( enable_color & & pprefix [ 0 ] ) {
strcat ( new_format , pprefix ) ;
new_format_length + = strlen ( pprefix ) ;
reset_color = true ;
}
while ( true ) {
if ( pformat [ 0 ] = = ' \\ ' & & pformat [ 1 ] = = ' i ' ) {
strcat ( new_format , " \ e[3m " ) ;
new_format_length + = strlen ( " \ e[3m " ) ;
reset_color = true ;
pformat + + ;
pformat + + ;
} else if ( pformat [ 0 ] = = ' \\ ' & & pformat [ 1 ] = = ' u ' ) {
strcat ( new_format , " \ e[4m " ) ;
new_format_length + = strlen ( " \ e[4m " ) ;
reset_color = true ;
pformat + + ;
pformat + + ;
} else if ( pformat [ 0 ] = = ' \\ ' & & pformat [ 1 ] = = ' b ' ) {
strcat ( new_format , " \ e[1m " ) ;
new_format_length + = strlen ( " \ e[1m " ) ;
reset_color = true ;
pformat + + ;
pformat + + ;
} else if ( pformat [ 0 ] = = ' \\ ' & & pformat [ 1 ] = = ' C ' ) {
press_any_key = true ;
rpline_number + + ;
pformat + + ;
pformat + + ;
reset_color = false ;
} else if ( pformat [ 0 ] = = ' \\ ' & & pformat [ 1 ] = = ' k ' ) {
press_any_key = true ;
rpline_number + + ;
pformat + + ;
pformat + + ;
} else if ( pformat [ 0 ] = = ' \\ ' & & pformat [ 1 ] = = ' c ' ) {
rpline_number + + ;
strcat ( new_format , " \ e[2J \ e[H " ) ;
new_format_length + = strlen ( " \ e[2J \ e[H " ) ;
pformat + + ;
pformat + + ;
} else if ( pformat [ 0 ] = = ' \\ ' & & pformat [ 1 ] = = ' L ' ) {
rpline_number + + ;
temp [ 0 ] = 0 ;
sprintf ( temp , " %ld " , rpline_number ) ;
strcat ( new_format , temp ) ;
new_format_length + = strlen ( temp ) ;
pformat + + ;
pformat + + ;
} else if ( pformat [ 0 ] = = ' \\ ' & & pformat [ 1 ] = = ' l ' ) {
rpline_number + + ;
temp [ 0 ] = 0 ;
sprintf ( temp , " %.5ld " , rpline_number ) ;
strcat ( new_format , temp ) ;
new_format_length + = strlen ( temp ) ;
pformat + + ;
pformat + + ;
} else if ( pformat [ 0 ] = = ' \\ ' & & pformat [ 1 ] = = ' T ' ) {
nsecs_t nsecs_now = nsecs ( ) ;
nsecs_t end = rprtime ? nsecs_now - rprtime : 0 ;
temp [ 0 ] = 0 ;
sprintf ( temp , " %s " , format_time ( end ) ) ;
strcat ( new_format , temp ) ;
new_format_length + = strlen ( temp ) ;
rprtime = nsecs_now ;
pformat + + ;
pformat + + ;
} else if ( pformat [ 0 ] = = ' \\ ' & & pformat [ 1 ] = = ' t ' ) {
rprtime = nsecs ( ) ;
pformat + + ;
pformat + + ;
} else {
new_format [ new_format_length ] = * pformat ;
new_format_length + + ;
if ( ! * pformat )
break ;
// printf("%c",*pformat);
pformat + + ;
}
}
if ( reset_color ) {
strcat ( new_format , " \ e[0m " ) ;
new_format_length + = strlen ( " \ e[0m " ) ;
}
new_format [ new_format_length ] = 0 ;
vfprintf ( f , new_format , args ) ;
fflush ( stdout ) ;
if ( press_any_key ) {
nsecs_t s = nsecs ( ) ;
fgetc ( stdin ) ;
rprtime + = nsecs ( ) - s ;
}
}
void rprintp ( const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
rprintpf ( stdout , " " , format , args ) ;
va_end ( args ) ;
}
void rprintf ( FILE * f , const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
rprintpf ( f , " " , format , args ) ;
va_end ( args ) ;
}
void rprint ( const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
rprintpf ( stdout , " " , format , args ) ;
va_end ( args ) ;
}
# define printf rprint
// Print line
void rprintlf ( FILE * f , const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
rprintpf ( f , " \\ l " , format , args ) ;
va_end ( args ) ;
}
void rprintl ( const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
rprintpf ( stdout , " \\ l " , format , args ) ;
va_end ( args ) ;
}
// Black
void rprintkf ( FILE * f , const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
rprintpf ( f , " \ e[30m " , format , args ) ;
va_end ( args ) ;
}
void rprintk ( const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
rprintpf ( stdout , " \ e[30m " , format , args ) ;
va_end ( args ) ;
}
// Red
void rprintrf ( FILE * f , const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
rprintpf ( f , " \ e[31m " , format , args ) ;
va_end ( args ) ;
}
void rprintr ( const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
rprintpf ( stdout , " \ e[31m " , format , args ) ;
va_end ( args ) ;
}
// Green
void rprintgf ( FILE * f , const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
rprintpf ( f , " \ e[32m " , format , args ) ;
va_end ( args ) ;
}
void rprintg ( const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
rprintpf ( stdout , " \ e[32m " , format , args ) ;
va_end ( args ) ;
}
// Yellow
void rprintyf ( FILE * f , const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
rprintpf ( f , " \ e[33m " , format , args ) ;
va_end ( args ) ;
}
void rprinty ( const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
rprintpf ( stdout , " \ e[33m " , format , args ) ;
va_end ( args ) ;
}
// Blue
void rprintbf ( FILE * f , const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
rprintpf ( f , " \ e[34m " , format , args ) ;
va_end ( args ) ;
}
void rprintb ( const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
rprintpf ( stdout , " \ e[34m " , format , args ) ;
va_end ( args ) ;
}
// Magenta
void rprintmf ( FILE * f , const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
rprintpf ( f , " \ e[35m " , format , args ) ;
va_end ( args ) ;
}
void rprintm ( const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
rprintpf ( stdout , " \ e[35m " , format , args ) ;
va_end ( args ) ;
}
// Cyan
void rprintcf ( FILE * f , const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
rprintpf ( f , " \ e[36m " , format , args ) ;
va_end ( args ) ;
}
void rprintc ( const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
rprintpf ( stdout , " \ e[36m " , format , args ) ;
va_end ( args ) ;
}
// White
void rprintwf ( FILE * f , const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
rprintpf ( f , " \ e[37m " , format , args ) ;
va_end ( args ) ;
}
void rprintw ( const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
rprintpf ( stdout , " \ e[37m " , format , args ) ;
va_end ( args ) ;
}
# endif
# include <errno.h>
# include <stdbool.h>
# include <stdint.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys/time.h>
# include <time.h>
# ifndef RLIB_TERMINAL_H
# define RLIB_TERMINAL_H
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# ifndef RTEST_H
# define RTEST_H
# ifndef REMO_H
# define REMO_H
# include <ctype.h>
# include <stdbool.h>
# include <stdio.h>
# include <string.h>
typedef struct {
const char * str ;
const char * description ;
} remo_t ;
remo_t remo [ ] = {
{ " \U0001F600 " , " Grinning Face " } , // 😀
{ " \U0001F601 " , " Beaming Face with Smiling Eyes " } , // 😁
{ " \U0001F602 " , " Face with Tears of Joy " } , // 😂
{ " \U0001F923 " , " Rolling on the Floor Laughing " } , // 🤣
{ " \U0001F603 " , " Grinning Face with Big Eyes " } , // 😃
{ " \U0001F604 " , " Grinning Face with Smiling Eyes " } , // 😄
{ " \U0001F609 " , " Winking Face " } , // 😉
{ " \U0001F60A " , " Smiling Face with Smiling Eyes " } , // 😊
{ " \U0001F60D " , " Smiling Face with Heart-Eyes " } , // 😍
{ " \U0001F618 " , " Face Blowing a Kiss " } , // 😘
{ " \U0001F617 " , " Kissing Face " } , // 😗
{ " \U0001F61A " , " Kissing Face with Closed Eyes " } , // 😚
{ " \U0001F642 " , " Slightly Smiling Face " } , // 🙂
{ " \U0001F643 " , " Upside-Down Face " } , // 🙃
{ " \U0001F970 " , " Smiling Face with Hearts " } , // 🥰
{ " \U0001F60B " , " Face Savoring Food " } , // 😋
{ " \U0001F61B " , " Face with Tongue " } , // 😛
{ " \U0001F61C " , " Winking Face with Tongue " } , // 😜
{ " \U0001F92A " , " Zany Face " } , // 🤪
{ " \U0001F929 " , " Star-Struck " } , // 🤩
{ " \U0001F631 " , " Face Screaming in Fear " } , // 😱
{ " \U0001F62D " , " Loudly Crying Face " } , // 😭
{ " \U0001F624 " , " Face with Steam From Nose " } , // 😤
{ " \U0001F620 " , " Angry Face " } , // 😠
{ " \U0001F621 " , " Pouting Face " } , // 😡
{ " \U0001F47B " , " Ghost " } , // 👻
{ " \U0001F480 " , " Skull " } , // 💀
{ " \U0001F4A9 " , " Pile of Poo " } , // 💩
{ " \U0001F47D " , " Alien " } , // 👽
// Geometric Shapes
{ " \U000025A0 " , " Black Square " } , // ■
{ " \U000025B2 " , " Upward Triangle " } , // ▲
{ " \U000025CF " , " Black Circle " } , // ●
{ " \U000025CB " , " White Circle " } , // ○
{ " \U00002B1B " , " Large Black Square " } , // ⬛
{ " \U00002B1C " , " Large White Square " } , // ⬜
// Mathematical Symbols
{ " \U00002200 " , " For All " } , // ∀
{ " \U00002203 " , " Exists " } , // ∃
{ " \U00002205 " , " Empty Set " } , // ∅
{ " \U00002207 " , " Nabla " } , // ∇
{ " \U0000220F " , " N-Ary Product " } , // ∏
{ " \U00002212 " , " Minus Sign " } , // −
{ " \U0000221E " , " Infinity " } , // ∞
// Arrows
{ " \U00002190 " , " Left Arrow " } , // ←
{ " \U00002191 " , " Up Arrow " } , // ↑
{ " \U00002192 " , " Right Arrow " } , // →
{ " \U00002193 " , " Down Arrow " } , // ↓
{ " \U00002195 " , " Up Down Arrow " } , // ↕
{ " \U00002197 " , " Up Right Arrow " } , // ↗
{ " \U00002198 " , " Down Right Arrow " } , // ↘
{ " \U000027A1 " , " Black Right Arrow " } , // ➡️
// Dingbats
{ " \U00002714 " , " Check Mark " } , // ✔️
{ " \U00002716 " , " Heavy Multiplication X " } , // ✖️
{ " \U00002728 " , " Sparkles " } , // ✨
{ " \U00002757 " , " Exclamation Mark " } , // ❗
{ " \U0000274C " , " Cross Mark " } , // ❌
{ " \U00002795 " , " Heavy Plus Sign " } , // ➕
// Miscellaneous Symbols
{ " \U00002600 " , " Sun " } , // ☀️
{ " \U00002614 " , " Umbrella with Rain Drops " } , // ☔
{ " \U00002620 " , " Skull and Crossbones " } , // ☠️
{ " \U000026A0 " , " Warning Sign " } , // ⚠️
{ " \U000026BD " , " Soccer Ball " } , // ⚽
{ " \U000026C4 " , " Snowman " } , // ⛄
// Stars and Asterisks
{ " \U00002733 " , " Eight Pointed Black Star " } , // ✳️
{ " \U00002734 " , " Eight Spoked Asterisk " } , // ✴️
{ " \U00002B50 " , " White Star " } , // ⭐
{ " \U0001F31F " , " Glowing Star " } , // 🌟
{ " \U00002728 " , " Sparkles " } , // ✨
// Animals and Nature
{ " \U0001F98A " , " Fox " } , // 🦊
{ " \U0001F415 " , " Dog " } , // 🐕
{ " \U0001F431 " , " Cat Face " } , // 🐱
{ " \U0001F435 " , " Monkey Face " } , // 🐵
{ " \U0001F408 " , " Black Cat " } , // 🐈
{ " \U0001F98C " , " Deer " } , // 🦌
{ " \U0001F344 " , " Mushroom " } , // 🍄
{ " \U0001F333 " , " Tree " } , // 🌳
// Weather and Space Symbols
{ " \U0001F308 " , " Rainbow " } , // 🌈
{ " \U0001F320 " , " Shooting Star " } , // 🌠
{ " \U00002600 " , " Sun " } , // ☀️
{ " \U00002601 " , " Cloud " } , // ☁️
{ " \U000026A1 " , " High Voltage " } , // ⚡
{ " \U0001F525 " , " Fire " } , // 🔥
{ " \U000026C4 " , " Snowman " } , // ⛄
{ " \U0001F30A " , " Water Wave " } , // 🌊
// Transport and Map Symbols
{ " \U0001F68C " , " Bus " } , // 🚌
{ " \U0001F697 " , " Car " } , // 🚗
{ " \U0001F6B2 " , " Bicycle " } , // 🚲
{ " \U0001F6A2 " , " Ship " } , // 🚢
{ " \U0001F681 " , " Helicopter " } , // 🚁
{ " \U0001F680 " , " Rocket " } , // 🚀
{ " \U0001F6EB " , " Airplane " } , // 🛫
// Currency Symbols
{ " \U00000024 " , " Dollar Sign " } , // $
{ " \U000000A3 " , " Pound Sign " } , // £
{ " \U000000A5 " , " Yen Sign " } , // ¥
{ " \U000020AC " , " Euro Sign " } , // €
{ " \U0001F4B5 " , " Dollar Banknote " } , // 💵
{ " \U0001F4B4 " , " Yen Banknote " } , // 💴
// Card Suits
{ " \U00002660 " , " Black Spade Suit " } , // ♠️
{ " \U00002663 " , " Black Club Suit " } , // ♣️
{ " \U00002665 " , " Black Heart Suit " } , // ♥️
{ " \U00002666 " , " Black Diamond Suit " } , // ♦️
{ " \U0001F0CF " , " Joker Card " } , // 🃏
// Office Supplies and Objects
{ " \U0001F4DA " , " Books " } , // 📚
{ " \U0001F4D7 " , " Green Book " } , // 📗
{ " \U0001F4C8 " , " Chart with Upwards Trend " } , // 📈
{ " \U0001F4C9 " , " Chart with Downwards Trend " } , // 📉
{ " \U0001F4B0 " , " Money Bag " } , // 💰
{ " \U0001F4B8 " , " Money with Wings " } , // 💸
{ " \U0001F4E6 " , " Package " } , // 📦
// Miscellaneous Symbols
{ " \U00002757 " , " Exclamation Mark " } , // ❗
{ " \U00002714 " , " Check Mark " } , // ✔️
{ " \U0000274C " , " Cross Mark " } , // ❌
{ " \U00002705 " , " Check Mark Button " } , // ✅
{ " \U00002B50 " , " White Star " } , // ⭐
{ " \U0001F31F " , " Glowing Star " } , // 🌟
{ " \U0001F4A1 " , " Light Bulb " } , // 💡
{ " \U0001F4A3 " , " Bomb " } , // 💣
{ " \U0001F4A9 " , " Pile of Poo " } , // 💩
// Musical Symbols
{ " \U0001F3B5 " , " Musical Note " } , // 🎵
{ " \U0001F3B6 " , " Multiple Musical Notes " } , // 🎶
{ " \U0001F3BC " , " Musical Score " } , // 🎼
{ " \U0001F399 " , " Studio Microphone " } , // 🎙️
{ " \U0001F3A4 " , " Microphone " } , // 🎤
// Food and Drink
{ " \U0001F35F " , " Cheese Wedge " } , // 🧀
{ " \U0001F355 " , " Slice of Pizza " } , // 🍕
{ " \U0001F32D " , " Taco " } , // 🌮
{ " \U0001F37D " , " Beer Mug " } , // 🍻
{ " \U0001F96B " , " Cup with Straw " } , // 🥤
{ " \U0001F32E " , " Hot Pepper " } , // 🌶️
{ " \U0001F95A " , " Potato " } , // 🥔
// Zodiac Signs
{ " \U00002600 " , " Aries " } , // ♈
{ " \U00002601 " , " Taurus " } , // ♉
{ " \U00002602 " , " Gemini " } , // ♊
{ " \U00002603 " , " Cancer " } , // ♋
{ " \U00002604 " , " Leo " } , // ♌
{ " \U00002605 " , " Virgo " } , // ♍
{ " \U00002606 " , " Libra " } , // ♎
{ " \U00002607 " , " Scorpio " } , // ♏
{ " \U00002608 " , " Sagittarius " } , // ♐
{ " \U00002609 " , " Capricorn " } , // ♑
{ " \U0000260A " , " Aquarius " } , // ♒
{ " \U0000260B " , " Pisces " } , // ♓
// Miscellaneous Shapes
{ " \U0001F4C8 " , " Chart Increasing " } , // 📈
{ " \U0001F4C9 " , " Chart Decreasing " } , // 📉
{ " \U0001F4CA " , " Bar Chart " } , // 📊
{ " \U0001F7E6 " , " Orange Circle " } , // 🟠
{ " \U0001F7E7 " , " Yellow Circle " } , // 🟡
{ " \U0001F7E8 " , " Green Circle " } , // 🟢
{ " \U0001F7E9 " , " Blue Circle " } , // 🔵
{ " \U0001F7EA " , " Purple Circle " } , // 🟣
// Flags
{ " \U0001F1E6 \U0001F1E9 " , " Flag of France " } , // 🇫🇷
{ " \U0001F1E8 \U0001F1E6 " , " Flag of Germany " } , // 🇩🇪
{ " \U0001F1FA \U0001F1F8 " , " Flag of United States " } , // 🇺🇸
{ " \U0001F1E7 \U0001F1F7 " , " Flag of Canada " } , // 🇨🇦
{ " \U0001F1EE \U0001F1F2 " , " Flag of Italy " } , // 🇮🇹
{ " \U0001F1F8 \U0001F1EC " , " Flag of Australia " } , // 🇦🇺
{ " \U0001F1F3 \U0001F1F4 " , " Flag of Spain " } , // 🇪🇸
// Additional Miscellaneous Symbols
{ " \U0001F4A5 " , " Collision " } , // 💥
{ " \U0001F4A6 " , " Sweat Droplets " } , // 💦
{ " \U0001F4A8 " , " Dashing Away " } , // 💨
{ " \U0001F50B " , " Battery " } , // 🔋
{ " \U0001F4BB " , " Laptop Computer " } , // 💻
{ " \U0001F4DE " , " Telephone " } , // 📞
{ " \U0001F4E7 " , " Incoming Envelope " } , // 📧
} ;
size_t remo_count = sizeof ( remo ) / sizeof ( remo [ 0 ] ) ;
void rstrtolower ( const char * input , char * output ) {
while ( * input ) {
* output = tolower ( * input ) ;
input + + ;
output + + ;
}
* output = 0 ;
}
bool rstrinstr ( const char * haystack , const char * needle ) {
char lower1 [ strlen ( haystack ) + 1 ] ;
char lower2 [ strlen ( needle ) + 1 ] ;
rstrtolower ( haystack , lower1 ) ;
rstrtolower ( needle , lower2 ) ;
return strstr ( lower1 , lower2 ) ? true : false ;
}
void remo_print ( ) {
for ( size_t i = 0 ; i < remo_count ; i + + ) {
printf ( " %s - %s \n " , remo [ i ] . str , remo [ i ] . description ) ;
}
}
const char * remo_get ( char * name ) {
for ( size_t i = 0 ; i < remo_count ; i + + ) {
if ( rstrinstr ( remo [ i ] . description , name ) ) {
return remo [ i ] . str ;
}
}
return NULL ;
}
# endif
# include <stdbool.h>
# include <stdio.h>
# include <unistd.h>
# define debug(fmt, ...) printf("%s:%d: " fmt, __FILE__, __LINE__, __VA_ARGS__);
char * rcurrent_banner ;
int rassert_count = 0 ;
unsigned short rtest_is_first = 1 ;
unsigned int rtest_fail_count = 0 ;
int rtest_end ( char * content ) {
// Returns application exit code. 0 == success
printf ( " %s " , content ) ;
printf ( " \n @assertions: %d \n " , rassert_count ) ;
printf ( " @memory: %s%s \n " , rmalloc_stats ( ) , rmalloc_count = = 0 ? remo_get ( " rainbow " ) : " fire " ) ;
if ( rmalloc_count ! = 0 ) {
printf ( " MEMORY ERROR %s \n " , remo_get ( " cross mark " ) ) ;
return rtest_fail_count > 0 ;
}
return rtest_fail_count > 0 ;
}
void rtest_test_banner ( char * content , char * file ) {
if ( rtest_is_first = = 1 ) {
char delimiter [ ] = " . " ;
char * d = delimiter ;
char f [ 2048 ] ;
strcpy ( f , file ) ;
printf ( " %s tests " , strtok ( f , d ) ) ;
rtest_is_first = 0 ;
setvbuf ( stdout , NULL , _IONBF , 0 ) ;
}
printf ( " \n - %s " , content ) ;
}
bool rtest_test_true_silent ( char * expr , int res , int line ) {
rassert_count + + ;
if ( res ) {
return true ;
}
rprintrf ( stderr , " \n ERROR on line %d: %s " , line , expr ) ;
rtest_fail_count + + ;
return false ;
}
bool rtest_test_true ( char * expr , int res , int line ) {
rassert_count + + ;
if ( res ) {
fprintf ( stdout , " %s " , remo_get ( " Slightly Smiling Face " ) ) ;
return true ;
}
rprintrf ( stderr , " \n ERROR %s on line %d: %s \n " , remo_get ( " skull " ) , line , expr ) ;
rtest_fail_count + + ;
return false ;
}
bool rtest_test_false_silent ( char * expr , int res , int line ) { return rtest_test_true_silent ( expr , ! res , line ) ; }
bool rtest_test_false ( char * expr , int res , int line ) { return rtest_test_true ( expr , ! res , line ) ; }
void rtest_test_skip ( char * expr , int line ) { rprintgf ( stderr , " \n @skip(%s) on line %d \n " , expr , line ) ; }
void rtest_test_assert ( char * expr , int res , int line ) {
if ( rtest_test_true ( expr , res , line ) ) {
return ;
}
rtest_end ( " " ) ;
exit ( 40 ) ;
}
# define rtest_banner(content) \
rcurrent_banner = content; \
rtest_test_banner(content, __FILE__);
# define rtest_true(expr) rtest_test_true(#expr, expr, __LINE__);
# define rtest_assert(expr) \
{ \
int __valid = expr ? 1 : 0; \
rtest_test_true(#expr, __valid, __LINE__); \
}; \
;
# define rassert(expr) \
{ \
int __valid = expr ? 1 : 0; \
rtest_test_true(#expr, __valid, __LINE__); \
}; \
;
# define rtest_asserts(expr) \
{ \
int __valid = expr ? 1 : 0; \
rtest_test_true_silent(#expr, __valid, __LINE__); \
};
# define rasserts(expr) \
{ \
int __valid = expr ? 1 : 0; \
rtest_test_true_silent(#expr, __valid, __LINE__); \
};
# define rtest_false(expr) \
rprintf(" [%s]\t%s\t\n", expr == 0 ? "OK" : "NOK", #expr); \
assert_count++; \
assert(#expr);
# define rtest_skip(expr) rtest_test_skip(#expr, __LINE__);
FILE * rtest_create_file ( char * path , char * content ) {
FILE * fd = fopen ( path , " wb " ) ;
char c ;
int index = 0 ;
while ( ( c = content [ index ] ) ! = 0 ) {
fputc ( c , fd ) ;
index + + ;
}
fclose ( fd ) ;
fd = fopen ( path , " rb " ) ;
return fd ;
}
void rtest_delete_file ( char * path ) { unlink ( path ) ; }
# endif
char * rfcaptured = NULL ;
void rfcapture ( FILE * f , char * buff , size_t size ) {
rfcaptured = buff ;
setvbuf ( f , rfcaptured , _IOFBF , size ) ;
}
void rfstopcapture ( FILE * f ) { setvbuf ( f , 0 , _IOFBF , 0 ) ; }
bool _r_disable_stdout_toggle = false ;
FILE * _r_original_stdout = NULL ;
bool rr_enable_stdout ( ) {
if ( _r_disable_stdout_toggle )
return false ;
if ( ! _r_original_stdout ) {
stdout = fopen ( " /dev/null " , " rb " ) ;
return false ;
}
if ( _r_original_stdout & & _r_original_stdout ! = stdout ) {
fclose ( stdout ) ;
}
stdout = _r_original_stdout ;
return true ;
}
bool rr_disable_stdout ( ) {
if ( _r_disable_stdout_toggle ) {
return false ;
}
if ( _r_original_stdout = = NULL ) {
_r_original_stdout = stdout ;
}
if ( stdout = = _r_original_stdout ) {
stdout = fopen ( " /dev/null " , " rb " ) ;
return true ;
}
return false ;
}
bool rr_toggle_stdout ( ) {
if ( ! _r_original_stdout ) {
rr_disable_stdout ( ) ;
return true ;
} else if ( stdout ! = _r_original_stdout ) {
rr_enable_stdout ( ) ;
return true ;
} else {
rr_disable_stdout ( ) ;
return true ;
}
}
typedef struct rprogressbar_t {
unsigned long current_value ;
unsigned long min_value ;
unsigned long max_value ;
unsigned int length ;
bool changed ;
double percentage ;
unsigned int width ;
unsigned long draws ;
FILE * fout ;
} rprogressbar_t ;
rprogressbar_t * rprogressbar_new ( long min_value , long max_value , unsigned int width , FILE * fout ) {
rprogressbar_t * pbar = ( rprogressbar_t * ) malloc ( sizeof ( rprogressbar_t ) ) ;
pbar - > min_value = min_value ;
pbar - > max_value = max_value ;
pbar - > current_value = min_value ;
pbar - > width = width ;
pbar - > draws = 0 ;
pbar - > length = 0 ;
pbar - > changed = false ;
pbar - > fout = fout ? fout : stdout ;
return pbar ;
}
void rprogressbar_free ( rprogressbar_t * pbar ) { free ( pbar ) ; }
void rprogressbar_draw ( rprogressbar_t * pbar ) {
if ( ! pbar - > changed ) {
return ;
} else {
pbar - > changed = false ;
}
pbar - > draws + + ;
char draws_text [ 22 ] ;
draws_text [ 0 ] = 0 ;
sprintf ( draws_text , " %ld " , pbar - > draws ) ;
char * draws_textp = draws_text ;
// bool draws_text_len = strlen(draws_text);
char bar_begin_char = ' ' ;
char bar_progress_char = ' ' ;
char bar_empty_char = ' ' ;
char bar_end_char = ' ' ;
char content [ 4096 ] = { 0 } ;
char bar_content [ 1024 ] ;
char buff [ 2048 ] = { 0 } ;
bar_content [ 0 ] = ' \r ' ;
bar_content [ 1 ] = bar_begin_char ;
unsigned int index = 2 ;
for ( unsigned long i = 0 ; i < pbar - > length ; i + + ) {
if ( * draws_textp ) {
bar_content [ index ] = * draws_textp ;
draws_textp + + ;
} else {
bar_content [ index ] = bar_progress_char ;
}
index + + ;
}
char infix [ ] = " \033 [0m " ;
for ( unsigned long i = 0 ; i < strlen ( infix ) ; i + + ) {
bar_content [ index ] = infix [ i ] ;
index + + ;
}
for ( unsigned long i = 0 ; i < pbar - > width - pbar - > length ; i + + ) {
bar_content [ index ] = bar_empty_char ;
index + + ;
}
bar_content [ index ] = bar_end_char ;
bar_content [ index + 1 ] = ' \0 ' ;
sprintf ( buff , " \033 [43m%s \033 [0m \033 [33m%.2f%% \033 [0m " , bar_content , pbar - > percentage * 100 ) ;
strcat ( content , buff ) ;
if ( pbar - > width = = pbar - > length ) {
strcat ( content , " \r " ) ;
for ( unsigned long i = 0 ; i < pbar - > width + 10 ; i + + ) {
strcat ( content , " " ) ;
}
strcat ( content , " \r " ) ;
}
fprintf ( pbar - > fout , " %s " , content ) ;
fflush ( pbar - > fout ) ;
}
bool rprogressbar_update ( rprogressbar_t * pbar , unsigned long value ) {
if ( value = = pbar - > current_value ) {
return false ;
}
pbar - > current_value = value ;
pbar - > percentage = ( double ) pbar - > current_value / ( double ) ( pbar - > max_value - pbar - > min_value ) ;
unsigned long new_length = ( unsigned long ) ( pbar - > percentage * pbar - > width ) ;
pbar - > changed = new_length ! = pbar - > length ;
if ( pbar - > changed ) {
pbar - > length = new_length ;
rprogressbar_draw ( pbar ) ;
return true ;
}
return false ;
}
size_t rreadline ( char * data , size_t len , bool strip_ln ) {
__attribute__ ( ( unused ) ) char * unused = fgets ( data , len , stdin ) ;
size_t length = strlen ( data ) ;
if ( length & & strip_ln )
data [ length - 1 ] = 0 ;
return length ;
}
void rlib_test_progressbar ( ) {
rtest_banner ( " Progress bar " ) ;
rprogressbar_t * pbar = rprogressbar_new ( 0 , 1000 , 10 , stderr ) ;
rprogressbar_draw ( pbar ) ;
// No draws executed, nothing to show
rassert ( pbar - > draws = = 0 ) ;
rprogressbar_update ( pbar , 500 ) ;
rassert ( pbar - > percentage = = 0.5 ) ;
rprogressbar_update ( pbar , 500 ) ;
rprogressbar_update ( pbar , 501 ) ;
rprogressbar_update ( pbar , 502 ) ;
// Should only have drawn one time since value did change, but percentage
// did not
rassert ( pbar - > draws = = 1 ) ;
// Changed is false because update function calls draw
rassert ( pbar - > changed = = false ) ;
rprogressbar_update ( pbar , 777 ) ;
rassert ( pbar - > percentage = = 0.777 ) ;
rprogressbar_update ( pbar , 1000 ) ;
rassert ( pbar - > percentage = = 1 ) ;
}
# endif
# define RBENCH(times, action) \
{ \
unsigned long utimes = (unsigned long)times; \
nsecs_t start = nsecs(); \
for (unsigned long i = 0; i < utimes; i++) { \
{ \
action; \
} \
} \
nsecs_t end = nsecs(); \
printf("%s\n", format_time(end - start)); \
}
# define RBENCHP(times, action) \
{ \
printf("\n"); \
nsecs_t start = nsecs(); \
unsigned int prev_percentage = 0; \
unsigned long utimes = (unsigned long)times; \
for (unsigned long i = 0; i < utimes; i++) { \
unsigned int percentage = ((long double)i / (long double)times) * 100; \
int percentage_changed = percentage != prev_percentage; \
__attribute__((unused)) int first = i == 0; \
__attribute__((unused)) int last = i == utimes - 1; \
{ action; }; \
if (percentage_changed) { \
printf("\r%d%%", percentage); \
fflush(stdout); \
\
prev_percentage = percentage; \
} \
} \
nsecs_t end = nsecs(); \
printf("\r%s\n", format_time(end - start)); \
}
struct rbench_t ;
typedef struct rbench_function_t {
# ifdef __cplusplus
void ( * call ) ( ) ;
# else
void ( * call ) ;
# endif
char name [ 256 ] ;
char group [ 256 ] ;
void * arg ;
void * data ;
bool first ;
bool last ;
int argc ;
unsigned long times_executed ;
nsecs_t average_execution_time ;
nsecs_t total_execution_time ;
} rbench_function_t ;
typedef struct rbench_t {
unsigned int function_count ;
rbench_function_t functions [ 100 ] ;
rbench_function_t * current ;
rprogressbar_t * progress_bar ;
bool show_progress ;
int winner ;
bool stdout ;
unsigned long times ;
bool silent ;
nsecs_t execution_time ;
# ifdef __cplusplus
void ( * add_function ) ( struct rbench_t * r , const char * name , const char * group , void ( * ) ( ) ) ;
# else
void ( * add_function ) ( struct rbench_t * r , const char * name , const char * group , void * ) ;
# endif
void ( * rbench_reset ) ( struct rbench_t * r ) ;
struct rbench_t * ( * execute ) ( struct rbench_t * r , long times ) ;
struct rbench_t * ( * execute1 ) ( struct rbench_t * r , long times , void * arg1 ) ;
struct rbench_t * ( * execute2 ) ( struct rbench_t * r , long times , void * arg1 , void * arg2 ) ;
struct rbench_t * ( * execute3 ) ( struct rbench_t * r , long times , void * arg1 , void * arg2 , void * arg3 ) ;
} rbench_t ;
FILE * _rbench_stdout = NULL ;
FILE * _rbench_stdnull = NULL ;
void rbench_toggle_stdout ( rbench_t * r ) {
if ( ! r - > stdout ) {
if ( _rbench_stdout = = NULL ) {
_rbench_stdout = stdout ;
}
if ( _rbench_stdnull = = NULL ) {
_rbench_stdnull = fopen ( " /dev/null " , " wb " ) ;
}
if ( stdout = = _rbench_stdout ) {
stdout = _rbench_stdnull ;
} else {
stdout = _rbench_stdout ;
}
}
}
void rbench_restore_stdout ( rbench_t * r ) {
if ( r - > stdout )
return ;
if ( _rbench_stdout ) {
stdout = _rbench_stdout ;
}
if ( _rbench_stdnull ) {
fclose ( _rbench_stdnull ) ;
_rbench_stdnull = NULL ;
}
}
rbench_t * rbench_new ( ) ;
rbench_t * _rbench = NULL ;
rbench_function_t * rbf ;
rbench_t * rbench ( ) {
if ( _rbench = = NULL ) {
_rbench = rbench_new ( ) ;
}
return _rbench ;
}
typedef void * ( * rbench_call ) ( ) ;
typedef void * ( * rbench_call1 ) ( void * ) ;
typedef void * ( * rbench_call2 ) ( void * , void * ) ;
typedef void * ( * rbench_call3 ) ( void * , void * , void * ) ;
# ifdef __cplusplus
void rbench_add_function ( rbench_t * rp , const char * name , const char * group , void ( * call ) ( ) ) {
# else
void rbench_add_function ( rbench_t * rp , const char * name , const char * group , void * call ) {
# endif
rbench_function_t * f = & rp - > functions [ rp - > function_count ] ;
rp - > function_count + + ;
f - > average_execution_time = 0 ;
f - > total_execution_time = 0 ;
f - > times_executed = 0 ;
f - > call = call ;
strcpy ( f - > name , name ) ;
strcpy ( f - > group , group ) ;
}
void rbench_reset_function ( rbench_function_t * f ) {
f - > average_execution_time = 0 ;
f - > times_executed = 0 ;
f - > total_execution_time = 0 ;
}
void rbench_reset ( rbench_t * rp ) {
for ( unsigned int i = 0 ; i < rp - > function_count ; i + + ) {
rbench_reset_function ( & rp - > functions [ i ] ) ;
}
}
int rbench_get_winner_index ( rbench_t * r ) {
int winner = 0 ;
nsecs_t time = 0 ;
for ( unsigned int i = 0 ; i < r - > function_count ; i + + ) {
if ( time = = 0 | | r - > functions [ i ] . total_execution_time < time ) {
winner = i ;
time = r - > functions [ i ] . total_execution_time ;
}
}
return winner ;
}
bool rbench_was_last_function ( rbench_t * r ) {
for ( unsigned int i = 0 ; i < r - > function_count ; i + + ) {
if ( i = = r - > function_count - 1 & & r - > current = = & r - > functions [ i ] )
return true ;
}
return false ;
}
rbench_function_t * rbench_execute_prepare ( rbench_t * r , int findex , long times , int argc ) {
rbench_toggle_stdout ( r ) ;
if ( findex = = 0 ) {
r - > execution_time = 0 ;
}
rbench_function_t * rf = & r - > functions [ findex ] ;
rf - > argc = argc ;
rbf = rf ;
r - > current = rf ;
if ( r - > show_progress )
r - > progress_bar = rprogressbar_new ( 0 , times , 20 , stderr ) ;
r - > times = times ;
// printf(" %s:%s gets executed for %ld times with %d
// arguments.\n",rf->group, rf->name, times,argc);
rbench_reset_function ( rf ) ;
return rf ;
}
void rbench_execute_finish ( rbench_t * r ) {
rbench_toggle_stdout ( r ) ;
if ( r - > progress_bar ) {
free ( r - > progress_bar ) ;
r - > progress_bar = NULL ;
}
r - > current - > average_execution_time = r - > current - > total_execution_time / r - > current - > times_executed ;
;
// printf(" %s:%s finished executing in
// %s\n",r->current->group,r->current->name,
// format_time(r->current->total_execution_time));
// rbench_show_results_function(r->current);
if ( rbench_was_last_function ( r ) ) {
rbench_restore_stdout ( r ) ;
unsigned int winner_index = rbench_get_winner_index ( r ) ;
r - > winner = winner_index + 1 ;
if ( ! r - > silent )
rprintgf ( stderr , " Benchmark results: \n " ) ;
nsecs_t total_time = 0 ;
for ( unsigned int i = 0 ; i < r - > function_count ; i + + ) {
rbf = & r - > functions [ i ] ;
total_time + = rbf - > total_execution_time ;
bool is_winner = winner_index = = i ;
if ( is_winner ) {
if ( ! r - > silent )
rprintyf ( stderr , " > %s:%s:%s \n " , format_time ( rbf - > total_execution_time ) , rbf - > group , rbf - > name ) ;
} else {
if ( ! r - > silent )
rprintbf ( stderr , " %s:%s:%s \n " , format_time ( rbf - > total_execution_time ) , rbf - > group , rbf - > name ) ;
}
}
if ( ! r - > silent )
rprintgf ( stderr , " Total execution time: %s \n " , format_time ( total_time ) ) ;
}
rbench_restore_stdout ( r ) ;
rbf = NULL ;
r - > current = NULL ;
}
struct rbench_t * rbench_execute ( rbench_t * r , long times ) {
for ( unsigned int i = 0 ; i < r - > function_count ; i + + ) {
rbench_function_t * f = rbench_execute_prepare ( r , i , times , 0 ) ;
rbench_call c = ( rbench_call ) f - > call ;
nsecs_t start = nsecs ( ) ;
f - > first = true ;
c ( ) ;
f - > first = false ;
f - > last = false ;
f - > times_executed + + ;
for ( int j = 1 ; j < times ; j + + ) {
c ( ) ;
f - > times_executed + + ;
f - > last = f - > times_executed = = r - > times - 1 ;
if ( r - > progress_bar ) {
rprogressbar_update ( r - > progress_bar , f - > times_executed ) ;
}
}
f - > total_execution_time = nsecs ( ) - start ;
r - > execution_time + = f - > total_execution_time ;
rbench_execute_finish ( r ) ;
}
return r ;
}
struct rbench_t * rbench_execute1 ( rbench_t * r , long times , void * arg1 ) {
for ( unsigned int i = 0 ; i < r - > function_count ; i + + ) {
rbench_function_t * f = rbench_execute_prepare ( r , i , times , 1 ) ;
rbench_call1 c = ( rbench_call1 ) f - > call ;
nsecs_t start = nsecs ( ) ;
f - > first = true ;
c ( arg1 ) ;
f - > first = false ;
f - > last = false ;
f - > times_executed + + ;
for ( int j = 1 ; j < times ; j + + ) {
c ( arg1 ) ;
f - > times_executed + + ;
f - > last = f - > times_executed = = r - > times - 1 ;
if ( r - > progress_bar ) {
rprogressbar_update ( r - > progress_bar , f - > times_executed ) ;
}
}
f - > total_execution_time = nsecs ( ) - start ;
r - > execution_time + = f - > total_execution_time ;
rbench_execute_finish ( r ) ;
}
return r ;
}
struct rbench_t * rbench_execute2 ( rbench_t * r , long times , void * arg1 , void * arg2 ) {
for ( unsigned int i = 0 ; i < r - > function_count ; i + + ) {
rbench_function_t * f = rbench_execute_prepare ( r , i , times , 2 ) ;
rbench_call2 c = ( rbench_call2 ) f - > call ;
nsecs_t start = nsecs ( ) ;
f - > first = true ;
c ( arg1 , arg2 ) ;
f - > first = false ;
f - > last = false ;
f - > times_executed + + ;
for ( int j = 1 ; j < times ; j + + ) {
c ( arg1 , arg2 ) ;
f - > times_executed + + ;
f - > last = f - > times_executed = = r - > times - 1 ;
if ( r - > progress_bar ) {
rprogressbar_update ( r - > progress_bar , f - > times_executed ) ;
}
}
f - > total_execution_time = nsecs ( ) - start ;
r - > execution_time + = f - > total_execution_time ;
rbench_execute_finish ( r ) ;
}
return r ;
}
struct rbench_t * rbench_execute3 ( rbench_t * r , long times , void * arg1 , void * arg2 , void * arg3 ) {
for ( unsigned int i = 0 ; i < r - > function_count ; i + + ) {
rbench_function_t * f = rbench_execute_prepare ( r , i , times , 3 ) ;
rbench_call3 c = ( rbench_call3 ) f - > call ;
nsecs_t start = nsecs ( ) ;
f - > first = true ;
c ( arg1 , arg2 , arg3 ) ;
f - > first = false ;
f - > last = false ;
f - > times_executed + + ;
for ( int j = 1 ; j < times ; j + + ) {
c ( arg1 , arg2 , arg3 ) ;
f - > times_executed + + ;
f - > last = f - > times_executed = = r - > times - 1 ;
if ( r - > progress_bar ) {
rprogressbar_update ( r - > progress_bar , f - > times_executed ) ;
}
}
f - > total_execution_time = nsecs ( ) - start ;
rbench_execute_finish ( r ) ;
}
return r ;
}
rbench_t * rbench_new ( ) {
rbench_t * r = ( rbench_t * ) malloc ( sizeof ( rbench_t ) ) ;
memset ( r , 0 , sizeof ( rbench_t ) ) ;
r - > add_function = rbench_add_function ;
r - > rbench_reset = rbench_reset ;
r - > execute1 = rbench_execute1 ;
r - > execute2 = rbench_execute2 ;
r - > execute3 = rbench_execute3 ;
r - > execute = rbench_execute ;
r - > stdout = true ;
r - > silent = false ;
r - > winner = 0 ;
r - > show_progress = true ;
return r ;
}
void rbench_free ( rbench_t * r ) { free ( r ) ; }
# endif
bool check_lcov ( ) {
char buffer [ 1024 * 64 ] ;
FILE * fp ;
fp = popen ( " lcov --help " , " r " ) ;
if ( fp = = NULL ) {
return false ;
}
if ( fgets ( buffer , sizeof ( buffer ) , fp ) = = NULL ) {
return false ;
}
pclose ( fp ) ;
return strstr ( buffer , " lcov: not found " ) ? false : true ;
}
int rcov_main ( int argc , char * argv [ ] ) {
if ( argc < 2 ) {
printf ( " Usage: [source.c] \n " ) ;
return 1 ;
}
char argstr [ 4096 ] = { 0 } ;
for ( int i = 2 ; i < argc ; i + + ) {
strcat ( argstr , argv [ i ] ) ;
strcat ( argstr , " " ) ;
}
if ( ! check_lcov ( ) ) {
printf ( " lcov is not installed. Please execute `sudo apt install lcov`. \n " ) ;
return 1 ;
}
char * source_file = argv [ 1 ] ;
char * commands [ ] = { " rm -f *.gcda 2>/dev/null " ,
" rm -f *.gcno 2>/dev/null " ,
" rm -f %s.coverage.info 2>/dev/null " ,
" gcc -pg -fprofile-arcs -ftest-coverage -g -o %s_coverage.o %s " ,
" ./%s_coverage.o " ,
" lcov --capture --directory . --output-file %s.coverage.info " ,
" genhtml %s.coverage.info --output-directory /tmp/%s.coverage " ,
" rm -f *.gcda 2>/dev/null " ,
" rm -f *.gcno 2>/dev/null " ,
" rm -f %s.coverage.info 2>/dev/null " , //"cat gmon.out",
" gprof %s_coverage.o gmon.out > output.rcov_analysis " ,
" rm -f gmon.out " ,
" cat output.rcov_analysis " ,
" rm output.rcov_analysis " ,
" rm -f %s_coverage.o " ,
" google-chrome /tmp/%s.coverage/index.html " } ;
uint command_count = sizeof ( commands ) / sizeof ( commands [ 0 ] ) ;
RBENCH ( 1 , {
for ( uint i = 0 ; i < command_count ; i + + ) {
char * formatted_command = sbuf ( " " ) ;
sprintf ( formatted_command , commands [ i ] , source_file , source_file ) ;
// printf("%s\n", formatted_command);
if ( formatted_command [ 0 ] = = ' . ' & & formatted_command [ 1 ] = = ' / ' ) {
strcat ( formatted_command , " " ) ;
strcat ( formatted_command , argstr ) ;
}
if ( system ( formatted_command ) ) {
printf ( " `%s` returned non-zero code. \n " , formatted_command ) ;
}
} ) ;
}
return 0 ;
}
# endif
# ifndef RHTTP_H
# define RHTTP_H
# include <arpa/inet.h>
# include <pthread.h>
# include <signal.h>
# include <stdarg.h>
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <time.h>
# include <unistd.h>
# define BUFF_SIZE 8096
# define RHTTP_MAX_CONNECTIONS 100
int rhttp_opt_error = 1 ;
int rhttp_opt_warn = 1 ;
int rhttp_opt_info = 1 ;
int rhttp_opt_port = 8080 ;
int rhttp_opt_debug = 0 ;
int rhttp_opt_request_logging = 0 ;
int rhttp_sock = 0 ;
int rhttp_opt_buffered = 0 ;
int rhttp_c = 0 ;
int rhttp_c_mutex_initialized = 0 ;
pthread_mutex_t rhttp_c_mutex ;
char rhttp_opt_host [ 1024 ] = " 0.0.0.0 " ;
unsigned int rhttp_connections_handled = 0 ;
typedef struct rhttp_header_t {
char * name ;
char * value ;
struct rhttp_header_t * next ;
} rhttp_header_t ;
typedef struct rhttp_request_t {
int c ;
int closed ;
bool keep_alive ;
nsecs_t start ;
char * raw ;
char * line ;
char * body ;
char * method ;
char * path ;
char * version ;
void * context ;
unsigned int bytes_received ;
rhttp_header_t * headers ;
} rhttp_request_t ;
char * rhttp_current_timestamp ( ) {
time_t current_time ;
time ( & current_time ) ;
struct tm * local_time = localtime ( & current_time ) ;
static char time_string [ 100 ] ;
time_string [ 0 ] = 0 ;
strftime ( time_string , sizeof ( time_string ) , " %Y-%m-%d %H:%M:%S " , local_time ) ;
return time_string ;
}
void rhttp_logs ( const char * prefix , const char * level , const char * format , va_list args ) {
char buf [ strlen ( format ) + BUFSIZ + 1 ] ;
buf [ 0 ] = 0 ;
sprintf ( buf , " %s%s %s %s \ e[0m " , prefix , rhttp_current_timestamp ( ) , level , format ) ;
vfprintf ( stdout , buf , args ) ;
}
void rhttp_log_info ( const char * format , . . . ) {
if ( ! rhttp_opt_info )
return ;
va_list args ;
va_start ( args , format ) ;
rhttp_logs ( " \ e[32m " , " INFO " , format , args ) ;
va_end ( args ) ;
}
void rhttp_log_debug ( const char * format , . . . ) {
if ( ! rhttp_opt_debug )
return ;
va_list args ;
va_start ( args , format ) ;
if ( rhttp_opt_debug )
rhttp_logs ( " \ e[33m " , " DEBUG " , format , args ) ;
va_end ( args ) ;
}
void rhttp_log_warn ( const char * format , . . . ) {
if ( ! rhttp_opt_warn )
return ;
va_list args ;
va_start ( args , format ) ;
rhttp_logs ( " \ e[34m " , " WARN " , format , args ) ;
va_end ( args ) ;
}
void rhttp_log_error ( const char * format , . . . ) {
if ( ! rhttp_opt_error )
return ;
va_list args ;
va_start ( args , format ) ;
rhttp_logs ( " \ e[35m " , " ERROR " , format , args ) ;
va_end ( args ) ;
}
void http_request_init ( rhttp_request_t * r ) {
r - > raw = NULL ;
r - > line = NULL ;
r - > body = NULL ;
r - > method = NULL ;
r - > path = NULL ;
r - > version = NULL ;
r - > start = 0 ;
r - > headers = NULL ;
r - > bytes_received = 0 ;
r - > closed = 0 ;
}
void rhttp_free_header ( rhttp_header_t * h ) {
if ( ! h )
return ;
rhttp_header_t * next = h - > next ;
free ( h - > name ) ;
free ( h - > value ) ;
free ( h ) ;
if ( next )
rhttp_free_header ( next ) ;
}
void rhttp_rhttp_free_headers ( rhttp_request_t * r ) {
if ( ! r - > headers )
return ;
rhttp_free_header ( r - > headers ) ;
r - > headers = NULL ;
}
rhttp_header_t * rhttp_parse_headers ( rhttp_request_t * s ) {
int first = 1 ;
char * body = strdup ( s - > body ) ;
char * body_original = body ;
while ( body & & * body ) {
char * line = __strtok_r ( first ? body : NULL , " \r \n " , & body ) ;
if ( ! line )
break ;
rhttp_header_t * h = ( rhttp_header_t * ) malloc ( sizeof ( rhttp_header_t ) ) ;
h - > name = NULL ;
h - > value = NULL ;
h - > next = NULL ;
char * name = __strtok_r ( line , " : " , & line ) ;
first = 0 ;
if ( ! name ) {
rhttp_free_header ( h ) ;
break ;
}
h - > name = strdup ( name ) ;
char * value = __strtok_r ( NULL , " \r \n " , & line ) ;
if ( ! value ) {
rhttp_free_header ( h ) ;
break ;
}
h - > value = value ? strdup ( value + 1 ) : strdup ( " " ) ;
h - > next = s - > headers ;
s - > headers = h ;
}
free ( body_original ) ;
return s - > headers ;
}
void rhttp_free_request ( rhttp_request_t * r ) {
if ( r - > raw ) {
free ( r - > raw ) ;
free ( r - > body ) ;
free ( r - > method ) ;
free ( r - > path ) ;
free ( r - > version ) ;
rhttp_rhttp_free_headers ( r ) ;
}
free ( r ) ;
}
long rhttp_header_get_long ( rhttp_request_t * r , const char * name ) {
rhttp_header_t * h = r - > headers ;
while ( h ) {
if ( ! strcmp ( h - > name , name ) )
return strtol ( h - > value , NULL , 10 ) ;
h = h - > next ;
}
return - 1 ;
}
char * rhttp_header_get_string ( rhttp_request_t * r , const char * name ) {
rhttp_header_t * h = r - > headers ;
while ( h ) {
if ( ! strcmp ( h - > name , name ) )
return h - > value & & * h - > value ? h - > value : NULL ;
h = h - > next ;
}
return NULL ;
}
void rhttp_print_header ( rhttp_header_t * h ) { rhttp_log_debug ( " Header: <%s> \" %s \" \n " , h - > name , h - > value ) ; }
void rhttp_print_headers ( rhttp_header_t * h ) {
while ( h ) {
rhttp_print_header ( h ) ;
h = h - > next ;
}
}
void rhttp_print_request_line ( rhttp_request_t * r ) { rhttp_log_info ( " %s %s %s \n " , r - > method , r - > path , r - > version ) ; }
void rhttp_print_request ( rhttp_request_t * r ) {
rhttp_print_request_line ( r ) ;
if ( rhttp_opt_debug )
rhttp_print_headers ( r - > headers ) ;
}
void rhttp_close ( rhttp_request_t * r ) {
if ( ! r )
return ;
if ( ! r - > closed )
close ( r - > c ) ;
rhttp_free_request ( r ) ;
}
rhttp_request_t * rhttp_parse_request ( int s ) {
rhttp_request_t * request = ( rhttp_request_t * ) malloc ( sizeof ( rhttp_request_t ) ) ;
http_request_init ( request ) ;
char buf [ BUFF_SIZE ] = { 0 } ;
request - > c = s ;
int breceived = 0 ;
while ( ! rstrendswith ( buf , " \r \n \r \n " ) ) {
int chunk_size = read ( s , buf + breceived , 1 ) ;
if ( chunk_size < = 0 ) {
close ( request - > c ) ;
request - > closed = 1 ;
return request ;
}
breceived + = chunk_size ;
}
if ( breceived < = 0 ) {
close ( request - > c ) ;
request - > closed = 1 ;
return request ;
}
buf [ breceived ] = ' \0 ' ;
char * original_buf = buf ;
char * b = original_buf ;
request - > raw = strdup ( b ) ;
b = original_buf ;
char * line = strtok ( b , " \r \n " ) ;
b = original_buf ;
char * body = b + strlen ( line ) + 2 ;
request - > body = strdup ( body ) ;
b = original_buf ;
char * method = strtok ( b , " " ) ;
char * path = strtok ( NULL , " " ) ;
char * version = strtok ( NULL , " " ) ;
request - > bytes_received = breceived ;
request - > line = line ;
request - > start = nsecs ( ) ;
request - > method = strdup ( method ) ;
request - > path = strdup ( path ) ;
request - > version = strdup ( version ) ;
request - > headers = NULL ;
request - > keep_alive = false ;
if ( rhttp_parse_headers ( request ) ) {
char * keep_alive_string = rhttp_header_get_string ( request , " Connection " ) ;
if ( keep_alive_string & & ! strcmp ( keep_alive_string , " keep-alive " ) ) {
request - > keep_alive = 1 ;
}
}
return request ;
}
void rhttp_close_server ( ) {
close ( rhttp_sock ) ;
close ( rhttp_c ) ;
printf ( " Connections handled: %d \n " , rhttp_connections_handled ) ;
printf ( " Gracefully closed \n " ) ;
exit ( 0 ) ;
}
size_t rhttp_send_drain ( int s , void * tsend , size_t to_send_len ) {
if ( to_send_len = = 0 & & * ( unsigned char * ) tsend ) {
to_send_len = strlen ( tsend ) ;
}
unsigned char * to_send = ( unsigned char * ) malloc ( to_send_len ) ;
unsigned char * to_send_original = to_send ;
memcpy ( to_send , tsend , to_send_len ) ;
// to_send[to_send_len] = '\0';
long bytes_sent = 0 ;
long bytes_sent_total = 0 ;
while ( 1 ) {
bytes_sent = send ( s , to_send + bytes_sent_total , to_send_len - bytes_sent_total , 0 ) ;
if ( bytes_sent < = 0 ) {
bytes_sent_total = 0 ;
break ;
}
bytes_sent_total + = bytes_sent ;
if ( bytes_sent_total = = ( long ) to_send_len ) {
break ;
} else if ( ! bytes_sent ) {
bytes_sent_total = 0 ;
// error
break ;
} else {
rhttp_log_info ( " Extra send of %d/%d bytes. \n " , bytes_sent_total , to_send_len ) ;
}
}
free ( to_send_original ) ;
return bytes_sent_total ;
}
typedef int ( * rhttp_request_handler_t ) ( rhttp_request_t * r ) ;
void rhttp_serve ( const char * host , int port , int backlog , int request_logging , int request_debug , rhttp_request_handler_t handler ,
void * context ) {
signal ( SIGPIPE , SIG_IGN ) ;
rhttp_sock = socket ( AF_INET , SOCK_STREAM , 0 ) ;
struct sockaddr_in addr ;
addr . sin_family = AF_INET ;
addr . sin_port = htons ( port ) ;
addr . sin_addr . s_addr = inet_addr ( host ? host : " 0.0.0.0 " ) ;
rhttp_opt_debug = request_debug ;
rhttp_opt_request_logging = request_logging ;
int opt = 1 ;
setsockopt ( rhttp_sock , SOL_SOCKET , SO_REUSEADDR , & opt , sizeof ( opt ) ) ;
if ( bind ( rhttp_sock , ( struct sockaddr * ) & addr , sizeof ( addr ) ) < 0 ) {
printf ( " Binding error \n " ) ;
exit ( 1 ) ;
}
listen ( rhttp_sock , backlog ) ;
while ( 1 ) {
struct sockaddr_in client_addr ;
int addrlen = sizeof ( client_addr ) ;
rhttp_c = accept ( rhttp_sock , ( struct sockaddr * ) & client_addr , ( socklen_t * ) & addrlen ) ;
rhttp_connections_handled + + ;
while ( true ) {
rhttp_request_t * r = rhttp_parse_request ( rhttp_c ) ;
r - > context = context ;
if ( ! r - > closed ) {
if ( ! handler ( r ) & & ! r - > closed ) {
rhttp_close ( r ) ;
}
}
if ( ! r - > keep_alive & & ! r - > closed ) {
rhttp_close ( r ) ;
} else if ( r - > keep_alive & & ! r - > closed ) {
}
if ( r - > closed ) {
break ;
}
rhttp_free_request ( r ) ;
}
}
}
unsigned int rhttp_calculate_number_char_count ( unsigned int number ) {
unsigned int width = 1 ;
unsigned int tcounter = number ;
while ( tcounter / 10 > = 1 ) {
tcounter = tcounter / 10 ;
width + + ;
}
return width ;
}
int rhttp_file_response ( rhttp_request_t * r , char * path ) {
if ( ! * path )
return 0 ;
FILE * f = fopen ( path , " rb " ) ;
if ( f = = NULL )
return 0 ;
size_t file_size = rfile_size ( path ) ;
char response [ 1024 ] = { 0 } ;
char content_type_header [ 100 ] = { 0 } ;
char * ext = strstr ( path , " . " ) ;
char * text_extensions = " .h,.c,.html " ;
if ( strstr ( text_extensions , ext ) ) {
sprintf ( content_type_header , " Content-Type: %s \r \n " , " text/html " ) ;
}
sprintf ( response , " HTTP/1.1 200 OK \r \n %sContent-Length:%ld \r \n \r \n " , content_type_header , file_size ) ;
if ( ! rhttp_send_drain ( r - > c , response , 0 ) ) {
rhttp_log_error ( " Error sending file: %s \n " , path ) ;
}
size_t bytes = 0 ;
size_t bytes_sent = 0 ;
unsigned char file_buff [ 1024 ] ;
while ( ( bytes = fread ( file_buff , sizeof ( char ) , sizeof ( file_buff ) , f ) ) ) {
if ( ! rhttp_send_drain ( r - > c , file_buff , bytes ) ) {
rhttp_log_error ( " Error sending file during chunking: %s \n " , path ) ;
}
bytes_sent + = bytes ;
}
if ( bytes_sent ! = file_size ) {
rhttp_send_drain ( r - > c , file_buff , file_size - bytes_sent ) ;
}
close ( r - > c ) ;
fclose ( f ) ;
return 1 ;
} ;
int rhttp_file_request_handler ( rhttp_request_t * r ) {
char * path = r - > path ;
while ( * path = = ' / ' | | * path = = ' . ' )
path + + ;
if ( strstr ( path , " .. " ) ) {
return 0 ;
}
return rhttp_file_response ( r , path ) ;
} ;
unsigned int counter = 100000000 ;
int rhttp_counter_request_handler ( rhttp_request_t * r ) {
if ( ! strncmp ( r - > path , " /counter " , strlen ( " /counter " ) ) ) {
counter + + ;
unsigned int width = rhttp_calculate_number_char_count ( counter ) ;
char to_send2 [ 1024 ] = { 0 } ;
sprintf ( to_send2 ,
" HTTP/1.1 200 OK \r \n Content-Length: %d \r \n Connection: "
" close \r \n \r \n %d " ,
width , counter ) ;
rhttp_send_drain ( r - > c , to_send2 , 0 ) ;
close ( r - > c ) ;
return 1 ;
}
return 0 ;
}
int rhttp_root_request_handler ( rhttp_request_t * r ) {
if ( ! strcmp ( r - > path , " / " ) ) {
char to_send [ 1024 ] = { 0 } ;
sprintf ( to_send , " HTTP/1.1 200 OK \r \n Content-Length: 3 \r \n Connection: "
" close \r \n \r \n Ok! " ) ;
rhttp_send_drain ( r - > c , to_send , 0 ) ;
close ( r - > c ) ;
return 1 ;
}
return 0 ;
}
int rhttp_error_404_handler ( rhttp_request_t * r ) {
char to_send [ 1024 ] = { 0 } ;
sprintf ( to_send , " HTTP/1.1 404 Document not found \r \n Content-Length: "
" 0 \r \n Connection: close \r \n \r \n " ) ;
rhttp_send_drain ( r - > c , to_send , 0 ) ;
close ( r - > c ) ;
return 1 ;
}
int rhttp_default_request_handler ( rhttp_request_t * r ) {
if ( rhttp_opt_debug | | rhttp_opt_request_logging )
rhttp_print_request ( r ) ;
if ( rhttp_counter_request_handler ( r ) ) {
// Counter handler
rhttp_log_info ( " Counter handler found for: %s \n " , r - > path ) ;
} else if ( rhttp_root_request_handler ( r ) ) {
// Root handler
rhttp_log_info ( " Root handler found for: %s \n " , r - > path ) ;
} else if ( rhttp_file_request_handler ( r ) ) {
rhttp_log_info ( " File %s sent \n " , r - > path ) ;
} else if ( rhttp_error_404_handler ( r ) ) {
rhttp_log_warn ( " Error 404 for: %s \n " , r - > path ) ;
// Error handler
} else {
rhttp_log_warn ( " No handler found for: %s \n " , r - > path ) ;
close ( rhttp_c ) ;
}
return 0 ;
}
int rhttp_main ( int argc , char * argv [ ] ) {
setvbuf ( stdout , NULL , _IOLBF , BUFSIZ ) ;
int opt ;
while ( ( opt = getopt ( argc , argv , " p:drh:bewi " ) ) ! = - 1 ) {
switch ( opt ) {
case ' i ' :
rhttp_opt_info = 1 ;
rhttp_opt_warn = 1 ;
rhttp_opt_error = 1 ;
break ;
case ' e ' :
rhttp_opt_error = 1 ;
rhttp_opt_warn = 0 ;
rhttp_opt_info = 0 ;
break ;
case ' w ' :
rhttp_opt_warn = 1 ;
rhttp_opt_error = 1 ;
rhttp_opt_info = 0 ;
break ;
case ' p ' :
rhttp_opt_port = atoi ( optarg ) ;
break ;
case ' b ' :
rhttp_opt_buffered = 1 ;
printf ( " Logging is buffered. Output may be incomplete. \n " ) ;
break ;
case ' h ' :
strcpy ( rhttp_opt_host , optarg ) ;
break ;
case ' d ' :
printf ( " Debug enabled \n " ) ;
rhttp_opt_debug = 1 ;
rhttp_opt_warn = 1 ;
rhttp_opt_info = 1 ;
rhttp_opt_error = 1 ;
break ;
case ' r ' :
printf ( " Request logging enabled \n " ) ;
rhttp_opt_request_logging = 1 ;
break ;
default :
printf ( " Usage: %s [-p port] [-h host] [-b] \n " , argv [ 0 ] ) ;
return 1 ;
}
}
printf ( " Starting server on: %s:%d \n " , rhttp_opt_host , rhttp_opt_port ) ;
if ( rhttp_opt_buffered )
setvbuf ( stdout , NULL , _IOFBF , BUFSIZ ) ;
rhttp_serve ( rhttp_opt_host , rhttp_opt_port , 1024 , rhttp_opt_request_logging , rhttp_opt_debug , rhttp_default_request_handler , NULL ) ;
return 0 ;
}
/* CLIENT CODE */
typedef struct rhttp_client_request_t {
char * host ;
int port ;
char * path ;
bool is_done ;
char * request ;
char * response ;
pthread_t thread ;
int bytes_received ;
} rhttp_client_request_t ;
rhttp_client_request_t * rhttp_create_request ( const char * host , int port , const char * path ) {
rhttp_client_request_t * r = ( rhttp_client_request_t * ) malloc ( sizeof ( rhttp_client_request_t ) ) ;
char request_line [ 4096 ] = { 0 } ;
sprintf ( request_line ,
" GET %s HTTP/1.1 \r \n "
" Host: localhost:8000 \r \n "
" Connection: close \r \n "
" Accept: */* \r \n "
" User-Agent: mhttpc \r \n "
" Accept-Language: en-US,en;q=0.5 \r \n "
" Accept-Encoding: gzip, deflate \r \n "
" \r \n " ,
path ) ;
r - > request = strdup ( request_line ) ;
r - > host = strdup ( host ) ;
r - > port = port ;
r - > path = strdup ( path ) ;
r - > is_done = false ;
r - > response = NULL ;
r - > bytes_received = 0 ;
return r ;
}
int rhttp_execute_request ( rhttp_client_request_t * r ) {
int s = socket ( AF_INET , SOCK_STREAM , 0 ) ;
struct sockaddr_in addr ;
addr . sin_family = AF_INET ;
addr . sin_port = htons ( r - > port ) ;
addr . sin_addr . s_addr = inet_addr ( r - > host ) ;
if ( connect ( s , ( struct sockaddr * ) & addr , sizeof ( addr ) ) < 0 ) {
return 0 ;
}
send ( s , r - > request , strlen ( r - > request ) , 0 ) ;
char buf [ 1024 * 1024 ] = { 0 } ;
int ret = recv ( s , buf , 1024 * 1024 , 0 ) ;
if ( ret > 0 ) {
r - > response = strdup ( buf ) ;
}
close ( s ) ;
return ret ;
}
void rhttp_reset_request ( rhttp_client_request_t * r ) {
free ( r - > response ) ;
r - > is_done = false ;
r - > response = NULL ;
r - > bytes_received = 0 ;
}
void rhttp_free_client_request ( rhttp_client_request_t * r ) {
if ( r - > request )
free ( r - > request ) ;
if ( r - > response )
free ( r - > response ) ;
if ( r - > host )
free ( r - > host ) ;
if ( r - > path )
free ( r - > path ) ;
free ( r ) ;
}
void rhttp_client_bench ( int workers , int times , const char * host , int port , const char * path ) {
rhttp_client_request_t * requests [ workers ] ;
while ( times > 0 ) {
for ( int i = 0 ; i < workers & & times ; i + + ) {
requests [ i ] = rhttp_create_request ( host , port , path ) ;
rhttp_execute_request ( requests [ i ] ) ;
times - - ;
}
}
}
char * rhttp_client_get ( const char * host , int port , const char * path ) {
if ( ! rhttp_c_mutex_initialized ) {
rhttp_c_mutex_initialized = 1 ;
pthread_mutex_init ( & rhttp_c_mutex , NULL ) ;
}
char http_response [ 1024 * 1024 ] ;
http_response [ 0 ] = 0 ;
rhttp_client_request_t * r = rhttp_create_request ( host , port , path ) ;
unsigned int reconnects = 0 ;
unsigned int reconnects_max = 100000 ;
while ( ! rhttp_execute_request ( r ) ) {
reconnects + + ;
tick ( ) ;
if ( reconnects = = reconnects_max ) {
fprintf ( stderr , " Maxium reconnects exceeded for %s:%d \n " , host , port ) ;
rhttp_free_client_request ( r ) ;
return NULL ;
}
}
r - > is_done = true ;
char * body = r - > response ? strstr ( r - > response , " \r \n \r \n " ) : NULL ;
pthread_mutex_lock ( & rhttp_c_mutex ) ;
if ( body ) {
strcpy ( http_response , body + 4 ) ;
} else {
strcpy ( http_response , r - > response ) ;
}
rhttp_free_client_request ( r ) ;
char * result = sbuf ( http_response ) ;
pthread_mutex_unlock ( & rhttp_c_mutex ) ;
return result ;
}
/*END CLIENT CODE */
# endif
# ifndef RJSON_H
# define RJSON_H
typedef struct rjson_t {
char * content ;
size_t length ;
size_t size ;
} rjson_t ;
rjson_t * rjson ( ) {
rjson_t * json = rmalloc ( sizeof ( rjson_t ) ) ;
json - > size = 1024 ;
json - > length = 0 ;
json - > content = ( char * ) rmalloc ( json - > size ) ;
json - > content [ 0 ] = 0 ;
return json ;
}
void rjson_write ( rjson_t * rjs , char * content ) {
size_t len = strlen ( content ) ;
while ( rjs - > size < rjs - > length + len + 1 ) {
rjs - > content = realloc ( rjs - > content , rjs - > size + 1024 ) ;
rjs - > size + = 1024 ;
}
strcat ( rjs - > content , content ) ;
rjs - > length + = len ;
}
void rjson_object_start ( rjson_t * rjs ) {
if ( rstrendswith ( rjs - > content , " } " ) )
rjson_write ( rjs , " , " ) ;
rjson_write ( rjs , " { " ) ;
}
void rjson_object_close ( rjson_t * rjs ) {
if ( rstrendswith ( rjs - > content , " , " ) ) {
rjs - > content [ rjs - > length - 1 ] = 0 ;
rjs - > length - - ;
}
rjson_write ( rjs , " } " ) ;
}
void rjson_array_start ( rjson_t * rjs ) {
if ( rjs - > length & & ( rstrendswith ( rjs - > content , " } " ) | | rstrendswith ( rjs - > content , " ] " ) ) )
rjson_write ( rjs , " , " ) ;
rjson_write ( rjs , " [ " ) ;
}
void rjson_array_close ( rjson_t * rjs ) {
if ( rstrendswith ( rjs - > content , " , " ) ) {
rjs - > content [ rjs - > length - 1 ] = 0 ;
rjs - > length - - ;
}
rjson_write ( rjs , " ] " ) ;
}
void rjson_kv_string ( rjson_t * rjs , char * key , char * value ) {
if ( rjs - > length & & ! rstrendswith ( rjs - > content , " { " ) & & ! rstrendswith ( rjs - > content , " [ " ) ) {
rjson_write ( rjs , " , " ) ;
}
rjson_write ( rjs , " \" " ) ;
rjson_write ( rjs , key ) ;
rjson_write ( rjs , " \" : \" " ) ;
char * value_str = ( char * ) rmalloc ( strlen ( value ) + 4096 ) ;
rstraddslashes ( value , value_str ) ;
rjson_write ( rjs , value_str ) ;
free ( value_str ) ;
rjson_write ( rjs , " \" " ) ;
}
void rjson_kv_int ( rjson_t * rjs , char * key , ulonglong value ) {
if ( rjs - > length & & ! rstrendswith ( rjs - > content , " { " ) & & ! rstrendswith ( rjs - > content , " [ " ) ) {
rjson_write ( rjs , " , " ) ;
}
rjson_write ( rjs , " \" " ) ;
rjson_write ( rjs , key ) ;
rjson_write ( rjs , " \" : " ) ;
char value_str [ 100 ] = { 0 } ;
sprintf ( value_str , " %lld " , value ) ;
rjson_write ( rjs , value_str ) ;
}
void rjson_kv_number ( rjson_t * rjs , char * key , ulonglong value ) {
if ( rjs - > length & & ! rstrendswith ( rjs - > content , " { " ) & & ! rstrendswith ( rjs - > content , " [ " ) ) {
rjson_write ( rjs , " , " ) ;
}
rjson_write ( rjs , " \" " ) ;
rjson_write ( rjs , key ) ;
rjson_write ( rjs , " \" : " ) ;
rjson_write ( rjs , " \" " ) ;
rjson_write ( rjs , sbuf ( rformat_number ( value ) ) ) ;
rjson_write ( rjs , " \" " ) ;
}
void rjson_kv_bool ( rjson_t * rjs , char * key , int value ) {
if ( rjs - > length & & ! rstrendswith ( rjs - > content , " { " ) & & ! rstrendswith ( rjs - > content , " [ " ) ) {
rjson_write ( rjs , " , " ) ;
}
rjson_write ( rjs , " \" " ) ;
rjson_write ( rjs , key ) ;
rjson_write ( rjs , " \" : " ) ;
rjson_write ( rjs , value > 0 ? " true " : " false " ) ;
}
void rjson_kv_duration ( rjson_t * rjs , char * key , nsecs_t value ) {
if ( rjs - > length & & ! rstrendswith ( rjs - > content , " { " ) & & ! rstrendswith ( rjs - > content , " [ " ) ) {
rjson_write ( rjs , " , " ) ;
}
rjson_write ( rjs , " \" " ) ;
rjson_write ( rjs , key ) ;
rjson_write ( rjs , " \" : " ) ;
rjson_write ( rjs , " \" " ) ;
rjson_write ( rjs , sbuf ( format_time ( value ) ) ) ;
rjson_write ( rjs , " \" " ) ;
}
void rjson_free ( rjson_t * rsj ) {
free ( rsj - > content ) ;
free ( rsj ) ;
}
void rjson_key ( rjson_t * rsj , char * key ) {
rjson_write ( rsj , " \" " ) ;
rjson_write ( rsj , key ) ;
rjson_write ( rsj , " \" : " ) ;
}
# endif
# ifndef RAUTOCOMPLETE_H
# define RAUTOCOMPLETE_H
# define R4_DEBUG
# ifndef RREX4_H
# define RREX4_H
# include <assert.h>
# include <ctype.h>
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# define R4_DEBUG_a
# ifdef R4_DEBUG
static int _r4_debug = 1 ;
# else
static int _r4_debug = 0 ;
# endif
static char * _format_function_name ( const char * name ) {
static char result [ 100 ] ;
result [ 0 ] = 0 ;
char * new_name = ( char * ) name ;
new_name + = 11 ;
if ( new_name [ 0 ] = = ' _ ' )
new_name + = 1 ;
if ( strlen ( new_name ) = = 0 ) {
return " - " ;
}
strcpy ( result , new_name ) ;
return result ;
}
# define DEBUG_VALIDATE_FUNCTION \
if (_r4_debug || r4->debug) \
printf("DEBUG: %s %s <%s> \"%s\"\n", _format_function_name(__func__), r4->valid ? "valid" : "INVALID", r4->expr, r4->str);
struct r4_t ;
void r4_enable_debug ( ) { _r4_debug = true ; }
void r4_disable_debug ( ) { _r4_debug = false ; }
typedef bool ( * r4_function ) ( struct r4_t * ) ;
typedef struct r4_t {
bool debug ;
bool valid ;
bool in_block ;
bool is_greedy ;
bool in_range ;
unsigned int backtracking ;
unsigned int loop_count ;
unsigned int in_group ;
unsigned int match_count ;
unsigned int validation_count ;
unsigned int start ;
unsigned int end ;
unsigned int length ;
bool ( * functions [ 254 ] ) ( struct r4_t * ) ;
bool ( * slash_functions [ 254 ] ) ( struct r4_t * ) ;
char * _str ;
char * _expr ;
char * match ;
char * str ;
char * expr ;
char * str_previous ;
char * expr_previous ;
char * * matches ;
} r4_t ;
static bool v4_initiated = false ;
typedef bool ( * v4_function_map ) ( r4_t * ) ;
v4_function_map v4_function_map_global [ 256 ] ;
v4_function_map v4_function_map_slash [ 256 ] ;
v4_function_map v4_function_map_block [ 256 ] ;
void r4_free_matches ( r4_t * r ) {
if ( ! r )
return ;
if ( r - > match ) {
free ( r - > match ) ;
r - > match = NULL ;
}
if ( ! r - > match_count ) {
return ;
}
for ( unsigned i = 0 ; i < r - > match_count ; i + + ) {
free ( r - > matches [ i ] ) ;
}
free ( r - > matches ) ;
r - > match_count = 0 ;
r - > matches = NULL ;
}
void r4_free ( r4_t * r ) {
if ( ! r )
return ;
r4_free_matches ( r ) ;
free ( r ) ;
}
static bool r4_backtrack ( r4_t * r4 ) ;
static bool r4_validate ( r4_t * r4 ) ;
static void r4_match_add ( r4_t * r4 , char * extracted ) ;
static bool r4_validate_literal ( r4_t * r4 ) {
DEBUG_VALIDATE_FUNCTION
if ( ! r4 - > valid )
return false ;
if ( * r4 - > str ! = * r4 - > expr ) {
r4 - > valid = false ;
} else {
r4 - > str + + ;
}
r4 - > expr + + ;
if ( r4 - > in_block | | r4 - > in_range | | ! r4 - > is_greedy ) {
return r4 - > valid ;
}
return r4_validate ( r4 ) ;
}
static bool r4_validate_question_mark ( r4_t * r4 ) {
DEBUG_VALIDATE_FUNCTION
r4 - > valid = true ;
r4 - > expr + + ;
return r4_validate ( r4 ) ;
}
static bool r4_validate_plus ( r4_t * r4 ) {
DEBUG_VALIDATE_FUNCTION
r4 - > expr + + ;
if ( r4 - > valid = = false ) {
return r4_validate ( r4 ) ;
}
char * expr_left = r4 - > expr_previous ;
char * expr_right = r4 - > expr ;
char * str = r4 - > str ;
char * return_expr = NULL ;
if ( * expr_right = = ' ) ' ) {
return_expr = expr_right ;
expr_right + + ;
}
r4 - > is_greedy = false ;
r4 - > expr = expr_left ;
while ( r4 - > valid ) {
if ( * expr_right ) {
r4 - > expr = expr_right ;
r4 - > is_greedy = true ;
if ( r4_backtrack ( r4 ) ) {
if ( return_expr ) {
r4 - > str = str ;
r4 - > expr = return_expr ;
}
return r4_validate ( r4 ) ;
} else {
r4 - > is_greedy = false ;
}
}
r4 - > valid = true ;
r4 - > expr = expr_left ;
r4 - > str = str ;
r4_validate ( r4 ) ;
str = r4 - > str ;
}
r4 - > is_greedy = true ;
r4 - > valid = true ;
r4 - > expr = return_expr ? return_expr : expr_right ;
return r4_validate ( r4 ) ;
}
static bool r4_validate_dollar ( r4_t * r4 ) {
DEBUG_VALIDATE_FUNCTION
r4 - > expr + + ;
r4 - > valid = * r4 - > str = = 0 ;
return r4_validate ( r4 ) ;
}
static bool r4_validate_roof ( r4_t * r4 ) {
DEBUG_VALIDATE_FUNCTION
if ( r4 - > str ! = r4 - > _str ) {
return false ;
}
r4 - > expr + + ;
return r4_validate ( r4 ) ;
}
static bool r4_validate_dot ( r4_t * r4 ) {
DEBUG_VALIDATE_FUNCTION
if ( * r4 - > str = = 0 ) {
return false ;
}
r4 - > expr + + ;
r4 - > valid = * r4 - > str ! = ' \n ' ;
r4 - > str + + ;
if ( r4 - > in_block | | r4 - > in_range | | ! r4 - > is_greedy ) {
return r4 - > valid ;
}
return r4_validate ( r4 ) ;
}
static bool r4_validate_asterisk ( r4_t * r4 ) {
DEBUG_VALIDATE_FUNCTION
r4 - > expr + + ;
if ( r4 - > valid = = false ) {
r4 - > valid = true ;
return r4 - > valid ;
// return r4_validate(r4);
}
char * expr_left = r4 - > expr_previous ;
char * expr_right = r4 - > expr ;
char * str = r4 - > str ;
char * return_expr = NULL ;
if ( * expr_right = = ' ) ' ) {
return_expr = expr_right ;
expr_right + + ;
}
r4 - > is_greedy = false ;
r4 - > expr = expr_left ;
while ( r4 - > valid ) {
if ( * expr_right ) {
r4 - > expr = expr_right ;
r4 - > is_greedy = true ;
if ( r4_backtrack ( r4 ) ) {
if ( return_expr ) {
r4 - > str = str ;
r4 - > expr = return_expr ;
}
return r4_validate ( r4 ) ;
} else {
r4 - > is_greedy = false ;
}
}
r4 - > valid = true ;
r4 - > expr = expr_left ;
r4 - > str = str ;
r4_validate ( r4 ) ;
str = r4 - > str ;
}
r4 - > is_greedy = true ;
r4 - > valid = true ;
r4 - > expr = return_expr ? return_expr : expr_right ;
return r4_validate ( r4 ) ;
}
static bool r4_validate_pipe ( r4_t * r4 ) {
DEBUG_VALIDATE_FUNCTION
r4 - > expr + + ;
if ( r4 - > valid = = true ) {
return true ;
} else {
r4 - > valid = true ;
}
return r4_validate ( r4 ) ;
}
static bool r4_validate_digit ( r4_t * r4 ) {
DEBUG_VALIDATE_FUNCTION
if ( ! isdigit ( * r4 - > str ) ) {
r4 - > valid = false ;
} else {
r4 - > str + + ;
}
r4 - > expr + + ;
if ( r4 - > in_block | | r4 - > in_range | | ! r4 - > is_greedy ) {
return r4 - > valid ;
}
return r4_validate ( r4 ) ;
}
static bool r4_validate_not_digit ( r4_t * r4 ) {
DEBUG_VALIDATE_FUNCTION
if ( isdigit ( * r4 - > str ) ) {
r4 - > valid = false ;
} else {
r4 - > str + + ;
}
r4 - > expr + + ;
if ( r4 - > in_block | | r4 - > in_range | | ! r4 - > is_greedy ) {
return r4 - > valid ;
}
return r4_validate ( r4 ) ;
}
static bool r4_validate_word ( r4_t * r4 ) {
DEBUG_VALIDATE_FUNCTION
if ( ! isalpha ( * r4 - > str ) ) {
r4 - > valid = false ;
} else {
r4 - > str + + ;
}
r4 - > expr + + ;
if ( r4 - > in_block | | r4 - > in_range | | ! r4 - > is_greedy ) {
return r4 - > valid ;
}
return r4_validate ( r4 ) ;
}
static bool r4_validate_not_word ( r4_t * r4 ) {
DEBUG_VALIDATE_FUNCTION
if ( isalpha ( * r4 - > str ) ) {
r4 - > valid = false ;
} else {
r4 - > str + + ;
}
r4 - > expr + + ;
if ( r4 - > in_block | | r4 - > in_range | | ! r4 - > is_greedy ) {
return r4 - > valid ;
}
return r4_validate ( r4 ) ;
}
static bool r4_isrange ( char * s ) {
if ( ! isalnum ( * s ) ) {
return false ;
}
if ( * ( s + 1 ) ! = ' - ' ) {
return false ;
}
return isalnum ( * ( s + 2 ) ) ;
}
static bool r4_validate_block_open ( r4_t * r4 ) {
DEBUG_VALIDATE_FUNCTION
if ( r4 - > valid = = false ) {
return false ;
}
char * expr_self = r4 - > expr ;
r4 - > expr + + ;
bool reversed = * r4 - > expr = = ' ^ ' ;
if ( reversed ) {
r4 - > expr + + ;
}
bool valid_once = false ;
r4 - > in_block = true ;
while ( * r4 - > expr ! = ' ] ' ) {
r4 - > valid = true ;
if ( r4_isrange ( r4 - > expr ) ) {
char s = * r4 - > expr ;
char e = * ( r4 - > expr + 2 ) ;
r4 - > expr + = 2 ;
if ( s > e ) {
char tempc = s ;
s = e ;
e = tempc ;
}
if ( * r4 - > str > = s & & * r4 - > str < = e ) {
if ( ! reversed ) {
r4 - > str + + ;
}
valid_once = true ;
break ;
} else {
r4 - > expr + + ;
}
} else if ( r4_validate ( r4 ) ) {
valid_once = true ;
if ( reversed )
r4 - > str - - ;
break ;
}
}
char * expr_end = strchr ( r4 - > expr , ' ] ' ) ;
r4 - > expr = expr_end ? expr_end : r4 - > expr ;
r4 - > in_block = false ;
r4 - > valid = expr_end & & ( ! reversed ? valid_once : ! valid_once ) ;
r4 - > expr + + ;
r4 - > expr_previous = expr_self ;
if ( r4 - > in_range | | ! r4 - > is_greedy ) {
return r4 - > valid ;
}
return r4_validate ( r4 ) ;
}
static bool r4_validate_whitespace ( r4_t * r4 ) {
DEBUG_VALIDATE_FUNCTION
r4 - > valid = strchr ( " \r \t \n " , * r4 - > str ) ! = NULL ;
r4 - > expr + + ;
if ( r4 - > valid ) {
r4 - > str + + ;
}
if ( r4 - > in_range | | r4 - > in_block | | ! r4 - > is_greedy ) {
return r4 - > valid ;
}
return r4_validate ( r4 ) ;
}
static bool r4_validate_not_whitespace ( r4_t * r4 ) {
DEBUG_VALIDATE_FUNCTION
r4 - > valid = strchr ( " \r \t \n " , * r4 - > str ) = = NULL ;
r4 - > expr + + ;
if ( r4 - > valid ) {
r4 - > str + + ;
}
if ( r4 - > in_range | | r4 - > in_block | | ! r4 - > is_greedy ) {
return r4 - > valid ;
}
return r4_validate ( r4 ) ;
}
static bool r4_validate_range ( r4_t * r4 ) {
DEBUG_VALIDATE_FUNCTION ;
if ( r4 - > valid = = false ) {
r4 - > expr + + ;
return false ;
}
char * previous = r4 - > expr_previous ;
r4 - > in_range = true ;
r4 - > expr + + ;
unsigned int start = 0 ;
while ( isdigit ( * r4 - > expr ) ) {
start = 10 * start ;
start + = * r4 - > expr - ' 0 ' ;
r4 - > expr + + ;
}
if ( start ! = 0 )
start - - ;
unsigned int end = 0 ;
bool variable_end_range = false ;
if ( * r4 - > expr = = ' , ' ) {
r4 - > expr + + ;
if ( ! isdigit ( * r4 - > expr ) ) {
variable_end_range = true ;
}
}
while ( isdigit ( * r4 - > expr ) ) {
end = end * 10 ;
end + = * r4 - > expr - ' 0 ' ;
r4 - > expr + + ;
}
r4 - > expr + + ;
bool valid = true ;
char * expr_right = r4 - > expr ;
for ( unsigned int i = 0 ; i < start ; i + + ) {
r4 - > expr = previous ;
valid = r4_validate ( r4 ) ;
if ( ! * r4 - > str )
break ;
if ( ! valid ) {
break ;
}
}
r4 - > expr = expr_right ;
r4 - > in_range = false ;
if ( ! r4 - > valid )
return false ;
return r4_validate ( r4 ) ;
for ( unsigned int i = start ; i < end ; i + + ) {
r4 - > expr = previous ;
valid = r4_validate ( r4 ) ;
if ( ! valid ) {
break ;
}
}
while ( variable_end_range ) {
r4 - > in_range = false ;
valid = r4_validate ( r4 ) ;
r4 - > in_range = true ;
if ( valid ) {
break ;
}
r4 - > in_range = true ;
valid = r4_validate ( r4 ) ;
r4 - > in_range = false ;
if ( ! valid ) {
break ;
}
}
r4 - > valid = valid ;
return r4_validate ( r4 ) ;
}
static bool r4_validate_group_close ( r4_t * r4 ) {
DEBUG_VALIDATE_FUNCTION
return r4 - > valid ;
}
static bool r4_validate_group_open ( r4_t * r4 ) {
DEBUG_VALIDATE_FUNCTION
char * expr_previous = r4 - > expr_previous ;
r4 - > expr + + ;
bool save_match = r4 - > in_group = = 0 ;
r4 - > in_group + + ;
char * str_extract_start = r4 - > str ;
bool valid = r4_validate ( r4 ) ;
if ( ! valid | | * r4 - > expr ! = ' ) ' ) {
// this is a valid case if not everything between () matches
r4 - > in_group - - ;
if ( save_match = = false ) {
r4 - > valid = true ;
}
// Not direct return? Not sure
return r4_validate ( r4 ) ;
}
// if(save_match){
// r4->match_count++;
// }
if ( save_match ) {
char * str_extract_end = r4 - > str ;
unsigned int extracted_length = str_extract_end - str_extract_start ;
// strlen(str_extract_start) - strlen(str_extract_end);
char * str_extracted = ( char * ) calloc ( sizeof ( char ) , extracted_length + 1 ) ;
strncpy ( str_extracted , str_extract_start , extracted_length ) ;
r4_match_add ( r4 , str_extracted ) ;
}
assert ( * r4 - > expr = = ' ) ' ) ;
r4 - > expr + + ;
r4 - > in_group - - ;
r4 - > expr_previous = expr_previous ;
return r4_validate ( r4 ) ;
}
static bool r4_validate_slash ( r4_t * r4 ) {
DEBUG_VALIDATE_FUNCTION
// The handling code for handling slashes is implemented in r4_validate
char * expr_previous = r4 - > expr_previous ;
r4 - > expr + + ;
r4_function f = v4_function_map_slash [ ( int ) * r4 - > expr ] ;
r4 - > expr_previous = expr_previous ;
return f ( r4 ) ;
}
static void r4_match_add ( r4_t * r4 , char * extracted ) {
r4 - > matches = ( char * * ) realloc ( r4 - > matches , ( r4 - > match_count + 1 ) * sizeof ( char * ) ) ;
r4 - > matches [ r4 - > match_count ] = extracted ;
r4 - > match_count + + ;
}
static bool r4_validate_word_boundary_start ( r4_t * r4 ) {
DEBUG_VALIDATE_FUNCTION
r4 - > expr + + ;
if ( ! r4 - > valid ) {
return r4 - > valid ;
}
r4 - > valid = isalpha ( * r4 - > str ) & & ( r4 - > str = = r4 - > _str | | ! isalpha ( * ( r4 - > str - 1 ) ) ) ;
if ( r4 - > in_range | | r4 - > in_block | | ! r4 - > is_greedy ) {
return r4 - > valid ;
}
return r4_validate ( r4 ) ;
}
static bool r4_validate_word_boundary_end ( r4_t * r4 ) {
DEBUG_VALIDATE_FUNCTION
r4 - > expr + + ;
if ( ! r4 - > valid ) {
return r4 - > valid ;
}
r4 - > valid = isalpha ( * r4 - > str ) & & ( * ( r4 - > str + 1 ) = = 0 | | ! isalpha ( * ( r4 - > str + 1 ) ) ) ;
if ( r4 - > in_range | | r4 - > in_block | | ! r4 - > is_greedy ) {
return r4 - > valid ;
}
return r4_validate ( r4 ) ;
}
static void v4_init_function_maps ( ) {
if ( v4_initiated )
return ;
v4_initiated = true ;
for ( __uint8_t i = 0 ; i < 255 ; i + + ) {
v4_function_map_global [ i ] = r4_validate_literal ;
v4_function_map_slash [ i ] = r4_validate_literal ;
v4_function_map_block [ i ] = r4_validate_literal ;
}
v4_function_map_global [ ' * ' ] = r4_validate_asterisk ;
v4_function_map_global [ ' ? ' ] = r4_validate_question_mark ;
v4_function_map_global [ ' + ' ] = r4_validate_plus ;
v4_function_map_global [ ' $ ' ] = r4_validate_dollar ;
v4_function_map_global [ ' ^ ' ] = r4_validate_roof ;
v4_function_map_global [ ' . ' ] = r4_validate_dot ;
v4_function_map_global [ ' | ' ] = r4_validate_pipe ;
v4_function_map_global [ ' \\ ' ] = r4_validate_slash ;
v4_function_map_global [ ' [ ' ] = r4_validate_block_open ;
v4_function_map_global [ ' { ' ] = r4_validate_range ;
v4_function_map_global [ ' ( ' ] = r4_validate_group_open ;
v4_function_map_global [ ' ) ' ] = r4_validate_group_close ;
v4_function_map_slash [ ' b ' ] = r4_validate_word_boundary_start ;
v4_function_map_slash [ ' B ' ] = r4_validate_word_boundary_end ;
v4_function_map_slash [ ' d ' ] = r4_validate_digit ;
v4_function_map_slash [ ' w ' ] = r4_validate_word ;
v4_function_map_slash [ ' D ' ] = r4_validate_not_digit ;
v4_function_map_slash [ ' W ' ] = r4_validate_not_word ;
v4_function_map_slash [ ' s ' ] = r4_validate_whitespace ;
v4_function_map_slash [ ' S ' ] = r4_validate_not_whitespace ;
v4_function_map_block [ ' \\ ' ] = r4_validate_slash ;
v4_function_map_block [ ' { ' ] = r4_validate_range ;
}
void r4_init ( r4_t * r4 ) {
v4_init_function_maps ( ) ;
if ( r4 = = NULL )
return ;
r4 - > debug = _r4_debug ;
r4 - > valid = true ;
r4 - > validation_count = 0 ;
r4 - > match_count = 0 ;
r4 - > start = 0 ;
r4 - > end = 0 ;
r4 - > length = 0 ;
r4 - > matches = NULL ;
}
static bool r4_looks_behind ( char c ) { return strchr ( " ?*+{ " , c ) ! = NULL ; }
r4_t * r4_new ( ) {
r4_t * r4 = ( r4_t * ) malloc ( sizeof ( r4_t ) ) ;
r4_init ( r4 ) ;
return r4 ;
}
static bool r4_pipe_next ( r4_t * r4 ) {
char * expr = r4 - > expr ;
while ( * expr ) {
if ( * expr = = ' | ' ) {
r4 - > expr = expr + 1 ;
r4 - > valid = true ;
return true ;
}
expr + + ;
}
return false ;
}
static bool r4_backtrack ( r4_t * r4 ) {
if ( _r4_debug )
printf ( " \033 [36mDEBUG: backtrack start (%d) \n " , r4 - > backtracking ) ;
r4 - > backtracking + + ;
char * str = r4 - > str ;
char * expr = r4 - > expr ;
bool result = r4_validate ( r4 ) ;
r4 - > backtracking - - ;
if ( result = = false ) {
r4 - > expr = expr ;
r4 - > str = str ;
}
if ( _r4_debug )
printf ( " DEBUG: backtrack end (%d) result: %d %s \n " , r4 - > backtracking , result , r4 - > backtracking = = 0 ? " \033 [0m " : " " ) ;
return result ;
}
static bool r4_validate ( r4_t * r4 ) {
DEBUG_VALIDATE_FUNCTION
r4 - > validation_count + + ;
char c_val = * r4 - > expr ;
if ( c_val = = 0 ) {
return r4 - > valid ;
}
if ( ! r4_looks_behind ( c_val ) ) {
r4 - > expr_previous = r4 - > expr ;
} else if ( r4 - > expr = = r4 - > _expr ) {
// Regex may not start with a look behind ufnction
return false ;
}
if ( ! r4 - > valid & & ! r4_looks_behind ( * r4 - > expr ) ) {
if ( ! r4_pipe_next ( r4 ) ) {
return false ;
}
}
r4_function f ;
if ( r4 - > in_block ) {
f = v4_function_map_block [ ( int ) c_val ] ;
} else {
f = v4_function_map_global [ ( int ) c_val ] ;
}
r4 - > valid = f ( r4 ) ;
return r4 - > valid ;
}
char * r4_get_match ( r4_t * r ) {
char * match = ( char * ) malloc ( r - > length + 1 ) ;
strncpy ( match , r - > _str + r - > start , r - > length ) ;
match [ r - > length ] = 0 ;
return match ;
}
static bool r4_search ( r4_t * r ) {
bool valid = true ;
char * str_next = r - > str ;
while ( * r - > str ) {
if ( ! ( valid = r4_validate ( r ) ) ) {
// Move next until we find a match
if ( ! r - > backtracking ) {
r - > start + + ;
}
str_next + + ;
r - > str = str_next ;
r - > expr = r - > _expr ;
r - > valid = true ;
} else {
/// HIGH DOUBT
if ( ! r - > backtracking ) {
// r->start = 0;
}
break ;
}
}
r - > valid = valid ;
if ( r - > valid ) {
r - > end = strlen ( r - > _str ) - strlen ( r - > str ) ;
r - > length = r - > end - r - > start ;
r - > match = r4_get_match ( r ) ;
}
return r - > valid ;
}
r4_t * r4 ( const char * str , const char * expr ) {
r4_t * r = r4_new ( ) ;
r - > _str = ( char * ) str ;
r - > _expr = ( char * ) expr ;
r - > match = NULL ;
r - > str = r - > _str ;
r - > expr = r - > _expr ;
r - > str_previous = r - > _str ;
r - > expr_previous = r - > expr ;
r - > in_block = false ;
r - > is_greedy = true ;
r - > in_group = 0 ;
r - > loop_count = 0 ;
r - > backtracking = 0 ;
r - > in_range = false ;
r4_search ( r ) ;
return r ;
}
r4_t * r4_next ( r4_t * r , char * expr ) {
if ( expr ) {
r - > _expr = expr ;
}
r - > backtracking = 0 ;
r - > expr = r - > _expr ;
r - > is_greedy = true ;
r - > in_block = false ;
r - > in_range = false ;
r - > in_group = false ;
r4_free_matches ( r ) ;
r4_search ( r ) ;
return r ;
}
bool r4_match ( char * str , char * expr ) {
r4_t * r = r4 ( str , expr ) ;
bool result = r - > valid ;
r4_free ( r ) ;
return result ;
}
# endif
# define rautocomplete_new rstring_list_new
# define rautocomplete_free rstring_list_free
# define rautocomplete_add rstring_list_add
# define rautocomplete_find rstring_list_find
# define rautocomplete_t rstring_list_t
# define rautocomplete_contains rstring_list_contains
char * r4_escape ( char * content ) {
size_t size = strlen ( content ) * 2 + 1 ;
char * escaped = ( char * ) calloc ( size , sizeof ( char ) ) ;
char * espr = escaped ;
char * to_escape = " ?*+()[]{}^$ \\ " ;
* espr = ' ( ' ;
espr + + ;
while ( * content ) {
if ( strchr ( to_escape , * content ) ) {
* espr = ' \\ ' ;
espr + + ;
}
* espr = * content ;
espr + + ;
content + + ;
}
* espr = ' . ' ;
espr + + ;
* espr = ' + ' ;
espr + + ;
* espr = ' ) ' ;
espr + + ;
* espr = 0 ;
return escaped ;
}
char * rautocomplete_find ( rstring_list_t * list , char * expr ) {
if ( ! list - > count )
return NULL ;
if ( ! expr | | ! strlen ( expr ) )
return NULL ;
char * escaped = r4_escape ( expr ) ;
for ( unsigned int i = list - > count - 1 ; i = = 0 ; i - - ) {
char * match ;
r4_t * r = r4 ( list - > strings [ i ] , escaped ) ;
if ( r - > valid & & r - > match_count = = 1 ) {
match = strdup ( r - > matches [ 0 ] ) ;
}
r4_free ( r ) ;
if ( match ) {
free ( escaped ) ;
return match ;
}
}
free ( escaped ) ;
return NULL ;
}
# endif
# ifndef RKEYTABLE_H
# define RKEYTABLE_H
/*
DERIVED FROM HASH TABLE K&R
*/
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
typedef struct rnklist {
struct rnklist * next ;
struct rnklist * last ;
char * name ;
char * defn ;
} rnklist ;
static rnklist * rkeytab = NULL ;
rnklist * rlkget ( char * s ) {
rnklist * np ;
for ( np = rkeytab ; np ! = NULL ; np = np - > next )
if ( strcmp ( s , np - > name ) = = 0 )
return np ; // Found
return NULL ; // Not found
}
char * rkget ( char * s ) {
rnklist * np = rlkget ( s ) ;
return np ? np - > defn : NULL ;
}
rnklist * rkset ( char * name , char * defn ) {
rnklist * np ;
if ( ( np = ( rlkget ( name ) ) ) = = NULL ) { // Not found
np = ( rnklist * ) malloc ( sizeof ( rnklist ) ) ;
np - > name = strdup ( name ) ;
np - > next = NULL ;
np - > last = NULL ;
if ( defn ) {
np - > defn = strdup ( defn ) ;
} else {
np - > defn = NULL ;
}
if ( rkeytab = = NULL ) {
rkeytab = np ;
rkeytab - > last = np ;
} else {
if ( rkeytab - > last )
rkeytab - > last - > next = np ;
rkeytab - > last = np ;
}
} else {
if ( np - > defn )
free ( ( void * ) np - > defn ) ;
if ( defn ) {
np - > defn = strdup ( defn ) ;
} else {
np - > defn = NULL ;
}
}
return np ;
}
# endif
# ifndef RHASHTABLE_H
# define RHASHTABLE_H
/*
ORIGINAL SOURCE IS FROM K&R
*/
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# define HASHSIZE 101
// Structure for the table entries
typedef struct rnlist {
struct rnlist * next ;
char * name ;
char * defn ;
} rnlist ;
// Hash table array
static rnlist * rhashtab [ HASHSIZE ] ;
// Hash function
unsigned rhash ( char * s ) {
unsigned hashval ;
for ( hashval = 0 ; * s ! = ' \0 ' ; s + + )
hashval = * s + 31 * hashval ;
return hashval % HASHSIZE ;
}
rnlist * rlget ( char * s ) {
rnlist * np ;
for ( np = rhashtab [ rhash ( s ) ] ; np ! = NULL ; np = np - > next )
if ( strcmp ( s , np - > name ) = = 0 )
return np ; // Found
return NULL ; // Not found
}
// Lookup function
char * rget ( char * s ) {
rnlist * np = rlget ( s ) ;
return np ? np - > defn : NULL ;
}
// Install function (adds a name and definition to the table)
struct rnlist * rset ( char * name , char * defn ) {
struct rnlist * np = NULL ;
unsigned hashval ;
if ( ( rlget ( name ) ) = = NULL ) { // Not found
np = ( struct rnlist * ) malloc ( sizeof ( * np ) ) ;
if ( np = = NULL | | ( np - > name = strdup ( name ) ) = = NULL )
return NULL ;
hashval = rhash ( name ) ;
np - > next = rhashtab [ hashval ] ;
rhashtab [ hashval ] = np ;
} else {
if ( np - > defn )
free ( ( void * ) np - > defn ) ;
np - > defn = NULL ;
}
if ( ( np - > defn = strdup ( defn ) ) = = NULL )
return NULL ;
return np ;
}
# endif
# ifndef RREX3_H
# define RREX3_H
# include <assert.h>
# include <ctype.h>
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
# ifndef RREX3_DEBUG
# define RREX3_DEBUG 0
# endif
struct rrex3_t ;
typedef void ( * rrex3_function ) ( struct rrex3_t * ) ;
typedef struct rrex3_t {
void ( * functions [ 254 ] ) ( struct rrex3_t * ) ;
void ( * slash_functions [ 254 ] ) ( struct rrex3_t * ) ;
bool valid ;
int match_count ;
int match_capacity ;
char * * matches ;
bool exit ;
char * __expr ;
char * __str ;
char * _expr ;
char * _str ;
char * expr ;
char * str ;
char * compiled ;
bool inside_brackets ;
bool inside_parentheses ;
bool pattern_error ;
bool match_from_start ;
char bytecode ;
rrex3_function function ;
struct {
void ( * function ) ( struct rrex3_t * ) ;
char * expr ;
char * str ;
char bytecode ;
} previous ;
struct {
void ( * function ) ( struct rrex3_t * ) ;
char * expr ;
char * str ;
char bytecode ;
} failed ;
} rrex3_t ;
static bool isdigitrange ( char * s ) {
if ( ! isdigit ( * s ) ) {
return false ;
}
if ( * ( s + 1 ) ! = ' - ' ) {
return false ;
}
return isdigit ( * ( s + 2 ) ) ;
}
static bool isalpharange ( char * s ) {
if ( ! isalpha ( * s ) ) {
return false ;
}
if ( * ( s + 1 ) ! = ' - ' ) {
return false ;
}
return isalpha ( * ( s + 2 ) ) ;
}
void rrex3_free_matches ( rrex3_t * rrex3 ) {
if ( ! rrex3 - > matches )
return ;
for ( int i = 0 ; i < rrex3 - > match_count ; i + + ) {
free ( rrex3 - > matches [ i ] ) ;
}
free ( rrex3 - > matches ) ;
rrex3 - > matches = NULL ;
rrex3 - > match_count = 0 ;
rrex3 - > match_capacity = 0 ;
}
void rrex3_free ( rrex3_t * rrex3 ) {
if ( ! rrex3 )
return ;
if ( rrex3 - > compiled ) {
free ( rrex3 - > compiled ) ;
rrex3 - > compiled = NULL ;
}
rrex3_free_matches ( rrex3 ) ;
free ( rrex3 ) ;
rrex3 = NULL ;
}
static bool rrex3_move ( rrex3_t * , bool ) ;
static void rrex3_set_previous ( rrex3_t * ) ;
inline static void rrex3_cmp_asterisk ( rrex3_t * ) ;
void rrex3_cmp_literal_range ( rrex3_t * rrex3 ) {
# if RREX3_DEBUG == 1
printf ( " Range check: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
rrex3_set_previous ( rrex3 ) ;
char start = * rrex3 - > expr ;
rrex3 - > expr + + ;
rrex3 - > expr + + ;
char end = * rrex3 - > expr ;
if ( * rrex3 - > str > = start & & * rrex3 - > str < = end ) {
rrex3 - > str + + ;
rrex3 - > valid = true ;
} else {
rrex3 - > valid = false ;
}
rrex3 - > expr + + ;
}
bool rrex3_is_function ( char chr ) {
if ( chr = = ' ] ' | | chr = = ' ) ' | | chr = = ' \\ ' | | chr = = ' ? ' | | chr = = ' + ' | | chr = = ' * ' )
return true ;
return false ;
}
inline static void rrex3_cmp_literal ( rrex3_t * rrex3 ) {
rrex3_set_previous ( rrex3 ) ;
if ( rrex3 - > inside_brackets ) {
if ( isalpharange ( rrex3 - > expr ) | | isdigitrange ( rrex3 - > expr ) ) {
rrex3_cmp_literal_range ( rrex3 ) ;
return ;
}
}
# if RREX3_DEBUG == 1
printf ( " Literal check: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
if ( * rrex3 - > expr = = 0 & & ! * rrex3 - > str ) {
printf ( " ERROR, EMPTY CHECK \n " ) ;
// exit(1);
}
if ( rrex3 - > valid = = false ) {
rrex3 - > expr + + ;
return ;
}
if ( * rrex3 - > expr = = * rrex3 - > str ) {
rrex3 - > expr + + ;
rrex3 - > str + + ;
rrex3 - > valid = true ;
// if(*rrex3->expr &&rrex3->functions[(int)*rrex3->expr] ==
// rrex3_cmp_literal && !rrex3->inside_brackets &&
//! rrex3_is_function(*rrex3->expr)){ rrex3_cmp_literal(rrex3);
// if(rrex3->valid == false){
// rrex3->expr--;
// rrex3->valid = true;
// }
// }
return ;
}
rrex3 - > expr + + ;
rrex3 - > valid = false ;
}
inline static void rrex3_cmp_dot ( rrex3_t * rrex3 ) {
# if RREX3_DEBUG == 1
printf ( " Dot check (any char): %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
rrex3_set_previous ( rrex3 ) ;
rrex3 - > expr + + ;
if ( ! rrex3 - > valid ) {
return ;
}
if ( * rrex3 - > str & & * rrex3 - > str ! = ' \n ' ) {
rrex3 - > str + + ;
if ( * rrex3 - > expr & & * rrex3 - > expr = = ' . ' ) {
rrex3_cmp_dot ( rrex3 ) ;
return ;
} /*else if(*rrex3->expr && (*rrex3->expr == '*' || *rrex3->expr ==
'+')){ char * next = strchr(rrex3->str,*(rrex3->expr + 1)); char *
space = strchr(rrex3->str,'\n'); if(next && (!space || space > next)){
rrex3->str = next;
}
}*/
} else {
rrex3 - > valid = false ;
}
}
inline static void rrex3_cmp_question_mark ( rrex3_t * rrex3 ) {
# if RREX3_DEBUG == 1
printf ( " Question mark check: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
rrex3_set_previous ( rrex3 ) ;
if ( rrex3 - > valid = = false )
rrex3 - > valid = true ;
rrex3 - > expr + + ;
}
inline static void rrex3_cmp_whitespace ( rrex3_t * rrex3 ) {
# if RREX3_DEBUG == 1
printf ( " Whitespace check: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
rrex3_set_previous ( rrex3 ) ;
char c = * rrex3 - > expr ;
rrex3 - > valid = c = = ' ' | | c = = ' \n ' | | c = = ' \t ' ;
if ( rrex3 - > valid ) {
rrex3 - > str + + ;
}
rrex3 - > expr + + ;
}
inline static void rrex3_cmp_whitespace_upper ( rrex3_t * rrex3 ) {
# if RREX3_DEBUG == 1
printf ( " Non whitespace check: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
rrex3_set_previous ( rrex3 ) ;
char c = * rrex3 - > expr ;
rrex3 - > valid = ! ( c = = ' ' | | c = = ' \n ' | | c = = ' \t ' ) ;
if ( rrex3 - > valid ) {
rrex3 - > str + + ;
}
rrex3 - > expr + + ;
}
inline static void rrex3_cmp_plus2 ( rrex3_t * rrex3 ) {
# if RREX3_DEBUG == 1
printf ( " Plus check: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
rrex3_set_previous ( rrex3 ) ;
if ( rrex3 - > valid ) {
rrex3 - > str - - ;
} else {
return ;
}
char * original_expr = rrex3 - > expr ;
char * next = original_expr + 1 ;
char * loop_expr = rrex3 - > previous . expr - 1 ;
if ( * loop_expr = = ' + ' ) {
rrex3 - > valid = false ;
rrex3 - > pattern_error = true ;
rrex3 - > expr + + ;
return ;
}
bool success_next = false ;
bool success_next_once = false ;
bool success_current = false ;
char * next_next = NULL ;
char * next_str = rrex3 - > str ;
while ( * rrex3 - > str ) {
// Check if next matches
char * original_str = rrex3 - > str ;
rrex3 - > expr = next ;
rrex3 - > valid = true ;
if ( rrex3_move ( rrex3 , false ) ) {
success_next = true ;
next_next = rrex3 - > expr ;
next_str = rrex3 - > str ;
success_next_once = true ;
} else {
success_next = false ;
}
if ( success_next_once & & ! success_next ) {
break ;
}
// Check if current matches
rrex3 - > str = original_str ;
rrex3 - > expr = loop_expr ;
rrex3 - > valid = true ;
if ( ! * rrex3 - > str | | ! rrex3_move ( rrex3 , false ) ) {
success_current = false ;
} else {
success_current = true ;
if ( ! success_next ) {
next_next = rrex3 - > expr + 1 ; // +1 is the * itself
next_str = rrex3 - > str ;
}
}
if ( success_next & & ! success_current ) {
break ;
}
}
if ( ! next_next )
rrex3 - > expr = next ;
else {
rrex3 - > expr = next_next ;
}
rrex3 - > str = next_str ;
rrex3 - > valid = true ;
}
inline static void rrex3_cmp_plus ( rrex3_t * rrex3 ) {
# if RREX3_DEBUG == 1
rprintg ( " Asterisk start check: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
if ( ! rrex3 - > valid ) {
rrex3 - > expr + + ;
return ;
}
char * left = rrex3 - > previous . expr ;
// printf("%s\n",rrex3->str);
char * right = rrex3 - > expr + 1 ;
if ( * right = = ' ) ' ) {
right + + ;
}
int right_valid = 0 ;
bool right_valid_once = false ;
char * expr = right ;
char * right_str = rrex3 - > str ;
;
char * right_expr = NULL ;
char * str = rrex3 - > str ;
bool first_time = true ;
bool left_valid = true ;
char * str_prev = NULL ;
bool valid_from_start = true ;
;
while ( * rrex3 - > str ) {
if ( ! left_valid & & ! right_valid ) {
break ;
}
if ( right_valid & & ! left_valid ) {
str = right_str ;
break ;
}
rrex3 - > expr = right ;
rrex3 - > str = str ;
# if RREX3_DEBUG == 1
printf ( " r " ) ;
# endif
if ( * rrex3 - > str & & rrex3_move ( rrex3 , false ) ) {
right_valid + + ;
right_str = rrex3 - > str ;
expr = rrex3 - > expr ;
if ( ! right_valid_once ) {
right_expr = rrex3 - > expr ;
right_valid_once = true ;
}
} else {
right_valid = 0 ;
}
if ( first_time ) {
first_time = false ;
valid_from_start = right_valid ;
}
if ( right_valid & & ! valid_from_start & & right_valid > 0 ) {
expr = right_expr - 1 ;
;
if ( * ( right - 1 ) = = ' ) ' ) {
expr = right - 1 ;
}
break ;
}
if ( ( ! right_valid & & right_valid_once ) ) {
expr = right_expr ;
if ( * ( right - 1 ) = = ' ) ' ) {
str = str_prev ;
expr = right - 1 ;
}
break ;
}
str_prev = str ;
rrex3 - > valid = true ;
rrex3 - > str = str ;
rrex3 - > expr = left ;
# if RREX3_DEBUG == 1
printf ( " l " ) ;
# endif
if ( rrex3_move ( rrex3 , false ) ) {
left_valid = true ;
str = rrex3 - > str ;
} else {
left_valid = false ;
}
}
rrex3 - > expr = expr ;
rrex3 - > str = str ;
rrex3 - > valid = true ;
# if RREX3_DEBUG == 1
rprintg ( " Asterisk end check: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
}
inline static void rrex3_cmp_asterisk ( rrex3_t * rrex3 ) {
# if RREX3_DEBUG == 1
rprintg ( " Asterisk start check: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
if ( ! rrex3 - > valid ) {
rrex3 - > valid = true ;
rrex3 - > expr + + ;
return ;
}
rrex3 - > str = rrex3 - > previous . str ;
char * left = rrex3 - > previous . expr ;
// printf("%s\n",rrex3->str);
char * right = rrex3 - > expr + 1 ;
if ( * right = = ' ) ' ) {
right + + ;
}
int right_valid = 0 ;
bool right_valid_once = false ;
char * expr = right ;
char * right_str = rrex3 - > str ;
;
char * right_expr = NULL ;
char * str = rrex3 - > str ;
bool first_time = true ;
bool left_valid = true ;
char * str_prev = NULL ;
bool valid_from_start = true ;
;
while ( * rrex3 - > str ) {
if ( ! left_valid & & ! right_valid ) {
break ;
}
if ( right_valid & & ! left_valid ) {
str = right_str ;
break ;
}
rrex3 - > expr = right ;
rrex3 - > str = str ;
# if RREX3_DEBUG == 1
printf ( " r " ) ;
# endif
if ( * rrex3 - > str & & rrex3_move ( rrex3 , false ) ) {
right_valid + + ;
right_str = rrex3 - > str ;
expr = rrex3 - > expr ;
if ( ! right_valid_once ) {
right_expr = rrex3 - > expr ;
right_valid_once = true ;
}
} else {
right_valid = 0 ;
}
if ( first_time ) {
first_time = false ;
valid_from_start = right_valid ;
}
if ( right_valid & & ! valid_from_start & & right_valid > 0 ) {
expr = right_expr - 1 ;
if ( * ( right - 1 ) = = ' ) ' ) {
expr = right - 1 ;
}
break ;
}
if ( ( ! right_valid & & right_valid_once ) ) {
expr = right_expr ;
if ( * ( right - 1 ) = = ' ) ' ) {
str = str_prev ;
expr = right - 1 ;
}
break ;
}
str_prev = str ;
rrex3 - > valid = true ;
rrex3 - > str = str ;
rrex3 - > expr = left ;
# if RREX3_DEBUG == 1
printf ( " l " ) ;
# endif
if ( rrex3_move ( rrex3 , false ) ) {
left_valid = true ;
str = rrex3 - > str ;
} else {
left_valid = false ;
}
}
rrex3 - > expr = expr ;
rrex3 - > str = str ;
rrex3 - > valid = true ;
# if RREX3_DEBUG == 1
rprintg ( " Asterisk end check: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
}
inline static void rrex3_cmp_asterisk2 ( rrex3_t * rrex3 ) {
# if RREX3_DEBUG == 1
rprintg ( " Asterisk start check: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
if ( ! rrex3 - > valid ) {
rrex3 - > valid = true ;
rrex3 - > expr + + ;
return ;
}
if ( * rrex3 - > previous . expr = = ' * ' ) {
// Support for **
rrex3 - > valid = false ;
// rrex3->pattern_error = true;
rrex3 - > expr + + ;
return ;
}
rrex3 - > str = rrex3 - > previous . str ;
;
char * next = rrex3 - > expr + 1 ;
char * next_original = NULL ;
if ( * next = = ' * ' ) {
next + + ;
}
if ( * next = = ' ) ' & & * ( next + 1 ) ) {
next_original = next ;
next + + ;
}
char * loop_expr = rrex3 - > previous . expr ;
bool success_next = false ;
bool success_next_once = false ;
bool success_current = false ;
char * right_next = NULL ;
char * right_str = rrex3 - > str ;
while ( * rrex3 - > str & & * rrex3 - > expr & & * rrex3 - > expr ! = ' ) ' ) {
// Remember original_str because it's modified
// by checking right and should be restored
// for checking left so they're matching the
// same value.
char * original_str = rrex3 - > str ;
// Check if right matches.
// if(*next != ')'){
rrex3 - > expr = next ;
rrex3 - > valid = true ;
if ( rrex3_move ( rrex3 , false ) ) {
// Match rright.
success_next = true ;
if ( ! next_original ) {
if ( ! success_next_once ) {
right_next = rrex3 - > expr ;
}
} else {
right_next = next_original ;
break ;
}
right_str = rrex3 - > str ;
success_next_once = true ;
} else {
// No match Right.
success_next = false ;
}
//}
if ( success_next_once & & ! success_next ) {
// Matched previous time but now doesn't.
break ;
}
// Check if left matches.
rrex3 - > str = original_str ;
rrex3 - > expr = loop_expr ;
rrex3 - > valid = true ;
if ( ! rrex3_move ( rrex3 , false ) ) {
// No match left.
success_current = false ;
} else {
// Match left.
success_current = true ;
// NOT SURE< WITHOUT DOET HETZELFDE:
// original_str = rrex3->str;
if ( ! success_next ) {
right_str = rrex3 - > str ;
if ( * rrex3 - > expr ! = ' ) ' ) {
right_next = rrex3 - > expr + 1 ; // +1 is the * itself
} else {
// break;
}
}
}
if ( ( success_next & & ! success_current ) | | ( ! success_next & & ! success_current ) ) {
break ;
}
}
rrex3 - > expr = right_next ;
rrex3 - > str = right_str ;
rrex3 - > valid = true ;
# if RREX3_DEBUG == 1
rprintg ( " Asterisk end check: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
}
inline static void rrex3_cmp_roof ( rrex3_t * rrex3 ) {
rrex3_set_previous ( rrex3 ) ;
# if RREX3_DEBUG == 1
printf ( " <Roof check: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
rrex3 - > valid = rrex3 - > str = = rrex3 - > _str ;
rrex3 - > match_from_start = true ;
rrex3 - > expr + + ;
}
inline static void rrex3_cmp_dollar ( rrex3_t * rrex3 ) {
rrex3_set_previous ( rrex3 ) ;
# if RREX3_DEBUG == 1
printf ( " Dollar check: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
if ( * rrex3 - > str | | ! rrex3 - > valid ) {
rrex3 - > valid = false ;
}
rrex3 - > expr + + ;
}
inline static void rrex3_cmp_w ( rrex3_t * rrex3 ) {
rrex3_set_previous ( rrex3 ) ;
rrex3 - > expr + + ;
# if RREX3_DEBUG == 1
printf ( " Word check: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
if ( isalpha ( * rrex3 - > str ) ) {
rrex3 - > str + + ;
} else {
rrex3 - > valid = false ;
}
}
inline static void rrex3_cmp_w_upper ( rrex3_t * rrex3 ) {
rrex3_set_previous ( rrex3 ) ;
rrex3 - > expr + + ;
# if RREX3_DEBUG == 1
printf ( " !Word check: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
if ( ! isalpha ( * rrex3 - > str ) ) {
rrex3 - > str + + ;
} else {
rrex3 - > valid = false ;
}
}
inline static void rrex3_cmp_d ( rrex3_t * rrex3 ) {
rrex3_set_previous ( rrex3 ) ;
rrex3 - > expr + + ;
# if RREX3_DEBUG == 1
printf ( " Digit check: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
if ( isdigit ( * rrex3 - > str ) ) {
rrex3 - > str + + ;
} else {
rrex3 - > valid = false ;
}
}
inline static void rrex3_cmp_d_upper ( rrex3_t * rrex3 ) {
rrex3_set_previous ( rrex3 ) ;
rrex3 - > expr + + ;
# if RREX3_DEBUG == 1
printf ( " !Digit check: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
if ( ! isdigit ( * rrex3 - > str ) ) {
rrex3 - > str + + ;
} else {
rrex3 - > valid = false ;
}
}
inline static void rrex3_cmp_slash ( rrex3_t * rrex3 ) {
rrex3_set_previous ( rrex3 ) ;
rrex3 - > expr + + ;
rrex3 - > bytecode = * rrex3 - > expr ;
rrex3 - > function = rrex3 - > slash_functions [ ( int ) rrex3 - > bytecode ] ;
rrex3 - > function ( rrex3 ) ;
}
inline static int collect_digits ( rrex3_t * rrex3 ) {
char output [ 20 ] ;
unsigned int digit_count = 0 ;
while ( isdigit ( * rrex3 - > expr ) ) {
output [ digit_count ] = * rrex3 - > expr ;
rrex3 - > expr + + ;
digit_count + + ;
}
output [ digit_count ] = 0 ;
return atoi ( output ) ;
}
inline static void rrex3_cmp_range ( rrex3_t * rrex3 ) {
char * loop_code = rrex3 - > previous . expr ;
char * expr_original = rrex3 - > expr ;
rrex3 - > expr + + ;
int range_start = collect_digits ( rrex3 ) - 1 ;
int range_end = 0 ;
if ( * rrex3 - > expr = = ' , ' ) {
rrex3 - > expr + + ;
range_end = collect_digits ( rrex3 ) ;
}
rrex3 - > expr + + ;
int times_valid = 0 ;
while ( * rrex3 - > str ) {
rrex3 - > expr = loop_code ;
rrex3_move ( rrex3 , false ) ;
if ( rrex3 - > valid = = false ) {
break ;
} else {
times_valid + + ;
}
if ( range_end ) {
if ( times_valid > = range_start & & times_valid = = range_end - 1 ) {
rrex3 - > valid = true ;
} else {
rrex3 - > valid = false ;
}
break ;
} else if ( range_start ) {
if ( times_valid = = range_start ) {
rrex3 - > valid = true ;
break ;
}
}
}
rrex3 - > valid = times_valid > = range_start ;
if ( rrex3 - > valid & & range_end ) {
rrex3 - > valid = times_valid < = range_end ;
}
rrex3 - > expr = strchr ( expr_original , ' } ' ) + 1 ;
}
inline static void rrex3_cmp_word_start_or_end ( rrex3_t * rrex3 ) {
# if RREX3_DEBUG == 1
if ( * rrex3 - > expr ! = ' B ' ) {
printf ( " Check word start or end: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
}
# endif
rrex3_set_previous ( rrex3 ) ;
bool valid = false ;
if ( isalpha ( * rrex3 - > str ) ) {
if ( rrex3 - > _str ! = rrex3 - > str ) {
if ( ! isalpha ( * ( rrex3 - > str - 1 ) ) ) {
valid = true ;
}
} else {
valid = true ;
}
} else if ( isalpha ( isalpha ( * rrex3 - > str ) & & ! isalpha ( * rrex3 - > str + 1 ) ) ) {
valid = true ;
}
rrex3 - > expr + + ;
rrex3 - > valid = valid ;
}
inline static void rrex3_cmp_word_not_start_or_end ( rrex3_t * rrex3 ) {
# if RREX3_DEBUG == 1
printf ( " Check word NOT start or end: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
rrex3_set_previous ( rrex3 ) ;
rrex3_cmp_word_start_or_end ( rrex3 ) ;
rrex3 - > valid = ! rrex3 - > valid ;
}
inline static void rrex3_cmp_brackets ( rrex3_t * rrex3 ) {
# if RREX3_DEBUG == 1
rprintb ( " \\ l Brackets start: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
rrex3_set_previous ( rrex3 ) ;
char * original_expr = rrex3 - > expr ;
rrex3 - > expr + + ;
rrex3 - > inside_brackets = true ;
bool valid_once = false ;
bool reversed = false ;
if ( * rrex3 - > expr = = ' ^ ' ) {
reversed = true ;
rrex3 - > expr + + ;
}
bool valid = false ;
while ( * rrex3 - > expr ! = ' ] ' & & * rrex3 - > expr ! = 0 ) {
rrex3 - > valid = true ;
valid = rrex3_move ( rrex3 , false ) ;
if ( reversed ) {
valid = ! valid ;
}
if ( valid ) {
valid_once = true ;
if ( ! reversed ) {
valid_once = true ;
break ;
}
} else {
if ( reversed ) {
valid_once = false ;
break ;
}
}
}
if ( valid_once & & reversed ) {
rrex3 - > str + + ;
}
while ( * rrex3 - > expr ! = ' ] ' & & * rrex3 - > expr ! = 0 )
rrex3 - > expr + + ;
if ( * rrex3 - > expr ! = 0 )
rrex3 - > expr + + ;
rrex3 - > valid = valid_once ;
rrex3 - > inside_brackets = false ;
char * previous_expr = rrex3 - > expr ;
rrex3 - > expr = original_expr ;
rrex3_set_previous ( rrex3 ) ;
rrex3 - > expr = previous_expr ;
# if RREX3_DEBUG == 1
rprintb ( " \\ l Brackets end: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
}
inline static void rrex3_cmp_pipe ( rrex3_t * rrex3 ) {
rrex3_set_previous ( rrex3 ) ;
# if RREX3_DEBUG == 1
printf ( " Pipe check: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
if ( rrex3 - > valid = = true ) {
rrex3 - > exit = true ;
} else {
rrex3 - > valid = true ;
}
rrex3 - > expr + + ;
}
inline static void rrex3_cmp_parentheses ( rrex3_t * rrex3 ) {
# if RREX3_DEBUG == 1
rprinty ( " \\ l Parentheses start check: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
rrex3_set_previous ( rrex3 ) ;
if ( ! rrex3 - > valid ) {
rrex3 - > expr + + ;
return ;
}
if ( rrex3 - > match_count = = rrex3 - > match_capacity ) {
rrex3 - > match_capacity + + ;
rrex3 - > matches = ( char * * ) realloc ( rrex3 - > matches , rrex3 - > match_capacity * sizeof ( char * ) ) ;
}
rrex3 - > matches [ rrex3 - > match_count ] = ( char * ) malloc ( strlen ( rrex3 - > str ) + 1 ) ;
strcpy ( rrex3 - > matches [ rrex3 - > match_count ] , rrex3 - > str ) ;
char * original_expr = rrex3 - > expr ;
char * original_str = rrex3 - > str ;
rrex3 - > expr + + ;
rrex3 - > inside_parentheses = true ;
while ( * rrex3 - > expr ! = ' ) ' & & ! rrex3 - > exit ) {
rrex3_move ( rrex3 , false ) ;
}
while ( * rrex3 - > expr ! = ' ) ' ) {
rrex3 - > expr + + ;
}
rrex3 - > expr + + ;
rrex3 - > inside_parentheses = false ;
char * previous_expr = rrex3 - > expr ;
rrex3 - > expr = original_expr ;
rrex3_set_previous ( rrex3 ) ;
rrex3 - > expr = previous_expr ;
if ( rrex3 - > valid = = false ) {
rrex3 - > str = original_str ;
free ( rrex3 - > matches [ rrex3 - > match_count ] ) ;
} else {
rrex3 - > matches [ rrex3 - > match_count ] [ strlen ( rrex3 - > matches [ rrex3 - > match_count ] ) - strlen ( rrex3 - > str ) ] = 0 ;
rrex3 - > match_count + + ;
}
# if RREX3_DEBUG == 1
rprinty ( " \\ l Parentheses end: %c:%c:%d \n " , * rrex3 - > expr , * rrex3 - > str , rrex3 - > valid ) ;
# endif
}
inline static void rrex3_reset ( rrex3_t * rrex3 ) {
rrex3_free_matches ( rrex3 ) ;
rrex3 - > valid = true ;
rrex3 - > pattern_error = false ;
rrex3 - > inside_brackets = false ;
rrex3 - > inside_parentheses = false ;
rrex3 - > exit = false ;
rrex3 - > previous . expr = NULL ;
rrex3 - > previous . str = NULL ;
rrex3 - > previous . bytecode = 0 ;
rrex3 - > failed . expr = NULL ;
rrex3 - > failed . str = NULL ;
rrex3 - > failed . bytecode = 0 ;
rrex3 - > match_from_start = false ;
}
void rrex3_init ( rrex3_t * rrex3 ) {
for ( __uint8_t i = 0 ; i < 254 ; i + + ) {
rrex3 - > functions [ i ] = rrex3_cmp_literal ;
rrex3 - > slash_functions [ i ] = rrex3_cmp_literal ;
}
rrex3 - > functions [ ' ? ' ] = rrex3_cmp_question_mark ;
rrex3 - > functions [ ' ^ ' ] = rrex3_cmp_roof ;
rrex3 - > functions [ ' $ ' ] = rrex3_cmp_dollar ;
rrex3 - > functions [ ' . ' ] = rrex3_cmp_dot ;
rrex3 - > functions [ ' * ' ] = rrex3_cmp_asterisk ;
rrex3 - > functions [ ' + ' ] = rrex3_cmp_plus ;
rrex3 - > functions [ ' | ' ] = rrex3_cmp_pipe ;
rrex3 - > functions [ ' \\ ' ] = rrex3_cmp_slash ;
rrex3 - > functions [ ' { ' ] = rrex3_cmp_range ;
rrex3 - > functions [ ' [ ' ] = rrex3_cmp_brackets ;
rrex3 - > functions [ ' ( ' ] = rrex3_cmp_parentheses ;
rrex3 - > slash_functions [ ' w ' ] = rrex3_cmp_w ;
rrex3 - > slash_functions [ ' W ' ] = rrex3_cmp_w_upper ;
rrex3 - > slash_functions [ ' d ' ] = rrex3_cmp_d ;
rrex3 - > slash_functions [ ' D ' ] = rrex3_cmp_d_upper ;
rrex3 - > slash_functions [ ' s ' ] = rrex3_cmp_whitespace ;
rrex3 - > slash_functions [ ' S ' ] = rrex3_cmp_whitespace_upper ;
rrex3 - > slash_functions [ ' b ' ] = rrex3_cmp_word_start_or_end ;
rrex3 - > slash_functions [ ' B ' ] = rrex3_cmp_word_not_start_or_end ;
rrex3 - > match_count = 0 ;
rrex3 - > match_capacity = 0 ;
rrex3 - > matches = NULL ;
rrex3 - > compiled = NULL ;
rrex3_reset ( rrex3 ) ;
}
rrex3_t * rrex3_new ( ) {
rrex3_t * rrex3 = ( rrex3_t * ) malloc ( sizeof ( rrex3_t ) ) ;
rrex3_init ( rrex3 ) ;
return rrex3 ;
}
rrex3_t * rrex3_compile ( rrex3_t * rrex , char * expr ) {
rrex3_t * rrex3 = rrex ? rrex : rrex3_new ( ) ;
char * compiled = ( char * ) malloc ( strlen ( expr ) + 1 ) ;
unsigned int count = 0 ;
while ( * expr ) {
if ( * expr = = ' [ ' & & * ( expr + 2 ) = = ' ] ' ) {
* compiled = * ( expr + 1 ) ;
expr + + ;
expr + + ;
} else if ( * expr = = ' [ ' & & * ( expr + 1 ) = = ' 0 ' & & * ( expr + 2 ) = = ' - ' & & * ( expr + 3 ) = = ' 9 ' & & * ( expr + 4 ) = = ' ] ' ) {
* compiled = ' \\ ' ;
compiled + + ;
* compiled = ' d ' ;
count + + ;
expr + + ;
expr + + ;
expr + + ;
expr + + ;
} else {
* compiled = * expr ;
}
if ( * compiled = = ' [ ' ) {
// in_brackets = true;
} else if ( * compiled = = ' ] ' ) {
// in_brackets = false;
}
expr + + ;
compiled + + ;
count + + ;
}
* compiled = 0 ;
compiled - = count ;
rrex3 - > compiled = compiled ;
return rrex3 ;
}
inline static void rrex3_set_previous ( rrex3_t * rrex3 ) {
rrex3 - > previous . function = rrex3 - > function ;
rrex3 - > previous . expr = rrex3 - > expr ;
rrex3 - > previous . str = rrex3 - > str ;
rrex3 - > previous . bytecode = * rrex3 - > expr ;
}
static bool rrex3_move ( rrex3_t * rrex3 , bool resume_on_fail ) {
char * original_expr = rrex3 - > expr ;
char * original_str = rrex3 - > str ;
rrex3 - > bytecode = * rrex3 - > expr ;
rrex3 - > function = rrex3 - > functions [ ( int ) rrex3 - > bytecode ] ;
rrex3 - > function ( rrex3 ) ;
if ( ! * rrex3 - > expr & & ! * rrex3 - > str ) {
rrex3 - > exit = true ;
return rrex3 - > valid ;
} else if ( ! * rrex3 - > expr ) {
// rrex3->valid = true;
return rrex3 - > valid ;
}
if ( rrex3 - > pattern_error ) {
rrex3 - > valid = false ;
return rrex3 - > valid ;
}
if ( resume_on_fail & & ! rrex3 - > valid & & * rrex3 - > expr ) {
// rrex3_set_previous(rrex3);
rrex3 - > failed . bytecode = rrex3 - > bytecode ;
rrex3 - > failed . function = rrex3 - > function ;
rrex3 - > failed . expr = original_expr ;
rrex3 - > failed . str = original_str ;
rrex3 - > bytecode = * rrex3 - > expr ;
rrex3 - > function = rrex3 - > functions [ ( int ) rrex3 - > bytecode ] ;
rrex3 - > function ( rrex3 ) ;
if ( ! rrex3 - > valid & & ! rrex3 - > pattern_error ) {
if ( * rrex3 - > str ) {
char * pipe_position = strstr ( rrex3 - > expr , " | " ) ;
if ( pipe_position ! = NULL ) {
rrex3 - > expr = pipe_position + 1 ;
rrex3 - > str = rrex3 - > _str ;
rrex3 - > valid = true ;
return true ;
}
}
if ( rrex3 - > match_from_start ) {
rrex3 - > valid = false ;
return rrex3 - > valid ;
}
if ( ! * rrex3 - > str + + ) {
rrex3 - > valid = false ;
return rrex3 - > valid ;
}
rrex3 - > expr = rrex3 - > _expr ;
if ( * rrex3 - > str )
rrex3 - > valid = true ;
}
} else {
}
return rrex3 - > valid ;
}
rrex3_t * rrex3 ( rrex3_t * rrex3 , char * str , char * expr ) {
# if RREX3_DEBUG == 1
printf ( " Regex check: %s:%s:%d \n " , expr , str , 1 ) ;
# endif
bool self_initialized = false ;
if ( rrex3 = = NULL ) {
self_initialized = true ;
rrex3 = rrex3_new ( ) ;
} else {
rrex3_reset ( rrex3 ) ;
}
rrex3 - > _str = str ;
rrex3 - > _expr = rrex3 - > compiled ? rrex3 - > compiled : expr ;
rrex3 - > str = rrex3 - > _str ;
rrex3 - > expr = rrex3 - > _expr ;
while ( * rrex3 - > expr & & ! rrex3 - > exit ) {
if ( ! rrex3_move ( rrex3 , true ) )
return NULL ;
}
rrex3 - > expr = rrex3 - > _expr ;
if ( rrex3 - > valid ) {
return rrex3 ;
} else {
if ( self_initialized ) {
rrex3_free ( rrex3 ) ;
}
return NULL ;
}
}
void rrex3_test ( ) {
rrex3_t * rrex = rrex3_new ( ) ;
assert ( rrex3 ( rrex , " \" stdio.h \" \" string.h \" \" sys/time.h \" " , " \" (.*) \" \" (.*) \" \" (.*) \" " ) ) ;
assert ( rrex3 ( rrex , " aaaaaaa " , " a*a$ " ) ) ;
// assert(rrex3("ababa", "a*b*a*b*a$"));
assert ( rrex3 ( rrex , " #include \" test.h \" a " , " #include.* \" .* \" a$ " ) ) ;
assert ( rrex3 ( rrex , " #include \" test.h \" a " , " #include.* \" .* \" a$ " ) ) ;
assert ( rrex3 ( rrex , " aaaaaad " , " a*d$ " ) ) ;
assert ( rrex3 ( rrex , " abcdef " , " abd?cdef " ) ) ;
assert ( ! rrex3 ( rrex , " abcdef " , " abd?def " ) ) ;
assert ( rrex3 ( rrex , " abcdef " , " def " ) ) ;
assert ( ! rrex3 ( rrex , " abcdef " , " ^def " ) ) ;
assert ( rrex3 ( rrex , " abcdef " , " def$ " ) ) ;
assert ( ! rrex3 ( rrex , " abcdef " , " ^abc$ " ) ) ;
assert ( rrex3 ( rrex , " aB!.#1 " , " ...... " ) ) ;
assert ( ! rrex3 ( rrex , " aB!.# \n " , " ...... " ) ) ;
assert ( ! rrex3 ( rrex , " aaaaaad " , " q+d$ " ) ) ;
assert ( rrex3 ( rrex , " aaaaaaa " , " a+a$ " ) ) ;
assert ( rrex3 ( rrex , " aaaaaad " , " q*d$ " ) ) ;
assert ( ! rrex3 ( rrex , " aaaaaad " , " ^q*d$ " ) ) ;
// Asterisk function
assert ( rrex3 ( rrex , " 123321 " , " 123*321 " ) ) ;
assert ( rrex3 ( rrex , " pony " , " p*ony " ) ) ;
assert ( rrex3 ( rrex , " pppony " , " p*ony " ) ) ;
assert ( rrex3 ( rrex , " ppony " , " p*pony " ) ) ;
assert ( rrex3 ( rrex , " pppony " , " pp*pony " ) ) ;
assert ( rrex3 ( rrex , " pppony " , " .*pony " ) ) ;
assert ( rrex3 ( rrex , " pony " , " .*ony " ) ) ;
assert ( rrex3 ( rrex , " pony " , " po*ny " ) ) ;
// assert(rrex3(rrex,"ppppony", "p*pppony"));
// Plus function
assert ( rrex3 ( rrex , " pony " , " p+ony " ) ) ;
assert ( ! rrex3 ( rrex , " ony " , " p+ony " ) ) ;
assert ( rrex3 ( rrex , " ppony " , " p+pony " ) ) ;
assert ( rrex3 ( rrex , " pppony " , " pp+pony " ) ) ;
assert ( rrex3 ( rrex , " pppony " , " .+pony " ) ) ;
assert ( rrex3 ( rrex , " pony " , " .+ony " ) ) ;
assert ( rrex3 ( rrex , " pony " , " po+ny " ) ) ;
// Slash functions
assert ( rrex3 ( rrex , " a " , " \\ w " ) ) ;
assert ( ! rrex3 ( rrex , " 1 " , " \\ w " ) ) ;
assert ( rrex3 ( rrex , " 1 " , " \\ W " ) ) ;
assert ( ! rrex3 ( rrex , " a " , " \\ W " ) ) ;
assert ( rrex3 ( rrex , " a " , " \\ S " ) ) ;
assert ( ! rrex3 ( rrex , " " , " \\ s " ) ) ;
assert ( ! rrex3 ( rrex , " \t " , " \\ s " ) ) ;
assert ( ! rrex3 ( rrex , " \n " , " \\ s " ) ) ;
assert ( rrex3 ( rrex , " 1 " , " \\ d " ) ) ;
assert ( ! rrex3 ( rrex , " a " , " \\ d " ) ) ;
assert ( rrex3 ( rrex , " a " , " \\ D " ) ) ;
assert ( ! rrex3 ( rrex , " 1 " , " \\ D " ) ) ;
assert ( rrex3 ( rrex , " abc " , " \\ b " ) ) ;
assert ( rrex3 ( rrex , " abc " , " \\ babc " ) ) ;
assert ( ! rrex3 ( rrex , " abc " , " a \\ b " ) ) ;
assert ( ! rrex3 ( rrex , " abc " , " ab \\ b " ) ) ;
assert ( ! rrex3 ( rrex , " abc " , " abc \\ b " ) ) ;
assert ( rrex3 ( rrex , " abc " , " a \\ Bbc " ) ) ;
assert ( rrex3 ( rrex , " abc " , " ab \\ B " ) ) ;
assert ( ! rrex3 ( rrex , " 1ab " , " 1 \\ Bab " ) ) ;
assert ( rrex3 ( rrex , " abc " , " a \\ Bbc " ) ) ;
// Escaping of special chars
assert ( rrex3 ( rrex , " ()+*. \\ " , " \\ ( \\ ) \\ + \\ * \\ . \\ \\ " ) ) ;
// Pipe
// assert(rrex3(rrex,"abc","abc|def"));
assert ( rrex3 ( rrex , " abc " , " def|jkl|abc " ) ) ;
assert ( rrex3 ( rrex , " abc " , " abc|def " ) ) ;
assert ( rrex3 ( rrex , " rhq " , " def|rhq|rha " ) ) ;
assert ( rrex3 ( rrex , " abc " , " abc|def " ) ) ;
// Repeat
assert ( rrex3 ( rrex , " aaaaa " , " a{4} " ) ) ;
assert ( rrex3 ( rrex , " aaaa " , " a{1,3}a " ) ) ;
// Range
assert ( rrex3 ( rrex , " abc " , " [abc][abc][abc]$ " ) ) ;
assert ( rrex3 ( rrex , " def " , " [^abc][^abc][^abc]$ " ) ) ;
assert ( rrex3 ( rrex , " defabc " , " [^abc][^abc][^abc]abc " ) ) ;
assert ( rrex3 ( rrex , " 0-9 " , " 0-9 " ) ) ;
assert ( rrex3 ( rrex , " 55-9 " , " [^6-9]5-9$ " ) ) ;
assert ( rrex3 ( rrex , " a " , " [a-z]$ " ) ) ;
assert ( rrex3 ( rrex , " A " , " [A-Z]$ " ) ) ;
assert ( rrex3 ( rrex , " 5 " , " [0-9]$ " ) ) ;
assert ( ! rrex3 ( rrex , " a " , " [^a-z]$ " ) ) ;
assert ( ! rrex3 ( rrex , " A " , " [^A-Z]$ " ) ) ;
assert ( ! rrex3 ( rrex , " 5 " , " [^0-9]$ " ) ) ;
assert ( rrex3 ( rrex , " 123abc " , " [0-9]*abc$ " ) ) ;
assert ( rrex3 ( rrex , " 123123 " , " [0-9]*$ " ) ) ;
// Parentheses
assert ( rrex3 ( rrex , " datadata " , " (data)* " ) ) ;
assert ( rrex3 ( rrex , " datadatapony " , " (data)*pony$ " ) ) ;
assert ( ! rrex3 ( rrex , " datadatapony " , " (d*p*ata)*pond$ " ) ) ;
assert ( rrex3 ( rrex , " datadatadato " , " (d*p*ata)*dato " ) ) ;
assert ( rrex3 ( rrex , " datadatadato " , " (d*p*ata)*dato$ " ) ) ;
assert ( ! rrex3 ( rrex , " datadatadato " , " (d*p*a*ta)*gato$ " ) ) ;
// Matches
assert ( rrex3 ( rrex , " 123 " , " (123) " ) ) ;
assert ( ! strcmp ( rrex - > matches [ 0 ] , " 123 " ) ) ;
assert ( rrex3 ( rrex , " 123321a " , " (123)([0-4][2]1)a$ " ) ) ;
assert ( ! strcmp ( rrex - > matches [ 1 ] , " 321 " ) ) ;
assert ( rrex3 ( rrex , " 123321a " , " (123)([0-4][2]1)a$ " ) ) ;
assert ( ! strcmp ( rrex - > matches [ 1 ] , " 321 " ) ) ;
assert ( rrex3 ( rrex , " aaaabc " , " (.*)c " ) ) ;
assert ( rrex3 ( rrex , " abcde " , " .....$ " ) ) ;
assert ( rrex3 ( rrex , " abcdefghijklmnopqrstuvwxyz " , " ..........................$ " ) ) ;
// printf("(%d)\n", rrex->valid);
assert ( rrex3 ( rrex , " #include <stdio.h> " , " #include.*<(.*)> " ) ) ;
assert ( ! strcmp ( rrex - > matches [ 0 ] , " stdio.h " ) ) ;
assert ( rrex3 ( rrex , " #include \" stdlib.h \" " , " #include. \" (.*) \" " ) ) ;
assert ( ! strcmp ( rrex - > matches [ 0 ] , " stdlib.h " ) ) ;
assert ( rrex3 ( rrex , " \" stdio.h \" \" string.h \" \" sys/time.h \" " , " \" (.*) \" \" (.*) \" \" (.*) \" " ) ) ;
assert ( ! strcmp ( rrex - > matches [ 0 ] , " stdio.h " ) ) ;
assert ( ! strcmp ( rrex - > matches [ 1 ] , " string.h " ) ) ;
assert ( ! strcmp ( rrex - > matches [ 2 ] , " sys/time.h " ) ) ;
assert ( rrex3 ( rrex , " #include <stdio.h> " , " #include.+<(.+)> " ) ) ;
assert ( ! strcmp ( rrex - > matches [ 0 ] , " stdio.h " ) ) ;
assert ( rrex3 ( rrex , " #include \" stdlib.h \" " , " #include.+ \" (.+) \" " ) ) ;
assert ( ! strcmp ( rrex - > matches [ 0 ] , " stdlib.h " ) ) ;
assert ( rrex3 ( rrex , " \" stdio.h \" \" string.h \" \" sys/time.h \" " , " \" (.+) \" \" (.+) \" \" (.+) \" " ) ) ;
assert ( ! strcmp ( rrex - > matches [ 0 ] , " stdio.h " ) ) ;
assert ( ! strcmp ( rrex - > matches [ 1 ] , " string.h " ) ) ;
assert ( ! strcmp ( rrex - > matches [ 2 ] , " sys/time.h " ) ) ;
assert ( rrex3 ( rrex , " int abc " , " int (.*)[; ]?$ " ) ) ;
assert ( ! strcmp ( rrex - > matches [ 0 ] , " abc " ) ) ;
assert ( rrex3 ( rrex , " int abc; " , " int (.*)[; ]?$ " ) ) ;
assert ( ! strcmp ( rrex - > matches [ 0 ] , " abc " ) ) ;
assert ( rrex3 ( rrex , " int abc " , " int (.*)[; ]?$ " ) ) ;
assert ( ! strcmp ( rrex - > matches [ 0 ] , " abc " ) ) ;
rrex3_free ( rrex ) ;
}
# endif
# ifndef RARENA_H
# define RARENA_H
# include <stdlib.h>
# include <string.h>
typedef struct arena_t {
unsigned char * memory ;
unsigned int pointer ;
unsigned int size ;
} arena_t ;
arena_t * arena_construct ( ) {
arena_t * arena = ( arena_t * ) rmalloc ( sizeof ( arena_t ) ) ;
arena - > memory = NULL ;
arena - > pointer = 0 ;
arena - > size = 0 ;
return arena ;
}
arena_t * arena_new ( size_t size ) {
arena_t * arena = arena_construct ( ) ;
arena - > memory = ( unsigned char * ) rmalloc ( size ) ;
arena - > size = size ;
return arena ;
}
void * arena_alloc ( arena_t * arena , size_t size ) {
if ( arena - > pointer + size > arena - > size ) {
return NULL ;
}
void * p = arena - > memory + arena - > pointer ;
arena - > pointer + = size ;
return p ;
}
void arena_free ( arena_t * arena ) {
// Just constructed and unused arena memory is NULL so no free needed
if ( arena - > memory ) {
rfree ( arena - > memory ) ;
}
rfree ( arena ) ;
}
void arena_reset ( arena_t * arena ) { arena - > pointer = 0 ; }
# endif
# ifndef RCASE_H
# define RCASE_H
# include <ctype.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys/types.h>
# define RCAMEL_CASE 1
# define RSNAKE_CASE 2
# define RINVALID_CASE 0
# define RCONST_TEST_T 4;
int rdetermine_case ( const char * str ) {
int length = strlen ( str ) ;
char p = 0 ;
while ( * str ) {
if ( p = = ' _ ' & & islower ( * str ) )
return RSNAKE_CASE ;
if ( p ! = ' _ ' & & ! isupper ( p ) & & isupper ( * str ) )
return RCAMEL_CASE ;
p = * str ;
str + + ;
}
return RINVALID_CASE ;
if ( length = = 0 ) {
return RINVALID_CASE ;
}
if ( strchr ( str , ' _ ' ) ) {
if ( str [ 0 ] = = ' _ ' | | str [ length - 1 ] = = ' _ ' | | strstr ( str , " __ " ) ) {
return RINVALID_CASE ;
}
for ( int i = 0 ; i < length ; i + + ) {
if ( ! islower ( str [ i ] ) & & str [ i ] ! = ' _ ' ) {
return RINVALID_CASE ;
}
}
return RSNAKE_CASE ;
} else {
if ( ! islower ( str [ 0 ] ) ) {
return RINVALID_CASE ;
}
for ( int i = 1 ; i < length ; i + + ) {
if ( str [ i ] = = ' _ ' ) {
return RINVALID_CASE ;
}
if ( isupper ( str [ i ] ) & & isupper ( str [ i - 1 ] ) ) {
return RINVALID_CASE ;
}
}
return RCAMEL_CASE ;
}
}
char * rsnake_to_camel ( const char * snake_case ) {
int length = strlen ( snake_case ) ;
char * camel_case = ( char * ) malloc ( length + 1 ) ;
int j = 0 ;
int toUpper = 0 ;
for ( int i = 0 ; i < length ; i + + ) {
if ( i > 0 & & snake_case [ i ] = = ' _ ' & & snake_case [ i + 1 ] = = ' T ' ) {
toUpper = 1 ;
if ( snake_case [ i + 1 ] = = ' T ' & & ( snake_case [ i + 2 ] ! = ' \n ' | | snake_case [ i + 2 ] ! = ' \0 ' | | snake_case [ i + 2 ] ! = ' ' ) ) {
toUpper = 0 ;
}
}
if ( snake_case [ i ] = = ' _ ' & & snake_case [ i + 1 ] ! = ' t ' ) {
toUpper = 1 ;
if ( snake_case [ i + 1 ] = = ' t ' & & ( snake_case [ i + 2 ] ! = ' \n ' | | snake_case [ i + 2 ] ! = ' \0 ' | | snake_case [ i + 2 ] ! = ' ' ) ) {
toUpper = 0 ;
}
} else if ( snake_case [ i ] = = ' _ ' & & snake_case [ i + 1 ] = = ' t ' & & ! isspace ( snake_case [ i + 2 ] ) ) {
toUpper = 1 ;
} else if ( snake_case [ i ] = = ' _ ' & & snake_case [ i + 1 ] = = ' T ' & & ! isspace ( snake_case [ i + 2 ] ) ) {
toUpper = 1 ;
camel_case [ j + + ] = ' _ ' ;
j + + ;
} else {
if ( toUpper ) {
camel_case [ j + + ] = toupper ( snake_case [ i ] ) ;
toUpper = 0 ;
} else {
camel_case [ j + + ] = snake_case [ i ] ;
}
}
}
camel_case [ j ] = ' \0 ' ;
return camel_case ;
}
char * rcamel_to_snake ( const char * camelCase ) {
int length = strlen ( camelCase ) ;
char * snake_case = ( char * ) malloc ( 2 * length + 1 ) ;
int j = 0 ;
for ( int i = 0 ; i < length ; i + + ) {
if ( isupper ( camelCase [ i ] ) ) {
if ( i ! = 0 ) {
snake_case [ j + + ] = ' _ ' ;
}
snake_case [ j + + ] = tolower ( camelCase [ i ] ) ;
} else {
snake_case [ j + + ] = camelCase [ i ] ;
}
}
snake_case [ j ] = ' \0 ' ;
return snake_case ;
}
char * rflip_case ( char * content ) {
if ( rdetermine_case ( content ) = = RSNAKE_CASE ) {
return rcamel_to_snake ( content ) ;
} else if ( rdetermine_case ( content ) = = RCAMEL_CASE ) {
return rsnake_to_camel ( content ) ;
} else {
rprintr ( " Could not determine case \n " ) ;
return NULL ;
}
}
char * rflip_case_file ( char * filepath ) {
size_t file_size = rfile_size ( filepath ) ;
if ( file_size = = 0 ) {
return NULL ;
}
char * content = ( char * ) malloc ( file_size ) ;
char * result = NULL ;
if ( rfile_readb ( filepath , content , file_size ) ) {
result = rflip_case ( content ) ;
if ( result ) {
free ( content ) ;
return result ;
} else {
return content ;
}
}
return result ;
}
int rcase_main ( int argc , char * argv [ ] ) {
if ( argc < 2 ) {
printf ( " usage: rcase <file> \n " ) ;
return 1 ;
}
for ( int i = 1 ; i < argc ; i + + ) {
char * result = rflip_case_file ( argv [ i ] ) ;
if ( result ) {
printf ( " %s \n " , result ) ;
free ( result ) ;
}
}
return 0 ;
}
# endif
# ifndef RTERM_H
# define RTERM_H
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys/ioctl.h>
# include <termios.h>
# include <unistd.h>
typedef struct winsize winsize_t ;
typedef struct rshell_keypress_t {
bool pressed ;
bool ctrl ;
bool shift ;
bool escape ;
char c ;
int ms ;
int fd ;
} rshell_keypress_t ;
typedef struct rterm_t {
bool show_cursor ;
bool show_footer ;
int ms_tick ;
rshell_keypress_t key ;
void ( * before_cursor_move ) ( struct rterm_t * ) ;
void ( * after_cursor_move ) ( struct rterm_t * ) ;
void ( * after_key_press ) ( struct rterm_t * ) ;
void ( * before_key_press ) ( struct rterm_t * ) ;
void ( * before_draw ) ( struct rterm_t * ) ;
void ( * after_draw ) ( struct rterm_t * ) ;
void * session ;
unsigned long iterations ;
void ( * tick ) ( struct rterm_t * ) ;
char * status_text ;
char * _status_text_previous ;
winsize_t size ;
struct {
int x ;
int y ;
int pos ;
int available ;
} cursor ;
} rterm_t ;
typedef void ( * rterm_event ) ( rterm_t * ) ;
void rterm_init ( rterm_t * rterm ) {
memset ( rterm , 0 , sizeof ( rterm_t ) ) ;
rterm - > show_cursor = true ;
rterm - > cursor . x = 0 ;
rterm - > cursor . y = 0 ;
rterm - > ms_tick = 100 ;
rterm - > _status_text_previous = NULL ;
}
void rterm_getwinsize ( winsize_t * w ) {
// Get the terminal size
if ( ioctl ( STDOUT_FILENO , TIOCGWINSZ , w ) = = - 1 ) {
perror ( " ioctl " ) ;
exit ( EXIT_FAILURE ) ;
}
}
void rrawfd ( int fd ) {
struct termios orig_termios ;
tcgetattr ( fd , & orig_termios ) ; // Get current terminal attributes
struct termios raw = orig_termios ;
raw . c_lflag & = ~ ( ICANON | ISIG | ECHO ) ; // ECHO // Disable canonical mode and echoing
raw . c_cc [ VMIN ] = 1 ;
raw . c_cc [ VTIME ] = 240 ; // Set timeout for read input
tcsetattr ( fd , TCSAFLUSH , & raw ) ;
}
// Terminal setup functions
void enableRawMode ( struct termios * orig_termios ) {
struct termios raw = * orig_termios ;
raw . c_lflag & = ~ ( ICANON | ECHO ) ; // Disable canonical mode and echoing
raw . c_cc [ VMIN ] = 1 ;
raw . c_cc [ VTIME ] = 240 ; // Set timeout for read input
tcsetattr ( STDIN_FILENO , TCSAFLUSH , & raw ) ;
}
void disableRawMode ( struct termios * orig_termios ) {
tcsetattr ( STDIN_FILENO , TCSAFLUSH ,
orig_termios ) ; // Restore original terminal settings
}
void rterm_clear_screen ( ) {
printf ( " \x1b [2J " ) ; // Clear the entire screen
printf ( " \x1b [H " ) ; // Move cursor to the home position (0,0)
}
void setBackgroundColor ( ) {
printf ( " \x1b [34m " ) ; // Set background color to blue
}
void rterm_move_cursor ( int x , int y ) {
printf ( " \x1b [%d;%dH " , y + 1 , x + 1 ) ; // Move cursor to (x, y)
}
void cursor_set ( rterm_t * rt , int x , int y ) {
rt - > cursor . x = x ;
rt - > cursor . y = y ;
rt - > cursor . pos = y * rt - > size . ws_col + x ;
rterm_move_cursor ( rt - > cursor . x , rt - > cursor . y ) ;
}
void cursor_restore ( rterm_t * rt ) { rterm_move_cursor ( rt - > cursor . x , rt - > cursor . y ) ; }
void rterm_print_status_bar ( rterm_t * rt , char c , unsigned long i ) {
if ( rt - > _status_text_previous & & ! strcmp ( rt - > _status_text_previous , rt - > status_text ) ) {
return ;
}
if ( rt - > _status_text_previous ) {
free ( rt - > _status_text_previous ) ;
}
rt - > _status_text_previous = strdup ( rt - > status_text ) ;
winsize_t ws = rt - > size ;
cursor_set ( rt , rt - > cursor . x , rt - > cursor . y ) ;
rterm_move_cursor ( 0 , ws . ws_row - 1 ) ;
char output_str [ 1024 ] ;
output_str [ 0 ] = 0 ;
// strcat(output_str, "\x1b[48;5;240m");
for ( int i = 0 ; i < ws . ws_col ; i + + ) {
strcat ( output_str , " " ) ;
}
char content [ 500 ] ;
content [ 0 ] = 0 ;
if ( ! rt - > status_text ) {
sprintf ( content , " \r p:%d:%d | k:%c:%d | i:%ld " , rt - > cursor . x + 1 , rt - > cursor . y + 1 , c = = 0 ? ' 0 ' : c , c , i ) ;
} else {
sprintf ( content , " \r %s " , rt - > status_text ) ;
}
strcat ( output_str , content ) ;
// strcat(output_str, "\x1b[0m");
printf ( " %s " , output_str ) ;
cursor_restore ( rt ) ;
}
void rterm_show_cursor ( ) {
printf ( " \x1b [?25h " ) ; // Show the cursor
}
void rterm_hide_cursor ( ) {
printf ( " \x1b [?25l " ) ; // Hide the cursor
}
rshell_keypress_t rshell_getkey ( rterm_t * rt ) {
static rshell_keypress_t press ;
press . c = 0 ;
press . ctrl = false ;
press . shift = false ;
press . escape = false ;
press . pressed = rfd_wait ( 0 , rt - > ms_tick ) ;
if ( ! press . pressed ) {
return press ;
}
press . c = getchar ( ) ;
char ch = press . c ;
if ( ch = = ' \x1b ' ) {
// Get detail
ch = getchar ( ) ;
if ( ch = = ' [ ' ) {
// non char key:
press . escape = true ;
ch = getchar ( ) ; // is a number. 1 if shift + arrow
press . c = ch ;
if ( ch > = ' 0 ' & & ch < = ' 9 ' )
ch = getchar ( ) ;
press . c = ch ;
if ( ch = = ' ; ' ) {
ch = getchar ( ) ;
press . c = ch ;
if ( ch = = ' 5 ' ) {
press . ctrl = true ;
press . c = getchar ( ) ; // De arrow
}
}
} else if ( ch = = 27 ) {
press . escape = true ;
press . c = ch ;
} else {
press . c = ch ;
}
}
return press ;
}
// Main function
void rterm_loop ( rterm_t * rt ) {
struct termios orig_termios ;
tcgetattr ( STDIN_FILENO , & orig_termios ) ; // Get current terminal attributes
enableRawMode ( & orig_termios ) ;
int x = 0 , y = 0 ; // Initial cursor position
char ch = 0 ;
;
while ( 1 ) {
rterm_getwinsize ( & rt - > size ) ;
rt - > cursor . available = rt - > size . ws_col * rt - > size . ws_row ;
if ( rt - > tick ) {
rt - > tick ( rt ) ;
}
rterm_hide_cursor ( ) ;
setBackgroundColor ( ) ;
rterm_clear_screen ( ) ;
if ( rt - > before_draw ) {
rt - > before_draw ( rt ) ;
}
rterm_print_status_bar ( rt , ch , rt - > iterations ) ;
if ( rt - > after_draw ) {
rt - > after_draw ( rt ) ;
}
if ( ! rt - > iterations | | ( x ! = rt - > cursor . x | | y ! = rt - > cursor . y ) ) {
if ( rt - > cursor . y = = rt - > size . ws_row ) {
rt - > cursor . y - - ;
}
if ( rt - > cursor . y < 0 ) {
rt - > cursor . y = 0 ;
}
x = rt - > cursor . x ;
y = rt - > cursor . y ;
if ( rt - > before_cursor_move )
rt - > before_cursor_move ( rt ) ;
cursor_set ( rt , rt - > cursor . x , rt - > cursor . y ) ;
if ( rt - > after_cursor_move )
rt - > after_cursor_move ( rt ) ;
// x = rt->cursor.x;
// y = rt->cursor.y;
}
if ( rt - > show_cursor )
rterm_show_cursor ( ) ;
fflush ( stdout ) ;
rt - > key = rshell_getkey ( rt ) ;
if ( rt - > key . pressed & & rt - > before_key_press ) {
rt - > before_key_press ( rt ) ;
}
rshell_keypress_t key = rt - > key ;
ch = key . c ;
if ( ch = = ' q ' )
break ; // Press 'q' to quit
if ( key . c = = - 1 ) {
nsleep ( 1000 * 1000 ) ;
}
// Escape
if ( key . escape ) {
switch ( key . c ) {
case 65 : // Move up
if ( rt - > cursor . y > - 1 )
rt - > cursor . y - - ;
break ;
case 66 : // Move down
if ( rt - > cursor . y < rt - > size . ws_row )
rt - > cursor . y + + ;
break ;
case 68 : // Move left
if ( rt - > cursor . x > 0 )
rt - > cursor . x - - ;
if ( key . ctrl )
rt - > cursor . x - = 4 ;
break ;
case 67 : // Move right
if ( rt - > cursor . x < rt - > size . ws_col ) {
rt - > cursor . x + + ;
}
if ( key . ctrl ) {
rt - > cursor . x + = 4 ;
}
break ;
}
}
if ( rt - > key . pressed & & rt - > after_key_press ) {
rt - > after_key_press ( rt ) ;
}
rt - > iterations + + ;
// usleep (1000);
}
// Cleanup
printf ( " \x1b [0m " ) ; // Reset colors
rterm_clear_screen ( ) ;
disableRawMode ( & orig_termios ) ;
}
# endif
# ifndef RTREE_H
# define RTREE_H
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
typedef struct rtree_t {
struct rtree_t * next ;
struct rtree_t * children ;
char c ;
void * data ;
} rtree_t ;
rtree_t * rtree_new ( ) {
rtree_t * b = ( rtree_t * ) rmalloc ( sizeof ( rtree_t ) ) ;
b - > next = NULL ;
b - > children = NULL ;
b - > c = 0 ;
b - > data = NULL ;
return b ;
}
rtree_t * rtree_set ( rtree_t * b , char * c , void * data ) {
while ( b ) {
if ( b - > c = = 0 ) {
b - > c = * c ;
c + + ;
if ( * c = = 0 ) {
b - > data = data ;
// printf("SET1 %c\n", b->c);
return b ;
}
} else if ( b - > c = = * c ) {
c + + ;
if ( * c = = 0 ) {
b - > data = data ;
return b ;
}
if ( b - > children ) {
b = b - > children ;
} else {
b - > children = rtree_new ( ) ;
b = b - > children ;
}
} else if ( b - > next ) {
b = b - > next ;
} else {
b - > next = rtree_new ( ) ;
b = b - > next ;
b - > c = * c ;
c + + ;
if ( * c = = 0 ) {
b - > data = data ;
return b ;
} else {
b - > children = rtree_new ( ) ;
b = b - > children ;
}
}
}
return NULL ;
}
rtree_t * rtree_find ( rtree_t * b , char * c ) {
while ( b ) {
if ( b - > c = = * c ) {
c + + ;
if ( * c = = 0 ) {
return b ;
}
b = b - > children ;
continue ;
}
b = b - > next ;
}
return NULL ;
}
void rtree_free ( rtree_t * b ) {
if ( ! b )
return ;
rtree_free ( b - > children ) ;
rtree_free ( b - > next ) ;
rfree ( b ) ;
}
void * rtree_get ( rtree_t * b , char * c ) {
rtree_t * t = rtree_find ( b , c ) ;
if ( t ) {
return t - > data ;
}
return NULL ;
}
# endif
# ifndef RLEXER_H
# define RLEXER_H
# include <ctype.h>
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys/types.h>
# define RTOKEN_VALUE_SIZE 1024
typedef enum rtoken_type_t {
RT_UNKNOWN = 0 ,
RT_SYMBOL ,
RT_NUMBER ,
RT_STRING ,
RT_PUNCT ,
RT_OPERATOR ,
RT_EOF = 10 ,
RT_BRACE_OPEN ,
RT_CURLY_BRACE_OPEN ,
RT_BRACKET_OPEN ,
RT_BRACE_CLOSE ,
RT_CURLY_BRACE_CLOSE ,
RT_BRACKET_CLOSE
} rtoken_type_t ;
typedef struct rtoken_t {
rtoken_type_t type ;
char value [ RTOKEN_VALUE_SIZE ] ;
unsigned int line ;
unsigned int col ;
} rtoken_t ;
static char * _content ;
static unsigned int _content_ptr ;
static unsigned int _content_line ;
static unsigned int _content_col ;
static int isgroupingchar ( char c ) {
return ( c = = ' { ' | | c = = ' } ' | | c = = ' ( ' | | c = = ' ) ' | | c = = ' [ ' | | c = = ' ] ' | | c = = ' " ' | | c = = ' \' ' ) ;
}
static int isoperator ( char c ) {
return ( c = = ' + ' | | c = = ' - ' | | c = = ' / ' | | c = = ' * ' | | c = = ' = ' | | c = = ' > ' | | c = = ' < ' | | c = = ' | ' | | c = = ' & ' ) ;
}
static rtoken_t rtoken_new ( ) {
rtoken_t token ;
memset ( & token , 0 , sizeof ( token ) ) ;
token . type = RT_UNKNOWN ;
return token ;
}
rtoken_t rlex_number ( ) {
rtoken_t token = rtoken_new ( ) ;
token . col = _content_col ;
token . line = _content_line ;
bool first_char = true ;
int dot_count = 0 ;
char c ;
while ( isdigit ( c = _content [ _content_ptr ] ) | | ( first_char & & _content [ _content_ptr ] = = ' - ' ) | |
( dot_count = = 0 & & _content [ _content_ptr ] = = ' . ' ) ) {
if ( c = = ' . ' )
dot_count + + ;
first_char = false ;
char chars [ ] = { c , 0 } ;
strcat ( token . value , chars ) ;
_content_ptr + + ;
_content_col + + ;
}
token . type = RT_NUMBER ;
return token ;
}
static rtoken_t rlex_symbol ( ) {
rtoken_t token = rtoken_new ( ) ;
token . col = _content_col ;
token . line = _content_line ;
char c ;
while ( isalpha ( _content [ _content_ptr ] ) | | _content [ _content_ptr ] = = ' _ ' ) {
c = _content [ _content_ptr ] ;
char chars [ ] = { c , 0 } ;
strcat ( token . value , chars ) ;
_content_ptr + + ;
_content_col + + ;
}
token . type = RT_SYMBOL ;
return token ;
}
static rtoken_t rlex_operator ( ) {
rtoken_t token = rtoken_new ( ) ;
token . col = _content_col ;
token . line = _content_line ;
char c ;
bool is_first = true ;
while ( isoperator ( _content [ _content_ptr ] ) ) {
if ( ! is_first ) {
if ( _content [ _content_ptr - 1 ] = = ' = ' & & _content [ _content_ptr ] = = ' - ' ) {
break ;
}
}
c = _content [ _content_ptr ] ;
char chars [ ] = { c , 0 } ;
strcat ( token . value , chars ) ;
_content_ptr + + ;
_content_col + + ;
is_first = false ;
}
token . type = RT_OPERATOR ;
return token ;
}
static rtoken_t rlex_punct ( ) {
rtoken_t token = rtoken_new ( ) ;
token . col = _content_col ;
token . line = _content_line ;
char c ;
bool is_first = true ;
while ( ispunct ( _content [ _content_ptr ] ) ) {
if ( ! is_first ) {
if ( _content [ _content_ptr ] = = ' " ' ) {
break ;
}
if ( _content [ _content_ptr ] = = ' \' ' ) {
break ;
}
if ( isgroupingchar ( _content [ _content_ptr ] ) ) {
break ;
}
if ( isoperator ( _content [ _content_ptr ] ) ) {
break ;
}
}
c = _content [ _content_ptr ] ;
char chars [ ] = { c , 0 } ;
strcat ( token . value , chars ) ;
_content_ptr + + ;
_content_col + + ;
is_first = false ;
}
token . type = RT_PUNCT ;
return token ;
}
static rtoken_t rlex_string ( ) {
rtoken_t token = rtoken_new ( ) ;
char c ;
token . col = _content_col ;
token . line = _content_line ;
char str_chr = _content [ _content_ptr ] ;
_content_ptr + + ;
while ( _content [ _content_ptr ] ! = str_chr ) {
c = _content [ _content_ptr ] ;
if ( c = = ' \\ ' ) {
_content_ptr + + ;
c = _content [ _content_ptr ] ;
if ( c = = ' n ' ) {
c = ' \n ' ;
} else if ( c = = ' r ' ) {
c = ' \r ' ;
} else if ( c = = ' t ' ) {
c = ' \t ' ;
} else if ( c = = str_chr ) {
c = str_chr ;
}
_content_col + + ;
}
char chars [ ] = { c , 0 } ;
strcat ( token . value , chars ) ;
_content_ptr + + ;
_content_col + + ;
}
_content_ptr + + ;
token . type = RT_STRING ;
return token ;
}
void rlex ( char * content ) {
_content = content ;
_content_ptr = 0 ;
_content_col = 1 ;
_content_line = 1 ;
}
static void rlex_repeat_str ( char * dest , char * src , unsigned int times ) {
for ( size_t i = 0 ; i < times ; i + + ) {
strcat ( dest , src ) ;
}
}
rtoken_t rtoken_create ( rtoken_type_t type , char * value ) {
rtoken_t token = rtoken_new ( ) ;
token . type = type ;
token . col = _content_col ;
token . line = _content_line ;
strcpy ( token . value , value ) ;
return token ;
}
rtoken_t rlex_next ( ) {
while ( true ) {
_content_col + + ;
if ( _content [ _content_ptr ] = = 0 ) {
return rtoken_create ( RT_EOF , " eof " ) ;
} else if ( _content [ _content_ptr ] = = ' \n ' ) {
_content_line + + ;
_content_col = 1 ;
_content_ptr + + ;
} else if ( isspace ( _content [ _content_ptr ] ) ) {
_content_ptr + + ;
} else if ( isdigit ( _content [ _content_ptr ] ) | | ( _content [ _content_ptr ] = = ' - ' & & isdigit ( _content [ _content_ptr + 1 ] ) ) ) {
return rlex_number ( ) ;
} else if ( isalpha ( _content [ _content_ptr ] ) | | _content [ _content_ptr ] = = ' _ ' ) {
return rlex_symbol ( ) ;
} else if ( _content [ _content_ptr ] = = ' " ' | | _content [ _content_ptr ] = = ' \' ' ) {
return rlex_string ( ) ;
} else if ( isoperator ( _content [ _content_ptr ] ) ) {
return rlex_operator ( ) ;
} else if ( ispunct ( _content [ _content_ptr ] ) ) {
if ( _content [ _content_ptr ] = = ' { ' ) {
_content_ptr + + ;
return rtoken_create ( RT_CURLY_BRACE_OPEN , " { " ) ;
}
if ( _content [ _content_ptr ] = = ' } ' ) {
_content_ptr + + ;
return rtoken_create ( RT_CURLY_BRACE_CLOSE , " } " ) ;
}
if ( _content [ _content_ptr ] = = ' ( ' ) {
_content_ptr + + ;
return rtoken_create ( RT_BRACE_OPEN , " ( " ) ;
}
if ( _content [ _content_ptr ] = = ' ) ' ) {
_content_ptr + + ;
return rtoken_create ( RT_BRACE_CLOSE , " ) " ) ;
}
if ( _content [ _content_ptr ] = = ' [ ' ) {
_content_ptr + + ;
return rtoken_create ( RT_BRACKET_OPEN , " [ " ) ;
}
if ( _content [ _content_ptr ] = = ' ] ' ) {
_content_ptr + + ;
return rtoken_create ( RT_BRACKET_CLOSE , " ] " ) ;
}
return rlex_punct ( ) ;
}
}
}
char * rlex_format ( char * content ) {
rlex ( content ) ;
char * result = ( char * ) malloc ( strlen ( content ) + 4096 ) ;
result [ 0 ] = 0 ;
unsigned int tab_index = 0 ;
char * tab_chars = " " ;
unsigned int col = 0 ;
rtoken_t token_previous ;
token_previous . value [ 0 ] = 0 ;
token_previous . type = RT_UNKNOWN ;
while ( true ) {
rtoken_t token = rlex_next ( ) ;
if ( token . type = = RT_EOF ) {
break ;
}
// col = strlen(token.value);
if ( col = = 0 ) {
rlex_repeat_str ( result , tab_chars , tab_index ) ;
// col = strlen(token.value);// strlen(tab_chars) * tab_index;
}
if ( token . type = = RT_STRING ) {
strcat ( result , " \" " ) ;
char string_with_slashes [ strlen ( token . value ) * 2 + 1 ] ;
rstraddslashes ( token . value , string_with_slashes ) ;
strcat ( result , string_with_slashes ) ;
strcat ( result , " \" " ) ;
// col+= strlen(token.value) + 2;
// printf("\n");
// printf("<<<%s>>>\n",token.value);
memcpy ( & token_previous , & token , sizeof ( token ) ) ;
continue ;
}
if ( ! ( strcmp ( token . value , " { " ) ) ) {
if ( col ! = 0 ) {
strcat ( result , " \n " ) ;
rlex_repeat_str ( result , " " , tab_index ) ;
}
strcat ( result , token . value ) ;
tab_index + + ;
strcat ( result , " \n " ) ;
col = 0 ;
memcpy ( & token_previous , & token , sizeof ( token ) ) ;
continue ;
} else if ( ! ( strcmp ( token . value , " } " ) ) ) {
unsigned int tab_indexed = 0 ;
if ( tab_index )
tab_index - - ;
strcat ( result , " \n " ) ;
rlex_repeat_str ( result , tab_chars , tab_index ) ;
tab_indexed + + ;
strcat ( result , token . value ) ;
strcat ( result , " \n " ) ;
col = 0 ;
memcpy ( & token_previous , & token , sizeof ( token ) ) ;
continue ;
}
if ( ( token_previous . type = = RT_SYMBOL & & token . type = = RT_NUMBER ) | |
( token_previous . type = = RT_NUMBER & & token . type = = RT_SYMBOL ) | | ( token_previous . type = = RT_PUNCT & & token . type = = RT_SYMBOL ) | |
( token_previous . type = = RT_BRACE_CLOSE & & token . type = = RT_SYMBOL ) | |
( token_previous . type = = RT_SYMBOL & & token . type = = RT_SYMBOL ) ) {
if ( token_previous . value [ 0 ] ! = ' , ' & & token_previous . value [ 0 ] ! = ' . ' ) {
if ( token . type ! = RT_OPERATOR & & token . value [ 0 ] ! = ' . ' ) {
strcat ( result , " \n " ) ;
rlex_repeat_str ( result , tab_chars , tab_index ) ;
}
}
}
if ( token . type = = RT_OPERATOR ) {
strcat ( result , " " ) ;
}
if ( token . type = = RT_STRING ) {
strcat ( result , " \" " ) ;
}
strcat ( result , token . value ) ;
if ( token . type = = RT_STRING ) {
strcat ( result , " \" " ) ;
}
if ( token . type = = RT_OPERATOR ) {
strcat ( result , " " ) ;
}
if ( ! strcmp ( token . value , " , " ) ) {
strcat ( result , " " ) ;
}
col + = strlen ( token . value ) ;
memcpy ( & token_previous , & token , sizeof ( token ) ) ;
}
return result ;
}
# endif
# ifndef RLIB_MAIN
# define RLIB_MAIN
# ifndef RMERGE_H
# define RMERGE_H
// #include "../mrex/rmatch.h"
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
bool has_error = false ;
char * extract_script_src_include ( char * line , char * include_path ) {
include_path [ 0 ] = 0 ;
rrex3_t * rrex ;
rrex = rrex3 ( NULL , line , " <script.*src= \" (.*) \" .*<.*script.*> " ) ;
if ( rrex ) {
strcpy ( include_path , rrex - > matches [ 0 ] ) ;
rrex3_free ( rrex ) ;
return include_path ;
}
return NULL ;
}
char * extract_c_local_include ( char * line , char * include_path ) {
//
/*
char res;
res= rmatch_extract(line, "#include.*"\".*\"");
printf("%MATCH:%s\n", res);
*/
include_path [ 0 ] = 0 ;
rrex3_t * rrex ;
rrex = rrex3 ( NULL , line , " [^ \\ \\ *]^#include .* \" (.*) \" " ) ;
if ( rrex ) {
strcpy ( include_path , rrex - > matches [ 0 ] ) ;
rrex3_free ( rrex ) ;
return include_path ;
}
return NULL ;
}
char * rmerge_readline ( FILE * f ) {
static char data [ 4096 ] ;
data [ 0 ] = 0 ;
int index = 0 ;
char c ;
while ( ( c = fgetc ( f ) ) ! = EOF ) {
if ( c ! = ' \0 ' ) {
data [ index ] = c ;
index + + ;
if ( c = = ' \n ' )
break ;
}
}
data [ index ] = 0 ;
if ( data [ 0 ] = = 0 )
return NULL ;
return data ;
}
void writestring ( FILE * f , char * line ) {
char c ;
while ( ( c = * line ) ! = ' \0 ' ) {
fputc ( c , f ) ;
line + + ;
}
}
char files_history [ 8096 ] ;
char files_duplicate [ 8096 ] ;
bool is_merging = false ;
void merge_file ( char * source , FILE * d ) {
if ( is_merging = = false ) {
is_merging = true ;
files_history [ 0 ] = 0 ;
files_duplicate [ 0 ] = 0 ;
}
if ( strstr ( files_history , source ) ) {
if ( strstr ( files_duplicate , source ) ) {
rprintmf ( stderr , " \\ l Already included: %s. Already on duplicate list. \n " , source ) ;
} else {
rprintcf ( stderr , " \\ l Already included: %s. Adding to duplicate list. \n " , source ) ;
strcat ( files_duplicate , source ) ;
strcat ( files_duplicate , " \n " ) ;
}
return ;
} else {
rprintgf ( stderr , " \\ l Merging: %s. \n " , source ) ;
strcat ( files_history , source ) ;
strcat ( files_history , " \n " ) ;
}
FILE * fd = fopen ( source , " rb " ) ;
if ( ! fd ) {
rprintrf ( stderr , " \\ l File does not exist: %s \n " , source ) ;
has_error = true ;
return ;
}
char * line ;
char include_path [ 4096 ] ;
while ( ( line = rmerge_readline ( fd ) ) ) {
include_path [ 0 ] = 0 ;
if ( ! * line )
break ;
//
char * inc = extract_c_local_include ( line , include_path ) ;
if ( ! inc )
inc = extract_script_src_include ( line , include_path ) ;
/*
if (!strncmp(line, "#include ", 9)) {
int index = 0;
while (line[index] != '"' && line[index] != 0) {
index++;
}
if (line[index] == '"') {
int pindex = 0;
index++;
while (line[index] != '"') {
include_path[pindex] = line[index];
pindex++;
index++;
}
if (line[index] != '"') {
include_path[0] = 0;
} else {
include_path[pindex] = '\0';
}
}
}*/
if ( inc ) {
merge_file ( inc , d ) ;
} else {
writestring ( d , line ) ;
}
}
fclose ( fd ) ;
writestring ( d , " \n " ) ;
}
int rmerge_main ( int argc , char * argv [ ] ) {
char * file_input = NULL ;
if ( argc ! = 2 ) {
printf ( " Usage: <input-file> \n " ) ;
} else {
file_input = argv [ 1 ] ;
// file_output = argv[2];
}
FILE * f = tmpfile ( ) ;
printf ( " // RETOOR - %s \n " , __DATE__ ) ;
merge_file ( file_input , f ) ;
rewind ( f ) ;
char * data ;
int line_number = 0 ;
while ( ( data = rmerge_readline ( f ) ) ) {
if ( line_number ) {
printf ( " /*%.5d*/ " , line_number ) ;
line_number + + ;
}
printf ( " %s " , data ) ;
}
printf ( " \n " ) ;
if ( has_error ) {
rprintrf ( stderr , " \\ l Warning: there are errors while merging this file. \n " ) ;
} else {
rprintgf ( stderr , " \\ l Merge succesful without error(s).%s \n " , remo_get ( " fire " ) ) ;
}
return 0 ;
}
# endif
void forward_argument ( int * argcc , char * argv [ ] ) {
int argc = * argcc ;
for ( int i = 0 ; i < argc ; i + + ) {
argv [ i ] = argv [ i + 1 ] ;
}
argc - - ;
* argcc = argc ;
}
int rlib_main ( int argc , char * argv [ ] ) {
if ( argc = = 1 ) {
printf ( " rlib \n \n " ) ;
printf ( " options: \n " ) ;
printf ( " httpd - a http file server. Accepts port as argument. \n " ) ;
printf ( " rmerge - a merge tool. Converts c source files to one file \n "
" with local includes by giving main file as argument. \n " ) ;
printf ( " rcov - coverage tool theat cleans up after himself. Based on "
" lcov. \n " ) ;
printf ( " rcase - tool to swap input file automatically between "
" camel case and snake case. \n " ) ;
return 0 ;
}
forward_argument ( & argc , argv ) ;
if ( ! strcmp ( argv [ 0 ] , " httpd " ) ) {
return rhttp_main ( argc , argv ) ;
}
if ( ! strcmp ( argv [ 0 ] , " rmerge " ) ) {
return rmerge_main ( argc , argv ) ;
}
if ( ! strcmp ( argv [ 0 ] , " rcov " ) ) {
return rcov_main ( argc , argv ) ;
}
if ( ! strcmp ( argv [ 0 ] , " rcase " ) ) {
return rcase_main ( argc , argv ) ;
}
return 0 ;
}
# endif
// END OF RLIB
# endif