Compare commits

..

1 Commits

Author SHA1 Message Date
Alessandro Mauri 6691a616e3 basic xkbcommon support 4 years ago
  1. 2
      LICENSE
  2. 17
      config.template
  3. 28
      hkd.1
  4. 460
      hkd.c
  5. 504
      keys.h
  6. 5
      makefile
  7. 6
      tests/makefile
  8. 43
      tests/replace.c
  9. 141
      tests/replace_v2.c

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2021 Alessandro Mauri Copyright (c) 2020 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,8 +9,7 @@
# 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
@ -36,12 +35,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.
# When parsing an hotkey or an alias commad, hkd automatically replaces every # To use an alias in an hotkey you have to replace the ':' before the command
# instance of known aliases in it. for an alias to be known must be declared # with '<' to indicate that the following string is an alias and not a command.
# before it's usage. # Aliases have to be declared before using them, they can also be concatenated.
# Examples: # Examples:
# @ term : alacritty @ term : alacritty
# @ volumeup: amixer -q sset Master 3%+ @ volumeup: amixer -q sset Master 3%+
# - leftmeta, p : term # - meta, p < term
# - VOLUMEUP : volumeup - XF86AudioRaiseVolume < volumeup

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 selected in the following order: The configuration files are searched 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,38 +62,31 @@ 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 ':' are also ignored, whitespaces after the ':' ignored until the first the ':' or '<' are also ignored, whitespaces after the ':' are not ignored.
non-whitespace character. The general syntax for a hotkey is:
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"
The command string gets analyzed and every instance of known aliases gets Commads are expanded using
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
@ -122,8 +115,7 @@ 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
# term gets substituted with xterm \- leftmeta, enter < term
\- leftmeta, enter : term
.EE .EE
.SH BUGS .SH BUGS

460
hkd.c

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2021 Alessandro Mauri * Copyright (c) 2020 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
@ -31,6 +31,7 @@
#include <fcntl.h> #include <fcntl.h>
#include <dirent.h> #include <dirent.h>
#include <unistd.h> #include <unistd.h>
#include <locale.h>
#include <signal.h> #include <signal.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/epoll.h> #include <sys/epoll.h>
@ -39,12 +40,12 @@
#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 <xkbcommon/xkbcommon.h>
#include "keys.h" #include <linux/input.h>
/* Value defines */ /* Value defines */
#define FILE_NAME_MAX_LENGTH 255 #define FILE_NAME_MAX_LENGTH 255
#define KEY_BUFFER_SIZE 16 #define KEY_BUFFER_SIZE 10
#define BLOCK_SIZE 512 #define BLOCK_SIZE 512
/* ANSI colors escape codes */ /* ANSI colors escape codes */
@ -63,8 +64,6 @@
#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))
@ -78,14 +77,12 @@ const char *config_paths[] = {
}; };
struct key_buffer { struct key_buffer {
unsigned short buf[KEY_BUFFER_SIZE]; xkb_keysym_t buf[KEY_BUFFER_SIZE];
unsigned int size; unsigned int size;
}; };
/* 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;
@ -99,21 +96,23 @@ struct hotkey_list_e {
struct hotkey_list_e *next; struct hotkey_list_e *next;
}; };
struct {
struct xkb_context *context;
struct xkb_rule_names names;
struct xkb_keymap *keymap;
struct xkb_state *state;
// struct xkb_compose_state *comp_state;
} keyboard;
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 */
int vflag = 0; int vflag = 0;
int dead = 0; /* Exit flag */ int dead = 0; /* Exit flag */
/* key buffer operations */ /* key buffer operations */
int key_buffer_add (struct key_buffer*, unsigned short); int key_buffer_add (struct key_buffer*, xkb_keysym_t);
int key_buffer_remove (struct key_buffer*, unsigned short); int key_buffer_remove (struct key_buffer*, xkb_keysym_t);
int key_buffer_compare_fuzzy (struct key_buffer *, struct key_buffer *); int key_buffer_compare_fuzzy (struct key_buffer *, struct key_buffer *);
int key_buffer_compare (struct key_buffer *, struct key_buffer *); int key_buffer_compare (struct key_buffer *, struct key_buffer *);
void key_buffer_reset (struct key_buffer *); void key_buffer_reset (struct key_buffer *);
@ -122,7 +121,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 *);
inline void remove_lock (void); 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);
@ -132,7 +131,6 @@ 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[])
{ {
@ -148,6 +146,8 @@ int main (int argc, char *argv[])
struct sigaction action; struct sigaction action;
struct input_event event; struct input_event event;
struct key_buffer pb = {{0}, 0}; /* Pressed keys buffer */ struct key_buffer pb = {{0}, 0}; /* Pressed keys buffer */
// struct xkb_compose_table_t *comp_table = NULL;
// const char *locale = NULL;
/* Parse command line arguments */ /* Parse command line arguments */
while ((opc = getopt(argc, argv, "vc:dh")) != -1) { while ((opc = getopt(argc, argv, "vc:dh")) != -1) {
@ -158,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(wrap_err("Bad malloc:")); die("malloc in main():");
strcpy(ext_config_file, optarg); strcpy(ext_config_file, optarg);
break; break;
case 'd': case 'd':
@ -175,13 +175,37 @@ 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);
sigaction(SIGCHLD, &action, NULL);
/* Initialize xkbcommon */
if (!(keyboard.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS)))
die("Error initializing xkbcommon context: ");
keyboard.names = (struct xkb_rule_names){
.rules = NULL,
.model = NULL,
.layout = "gb",
.variant = NULL,
.options = NULL,
};
if (sigaction(SIGINT, &action, NULL) == -1) if (!(keyboard.keymap = xkb_keymap_new_from_names(keyboard.context,
die(wrap_err("Error setting interrupt handler:")); &keyboard.names, XKB_KEYMAP_COMPILE_NO_FLAGS)))
if (sigaction(SIGUSR1, &action, NULL) == -1) die("Error compiling keymap: ");
die(wrap_err("Error setting interrupt handler:"));
if (sigaction(SIGCHLD, &action, NULL) == -1) if (!(keyboard.state = xkb_state_new(keyboard.keymap)))
die(wrap_err("Error setting interrupt handler:")); die("Error creating keyboard state: ");
/*
locale = setlocale(LC_CTYPE, NULL);
if (!(comp_table = xkb_compose_table_new_from_locale(keyboard.context,
locale, XKB_COMPOSE_COMPILE_NO_FLAGS)))
die("Error compiling compose table: ");
if (!(keyboard.comp_state = xkb_compose_state_new(comp_table,
XKB_COMPOSE_TABLE_NO_FLAGS)))
die("Error creating compose state: ");
*/
/* Parse config file */ /* Parse config file */
parse_config_file(); parse_config_file();
@ -189,7 +213,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(wrap_err("Can't open lock file:")); die("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;
@ -200,12 +224,15 @@ int main (int argc, char *argv[])
/* If a dump is requested print the hotkey list then exit */ /* If a dump is requested print the hotkey list then exit */
if (dump) { if (dump) {
char key_name[64];
printf("DUMPING HOTKEY LIST\n\n"); printf("DUMPING HOTKEY LIST\n\n");
for (struct hotkey_list_e *tmp = hotkey_list; tmp; tmp = tmp->next) { for (struct hotkey_list_e *tmp = hotkey_list; tmp; tmp = tmp->next) {
printf("Hotkey\n"); printf("Hotkey\n");
printf("\tKeys: "); printf("\tKeys: ");
for (unsigned int i = 0; i < tmp->data.kb.size; i++) for (unsigned int i = 0; i < tmp->data.kb.size; i++) {
printf("%s ", code_to_name(tmp->data.kb.buf[i])); xkb_keysym_get_name(tmp->data.kb.buf[i], key_name, 64);
printf("%s ", key_name);
}
printf("\n\tMatching: %s\n", tmp->fuzzy ? "fuzzy" : "ordered"); printf("\n\tMatching: %s\n", tmp->fuzzy ? "fuzzy" : "ordered");
printf("\tCommand: %s\n\n", tmp->command); printf("\tCommand: %s\n\n", tmp->command);
} }
@ -217,9 +244,10 @@ int main (int argc, char *argv[])
/* Prepare directory update watcher */ /* Prepare directory update watcher */
if (event_watcher < 0) if (event_watcher < 0)
die(wrap_err("Could not call inotify_init:")); die("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(wrap_err("Could not add /dev/input to the watch list:")); die("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);
@ -229,8 +257,13 @@ int main (int argc, char *argv[])
int t = 0; int t = 0;
static unsigned int prev_size; static unsigned int prev_size;
static struct epoll_event ev_type; static struct epoll_event ev_type;
static xkb_keycode_t keycode;
static xkb_keysym_t keysym;
// enum xkb_state_component changed;
struct hotkey_list_e *tmp; struct hotkey_list_e *tmp;
char buf[EVENT_BUF_LEN]; char buf[EVENT_BUF_LEN];
static char key_name[64];
// enum xkb_compose_status status;
/* On linux use epoll(2) as it gives better performance */ /* On linux use epoll(2) as it gives better performance */
if (epoll_wait(ev_fd, &ev_type, fd_num, -1) < 0 || dead) { if (epoll_wait(ev_fd, &ev_type, fd_num, -1) < 0 || dead) {
@ -245,7 +278,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(wrap_err("Could not close event fd list (ev_fd):")); die("Could not close event filedescriptors 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;
} }
@ -256,24 +289,32 @@ int main (int argc, char *argv[])
read_b = read(fds[i], &event, sizeof(struct input_event)); read_b = read(fds[i], &event, sizeof(struct input_event));
if (read_b != sizeof(struct input_event)) continue; if (read_b != sizeof(struct input_event)) continue;
/* Ignore touchpad events */ if (event.type != EV_KEY)
if ( continue;
event.type == EV_KEY &&
event.code != BTN_TOUCH && // evdev offset
event.code != BTN_TOOL_FINGER && keycode = event.code + 8;
event.code != BTN_TOOL_DOUBLETAP && if (event.value == 2 && !xkb_keymap_key_repeats(
event.code != BTN_TOOL_TRIPLETAP keyboard.keymap, keycode))
) { continue;
switch (event.value) { if (event.value) {
/* Key released */ keysym = xkb_state_key_get_one_sym(keyboard.state, keycode);
case 0: // kbd_compose_state_feed(keyboard.comp_state, keysym);
key_buffer_remove(&pb, event.code);
break;
/* Key pressed */
case 1:
key_buffer_add(&pb, event.code);
break;
} }
/* status = xkb_compose_state_get_status(keyboard.comp_state);
if (status == XKB_COMPOSE_CANCELLED || status == XKB_COMPOSE_COMPOSED)
xkb_compose_state_reset(kbd->compose_state);
*/
if (event.value) { /* Key pressed */
xkb_state_update_key(keyboard.state,
keycode, XKB_KEY_UP);
key_buffer_add(&pb, keysym);
} else {/* Key released */
xkb_state_update_key(keyboard.state,
keycode, XKB_KEY_DOWN);
key_buffer_remove(&pb, keysym);
} }
} }
@ -283,7 +324,8 @@ int main (int argc, char *argv[])
if (vflag) { if (vflag) {
printf("Pressed keys: "); printf("Pressed keys: ");
for (unsigned int i = 0; i < pb.size; i++) for (unsigned int i = 0; i < pb.size; i++)
printf("%s ", code_to_name(pb.buf[i])); xkb_keysym_get_name(pb.buf[i], key_name, 64);
printf("%s ", key_name);
putchar('\n'); putchar('\n');
} }
@ -305,17 +347,21 @@ int main (int argc, char *argv[])
wait(NULL); wait(NULL);
if (!dead) if (!dead)
fprintf(stderr, red("An error occured: %s\n"), errno ? strerror(errno): "idk"); fprintf(stderr, red("An error occured: %s\n"), errno ? strerror(errno): "idk");
xkb_state_unref(keyboard.state);
xkb_keymap_unref(keyboard.keymap);
// xkb_compose_state_unref(keyboard.comp_state);
xkb_context_unref(keyboard.context);
close(ev_fd); close(ev_fd);
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(wrap_err("Error closing file descriptors:")); die("Error closing file descriptors:");
return 0; return 0;
} }
/* Adds a keycode to the pressed buffer if it is not already present /* Adds a keycode to the pressed buffer if it is not already present
* Returns non zero if the key was not added. */ * Returns non zero if the key was not added. */
int key_buffer_add (struct key_buffer *pb, unsigned short key) int key_buffer_add (struct key_buffer *pb, xkb_keysym_t key)
{ {
if (!pb) return 1; if (!pb) return 1;
/* Linear search if the key is already buffered */ /* Linear search if the key is already buffered */
@ -332,7 +378,7 @@ int key_buffer_add (struct key_buffer *pb, unsigned short key)
/* Removes a keycode from a pressed buffer if it is present returns /* Removes a keycode from a pressed buffer if it is present returns
* non zero in case of failure (key not present or buffer empty). */ * non zero in case of failure (key not present or buffer empty). */
int key_buffer_remove (struct key_buffer *pb, unsigned short key) int key_buffer_remove (struct key_buffer *pb, xkb_keysym_t key)
{ {
if (!pb) return 1; if (!pb) return 1;
@ -357,7 +403,7 @@ void int_handler (int signum)
switch (signum) { switch (signum) {
case SIGINT: case SIGINT:
if (dead) if (dead)
die(wrap_err("An error occured, exiting")); die("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;
@ -387,20 +433,20 @@ void exec_command (char *command)
return; return;
default: default:
/* Some other error */ /* Some other error */
fprintf(stderr, wrap_err("Could not parse, %s is not valid\n"), command); fprintf(stderr, "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, wrap_err("Could not create child process: %s"), strerror(errno)); fprintf(stderr, "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(wrap_err("%s:"), command); die("%s:", command);
break; break;
default: default:
while (waitpid(cpid, NULL, WNOHANG) == -1) {} while (waitpid(cpid, NULL, WNOHANG) == -1) {}
@ -419,7 +465,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(wrap_err("Could not open /dev/input:")); die("Could not open /dev/input:");
(*fd_num) = 0; (*fd_num) = 0;
@ -460,7 +506,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(wrap_err("Realloc file descriptors:")); die("realloc file descriptors:");
(*fds) = (int *) tmp_p; (*fds) = (int *) tmp_p;
(*fds)[(*fd_num)] = tmp_fd; (*fds)[(*fd_num)] = tmp_fd;
@ -471,7 +517,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(wrap_err("Could not open any devices, exiting")); die("Could not open any devices, exiting");
} }
} }
@ -481,12 +527,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(wrap_err("epoll_create failed:")); die("epoll_create failed in prepare_epoll:");
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(wrap_err("Could not add file descriptor to the epoll list:")); die("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(wrap_err("Could not add file descriptor to the epoll list:")); die("Could not add file descriptor to the epoll list:");
return ev_fd; return ev_fd;
} }
@ -534,12 +580,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 (is_empty(cmd) || !(size = strlen(cmd))) if (!(size = strlen(cmd)))
return; return;
if (!(tmp = malloc(sizeof(struct hotkey_list_e)))) if (!(tmp = malloc(sizeof(struct hotkey_list_e))))
die(wrap_err("Bad malloc:")); die("Memory allocation failed in hotkey_list_add():");
if (!(tmp->command = malloc(size + 1))) if (!(tmp->command = malloc(size + 1)))
die(wrap_err("Bad malloc:")); die("Memory allocation failed in hotkey_list_add():");
strcpy(tmp->command, cmd); strcpy(tmp->command, cmd);
tmp->data = *dt; tmp->data = *dt;
tmp->fuzzy = f; tmp->fuzzy = f;
@ -575,22 +621,22 @@ 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};
int config_file; FILE *fd;
/* 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 eof = 0; int cmd_is_alias = 0;
int token_size = 0; int alloc_tmp = 0, alloc_size = 0;
int i_tmp = 0, linenum = 1; int i_tmp = 0, linenum = 1;
char *buffer; char block[BLOCK_SIZE + 1] = {0};
char *bb = NULL; char *bb = NULL;
char *keys = NULL; char *keys = NULL;
char *cmd = NULL; char *cmd = NULL;
char *cp_tmp = NULL; char *cp_tmp = NULL;
union hotkey_main_data dt = {0}; union hotkey_main_data dt = {0};
unsigned short us_tmp = 0; xkb_keysym_t ks_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:
@ -599,15 +645,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(wrap_err("Not enough space:")); die("Not enough space:");
default: default:
die(wrap_err("Path not valid:")); die("Path not valid:");
} }
config_file = open(result.we_wordv[0], O_RDONLY | O_NONBLOCK); fd = fopen(result.we_wordv[0], "r");
wordfree(&result); wordfree(&result);
if (config_file < 0) if (!fd)
die(wrap_err("Error opening config file:")); die("Error opening config file:");
free(ext_config_file); free(ext_config_file);
ext_config_file = NULL; ext_config_file = NULL;
} else { } else {
@ -619,54 +665,51 @@ 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(wrap_err("Not enough space:")); die("Not enough space:");
default: default:
die(wrap_err("Path not valid:")); die("Path not valid:");
} }
config_file = open(result.we_wordv[0], O_RDONLY | O_NONBLOCK); fd = fopen(result.we_wordv[0], "r");
wordfree(&result); wordfree(&result);
if (config_file >= 0) if (fd)
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 (!config_file) if (!fd)
die(wrap_err("Could not open any config files, check stderr for more details")); die("Could not open any config files, check the log 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 (!eof) { while (block_state != END) {
// FIXME: incorect line counting, especially for multiline commands int tmp = 0;
memset(block, 0, BLOCK_SIZE + 1);
tmp = fread(block, sizeof(char), BLOCK_SIZE, fd);
if (!tmp)
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) { switch (parse_state) {
// First state // First state
case NORM: case NORM:
// remove whitespaces // remove whitespaces
while (isblank(*bb)) while (isblank(*bb) && *bb)
bb++; bb++;
// get state // get state
switch (*bb) { switch (*bb) {
#if defined(__X86_64__) || defined(__i386__)
case EOF:
#endif
case '\0': case '\0':
eof = 1; // If it is the end of the last block exit
block_state = block_state == LAST_BL ? END : NEW_BL;
break; break;
case '\n': case '\n':
case '#': case '#':
@ -679,10 +722,15 @@ void parse_config_file (void)
break; break;
// Skip line (comment) // Skip line (comment)
case LINE_SKIP: case LINE_SKIP:
for (;(bb - buffer) < file_size && *bb != '\n'; bb++); while (*bb != '\n' && *bb)
bb++;
if (*bb) {
bb++; bb++;
linenum++; linenum++;
parse_state = NORM; parse_state = NORM;
} else {
block_state = NEW_BL;
}
break; break;
// Get compairson method // Get compairson method
case GET_TYPE: case GET_TYPE:
@ -697,8 +745,8 @@ void parse_config_file (void)
type = ALIAS; type = ALIAS;
break; break;
default: default:
die(wrap_err("Error at line %d: " die("Error at line %d: "
"hotkey definition must start with '-', '*' or '@'"), "hotkey definition must start with '-', '*' or '@'",
linenum); linenum);
break; break;
} }
@ -707,78 +755,128 @@ void parse_config_file (void)
break; break;
// Get keys // Get keys
case 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 (!keys) {
if (bb[token_size] == '\n') if (!(keys = malloc(alloc_size = (sizeof(char) * 64))))
die(wrap_err("Error at line %d: " die("malloc for keys in parse_config_file():");
"no command specified, missing ':' after keys"), memset(keys, 0, alloc_size);
linenum); } else if (alloc_tmp >= alloc_size) {
keys = malloc(token_size + 1); if (!(keys = realloc(keys, alloc_size = alloc_size * 2)))
if (!keys) die("realloc for keys in parse_config_file():");
die(wrap_err("Bad malloc parsing keys:")); memset(&keys[alloc_size / 2], 0, alloc_size / 2);
memcpy(keys, bb, token_size); }
keys[token_size] = '\0';
bb += token_size + 1; 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; parse_state = GET_CMD;
break; break;
} else {
die("Error at line %d: "
"no command specified, missing ':' or '<' after keys",
linenum);
}
break;
// Get command // Get command
case GET_CMD: case GET_CMD:
for (token_size = 0; token_size < (file_size - !(bb - buffer)); token_size++) { if (!cmd) {
if (bb[token_size] == ':') 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; break;
if (bb[token_size] == '\n' && bb[token_size - token_size ? 1 : 0] != '\\') } else {
strncat(cmd, bb, alloc_tmp);
if (!(bb[alloc_tmp - 1] == '\\'))
parse_state = LAST;
bb += alloc_tmp + 1;
linenum++;
break; 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; break;
case LAST: case LAST:
if (!keys) if (!keys)
die(wrap_err("Keys is NULL")); die("error");
i_tmp = strlen(keys); i_tmp = strlen(keys);
for (int i = 0; i < i_tmp; i++) { for (int i = 0; i < i_tmp; i++) {
if (isblank(keys[i])) if (isblank(keys[i])) {
memmove(&keys[i], &keys[i + 1], i_tmp - i); memmove(&keys[i], &keys[i + 1], --i_tmp);
keys[i_tmp] = '\0';
}
} }
cp_tmp = strtok(keys, ","); cp_tmp = strtok(keys, ",");
if(!cp_tmp) if(!cp_tmp)
die(wrap_err("Error at line %d: " die("Error at line %d: "
"keys not present"), linenum - 1); "keys not present", linenum - 1);
if (type != ALIAS) { if (type != ALIAS) {
do { do {
if (!(us_tmp = key_to_code(cp_tmp))) { if ((ks_tmp = xkb_keysym_from_name(cp_tmp, XKB_KEYSYM_NO_FLAGS)) == XKB_KEY_NoSymbol) {
die(wrap_err("Error at line %d: " die("Error at line %d: "
"%s is not a valid key"), "%s is not a valid key",
linenum - 1, cp_tmp); linenum - 1, cp_tmp);
} }
if (key_buffer_add(&dt.kb, us_tmp)) if (key_buffer_add(&dt.kb, ks_tmp))
die(wrap_err("Too many keys")); die("Too many keys");
} while ((cp_tmp = strtok(NULL, ","))); } while ((cp_tmp = strtok(NULL, ",")));
} else { } else {
if (!(dt.name = malloc(strlen(cp_tmp) + 1))) if (!(dt.name = malloc(strlen(cp_tmp) + 1)))
die(wrap_err("Bad malloc:")); die("malloc in parse_config_file():");
strcpy(dt.name, cp_tmp); 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; cp_tmp = cmd;
while (isblank(*cp_tmp)) while (isblank(*cp_tmp))
cp_tmp++; cp_tmp++;
if (*cp_tmp == '\0') if (*cp_tmp == '\0')
die(wrap_err("Error at line %d: " die("Error at line %d: "
"command not present"), linenum - 1); "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);
@ -791,23 +889,24 @@ void parse_config_file (void)
parse_state = NORM; parse_state = NORM;
break; break;
default: default:
die(wrap_err("Unknown state")); die("Unknown state in parse_config_file");
break; break;
} }
} }
munmap(buffer, file_size); }
for (struct hotkey_list_e *hkl = hotkey_list, *tmp; hkl; hkl = hkl->next) {
for (struct hotkey_list_e *hkl = hotkey_list, *tmp; hkl;) { if (hkl->fuzzy == ALIAS) {
tmp = hkl; tmp = hkl;
hkl = hkl->next; 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 << (tmp->data.kb.size - 1); hotkey_size_mask |= 1 << (hkl->data.kb.size - 1);
}
} }
} }
/*
unsigned short key_to_code (char *key) unsigned short key_to_code (char *key)
{ {
for (char *tmp = key; *tmp; tmp++) { for (char *tmp = key; *tmp; tmp++) {
@ -820,6 +919,7 @@ unsigned short key_to_code (char *key)
} }
return 0; return 0;
} }
*/
void remove_lock (void) void remove_lock (void)
{ {
@ -846,6 +946,7 @@ void die(const char *fmt, ...)
exit(errno ? errno : 1); exit(errno ? errno : 1);
} }
/*
const char * code_to_name (unsigned int code) const char * code_to_name (unsigned int code)
{ {
for (int i = 0; i < array_size_const(key_conversion_table); i++) { for (int i = 0; i < array_size_const(key_conversion_table); i++) {
@ -854,6 +955,7 @@ const char * code_to_name (unsigned int code)
} }
return "Key not recognized"; return "Key not recognized";
} }
*/
void usage (void) void usage (void)
{ {
@ -864,47 +966,3 @@ 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);
}

504
keys.h

@ -1,504 +0,0 @@
#ifndef _H_KEYS
#define _H_KEYS
#include <linux/input.h>
struct {
const char *const name;
const unsigned short value;
} key_conversion_table[] =
{{"ESC", KEY_ESC},
{"1", KEY_1},
{"2", KEY_2},
{"3", KEY_3},
{"4", KEY_4},
{"5", KEY_5},
{"6", KEY_6},
{"7", KEY_7},
{"8", KEY_8},
{"9", KEY_9},
{"0", KEY_0},
{"MINUS", KEY_MINUS},
{"EQUAL", KEY_EQUAL},
{"BACKSPACE", KEY_BACKSPACE},
{"TAB", KEY_TAB},
{"Q", KEY_Q},
{"W", KEY_W},
{"E", KEY_E},
{"R", KEY_R},
{"T", KEY_T},
{"Y", KEY_Y},
{"U", KEY_U},
{"I", KEY_I},
{"O", KEY_O},
{"P", KEY_P},
{"LEFTBRACE", KEY_LEFTBRACE},
{"RIGHTBRACE", KEY_RIGHTBRACE},
{"ENTER", KEY_ENTER},
{"LEFTCTRL", KEY_LEFTCTRL},
{"A", KEY_A},
{"S", KEY_S},
{"D", KEY_D},
{"F", KEY_F},
{"G", KEY_G},
{"H", KEY_H},
{"J", KEY_J},
{"K", KEY_K},
{"L", KEY_L},
{"SEMICOLON", KEY_SEMICOLON},
{"APOSTROPHE", KEY_APOSTROPHE},
{"GRAVE", KEY_GRAVE},
{"LEFTSHIFT", KEY_LEFTSHIFT},
{"BACKSLASH", KEY_BACKSLASH},
{"Z", KEY_Z},
{"X", KEY_X},
{"C", KEY_C},
{"V", KEY_V},
{"B", KEY_B},
{"N", KEY_N},
{"M", KEY_M},
{"COMMA", KEY_COMMA},
{"DOT", KEY_DOT},
{"SLASH", KEY_SLASH},
{"RIGHTSHIFT", KEY_RIGHTSHIFT},
{"KPASTERISK", KEY_KPASTERISK},
{"LEFTALT", KEY_LEFTALT},
{"SPACE", KEY_SPACE},
{"CAPSLOCK", KEY_CAPSLOCK},
{"F1", KEY_F1},
{"F2", KEY_F2},
{"F3", KEY_F3},
{"F4", KEY_F4},
{"F5", KEY_F5},
{"F6", KEY_F6},
{"F7", KEY_F7},
{"F8", KEY_F8},
{"F9", KEY_F9},
{"F10", KEY_F10},
{"NUMLOCK", KEY_NUMLOCK},
{"SCROLLLOCK", KEY_SCROLLLOCK},
{"KP7", KEY_KP7},
{"KP8", KEY_KP8},
{"KP9", KEY_KP9},
{"KPMINUS", KEY_KPMINUS},
{"KP4", KEY_KP4},
{"KP5", KEY_KP5},
{"KP6", KEY_KP6},
{"KPPLUS", KEY_KPPLUS},
{"KP1", KEY_KP1},
{"KP2", KEY_KP2},
{"KP3", KEY_KP3},
{"KP0", KEY_KP0},
{"KPDOT", KEY_KP0},
{"ZENKAKUHANKAKU", KEY_ZENKAKUHANKAKU},
{"102ND", KEY_102ND},
{"F11", KEY_F11},
{"F12", KEY_F12},
{"RO", KEY_RO},
{"KATAKANA", KEY_KATAKANA},
{"HIRAGANA", KEY_HIRAGANA},
{"HENKAN", KEY_HENKAN},
{"KATAKANAHIRAGANA", KEY_KATAKANAHIRAGANA},
{"MUHENKAN", KEY_MUHENKAN},
{"KPJPCOMMA", KEY_KPJPCOMMA},
{"KPENTER", KEY_KPENTER},
{"RIGHTCTRL", KEY_RIGHTCTRL},
{"KPSLASH", KEY_KPSLASH},
{"SYSRQ", KEY_SYSRQ},
{"RIGHTALT", KEY_RIGHTALT},
{"LINEFEED", KEY_LINEFEED},
{"HOME", KEY_HOME},
{"UP", KEY_UP},
{"PAGEUP", KEY_PAGEUP},
{"LEFT", KEY_LEFT},
{"RIGHT", KEY_RIGHT},
{"END", KEY_END},
{"DOWN", KEY_DOWN},
{"PAGEDOWN", KEY_PAGEDOWN},
{"INSERT", KEY_INSERT},
{"DELETE", KEY_DELETE},
{"MACRO", KEY_MACRO},
{"MUTE", KEY_MUTE},
{"VOLUMEDOWN", KEY_VOLUMEDOWN},
{"VOLUMEUP", KEY_VOLUMEUP},
{"POWER", KEY_POWER},
{"KPEQUAL", KEY_KPEQUAL},
{"KPPLUSMINUS", KEY_KPPLUSMINUS},
{"PAUSE", KEY_PAUSE},
{"SCALE", KEY_SCALE},
{"KPCOMMA", KEY_KPCOMMA},
{"HANGEUL", KEY_HANGEUL},
{"HANGUEL", KEY_HANGEUL},
{"HANJA", KEY_HANJA},
{"YEN", KEY_YEN},
{"LEFTMETA", KEY_LEFTMETA},
{"RIGHTMETA", KEY_LEFTMETA},
{"COMPOSE", KEY_COMPOSE},
{"STOP", KEY_STOP},
{"AGAIN", KEY_AGAIN},
{"PROPS", KEY_PROPS},
{"UNDO", KEY_UNDO},
{"FRONT", KEY_FRONT},
{"COPY", KEY_COPY},
{"OPEN", KEY_OPEN},
{"PASTE", KEY_PASTE},
{"FIND", KEY_FIND},
{"CUT", KEY_CUT},
{"HELP", KEY_HELP},
{"MENU", KEY_MENU},
{"CALC", KEY_CALC},
{"SETUP", KEY_SETUP},
{"SLEEP", KEY_SLEEP},
{"WAKEUP", KEY_WAKEUP},
{"FILE", KEY_FILE},
{"SENDFILE", KEY_SENDFILE},
{"DELETEFILE", KEY_DELETEFILE},
{"XFER", KEY_XFER},
{"PROG1", KEY_PROG1},
{"PROG2", KEY_PROG2},
{"WWW", KEY_WWW},
{"MSDOS", KEY_MSDOS},
{"COFFEE", KEY_COFFEE},
{"SCREENLOCK", KEY_COFFEE},
{"DIRECTION", KEY_DIRECTION},
{"CYCLEWINDOWS", KEY_CYCLEWINDOWS},
{"MAIL", KEY_MAIL},
{"BOOKMARKS", KEY_BOOKMARKS},
{"COMPUTER", KEY_COMPUTER},
{"BACK", KEY_BACK},
{"FORWARD", KEY_FORWARD},
{"CLOSECD", KEY_CLOSECD},
{"EJECTCD", KEY_EJECTCD},
{"EJECTCLOSECD", KEY_EJECTCLOSECD},
{"NEXTSONG", KEY_NEXTSONG},
{"PLAYPAUSE", KEY_PLAYPAUSE},
{"PREVIOUSSONG", KEY_PREVIOUSSONG},
{"STOPCD", KEY_STOPCD},
{"RECORD", KEY_RECORD},
{"REWIND", KEY_REWIND},
{"PHONE", KEY_PHONE},
{"ISO", KEY_ISO},
{"CONFIG", KEY_CONFIG},
{"HOMEPAGE", KEY_HOMEPAGE},
{"REFRESH", KEY_REFRESH},
{"EXIT", KEY_EXIT},
{"MOVE", KEY_MOVE},
{"EDIT", KEY_EDIT},
{"SCROLLUP", KEY_SCROLLUP},
{"SCROLLDOWN", KEY_SCROLLDOWN},
{"KPLEFTPAREN", KEY_KPLEFTPAREN},
{"KPRIGHTPAREN", KEY_KPRIGHTPAREN},
{"NEW", KEY_NEW},{"REDO", KEY_REDO},
{"F13", KEY_F13},{"F14", KEY_F14},
{"F15", KEY_F15},{"F16", KEY_F16},
{"F17", KEY_F17},{"F18", KEY_F18},
{"F19", KEY_F19},{"F20", KEY_F20},
{"F21", KEY_F21},{"F22", KEY_F22},
{"F23", KEY_F23},{"F24", KEY_F24},
{"PLAYCD", KEY_PLAYCD},
{"PAUSECD", KEY_PAUSECD},
{"PROG3", KEY_PROG3},
{"PROG4", KEY_PROG4},
{"DASHBOARD", KEY_DASHBOARD},
{"SUSPEND", KEY_SUSPEND},
{"CLOSE", KEY_CLOSE},
{"PLAY", KEY_PLAY},
{"FASTFORWARD", KEY_FASTFORWARD},
{"BASSBOOST", KEY_BASSBOOST},
{"PRINT", KEY_PRINT},
{"HP", KEY_HP},
{"CAMERA", KEY_CAMERA},
{"SOUND", KEY_SOUND},
{"QUESTION", KEY_QUESTION},
{"EMAIL", KEY_EMAIL},
{"CHAT", KEY_CHAT},
{"SEARCH", KEY_SEARCH},
{"CONNECT", KEY_CONNECT},
{"FINANCE", KEY_FINANCE},
{"SPORT", KEY_SPORT},
{"SHOP", KEY_SHOP},
{"ALTERASE", KEY_ALTERASE},
{"CANCEL", KEY_CANCEL},
{"BRIGHTNESSDOWN", KEY_BRIGHTNESSDOWN},
{"BRIGHTNESSUP", KEY_BRIGHTNESSUP},
{"MEDIA", KEY_MEDIA},
{"SWITCHVIDEOMODE", KEY_SWITCHVIDEOMODE},
{"KBDILLUMTOGGLE", KEY_KBDILLUMTOGGLE},
{"KBDILLUMDOWN", KEY_KBDILLUMDOWN},
{"KBDILLUMUP", KEY_KBDILLUMUP},
{"SEND", KEY_SEND},
{"REPLY", KEY_REPLY},
{"FORWARDMAIL", KEY_FORWARDMAIL},
{"SAVE", KEY_SAVE},
{"DOCUMENTS", KEY_DOCUMENTS},
{"BATTERY", KEY_BATTERY},
{"BLUETOOTH", KEY_BLUETOOTH},
{"WLAN", KEY_WLAN},
{"UWB", KEY_UWB},
{"UNKNOWN", KEY_UNKNOWN},
{"VIDEO_NEXT", KEY_VIDEO_NEXT},
{"VIDEO_PREV", KEY_VIDEO_PREV},
{"BRIGHTNESS_CYCLE", KEY_BRIGHTNESS_CYCLE},
{"BRIGHTNESS_ZERO", KEY_BRIGHTNESS_ZERO},
{"DISPLAY_OFF", KEY_DISPLAY_OFF},
{"WIMAX", KEY_WIMAX},
{"RFKILL", KEY_RFKILL},
{"BTN_MISC", BTN_MISC},
{"BTN_0", BTN_0},
{"BTN_1", BTN_1},
{"BTN_2", BTN_2},
{"BTN_3", BTN_3},
{"BTN_4", BTN_4},
{"BTN_5", BTN_5},
{"BTN_6", BTN_6},
{"BTN_7", BTN_7},
{"BTN_8", BTN_8},
{"BTN_9", BTN_9},
{"BTN_MOUSE", BTN_MOUSE},
{"BTN_LEFT", BTN_LEFT},
{"BTN_RIGHT", BTN_RIGHT},
{"BTN_MIDDLE", BTN_MIDDLE},
{"BTN_SIDE", BTN_SIDE},
{"BTN_EXTRA", BTN_EXTRA},
{"BTN_FORWARD", BTN_FORWARD},
{"BTN_BACK", BTN_BACK},
{"BTN_TASK", BTN_TASK},
{"BTN_JOYSTICK", BTN_JOYSTICK},
{"BTN_TRIGGER", BTN_TRIGGER},
{"BTN_THUMB", BTN_THUMB},
{"BTN_THUMB2", BTN_THUMB2},
{"BTN_TOP", BTN_TOP},
{"BTN_TOP2", BTN_TOP2},
{"BTN_PINKIE", BTN_PINKIE},
{"BTN_BASE", BTN_BASE},
{"BTN_BASE2", BTN_BASE2},
{"BTN_BASE3", BTN_BASE3},
{"BTN_BASE4", BTN_BASE4},
{"BTN_BASE5", BTN_BASE5},
{"BTN_BASE6", BTN_BASE6},
{"BTN_DEAD", BTN_DEAD},
{"BTN_GAMEPAD", BTN_GAMEPAD},
{"BTN_A", BTN_A},
{"BTN_B", BTN_B},
{"BTN_C", BTN_C},
{"BTN_X", BTN_X},
{"BTN_Y", BTN_Y},
{"BTN_Z", BTN_Z},
{"BTN_TL", BTN_TL},
{"BTN_TR", BTN_TR},
{"BTN_TL2", BTN_TL2},
{"BTN_TR2", BTN_TR2},
{"BTN_SELECT", BTN_SELECT},
{"BTN_START", BTN_START},
{"BTN_MODE", BTN_MODE},
{"BTN_THUMBL", BTN_THUMBL},
{"BTN_THUMBR", BTN_THUMBR},
{"BTN_DIGI", BTN_DIGI},
{"BTN_TOOL_PEN", BTN_TOOL_PEN},
{"BTN_TOOL_RUBBER", BTN_TOOL_RUBBER},
{"BTN_TOOL_BRUSH", BTN_TOOL_RUBBER},
{"BTN_TOOL_PENCIL", BTN_TOOL_PENCIL},
{"BTN_TOOL_AIRBRUSH", BTN_TOOL_AIRBRUSH},
{"BTN_TOOL_FINGER", BTN_TOOL_FINGER},
{"BTN_TOOL_MOUSE", BTN_TOOL_MOUSE},
{"BTN_TOOL_LENS", BTN_TOOL_LENS},
{"BTN_TOUCH", BTN_TOUCH},
{"BTN_STYLUS", BTN_STYLUS},
{"BTN_STYLUS2", BTN_STYLUS2},
{"BTN_TOOL_DOUBLETAP", BTN_TOOL_DOUBLETAP},
{"BTN_TOOL_TRIPLETAP", BTN_TOOL_TRIPLETAP},
{"BTN_TOOL_QUADTAP", BTN_TOOL_QUADTAP},
{"BTN_WHEEL", BTN_WHEEL},
{"BTN_GEAR_DOWN", BTN_GEAR_DOWN},
{"BTN_GEAR_UP", BTN_GEAR_UP},
{"OK", KEY_OK},
{"SELECT", KEY_SELECT},
{"GOTO", KEY_GOTO},
{"CLEAR", KEY_CLEAR},
{"POWER2", KEY_POWER2},
{"OPTION", KEY_OPTION},
{"INFO", KEY_INFO},
{"TIME", KEY_TIME},
{"VENDOR", KEY_VENDOR},
{"ARCHIVE", KEY_ARCHIVE},
{"PROGRAM", KEY_PROGRAM},
{"CHANNEL", KEY_CHANNEL},
{"FAVORITES", KEY_FAVORITES},
{"EPG", KEY_EPG},
{"PVR", KEY_PVR},
{"MHP", KEY_MHP},
{"LANGUAGE", KEY_LANGUAGE},
{"TITLE", KEY_TITLE},
{"SUBTITLE", KEY_SUBTITLE},
{"ANGLE", KEY_ANGLE},
{"ZOOM", KEY_ZOOM},
{"MODE", KEY_MODE},
{"KEYBOARD", KEY_KEYBOARD},
{"SCREEN", KEY_SCREEN},
{"PC", KEY_PC},
{"TV", KEY_TV},
{"TV2", KEY_TV2},
{"VCR", KEY_VCR},
{"VCR2", KEY_VCR2},
{"SAT", KEY_SAT},
{"SAT2", KEY_SAT2},
{"CD", KEY_CD},
{"TAPE", KEY_TAPE},
{"RADIO", KEY_RADIO},
{"TUNER", KEY_TUNER},
{"PLAYER", KEY_PLAYER},
{"TEXT", KEY_TEXT},
{"DVD", KEY_DVD},
{"AUX", KEY_AUX},
{"MP3", KEY_MP3},
{"AUDIO", KEY_AUDIO},
{"VIDEO", KEY_VIDEO},
{"DIRECTORY", KEY_DIRECTORY},
{"LIST", KEY_LIST},
{"MEMO", KEY_MEMO},
{"CALENDAR", KEY_CALENDAR},
{"RED", KEY_RED},
{"GREEN", KEY_GREEN},
{"YELLOW", KEY_YELLOW},
{"BLUE", KEY_BLUE},
{"CHANNELUP", KEY_CHANNELUP},
{"CHANNELDOWN", KEY_CHANNELDOWN},
{"FIRST", KEY_FIRST},{"LAST", KEY_LAST},
{"AB", KEY_AB},
{"NEXT", KEY_NEXT},
{"RESTART", KEY_RESTART},
{"SLOW", KEY_SLOW},
{"SHUFFLE", KEY_SHUFFLE},
{"BREAK", KEY_BREAK},
{"PREVIOUS", KEY_PREVIOUS},
{"DIGITS", KEY_DIGITS},
{"TEEN", KEY_TEEN},
{"TWEN", KEY_TWEN},
{"VIDEOPHONE", KEY_VIDEOPHONE},
{"GAMES", KEY_GAMES},
{"ZOOMIN", KEY_ZOOMIN},
{"ZOOMOUT", KEY_ZOOMOUT},
{"ZOOMRESET", KEY_ZOOMRESET},
{"WORDPROCESSOR", KEY_WORDPROCESSOR},
{"EDITOR", KEY_EDITOR},
{"SPREADSHEET", KEY_SPREADSHEET},
{"GRAPHICSEDITOR", KEY_GRAPHICSEDITOR},
{"PRESENTATION", KEY_PRESENTATION},
{"DATABASE", KEY_DATABASE},
{"NEWS", KEY_NEWS},
{"VOICEMAIL", KEY_VOICEMAIL},
{"ADDRESSBOOK", KEY_ADDRESSBOOK},
{"MESSENGER", KEY_MESSENGER},
{"DISPLAYTOGGLE", KEY_DISPLAYTOGGLE},
{"SPELLCHECK", KEY_SPELLCHECK},
{"LOGOFF", KEY_LOGOFF},
{"DOLLAR", KEY_DOLLAR},
{"EURO", KEY_EURO},
{"FRAMEBACK", KEY_FRAMEBACK},
{"FRAMEFORWARD", KEY_FRAMEFORWARD},
{"CONTEXT_MENU", KEY_CONTEXT_MENU},
{"MEDIA_REPEAT", KEY_MEDIA_REPEAT},
{"10CHANNELSUP", KEY_10CHANNELSUP},
{"10CHANNELSDOWN", KEY_10CHANNELSDOWN},
{"DEL_EOL", KEY_DEL_EOL},
{"DEL_EOS", KEY_DEL_EOS},
{"INS_LINE", KEY_INS_LINE},
{"DEL_LINE", KEY_DEL_LINE},
{"FN", KEY_FN},
{"FN_ESC", KEY_FN_ESC},
{"FN_F1", KEY_FN_F1},
{"FN_F2", KEY_FN_F2},
{"FN_F3", KEY_FN_F3},
{"FN_F4", KEY_FN_F4},
{"FN_F5", KEY_FN_F5},
{"FN_F6", KEY_FN_F6},
{"FN_F7", KEY_FN_F7},
{"FN_F8", KEY_FN_F8},
{"FN_F9", KEY_FN_F9},
{"FN_F10", KEY_FN_F10},
{"FN_F11", KEY_FN_F11},
{"FN_F12", KEY_FN_F12},
{"FN_1", KEY_FN_1},
{"FN_2", KEY_FN_2},
{"FN_D", KEY_FN_D},
{"FN_E", KEY_FN_E},
{"FN_F", KEY_FN_F},
{"FN_S", KEY_FN_S},
{"FN_B", KEY_FN_B},
{"BRL_DOT1", KEY_BRL_DOT1},
{"BRL_DOT2", KEY_BRL_DOT2},
{"BRL_DOT3", KEY_BRL_DOT3},
{"BRL_DOT4", KEY_BRL_DOT4},
{"BRL_DOT5", KEY_BRL_DOT5},
{"BRL_DOT6", KEY_BRL_DOT6},
{"BRL_DOT7", KEY_BRL_DOT7},
{"BRL_DOT8", KEY_BRL_DOT8},
{"BRL_DOT9", KEY_BRL_DOT9},
{"BRL_DOT10", KEY_BRL_DOT10},
{"NUMERIC_0", KEY_NUMERIC_0},
{"NUMERIC_1", KEY_NUMERIC_1},
{"NUMERIC_2", KEY_NUMERIC_2},
{"NUMERIC_3", KEY_NUMERIC_3},
{"NUMERIC_4", KEY_NUMERIC_4},
{"NUMERIC_5", KEY_NUMERIC_5},
{"NUMERIC_6", KEY_NUMERIC_6},
{"NUMERIC_7", KEY_NUMERIC_7},
{"NUMERIC_8", KEY_NUMERIC_8},
{"NUMERIC_9", KEY_NUMERIC_9},
{"NUMERIC_STAR", KEY_NUMERIC_STAR},
{"NUMERIC_POUND", KEY_NUMERIC_POUND},
{"CAMERA_FOCUS", KEY_CAMERA_FOCUS},
{"WPS_BUTTON", KEY_WPS_BUTTON},
{"TOUCHPAD_TOGGLE", KEY_TOUCHPAD_TOGGLE},
{"TOUCHPAD_ON", KEY_TOUCHPAD_ON},
{"TOUCHPAD_OFF", KEY_TOUCHPAD_OFF},
{"BTN_TRIGGER_HAPPY", BTN_TRIGGER_HAPPY},
{"BTN_TRIGGER_HAPPY1", BTN_TRIGGER_HAPPY1},
{"BTN_TRIGGER_HAPPY2", BTN_TRIGGER_HAPPY2},
{"BTN_TRIGGER_HAPPY3", BTN_TRIGGER_HAPPY3},
{"BTN_TRIGGER_HAPPY4", BTN_TRIGGER_HAPPY4},
{"BTN_TRIGGER_HAPPY5", BTN_TRIGGER_HAPPY5},
{"BTN_TRIGGER_HAPPY6", BTN_TRIGGER_HAPPY6},
{"BTN_TRIGGER_HAPPY7", BTN_TRIGGER_HAPPY7},
{"BTN_TRIGGER_HAPPY8", BTN_TRIGGER_HAPPY8},
{"BTN_TRIGGER_HAPPY9", BTN_TRIGGER_HAPPY9},
{"BTN_TRIGGER_HAPPY10", BTN_TRIGGER_HAPPY10},
{"BTN_TRIGGER_HAPPY11", BTN_TRIGGER_HAPPY11},
{"BTN_TRIGGER_HAPPY12", BTN_TRIGGER_HAPPY12},
{"BTN_TRIGGER_HAPPY13", BTN_TRIGGER_HAPPY13},
{"BTN_TRIGGER_HAPPY14", BTN_TRIGGER_HAPPY14},
{"BTN_TRIGGER_HAPPY15", BTN_TRIGGER_HAPPY15},
{"BTN_TRIGGER_HAPPY16", BTN_TRIGGER_HAPPY16},
{"BTN_TRIGGER_HAPPY17", BTN_TRIGGER_HAPPY17},
{"BTN_TRIGGER_HAPPY18", BTN_TRIGGER_HAPPY18},
{"BTN_TRIGGER_HAPPY19", BTN_TRIGGER_HAPPY19},
{"BTN_TRIGGER_HAPPY20", BTN_TRIGGER_HAPPY20},
{"BTN_TRIGGER_HAPPY21", BTN_TRIGGER_HAPPY21},
{"BTN_TRIGGER_HAPPY22", BTN_TRIGGER_HAPPY22},
{"BTN_TRIGGER_HAPPY23", BTN_TRIGGER_HAPPY23},
{"BTN_TRIGGER_HAPPY24", BTN_TRIGGER_HAPPY24},
{"BTN_TRIGGER_HAPPY25", BTN_TRIGGER_HAPPY25},
{"BTN_TRIGGER_HAPPY26", BTN_TRIGGER_HAPPY26},
{"BTN_TRIGGER_HAPPY27", BTN_TRIGGER_HAPPY27},
{"BTN_TRIGGER_HAPPY28", BTN_TRIGGER_HAPPY28},
{"BTN_TRIGGER_HAPPY29", BTN_TRIGGER_HAPPY29},
{"BTN_TRIGGER_HAPPY30", BTN_TRIGGER_HAPPY30},
{"BTN_TRIGGER_HAPPY31", BTN_TRIGGER_HAPPY31},
{"BTN_TRIGGER_HAPPY32", BTN_TRIGGER_HAPPY32},
{"BTN_TRIGGER_HAPPY33", BTN_TRIGGER_HAPPY33},
{"BTN_TRIGGER_HAPPY34", BTN_TRIGGER_HAPPY34},
{"BTN_TRIGGER_HAPPY35", BTN_TRIGGER_HAPPY35},
{"BTN_TRIGGER_HAPPY36", BTN_TRIGGER_HAPPY36},
{"BTN_TRIGGER_HAPPY37", BTN_TRIGGER_HAPPY37},
{"BTN_TRIGGER_HAPPY38", BTN_TRIGGER_HAPPY38},
{"BTN_TRIGGER_HAPPY39", BTN_TRIGGER_HAPPY39},
{"BTN_TRIGGER_HAPPY40", BTN_TRIGGER_HAPPY40},
/* Aliases */
{"CTRL", KEY_LEFTCTRL},
{"META", KEY_LEFTMETA},
{"ALT", KEY_LEFTALT},
{"SHIFT", KEY_LEFTSHIFT},
{"PRINTSCR", KEY_SYSRQ},
{"MIC_MUTE", KEY_F20}};
#endif

@ -1,13 +1,14 @@
CC ?= gcc CC ?= gcc
CFLAGS = -Wall -Werror -pedantic --std=c99 -O2 CFLAGS = -Wall -Werror -pedantic --std=c99 -O2
VERSION = 0.5 LDFLAGS = -lxkbcommon
VERSION = 0.4
PREFIX = /usr/local PREFIX = /usr/local
MANPREFIX = ${PREFIX}/share/man MANPREFIX = ${PREFIX}/share/man
hkd: hkd.c hkd: hkd.c
debug: debug:
gcc -Wall -O0 -g hkd.c -o hkd_debug gcc -Wall -O0 -g hkd.c -lxkbcommon -o hkd_debug
install: hkd install: hkd
mkdir -p ${DESTDIR}${PREFIX}/bin mkdir -p ${DESTDIR}${PREFIX}/bin

@ -5,10 +5,6 @@ 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
@ -18,4 +14,4 @@ inotify: inotify.c
struct_init: struct_init.c struct_init: struct_init.c
clean: clean:
rm -f *.o parse parse_v2 replace replace_v2 ioctl inotify struct_init rm -f *.o parse parse_v2 ioctl inotify struct_init

@ -1,43 +0,0 @@
#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;
}

@ -1,141 +0,0 @@
#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…
Cancel
Save