Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4e70627635 | |||
| eb8eec916f | |||
| 29bc56fc36 | |||
| fb130ed809 | |||
| 82b25f1e50 | |||
| 1b00896fc7 |
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2020 Alessandro Mauri
|
Copyright (c) 2021 Alessandro Mauri
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@ -9,7 +9,8 @@
|
|||||||
|
|
||||||
# Leading or trailing whitespaces are ignored, whitespaces between the marker
|
# Leading or trailing whitespaces are ignored, whitespaces between the marker
|
||||||
# And the ':' are also ignored, the general syntax for a hotkey is:
|
# And the ':' are also ignored, the general syntax for a hotkey is:
|
||||||
# marker keys: command
|
# <marker> <keys>: <command>
|
||||||
|
#
|
||||||
# Whitespaces after the ':' count as that counts as the executed command for
|
# Whitespaces after the ':' count as that counts as the executed command for
|
||||||
# the hotkey.
|
# the hotkey.
|
||||||
# Commads are expanded using wordexp(3) so "|&;<>(){}" as well as unescaped
|
# Commads are expanded using wordexp(3) so "|&;<>(){}" as well as unescaped
|
||||||
@ -35,12 +36,12 @@
|
|||||||
# command, to declaring aliases is similar to declaring an hotkey, you must
|
# command, to declaring aliases is similar to declaring an hotkey, you must
|
||||||
# start the line with '@' to indicate an alias, give it a name (mind that this
|
# start the line with '@' to indicate an alias, give it a name (mind that this
|
||||||
# is case sensitive), and a command after a ':', just like in hotkeys.
|
# is case sensitive), and a command after a ':', just like in hotkeys.
|
||||||
# To use an alias in an hotkey you have to replace the ':' before the command
|
# When parsing an hotkey or an alias commad, hkd automatically replaces every
|
||||||
# with '<' to indicate that the following string is an alias and not a command.
|
# instance of known aliases in it. for an alias to be known must be declared
|
||||||
# Aliases have to be declared before using them, they can also be concatenated.
|
# before it's usage.
|
||||||
|
|
||||||
# Examples:
|
# Examples:
|
||||||
# @ term : alacritty
|
# @ term : alacritty
|
||||||
# @ volumeup: amixer -q sset Master 3%+
|
# @ volumeup: amixer -q sset Master 3%+
|
||||||
# - leftmeta, p < term
|
# - leftmeta, p : term
|
||||||
# - VOLUMEUP < volumeup
|
# - VOLUMEUP : volumeup
|
||||||
|
|||||||
28
hkd.1
28
hkd.1
@ -36,7 +36,7 @@ override default configuration file location, instead using the specified
|
|||||||
as the temporary config file
|
as the temporary config file
|
||||||
|
|
||||||
.SH FILES
|
.SH FILES
|
||||||
The configuration files are searched in the following order:
|
The configuration files are selected in the following order:
|
||||||
.I $XDG_CONFIG_HOME/hkd/config, $HOME/.config/hkd/config, /etc/hkd/config
|
.I $XDG_CONFIG_HOME/hkd/config, $HOME/.config/hkd/config, /etc/hkd/config
|
||||||
|
|
||||||
.SH USAGE
|
.SH USAGE
|
||||||
@ -62,31 +62,38 @@ Normal matching means that the keys need to be pressed in the same order as they
|
|||||||
are declared, whereas fuzzy matching means that they can be pressed in any order.
|
are declared, whereas fuzzy matching means that they can be pressed in any order.
|
||||||
Aliases are a name-command pair that can be used instead of commands in hotkey
|
Aliases are a name-command pair that can be used instead of commands in hotkey
|
||||||
definitions.
|
definitions.
|
||||||
|
|
||||||
|
|
||||||
.SS "Hotkey definition"
|
.SS "Hotkey definition"
|
||||||
Leading or trailing whitespaces are ignored, whitespaces between the marker and
|
Leading or trailing whitespaces are ignored, whitespaces between the marker and
|
||||||
the ':' or '<' are also ignored, whitespaces after the ':' are not ignored.
|
the ':' are also ignored, whitespaces after the ':' ignored until the first
|
||||||
The general syntax for a hotkey is:
|
non-whitespace character.
|
||||||
|
The general syntax for an hotkey is:
|
||||||
.EX
|
.EX
|
||||||
<'*' or '\-'> <keys>: <command>
|
<'*' or '\-'> <keys>: <command>
|
||||||
.EE
|
.EE
|
||||||
if a hotkeys uses an explicit command, if you want to use an alias it becomes:
|
|
||||||
.EX
|
|
||||||
<'*' or '\-'> <keys> < <alias name>
|
|
||||||
.EE
|
|
||||||
note the '<' instead of ':'.
|
|
||||||
.SS "Alias definition"
|
.SS "Alias definition"
|
||||||
The general syntax for aliases is:
|
The general syntax for aliases is:
|
||||||
.EX
|
.EX
|
||||||
@ <name>: <command>
|
@ <name>: <command>
|
||||||
.EE
|
.EE
|
||||||
beware that alias names are case sensitive.
|
beware that alias names are case sensitive.
|
||||||
|
|
||||||
.SS "Command field"
|
.SS "Command field"
|
||||||
Commads are expanded using
|
The command string gets analyzed and every instance of known aliases gets
|
||||||
|
replaced with the appropriate command, this also applies for the command field
|
||||||
|
in alias definitions.
|
||||||
|
|
||||||
|
Upon execution commads are expanded using
|
||||||
.BR wordexp(3)
|
.BR wordexp(3)
|
||||||
so "|&;<>(){}" as well as unescaped newlines are forbidden and will result in
|
so "|&;<>(){}" as well as unescaped newlines are forbidden and will result in
|
||||||
error, read the manpage for
|
error, read the manpage for
|
||||||
.BR wordexp(3)
|
.BR wordexp(3)
|
||||||
for more info about the possible word expansion capabilities.
|
for more info about the possible word expansion capabilities.
|
||||||
|
|
||||||
|
|
||||||
.SS "Keys field"
|
.SS "Keys field"
|
||||||
Possible keys are taken directly from linux's input.h header file, those
|
Possible keys are taken directly from linux's input.h header file, those
|
||||||
include normal keys, multimedia keys, special keys and button events, for the
|
include normal keys, multimedia keys, special keys and button events, for the
|
||||||
@ -115,7 +122,8 @@ This is a valid config file example
|
|||||||
\- LEFALT,leftshift,S: ~/screenshot.sh \-c
|
\- LEFALT,leftshift,S: ~/screenshot.sh \-c
|
||||||
* LEFTMETA,1, D: $SCRIPTDIR/wonkyscript
|
* LEFTMETA,1, D: $SCRIPTDIR/wonkyscript
|
||||||
\- LEFTMETA ,LEFTALT,LEFTSHIFT,S : shutdown now
|
\- LEFTMETA ,LEFTALT,LEFTSHIFT,S : shutdown now
|
||||||
\- leftmeta, enter < term
|
# term gets substituted with xterm
|
||||||
|
\- leftmeta, enter : term
|
||||||
.EE
|
.EE
|
||||||
|
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
|
|||||||
515
hkd.c
515
hkd.c
@ -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
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -39,6 +39,7 @@
|
|||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
#include "keys.h"
|
#include "keys.h"
|
||||||
|
|
||||||
/* Value defines */
|
/* Value defines */
|
||||||
@ -62,6 +63,8 @@
|
|||||||
#define test_bit(yalv, abs_b) ((((char *)abs_b)[yalv/8] & (1<<yalv%8)) > 0)
|
#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(val) (val ? sizeof(val)/sizeof(val[0]) : 0)
|
||||||
#define array_size_const(val) ((int)(sizeof(val)/sizeof(val[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_SIZE (sizeof(struct inotify_event))
|
||||||
#define EVENT_BUF_LEN (1024*(EVENT_SIZE+16))
|
#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
|
/* 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 {
|
union hotkey_main_data {
|
||||||
struct key_buffer kb;
|
struct key_buffer kb;
|
||||||
@ -95,6 +100,12 @@ struct hotkey_list_e {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct hotkey_list_e *hotkey_list = NULL;
|
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;
|
unsigned long hotkey_size_mask = 0;
|
||||||
char *ext_config_file = NULL;
|
char *ext_config_file = NULL;
|
||||||
/* Global flags */
|
/* Global flags */
|
||||||
@ -111,7 +122,7 @@ void int_handler (int signum);
|
|||||||
void exec_command (char *);
|
void exec_command (char *);
|
||||||
void parse_config_file (void);
|
void parse_config_file (void);
|
||||||
void update_descriptors_list (int **, int *);
|
void update_descriptors_list (int **, int *);
|
||||||
void remove_lock (void);
|
inline void remove_lock (void);
|
||||||
void die (const char *, ...);
|
void die (const char *, ...);
|
||||||
void usage (void);
|
void usage (void);
|
||||||
int prepare_epoll (int *, int, int);
|
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_add (struct hotkey_list_e *, union hotkey_main_data *, char *, int);
|
||||||
void hotkey_list_destroy (struct hotkey_list_e *);
|
void hotkey_list_destroy (struct hotkey_list_e *);
|
||||||
void hotkey_list_remove (struct hotkey_list_e *, 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[])
|
int main (int argc, char *argv[])
|
||||||
{
|
{
|
||||||
@ -146,7 +158,7 @@ int main (int argc, char *argv[])
|
|||||||
case 'c':
|
case 'c':
|
||||||
ext_config_file = malloc(strlen(optarg) + 1);
|
ext_config_file = malloc(strlen(optarg) + 1);
|
||||||
if (!ext_config_file)
|
if (!ext_config_file)
|
||||||
die("malloc in main():");
|
die(wrap_err("Bad malloc:"));
|
||||||
strcpy(ext_config_file, optarg);
|
strcpy(ext_config_file, optarg);
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
@ -163,9 +175,13 @@ int main (int argc, char *argv[])
|
|||||||
dead = 0;
|
dead = 0;
|
||||||
memset(&action, 0, sizeof(action));
|
memset(&action, 0, sizeof(action));
|
||||||
action.sa_handler = int_handler;
|
action.sa_handler = int_handler;
|
||||||
sigaction(SIGINT, &action, NULL);
|
|
||||||
sigaction(SIGUSR1, &action, NULL);
|
if (sigaction(SIGINT, &action, NULL) == -1)
|
||||||
sigaction(SIGCHLD, &action, NULL);
|
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 */
|
||||||
parse_config_file();
|
parse_config_file();
|
||||||
@ -173,7 +189,7 @@ int main (int argc, char *argv[])
|
|||||||
/* Check if hkd is already running */
|
/* Check if hkd is already running */
|
||||||
lock_file_descriptor = open(LOCK_FILE, O_RDWR | O_CREAT, 0600);
|
lock_file_descriptor = open(LOCK_FILE, O_RDWR | O_CREAT, 0600);
|
||||||
if (lock_file_descriptor < 0)
|
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_start = 0;
|
||||||
fl.l_len = 0;
|
fl.l_len = 0;
|
||||||
fl.l_type = F_WRLCK;
|
fl.l_type = F_WRLCK;
|
||||||
@ -201,10 +217,9 @@ int main (int argc, char *argv[])
|
|||||||
|
|
||||||
/* Prepare directory update watcher */
|
/* Prepare directory update watcher */
|
||||||
if (event_watcher < 0)
|
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)
|
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 */
|
/* Prepare epoll list */
|
||||||
ev_fd = prepare_epoll(fds, fd_num, event_watcher);
|
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
|
sleep(1); // wait for devices to settle
|
||||||
update_descriptors_list(&fds, &fd_num);
|
update_descriptors_list(&fds, &fd_num);
|
||||||
if (close(ev_fd) < 0)
|
if (close(ev_fd) < 0)
|
||||||
die("Could not close event filedescriptors list (ev_fd):");
|
die(wrap_err("Could not close event fd list (ev_fd):"));
|
||||||
ev_fd = prepare_epoll(fds, fd_num, event_watcher);
|
ev_fd = prepare_epoll(fds, fd_num, event_watcher);
|
||||||
goto mainloop_begin;
|
goto mainloop_begin;
|
||||||
}
|
}
|
||||||
@ -294,7 +309,7 @@ int main (int argc, char *argv[])
|
|||||||
close(event_watcher);
|
close(event_watcher);
|
||||||
for (int i = 0; i < fd_num; i++)
|
for (int i = 0; i < fd_num; i++)
|
||||||
if (close(fds[i]) == -1)
|
if (close(fds[i]) == -1)
|
||||||
die("Error closing file descriptors:");
|
die(wrap_err("Error closing file descriptors:"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,7 +357,7 @@ void int_handler (int signum)
|
|||||||
switch (signum) {
|
switch (signum) {
|
||||||
case SIGINT:
|
case SIGINT:
|
||||||
if (dead)
|
if (dead)
|
||||||
die("An error occured, exiting");
|
die(wrap_err("An error occured, exiting"));
|
||||||
if (vflag)
|
if (vflag)
|
||||||
printf(yellow("Received interrupt signal, exiting gracefully...\n"));
|
printf(yellow("Received interrupt signal, exiting gracefully...\n"));
|
||||||
dead = 1;
|
dead = 1;
|
||||||
@ -372,20 +387,20 @@ void exec_command (char *command)
|
|||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
/* Some other error */
|
/* 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_t cpid;
|
pid_t cpid;
|
||||||
switch (cpid = fork()) {
|
switch (cpid = fork()) {
|
||||||
case -1:
|
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);
|
wordfree(&result);
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
/* This is the child process, execute the command */
|
/* This is the child process, execute the command */
|
||||||
execvp(result.we_wordv[0], result.we_wordv);
|
execvp(result.we_wordv[0], result.we_wordv);
|
||||||
die("%s:", command);
|
die(wrap_err("%s:"), command);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
while (waitpid(cpid, NULL, WNOHANG) == -1) {}
|
while (waitpid(cpid, NULL, WNOHANG) == -1) {}
|
||||||
@ -404,7 +419,7 @@ void update_descriptors_list (int **fds, int *fd_num)
|
|||||||
/* Open the event directory */
|
/* Open the event directory */
|
||||||
DIR *ev_dir = opendir(EVDEV_ROOT_DIR);
|
DIR *ev_dir = opendir(EVDEV_ROOT_DIR);
|
||||||
if (!ev_dir)
|
if (!ev_dir)
|
||||||
die("Could not open /dev/input:");
|
die(wrap_err("Could not open /dev/input:"));
|
||||||
|
|
||||||
(*fd_num) = 0;
|
(*fd_num) = 0;
|
||||||
|
|
||||||
@ -431,7 +446,7 @@ void update_descriptors_list (int **fds, int *fd_num)
|
|||||||
memset(evtype_b, 0, sizeof(evtype_b));
|
memset(evtype_b, 0, sizeof(evtype_b));
|
||||||
if (ioctl(tmp_fd, EVIOCGBIT(0, EV_MAX), evtype_b) < 0) {
|
if (ioctl(tmp_fd, EVIOCGBIT(0, EV_MAX), evtype_b) < 0) {
|
||||||
if (vflag)
|
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);
|
close(tmp_fd);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -445,7 +460,7 @@ void update_descriptors_list (int **fds, int *fd_num)
|
|||||||
|
|
||||||
tmp_p = realloc((*fds), sizeof(int) * ((*fd_num) + 1));
|
tmp_p = realloc((*fds), sizeof(int) * ((*fd_num) + 1));
|
||||||
if (!tmp_p)
|
if (!tmp_p)
|
||||||
die("realloc file descriptors:");
|
die(wrap_err("Realloc file descriptors:"));
|
||||||
(*fds) = (int *) tmp_p;
|
(*fds) = (int *) tmp_p;
|
||||||
|
|
||||||
(*fds)[(*fd_num)] = tmp_fd;
|
(*fds)[(*fd_num)] = tmp_fd;
|
||||||
@ -456,7 +471,7 @@ void update_descriptors_list (int **fds, int *fd_num)
|
|||||||
if (vflag)
|
if (vflag)
|
||||||
printf(green("Monitoring %d devices\n"), *fd_num);
|
printf(green("Monitoring %d devices\n"), *fd_num);
|
||||||
} else {
|
} 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;
|
static struct epoll_event epoll_read_ev;
|
||||||
epoll_read_ev.events = EPOLLIN;
|
epoll_read_ev.events = EPOLLIN;
|
||||||
if (ev_fd < 0)
|
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)
|
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++)
|
for (int i = 0; i < fd_num; i++)
|
||||||
if (epoll_ctl(ev_fd, EPOLL_CTL_ADD, fds[i], &epoll_read_ev) < 0)
|
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;
|
return ev_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -519,12 +534,12 @@ void hotkey_list_add (struct hotkey_list_e *head, union hotkey_main_data *dt, ch
|
|||||||
{
|
{
|
||||||
int size;
|
int size;
|
||||||
struct hotkey_list_e *tmp;
|
struct hotkey_list_e *tmp;
|
||||||
if (!(size = strlen(cmd)))
|
if (is_empty(cmd) || !(size = strlen(cmd)))
|
||||||
return;
|
return;
|
||||||
if (!(tmp = malloc(sizeof(struct hotkey_list_e))))
|
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)))
|
if (!(tmp->command = malloc(size + 1)))
|
||||||
die("Memory allocation failed in hotkey_list_add():");
|
die(wrap_err("Bad malloc:"));
|
||||||
strcpy(tmp->command, cmd);
|
strcpy(tmp->command, cmd);
|
||||||
tmp->data = *dt;
|
tmp->data = *dt;
|
||||||
tmp->fuzzy = f;
|
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)
|
void parse_config_file (void)
|
||||||
{
|
{
|
||||||
wordexp_t result = {0};
|
wordexp_t result = {0};
|
||||||
FILE *fd;
|
int config_file;
|
||||||
/* normal, skip line, get matching, get keys, get command, output */
|
/* 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 {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;
|
enum {HK_NORM = 0, HK_FUZZY = 1, ALIAS = -1} type;
|
||||||
int cmd_is_alias = 0;
|
int eof = 0;
|
||||||
int alloc_tmp = 0, alloc_size = 0;
|
int token_size = 0;
|
||||||
int i_tmp = 0, linenum = 1;
|
int i_tmp = 0, linenum = 1;
|
||||||
char block[BLOCK_SIZE + 1] = {0};
|
char *buffer;
|
||||||
char *bb = NULL;
|
char *bb = NULL;
|
||||||
char *keys = NULL;
|
char *keys = NULL;
|
||||||
char *cmd = NULL;
|
char *cmd = NULL;
|
||||||
@ -576,6 +590,7 @@ void parse_config_file (void)
|
|||||||
union hotkey_main_data dt = {0};
|
union hotkey_main_data dt = {0};
|
||||||
unsigned short us_tmp = 0;
|
unsigned short us_tmp = 0;
|
||||||
|
|
||||||
|
/* Choose config file */
|
||||||
if (ext_config_file) {
|
if (ext_config_file) {
|
||||||
switch (wordexp(ext_config_file, &result, 0)) {
|
switch (wordexp(ext_config_file, &result, 0)) {
|
||||||
case 0:
|
case 0:
|
||||||
@ -584,15 +599,15 @@ void parse_config_file (void)
|
|||||||
/* If the error was WRDE_NOSPACE,
|
/* If the error was WRDE_NOSPACE,
|
||||||
* then perhaps part of the result was allocated */
|
* then perhaps part of the result was allocated */
|
||||||
wordfree (&result);
|
wordfree (&result);
|
||||||
die("Not enough space:");
|
die(wrap_err("Not enough space:"));
|
||||||
default:
|
default:
|
||||||
die("Path not valid:");
|
die(wrap_err("Path not valid:"));
|
||||||
}
|
}
|
||||||
|
|
||||||
fd = fopen(result.we_wordv[0], "r");
|
config_file = open(result.we_wordv[0], O_RDONLY | O_NONBLOCK);
|
||||||
wordfree(&result);
|
wordfree(&result);
|
||||||
if (!fd)
|
if (config_file < 0)
|
||||||
die("Error opening config file:");
|
die(wrap_err("Error opening config file:"));
|
||||||
free(ext_config_file);
|
free(ext_config_file);
|
||||||
ext_config_file = NULL;
|
ext_config_file = NULL;
|
||||||
} else {
|
} else {
|
||||||
@ -604,244 +619,192 @@ void parse_config_file (void)
|
|||||||
/* If the error was WRDE_NOSPACE,
|
/* If the error was WRDE_NOSPACE,
|
||||||
* then perhaps part of the result was allocated */
|
* then perhaps part of the result was allocated */
|
||||||
wordfree (&result);
|
wordfree (&result);
|
||||||
die("Not enough space:");
|
die(wrap_err("Not enough space:"));
|
||||||
default:
|
default:
|
||||||
die("Path not valid:");
|
die(wrap_err("Path not valid:"));
|
||||||
}
|
}
|
||||||
|
|
||||||
fd = fopen(result.we_wordv[0], "r");
|
config_file = open(result.we_wordv[0], O_RDONLY | O_NONBLOCK);
|
||||||
wordfree(&result);
|
wordfree(&result);
|
||||||
if (fd)
|
if (config_file >= 0)
|
||||||
break;
|
break;
|
||||||
if (vflag)
|
if (vflag)
|
||||||
printf(yellow("config file not found at %s\n"), config_paths[i]);
|
printf(yellow("config file not found at %s\n"), config_paths[i]);
|
||||||
}
|
}
|
||||||
if (!fd)
|
if (!config_file)
|
||||||
die("Could not open any config files, check the log for more details");
|
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_destroy(hotkey_list);
|
||||||
hotkey_list = NULL;
|
hotkey_list = NULL;
|
||||||
while (block_state != END) {
|
while (!eof) {
|
||||||
int tmp = 0;
|
// FIXME: incorect line counting, especially for multiline commands
|
||||||
memset(block, 0, BLOCK_SIZE + 1);
|
switch (parse_state) {
|
||||||
tmp = fread(block, sizeof(char), BLOCK_SIZE, fd);
|
// First state
|
||||||
if (!tmp)
|
case NORM:
|
||||||
break;
|
// remove whitespaces
|
||||||
if (tmp < BLOCK_SIZE || feof(fd))
|
while (isblank(*bb))
|
||||||
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;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
// Skip line (comment)
|
|
||||||
case LINE_SKIP:
|
|
||||||
while (*bb != '\n' && *bb)
|
|
||||||
bb++;
|
|
||||||
if (*bb) {
|
|
||||||
bb++;
|
|
||||||
linenum++;
|
|
||||||
parse_state = NORM;
|
|
||||||
} else {
|
|
||||||
block_state = NEW_BL;
|
|
||||||
}
|
|
||||||
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++;
|
bb++;
|
||||||
parse_state = GET_KEYS;
|
// get state
|
||||||
|
switch (*bb) {
|
||||||
|
case '\0':
|
||||||
|
eof = 1;
|
||||||
break;
|
break;
|
||||||
// Get keys
|
case '\n':
|
||||||
case GET_KEYS:
|
case '#':
|
||||||
if (!keys) {
|
parse_state = LINE_SKIP;
|
||||||
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;
|
|
||||||
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;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (alloc_tmp = 0; bb[alloc_tmp] && bb[alloc_tmp] != '\n' &&
|
|
||||||
alloc_tmp < alloc_size; alloc_tmp++);
|
|
||||||
|
|
||||||
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("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);
|
|
||||||
|
|
||||||
if (type != ALIAS)
|
|
||||||
key_buffer_reset(&dt.kb);
|
|
||||||
free(keys);
|
|
||||||
free(cmd);
|
|
||||||
cp_tmp = keys = cmd = NULL;
|
|
||||||
i_tmp = 0;
|
|
||||||
parse_state = NORM;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
die("Unknown state in parse_config_file");
|
parse_state = GET_TYPE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
|
case '*':
|
||||||
|
type = HK_FUZZY;
|
||||||
|
break;
|
||||||
|
case '@':
|
||||||
|
type = ALIAS;
|
||||||
|
break;
|
||||||
|
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;
|
||||||
|
if (bb[token_size] == '\n' && bb[token_size - token_size ? 1 : 0] != '\\')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
|
||||||
|
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(wrap_err("Unknown state"));
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (struct hotkey_list_e *hkl = hotkey_list, *tmp; hkl; hkl = hkl->next) {
|
munmap(buffer, file_size);
|
||||||
if (hkl->fuzzy == ALIAS) {
|
|
||||||
tmp = hkl;
|
for (struct hotkey_list_e *hkl = hotkey_list, *tmp; hkl;) {
|
||||||
hkl = hkl->next;
|
tmp = hkl;
|
||||||
|
hkl = hkl->next;
|
||||||
|
if (tmp->fuzzy == ALIAS)
|
||||||
hotkey_list_remove(hotkey_list, tmp);
|
hotkey_list_remove(hotkey_list, tmp);
|
||||||
} else {
|
else
|
||||||
hotkey_size_mask |= 1 << (hkl->data.kb.size - 1);
|
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");
|
"\t-c file uses the specified file as config\n");
|
||||||
exit(EXIT_SUCCESS);
|
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);
|
||||||
|
}
|
||||||
|
|||||||
2
makefile
2
makefile
@ -1,6 +1,6 @@
|
|||||||
CC ?= gcc
|
CC ?= gcc
|
||||||
CFLAGS = -Wall -Werror -pedantic --std=c99 -O2
|
CFLAGS = -Wall -Werror -pedantic --std=c99 -O2
|
||||||
VERSION = 0.4
|
VERSION = 0.5
|
||||||
PREFIX = /usr/local
|
PREFIX = /usr/local
|
||||||
MANPREFIX = ${PREFIX}/share/man
|
MANPREFIX = ${PREFIX}/share/man
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,10 @@ parse: parse.c
|
|||||||
|
|
||||||
parse_v2: parse_v2.c
|
parse_v2: parse_v2.c
|
||||||
|
|
||||||
|
replace: replace.c
|
||||||
|
|
||||||
|
replace_v2: replace_v2.c
|
||||||
|
|
||||||
ioctl: ioctl.c
|
ioctl: ioctl.c
|
||||||
|
|
||||||
evtest: evtest.c
|
evtest: evtest.c
|
||||||
@ -14,4 +18,4 @@ inotify: inotify.c
|
|||||||
struct_init: struct_init.c
|
struct_init: struct_init.c
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o parse parse_v2 ioctl inotify struct_init
|
rm -f *.o parse parse_v2 replace replace_v2 ioctl inotify struct_init
|
||||||
|
|||||||
43
tests/replace.c
Normal file
43
tests/replace.c
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* recursively replaces every instance of m(match) with r(eplace) inside of s */
|
||||||
|
void replace (char *s, const char *m, const char *r)
|
||||||
|
{
|
||||||
|
static int off = 0;
|
||||||
|
int d = strlen(r) - strlen(m);
|
||||||
|
int ss = strlen(s);
|
||||||
|
char *pos;
|
||||||
|
|
||||||
|
if ((pos = strstr(s + off, m))) {
|
||||||
|
|
||||||
|
char *tmp;
|
||||||
|
int rs = strlen(r);
|
||||||
|
int ms = strlen(m);
|
||||||
|
|
||||||
|
if (d > 0) {
|
||||||
|
if (!(tmp = realloc(s, ss + 2 + d)))
|
||||||
|
exit(-1);
|
||||||
|
s = tmp;
|
||||||
|
}
|
||||||
|
memmove(pos + rs, pos + ms, strlen(pos) - ms + 1);
|
||||||
|
memcpy(pos, r, rs);
|
||||||
|
off += rs;
|
||||||
|
replace(s, m, r);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (void) {
|
||||||
|
char *s = strdup(" volup");
|
||||||
|
|
||||||
|
printf("original: %s\n", s);
|
||||||
|
replace(s, "volup", "this short, like a lot--------");
|
||||||
|
printf("replaced: %s\n", s);
|
||||||
|
|
||||||
|
free(s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
141
tests/replace_v2.c
Normal file
141
tests/replace_v2.c
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
char * replace (const char *s, const char *m, const char *r)
|
||||||
|
{
|
||||||
|
char *new_s = strdup(s);
|
||||||
|
int ms = strlen(m), rs = strlen(r);
|
||||||
|
char *pos, *tmp;
|
||||||
|
int off = 0;
|
||||||
|
|
||||||
|
while((pos = strstr(new_s + off, m))) {
|
||||||
|
int ps = strlen(pos), ss = strlen(new_s);
|
||||||
|
|
||||||
|
if (rs > ms) {
|
||||||
|
if (!(tmp = realloc(new_s, ss + 1 + (rs - ms))))
|
||||||
|
exit(-10);
|
||||||
|
new_s = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
memmove(pos + rs, pos + ms, ps - ms);
|
||||||
|
memcpy(pos, r, rs);
|
||||||
|
off += rs;
|
||||||
|
}
|
||||||
|
return new_s;
|
||||||
|
}
|
||||||
|
|
||||||
|
char * replace_fast (const char *s, const char *m, const char *r)
|
||||||
|
{
|
||||||
|
char *new_s = strdup(s);
|
||||||
|
int ms = strlen(m), rs = strlen(r);
|
||||||
|
char *t1;
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
int *offs = NULL, o = 0, *t2;
|
||||||
|
int nss = strlen(new_s);
|
||||||
|
while ((t1 = strstr(new_s + o, m))) {
|
||||||
|
if (!(t2 = realloc(offs, sizeof(int) * (count + 1))))
|
||||||
|
exit(-10);
|
||||||
|
offs = t2;
|
||||||
|
offs[count] = (t1 - new_s) + (rs - ms) * count;
|
||||||
|
o = (t1 - new_s) + 1;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((rs - ms) > 0) {
|
||||||
|
if (!(t1 = realloc(new_s, nss + (rs - ms) * count)))
|
||||||
|
exit(-5);
|
||||||
|
new_s = t1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
char* x = new_s + offs[i];
|
||||||
|
int d = strlen(x) - ms;
|
||||||
|
memmove(x + rs, x + ms, d);
|
||||||
|
memcpy(x, r, rs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void replace_fast_2 (char **s, const char *m, const char *r)
|
||||||
|
{
|
||||||
|
char **new_s = s;
|
||||||
|
int ms = strlen(m), rs = strlen(r);
|
||||||
|
char *t1;
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
int *offs = NULL, o = 0, *t2;
|
||||||
|
int nss = strlen(*new_s);
|
||||||
|
while ((t1 = strstr(*new_s + o, m))) {
|
||||||
|
/* check if the match is surrounded by whitespace */
|
||||||
|
if ((t1[ms] == '\0' || isblank(t1[ms]))
|
||||||
|
&& isblank(t1 > *new_s ? *(t1 - 1) : ' ')) {
|
||||||
|
if (!(t2 = realloc(offs, sizeof(int) * (count + 1))))
|
||||||
|
exit(-1);
|
||||||
|
offs = t2;
|
||||||
|
offs[count] = (t1 - *new_s) + (rs - ms) * count;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
o = (t1 - *new_s) + 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((rs - ms) > 0) {
|
||||||
|
if (!(t1 = realloc(*new_s, nss + (rs - ms) * count)))
|
||||||
|
exit(-1);
|
||||||
|
*new_s = t1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
char* x = *new_s + offs[i];
|
||||||
|
int d = strlen(x) - ms;
|
||||||
|
memmove(x + rs, x + ms, d);
|
||||||
|
memcpy(x, r, rs);
|
||||||
|
}
|
||||||
|
if (offs)
|
||||||
|
free(offs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(void){
|
||||||
|
clock_t t1, t2;
|
||||||
|
char *s = " volup";
|
||||||
|
|
||||||
|
|
||||||
|
printf("Before: %s\n", s);
|
||||||
|
if ((t1 = clock()) == (clock_t)-1)
|
||||||
|
exit(-1);
|
||||||
|
char *r = replace(s, "volup", "I am alive, I'm aliveeeeee!");
|
||||||
|
t2 = clock();
|
||||||
|
printf("After replace: %s\n", r);
|
||||||
|
printf("Time took: %f\n\n", (t2-t1)/(CLOCKS_PER_SEC/10e3));
|
||||||
|
free(r);
|
||||||
|
|
||||||
|
|
||||||
|
printf("Before: %s\n", s);
|
||||||
|
if ((t1 = clock()) == (clock_t)-1)
|
||||||
|
exit(-1);
|
||||||
|
char *x = replace_fast(s, "volup", "I am alive, I'm aliveeeeee!");
|
||||||
|
t2 = clock();
|
||||||
|
printf("After replace_fast: %s\n", x);
|
||||||
|
printf("Time took: %f\n\n", (t2-t1)/(CLOCKS_PER_SEC/10e3));
|
||||||
|
free(x);
|
||||||
|
|
||||||
|
printf("Before: %s\n", s);
|
||||||
|
char *s1 = strdup(s);
|
||||||
|
if ((t1 = clock()) == (clock_t)-1)
|
||||||
|
exit(-1);
|
||||||
|
replace_fast_2(&s1, "volup", "I am alive, I'm aliveeeeee!");
|
||||||
|
t2 = clock();
|
||||||
|
printf("After replace_fast: %s\n", s1);
|
||||||
|
printf("Time took: %f\n\n", (t2-t1)/(CLOCKS_PER_SEC/10e3));
|
||||||
|
free(s1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user