@ -1,5 +1,5 @@
/*
* Copyright ( c ) 2020 Alessandro Mauri
* Copyright ( c ) 2021 Alessandro Mauri
*
* Permission is hereby granted , free of charge , to any person obtaining a copy
* of this software and associated documentation files ( the " Software " ) , to deal
@ -39,6 +39,7 @@
# include <ctype.h>
# include <sys/stat.h>
# include <stdarg.h>
# include <sys/mman.h>
# include "keys.h"
/* Value defines */
@ -62,6 +63,8 @@
# define test_bit(yalv, abs_b) ((((char *)abs_b)[yalv / 8] & (1<<yalv%8)) > 0)
# define array_size(val) (val ? sizeof(val) / sizeof(val[0]) : 0)
# define array_size_const(val) ((int)(sizeof(val) / sizeof(val[0])))
# define wrap_err(s) "[%s] " s, __func__
# define is_empty(s) (!(s) || !(s)[0])
# define EVENT_SIZE (sizeof(struct inotify_event))
# define EVENT_BUF_LEN (1024*(EVENT_SIZE+16))
@ -80,7 +83,9 @@ struct key_buffer {
} ;
/* Hotkey list: linked list that holds all valid hoteys parsed from the
* config file and the corresponding command */
* config file and the corresponding command
* TODO : re - implement hotkey_list as a hash table to make searching O ( 1 )
*/
union hotkey_main_data {
struct key_buffer kb ;
@ -95,6 +100,12 @@ struct hotkey_list_e {
} ;
struct hotkey_list_e * hotkey_list = NULL ;
/* TODO: add hotkey_range struct as a second check to avoid accessing the list
* struct {
* unsigned int min ;
* unsigned int max ;
* } hotkey_range ;
*/
unsigned long hotkey_size_mask = 0 ;
char * ext_config_file = NULL ;
/* Global flags */
@ -111,7 +122,7 @@ void int_handler (int signum);
void exec_command ( char * ) ;
void parse_config_file ( void ) ;
void update_descriptors_list ( int * * , int * ) ;
void remove_lock ( void ) ;
inline void remove_lock ( void ) ;
void die ( const char * , . . . ) ;
void usage ( void ) ;
int prepare_epoll ( int * , int , int ) ;
@ -121,6 +132,7 @@ const char * code_to_name (unsigned int);
void hotkey_list_add ( struct hotkey_list_e * , union hotkey_main_data * , char * , int ) ;
void hotkey_list_destroy ( struct hotkey_list_e * ) ;
void hotkey_list_remove ( struct hotkey_list_e * , struct hotkey_list_e * ) ;
void replace ( char * * , const char * , const char * ) ;
int main ( int argc , char * argv [ ] )
{
@ -146,7 +158,7 @@ int main (int argc, char *argv[])
case ' c ' :
ext_config_file = malloc ( strlen ( optarg ) + 1 ) ;
if ( ! ext_config_file )
die ( " malloc in main() : " ) ;
die ( wrap_err ( " Bad malloc:" ) ) ;
strcpy ( ext_config_file , optarg ) ;
break ;
case ' d ' :
@ -163,9 +175,13 @@ int main (int argc, char *argv[])
dead = 0 ;
memset ( & action , 0 , sizeof ( action ) ) ;
action . sa_handler = int_handler ;
sigaction ( SIGINT , & action , NULL ) ;
sigaction ( SIGUSR1 , & action , NULL ) ;
sigaction ( SIGCHLD , & action , NULL ) ;
if ( sigaction ( SIGINT , & action , NULL ) = = - 1 )
die ( wrap_err ( " Error setting interrupt handler: " ) ) ;
if ( sigaction ( SIGUSR1 , & action , NULL ) = = - 1 )
die ( wrap_err ( " Error setting interrupt handler: " ) ) ;
if ( sigaction ( SIGCHLD , & action , NULL ) = = - 1 )
die ( wrap_err ( " Error setting interrupt handler: " ) ) ;
/* Parse config file */
parse_config_file ( ) ;
@ -173,7 +189,7 @@ int main (int argc, char *argv[])
/* Check if hkd is already running */
lock_file_descriptor = open ( LOCK_FILE , O_RDWR | O_CREAT , 0600 ) ;
if ( lock_file_descriptor < 0 )
die ( " Can't open lock file: " ) ;
die ( wrap_err ( " Can't open lock file: " ) ) ;
fl . l_start = 0 ;
fl . l_len = 0 ;
fl . l_type = F_WRLCK ;
@ -201,10 +217,9 @@ int main (int argc, char *argv[])
/* Prepare directory update watcher */
if ( event_watcher < 0 )
die ( " Could not call inotify_init: " ) ;
die ( wrap_err ( " Could not call inotify_init: " ) ) ;
if ( inotify_add_watch ( event_watcher , EVDEV_ROOT_DIR , IN_CREATE | IN_DELETE ) < 0 )
die ( " Could not add /dev/input to the watch list: " ) ;
die ( wrap_err ( " Could not add /dev/input to the watch list: " ) ) ;
/* Prepare epoll list */
ev_fd = prepare_epoll ( fds , fd_num , event_watcher ) ;
@ -230,7 +245,7 @@ int main (int argc, char *argv[])
sleep ( 1 ) ; // wait for devices to settle
update_descriptors_list ( & fds , & fd_num ) ;
if ( close ( ev_fd ) < 0 )
die ( " Could not close event file descriptors list (ev_fd): " ) ;
die ( wrap_err ( " Could not close event fd list (ev_fd): " ) ) ;
ev_fd = prepare_epoll ( fds , fd_num , event_watcher ) ;
goto mainloop_begin ;
}
@ -294,7 +309,7 @@ int main (int argc, char *argv[])
close ( event_watcher ) ;
for ( int i = 0 ; i < fd_num ; i + + )
if ( close ( fds [ i ] ) = = - 1 )
die ( " Error closing file descriptors: " ) ;
die ( wrap_err ( " Error closing file descriptors: " ) ) ;
return 0 ;
}
@ -342,7 +357,7 @@ void int_handler (int signum)
switch ( signum ) {
case SIGINT :
if ( dead )
die ( " An error occured, exiting " ) ;
die ( wrap_err ( " An error occured, exiting " ) ) ;
if ( vflag )
printf ( yellow ( " Received interrupt signal, exiting gracefully... \n " ) ) ;
dead = 1 ;
@ -372,20 +387,20 @@ void exec_command (char *command)
return ;
default :
/* Some other error */
fprintf ( stderr , " Could not parse, %s is not valid \n " , command ) ;
fprintf ( stderr , wrap_err ( " Could not parse, %s is not valid \n " ) , command ) ;
return ;
}
pid_t cpid ;
switch ( cpid = fork ( ) ) {
case - 1 :
fprintf ( stderr , " Could not create child process: %s " , strerror ( errno ) ) ;
fprintf ( stderr , wrap_err ( " Could not create child process: %s " ) , strerror ( errno ) ) ;
wordfree ( & result ) ;
break ;
case 0 :
/* This is the child process, execute the command */
execvp ( result . we_wordv [ 0 ] , result . we_wordv ) ;
die ( " %s: " , command ) ;
die ( wrap_err ( " %s: " ) , command ) ;
break ;
default :
while ( waitpid ( cpid , NULL , WNOHANG ) = = - 1 ) { }
@ -404,7 +419,7 @@ void update_descriptors_list (int **fds, int *fd_num)
/* Open the event directory */
DIR * ev_dir = opendir ( EVDEV_ROOT_DIR ) ;
if ( ! ev_dir )
die ( " Could not open /dev/input: " ) ;
die ( wrap_err ( " Could not open /dev/input: " ) ) ;
( * fd_num ) = 0 ;
@ -431,7 +446,7 @@ void update_descriptors_list (int **fds, int *fd_num)
memset ( evtype_b , 0 , sizeof ( evtype_b ) ) ;
if ( ioctl ( tmp_fd , EVIOCGBIT ( 0 , EV_MAX ) , evtype_b ) < 0 ) {
if ( vflag )
printf ( red ( " Could not read capabilities of device %s \n " ) , ev_path ) ;
printf ( red ( " Could not read capabilities of device %s \n " ) , ev_path ) ;
close ( tmp_fd ) ;
continue ;
}
@ -445,7 +460,7 @@ void update_descriptors_list (int **fds, int *fd_num)
tmp_p = realloc ( ( * fds ) , sizeof ( int ) * ( ( * fd_num ) + 1 ) ) ;
if ( ! tmp_p )
die ( " r ealloc file descriptors:" ) ;
die ( wrap_err ( " R ealloc file descriptors:" ) ) ;
( * fds ) = ( int * ) tmp_p ;
( * fds ) [ ( * fd_num ) ] = tmp_fd ;
@ -456,7 +471,7 @@ void update_descriptors_list (int **fds, int *fd_num)
if ( vflag )
printf ( green ( " Monitoring %d devices \n " ) , * fd_num ) ;
} else {
die ( " Could not open any devices, exiting " ) ;
die ( wrap_err ( " Could not open any devices, exiting " ) ) ;
}
}
@ -466,12 +481,12 @@ int prepare_epoll (int *fds, int fd_num, int event_watcher)
static struct epoll_event epoll_read_ev ;
epoll_read_ev . events = EPOLLIN ;
if ( ev_fd < 0 )
die ( " epoll_create failed in prepare_epoll : " ) ;
die ( wrap_err ( " epoll_create failed: " ) ) ;
if ( epoll_ctl ( ev_fd , EPOLL_CTL_ADD , event_watcher , & epoll_read_ev ) < 0 )
die ( " Could not add file descriptor to the epoll list: " ) ;
die ( wrap_err ( " Could not add file descriptor to the epoll list: " ) ) ;
for ( int i = 0 ; i < fd_num ; i + + )
if ( epoll_ctl ( ev_fd , EPOLL_CTL_ADD , fds [ i ] , & epoll_read_ev ) < 0 )
die ( " Could not add file descriptor to the epoll list: " ) ;
die ( wrap_err ( " Could not add file descriptor to the epoll list: " ) ) ;
return ev_fd ;
}
@ -519,12 +534,12 @@ void hotkey_list_add (struct hotkey_list_e *head, union hotkey_main_data *dt, ch
{
int size ;
struct hotkey_list_e * tmp ;
if ( ! ( size = strlen ( cmd ) ) )
if ( is_empty ( cmd ) | | ! ( size = strlen ( cmd ) ) )
return ;
if ( ! ( tmp = malloc ( sizeof ( struct hotkey_list_e ) ) ) )
die ( " Memory allocation failed in hotkey_list_add(): " ) ;
die ( wrap_err ( " Bad malloc: " ) ) ;
if ( ! ( tmp - > command = malloc ( size + 1 ) ) )
die ( " Memory allocation failed in hotkey_list_add(): " ) ;
die ( wrap_err ( " Bad malloc: " ) ) ;
strcpy ( tmp - > command , cmd ) ;
tmp - > data = * dt ;
tmp - > fuzzy = f ;
@ -560,15 +575,14 @@ void hotkey_list_remove (struct hotkey_list_e *head, struct hotkey_list_e *elem)
void parse_config_file ( void )
{
wordexp_t result = { 0 } ;
FILE * fd ;
int config_file ;
/* normal, skip line, get matching, get keys, get command, output */
enum { NORM , LINE_SKIP , GET_TYPE , GET_KEYS , GET_CMD , LAST } parse_state = NORM ;
enum { CONT , NEW_BL , LAST_BL , END } block_state = CONT ; /* continue, new block, last block, end */
enum { HK_NORM = 0 , HK_FUZZY = 1 , ALIAS = - 1 } type ;
int cmd_is_alias = 0 ;
int alloc_tmp = 0 , alloc _size = 0 ;
int eof = 0 ;
int token _size = 0 ;
int i_tmp = 0 , linenum = 1 ;
char block [ BLOCK_SIZE + 1 ] = { 0 } ;
char * buffer ;
char * bb = NULL ;
char * keys = NULL ;
char * cmd = NULL ;
@ -576,6 +590,7 @@ void parse_config_file (void)
union hotkey_main_data dt = { 0 } ;
unsigned short us_tmp = 0 ;
/* Choose config file */
if ( ext_config_file ) {
switch ( wordexp ( ext_config_file , & result , 0 ) ) {
case 0 :
@ -584,15 +599,15 @@ void parse_config_file (void)
/* If the error was WRDE_NOSPACE,
* then perhaps part of the result was allocated */
wordfree ( & result ) ;
die ( " Not enough space: " ) ;
die ( wrap_err ( " Not enough space: " ) ) ;
default :
die ( " Path not valid: " ) ;
die ( wrap_err ( " Path not valid: " ) ) ;
}
fd = f open( result . we_wordv [ 0 ] , " r " ) ;
config_file = open ( result . we_wordv [ 0 ] , O_RDONLY | O_NONBLOCK ) ;
wordfree ( & result ) ;
if ( ! fd )
die ( " Error opening config file: " ) ;
if ( config_file < 0 )
die ( wrap_err ( " Error opening config file: " ) ) ;
free ( ext_config_file ) ;
ext_config_file = NULL ;
} else {
@ -604,244 +619,192 @@ void parse_config_file (void)
/* If the error was WRDE_NOSPACE,
* then perhaps part of the result was allocated */
wordfree ( & result ) ;
die ( " Not enough space: " ) ;
die ( wrap_err ( " Not enough space: " ) ) ;
default :
die ( " Path not valid: " ) ;
die ( wrap_err ( " Path not valid: " ) ) ;
}
fd = f open( result . we_wordv [ 0 ] , " r " ) ;
config_file = open ( result . we_wordv [ 0 ] , O_RDONLY | O_NONBLOCK ) ;
wordfree ( & result ) ;
if ( fd )
if ( config_file > = 0 )
break ;
if ( vflag )
printf ( yellow ( " config file not found at %s \n " ) , config_paths [ i ] ) ;
}
if ( ! fd )
die ( " Could not open any config files, check the log for more details " ) ;
if ( ! config_file )
die ( wrap_err ( " Could not open any config files, check stderr for more details " ) ) ;
}
/* Using mmap because of simplicity, most config files are smaller than
* a page but this method mostly ensures that big files are taken care
* of efficiently and reduces the overall complexity of the code .
* Furthermore we only need this space when parsing the config file ,
* afterwards we release it .
*/
struct stat sb ;
int file_size ;
if ( fstat ( config_file , & sb ) = = - 1 )
die ( " fstat " ) ;
file_size = sb . st_size ;
// FIXME: page align size
buffer = mmap ( NULL , file_size , PROT_READ , MAP_PRIVATE , config_file , 0 ) ;
if ( buffer = = MAP_FAILED )
die ( wrap_err ( " mmap failed: " ) ) ;
close ( config_file ) ;
bb = buffer ;
hotkey_list_destroy ( hotkey_list ) ;
hotkey_list = NULL ;
while ( block_state ! = END ) {
int tmp = 0 ;
memset ( block , 0 , BLOCK_SIZE + 1 ) ;
tmp = fread ( block , sizeof ( char ) , BLOCK_SIZE , fd ) ;
if ( ! tmp )
while ( ! eof ) {
// FIXME: incorect line counting, especially for multiline commands
switch ( parse_state ) {
// First state
case NORM :
// remove whitespaces
while ( isblank ( * bb ) )
bb + + ;
// get state
switch ( * bb ) {
case ' \0 ' :
eof = 1 ;
break ;
case ' \n ' :
case ' # ' :
parse_state = LINE_SKIP ;
break ;
default :
parse_state = GET_TYPE ;
break ;
}
break ;
if ( tmp < BLOCK_SIZE | | feof ( fd ) )
block_state = LAST_BL ;
else
block_state = CONT ;
bb = block ;
while ( block_state = = CONT | | block_state = = LAST_BL ) {
switch ( parse_state ) {
// First state
case NORM :
// remove whitespaces
while ( isblank ( * bb ) & & * bb )
bb + + ;
// get state
switch ( * bb ) {
# if defined(__X86_64__) || defined(__i386__)
case EOF :
# endif
case ' \0 ' :
// If it is the end of the last block exit
block_state = block_state = = LAST_BL ? END : NEW_BL ;
break ;
case ' \n ' :
case ' # ' :
parse_state = LINE_SKIP ;
break ;
default :
parse_state = GET_TYPE ;
break ;
}
// Skip line (comment)
case LINE_SKIP :
for ( ; ( bb - buffer ) < file_size & & * bb ! = ' \n ' ; bb + + ) ;
bb + + ;
linenum + + ;
parse_state = NORM ;
break ;
// Get compairson method
case GET_TYPE :
switch ( * bb ) {
case ' - ' :
type = HK_NORM ;
break ;
// Skip line (comment)
case LINE_SKIP :
while ( * bb ! = ' \n ' & & * bb )
bb + + ;
if ( * bb ) {
bb + + ;
linenum + + ;
parse_state = NORM ;
} else {
block_state = NEW_BL ;
}
case ' * ' :
type = HK_FUZZY ;
break ;
// Get compairson method
case GET_TYPE :
switch ( * bb ) {
case ' - ' :
type = HK_NORM ;
break ;
case ' * ' :
type = HK_FUZZY ;
break ;
case ' @ ' :
type = ALIAS ;
break ;
default :
die ( " Error at line %d: "
" hotkey definition must start with '-', '*' or '@' " ,
linenum ) ;
break ;
}
bb + + ;
parse_state = GET_KEYS ;
case ' @ ' :
type = ALIAS ;
break ;
// Get keys
case GET_KEYS :
if ( ! keys ) {
if ( ! ( keys = malloc ( alloc_size = ( sizeof ( char ) * 64 ) ) ) )
die ( " malloc for keys in parse_config_file(): " ) ;
memset ( keys , 0 , alloc_size ) ;
} else if ( alloc_tmp > = alloc_size ) {
if ( ! ( keys = realloc ( keys , alloc_size = alloc_size * 2 ) ) )
die ( " realloc for keys in parse_config_file(): " ) ;
memset ( & keys [ alloc_size / 2 ] , 0 , alloc_size / 2 ) ;
}
for ( alloc_tmp = 0 ; bb [ alloc_tmp ] & &
bb [ alloc_tmp ] ! = ' : ' & &
bb [ alloc_tmp ] ! = ' < ' & &
bb [ alloc_tmp ] ! = ' \n ' & &
alloc_tmp < alloc_size ; alloc_tmp + + ) ;
if ( ! bb [ alloc_tmp ] | | alloc_tmp = = alloc_size ) {
strncat ( keys , bb , alloc_tmp ) ;
bb + = alloc_tmp ;
if ( block_state = = LAST_BL )
die ( " Keys not finished before end of file " ) ;
else
block_state = NEW_BL ;
default :
die ( wrap_err ( " Error at line %d: "
" hotkey definition must start with '-', '*' or '@' " ) ,
linenum ) ;
break ;
}
bb + + ;
parse_state = GET_KEYS ;
break ;
// Get keys
case GET_KEYS :
for ( token_size = 0 ; token_size < ( file_size - ( bb - buffer ) ) & & ! ( bb [ token_size ] = = ' : ' | | bb [ token_size ] = = ' \n ' ) ; token_size + + ) ;
if ( bb [ token_size ] = = ' \n ' )
die ( wrap_err ( " Error at line %d: "
" no command specified, missing ':' after keys " ) ,
linenum ) ;
keys = malloc ( token_size + 1 ) ;
if ( ! keys )
die ( wrap_err ( " Bad malloc parsing keys: " ) ) ;
memcpy ( keys , bb , token_size ) ;
keys [ token_size ] = ' \0 ' ;
bb + = token_size + 1 ;
parse_state = GET_CMD ;
break ;
// Get command
case GET_CMD :
for ( token_size = 0 ; token_size < ( file_size - ! ( bb - buffer ) ) ; token_size + + ) {
if ( bb [ token_size ] = = ' : ' )
break ;
} else if ( bb [ alloc_tmp ] = = ' : ' | | bb [ alloc_tmp ] = = ' < ' ) {
cmd_is_alias = ( bb [ alloc_tmp ] = = ' < ' ) ;
strncat ( keys , bb , alloc_tmp ) ;
bb + = alloc_tmp + 1 ;
parse_state = GET_CMD ;
if ( bb [ token_size ] = = ' \n ' & & bb [ token_size - token_size ? 1 : 0 ] ! = ' \\ ' )
break ;
} else {
die ( " Error at line %d: "
" no command specified, missing ':' or '<' after keys " ,
linenum ) ;
}
break ;
// Get command
case GET_CMD :
if ( ! cmd ) {
if ( ! ( cmd = malloc ( alloc_size = ( sizeof ( char ) * 128 ) ) ) )
die ( " malloc for cmd in parse_config_file(): " ) ;
memset ( cmd , 0 , alloc_size ) ;
} else if ( alloc_tmp > = alloc_size ) {
if ( ! ( cmd = realloc ( cmd , alloc_size = alloc_size * 2 ) ) )
die ( " realloc for cmd in parse_config_file(): " ) ;
memset ( & cmd [ alloc_size / 2 ] , 0 , alloc_size / 2 ) ;
}
}
cmd = malloc ( token_size + 1 ) ;
if ( ! cmd )
die ( wrap_err ( " Bad malloc parsing command: " ) ) ;
memcpy ( cmd , bb , token_size ) ;
cmd [ token_size ] = ' \0 ' ;
bb + = token_size ;
parse_state = LAST ;
break ;
case LAST :
if ( ! keys )
die ( wrap_err ( " Keys is NULL " ) ) ;
i_tmp = strlen ( keys ) ;
for ( int i = 0 ; i < i_tmp ; i + + ) {
if ( isblank ( keys [ i ] ) )
memmove ( & keys [ i ] , & keys [ i + 1 ] , i_tmp - i ) ;
}
cp_tmp = strtok ( keys , " , " ) ;
if ( ! cp_tmp )
die ( wrap_err ( " Error at line %d: "
" keys not present " ) , linenum - 1 ) ;
if ( type ! = ALIAS ) {
do {
if ( ! ( us_tmp = key_to_code ( cp_tmp ) ) ) {
die ( wrap_err ( " Error at line %d: "
" %s is not a valid key " ) ,
linenum - 1 , cp_tmp ) ;
}
if ( key_buffer_add ( & dt . kb , us_tmp ) )
die ( wrap_err ( " Too many keys " ) ) ;
} while ( ( cp_tmp = strtok ( NULL , " , " ) ) ) ;
} else {
if ( ! ( dt . name = malloc ( strlen ( cp_tmp ) + 1 ) ) )
die ( wrap_err ( " Bad malloc: " ) ) ;
strcpy ( dt . name , cp_tmp ) ;
}
for ( alloc_tmp = 0 ; bb [ alloc_tmp ] & & bb [ alloc_tmp ] ! = ' \n ' & &
alloc_tmp < alloc_size ; alloc_tmp + + ) ;
/* search the command in the known aliases and replace */
struct hotkey_list_e * hkl = hotkey_list ;
while ( hkl & & hkl - > fuzzy = = ALIAS ) {
replace ( & cmd , hkl - > data . name , hkl - > command ) ;
hkl = hkl - > next ;
}
if ( ! bb [ alloc_tmp ] | | alloc_tmp = = alloc_size ) {
strncat ( cmd , bb , alloc_tmp ) ;
bb + = alloc_tmp ;
if ( block_state = = LAST_BL )
die ( " Command not finished before end of file " ) ;
else
block_state = NEW_BL ;
break ;
} else {
strncat ( cmd , bb , alloc_tmp ) ;
if ( ! ( bb [ alloc_tmp - 1 ] = = ' \\ ' ) )
parse_state = LAST ;
bb + = alloc_tmp + 1 ;
linenum + + ;
break ;
}
break ;
case LAST :
if ( ! keys )
die ( " error " ) ;
i_tmp = strlen ( keys ) ;
for ( int i = 0 ; i < i_tmp ; i + + ) {
if ( isblank ( keys [ i ] ) ) {
memmove ( & keys [ i ] , & keys [ i + 1 ] , - - i_tmp ) ;
keys [ i_tmp ] = ' \0 ' ;
}
}
cp_tmp = strtok ( keys , " , " ) ;
if ( ! cp_tmp )
die ( " Error at line %d: "
" keys not present " , linenum - 1 ) ;
if ( type ! = ALIAS ) {
do {
if ( ! ( us_tmp = key_to_code ( cp_tmp ) ) ) {
die ( " Error at line %d: "
" %s is not a valid key " ,
linenum - 1 , cp_tmp ) ;
}
if ( key_buffer_add ( & dt . kb , us_tmp ) )
die ( " Too many keys " ) ;
} while ( ( cp_tmp = strtok ( NULL , " , " ) ) ) ;
} else {
if ( ! ( dt . name = malloc ( strlen ( cp_tmp ) + 1 ) ) )
die ( " malloc in parse_config_file(): " ) ;
strcpy ( dt . name , cp_tmp ) ;
}
cp_tmp = cmd ;
while ( isblank ( * cp_tmp ) )
cp_tmp + + ;
if ( * cp_tmp = = ' \0 ' )
die ( wrap_err ( " Error at line %d: "
" command not present " ) , linenum - 1 ) ;
cp_tmp = cmd ;
while ( isblank ( * cp_tmp ) )
cp_tmp + + ;
if ( * cp_tmp = = ' \0 ' )
die ( " Error at line %d: "
" command not present " , linenum - 1 ) ;
if ( cmd_is_alias ) {
struct hotkey_list_e * hkl = hotkey_list ;
// stolen way of removing leading spaces
char * end = cp_tmp + strlen ( cp_tmp ) - 1 ;
while ( end > cp_tmp & & isspace ( ( unsigned char ) * end ) ) end - - ;
end [ 1 ] = ' \0 ' ;
while ( hkl & & ! ( hkl - > fuzzy = = ALIAS & & strstr ( hkl - > data . name , cp_tmp ) ) )
hkl = hkl - > next ;
if ( hkl ) {
cp_tmp = hkl - > command ;
} else {
die ( " Error at line %d: "
" alias %s not found " , linenum - 1 ,
cp_tmp ) ;
}
}
hotkey_list_add ( hotkey_list , & dt , cp_tmp , type ) ;
hotkey_list_add ( hotkey_list , & dt , cp_tmp , type ) ;
if ( type ! = ALIAS )
key_buffer_reset ( & dt . kb ) ;
free ( keys ) ;
free ( cmd ) ;
cp_tmp = keys = cmd = NULL ;
i_tmp = 0 ;
parse_state = NORM ;
break ;
default :
die ( " Unknown state in parse_config_file " ) ;
break ;
if ( type ! = ALIAS )
key_buffer_reset ( & dt . kb ) ;
free ( keys ) ;
free ( cmd ) ;
cp_tmp = keys = cmd = NULL ;
i_tmp = 0 ;
parse_state = NORM ;
break ;
default :
die ( wrap_err ( " Unknown state " ) ) ;
break ;
}
}
}
for ( struct hotkey_list_e * hkl = hotkey_list , * tmp ; hkl ; hkl = hkl - > next ) {
if ( hkl - > fuzzy = = ALIAS ) {
tmp = hkl ;
hkl = hkl - > next ;
munmap ( buffer , file_size ) ;
for ( struct hotkey_list_e * hkl = hotkey_list , * tmp ; hkl ; ) {
tmp = hkl ;
hkl = hkl - > next ;
if ( tmp - > fuzzy = = ALIAS )
hotkey_list_remove ( hotkey_list , tmp ) ;
} else {
hotkey_size_mask | = 1 < < ( hkl - > data . kb . size - 1 ) ;
}
else
hotkey_size_mask | = 1 < < ( tmp - > data . kb . size - 1 ) ;
}
}
@ -901,3 +864,47 @@ void usage (void)
" \t -c file uses the specified file as config \n " ) ;
exit ( EXIT_SUCCESS ) ;
}
/* replaces every instance of m(match) with r(eplace) inside of s */
void replace ( char * * s , const char * m , const char * r )
{
if ( is_empty ( s ) | | is_empty ( * s ) | | is_empty ( m ) | | is_empty ( r ) )
return ;
int ms = strlen ( m ) , rs = strlen ( r ) ;
int count = 0 , o = 0 ;
int * offs = NULL , * t2 = NULL ;
char * t1 = NULL ;
while ( ( t1 = strstr ( ( * s ) + o , m ) ) ) {
/* check if the match is surrounded by whitespace */
if ( ( t1 [ ms ] = = ' \0 ' | | isblank ( t1 [ ms ] ) )
& & isblank ( t1 > * s ? * ( t1 - 1 ) : ' ' ) ) {
if ( ! ( t2 = realloc ( offs , sizeof ( int ) * ( count + 1 ) ) ) )
die ( wrap_err ( " Bad realloc: " ) ) ;
offs = t2 ;
offs [ count ] = ( t1 - * s ) + ( rs - ms ) * count ;
count + + ;
}
o = ( t1 - * s ) + 1 ;
}
if ( ! offs )
return ;
int nss = strlen ( * s ) ;
if ( ( rs - ms ) > 0 ) {
if ( ! ( t1 = realloc ( * s , nss + 1 + ( rs - ms ) * count ) ) )
die ( wrap_err ( " Bad realloc: " ) ) ;
* s = t1 ;
}
for ( int i = 0 ; i < count ; i + + ) {
char * x = * s + offs [ i ] ;
int d = strlen ( x ) - ms ;
memmove ( x + rs , x + ms , d ) ;
memcpy ( x , r , rs ) ;
}
if ( offs )
free ( offs ) ;
}