diff --git a/config.template b/config.template index 072d7aa..6dc859f 100644 --- a/config.template +++ b/config.template @@ -9,7 +9,8 @@ # Leading or trailing whitespaces are ignored, whitespaces between the marker # And the ':' are also ignored, the general syntax for a hotkey is: -# marker keys: command +# : +# # Whitespaces after the ':' count as that counts as the executed command for # the hotkey. # 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 # 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. -# To use an alias in an hotkey you have to replace the ':' before the command -# with '<' to indicate that the following string is an alias and not a command. -# Aliases have to be declared before using them, they can also be concatenated. +# When parsing an hotkey or an alias commad, hkd automatically replaces every +# instance of known aliases in it. for an alias to be known must be declared +# before it's usage. # Examples: # @ term : alacritty # @ volumeup: amixer -q sset Master 3%+ -# - leftmeta, p < term -# - VOLUMEUP < volumeup +# - leftmeta, p : term +# - VOLUMEUP : volumeup diff --git a/hkd.1 b/hkd.1 index 072d8a9..0f71840 100644 --- a/hkd.1 +++ b/hkd.1 @@ -36,7 +36,7 @@ override default configuration file location, instead using the specified as the temporary config file .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 .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. Aliases are a name-command pair that can be used instead of commands in hotkey definitions. + + .SS "Hotkey definition" Leading or trailing whitespaces are ignored, whitespaces between the marker and -the ':' or '<' are also ignored, whitespaces after the ':' are not ignored. -The general syntax for a hotkey is: +the ':' are also ignored, whitespaces after the ':' ignored until the first +non-whitespace character. +The general syntax for an hotkey is: .EX <'*' or '\-'> : .EE -if a hotkeys uses an explicit command, if you want to use an alias it becomes: -.EX -<'*' or '\-'> < -.EE -note the '<' instead of ':'. + + .SS "Alias definition" The general syntax for aliases is: .EX @ : .EE beware that alias names are case sensitive. + .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) so "|&;<>(){}" as well as unescaped newlines are forbidden and will result in error, read the manpage for .BR wordexp(3) for more info about the possible word expansion capabilities. + + .SS "Keys field" 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 @@ -115,7 +122,8 @@ This is a valid config file example \- LEFALT,leftshift,S: ~/screenshot.sh \-c * LEFTMETA,1, D: $SCRIPTDIR/wonkyscript \- LEFTMETA ,LEFTALT,LEFTSHIFT,S : shutdown now -\- leftmeta, enter < term +# term gets substituted with xterm +\- leftmeta, enter : term .EE .SH BUGS diff --git a/hkd.c b/hkd.c index ea300e2..c157838 100644 --- a/hkd.c +++ b/hkd.c @@ -39,6 +39,7 @@ #include #include #include +#include #include "keys.h" /* Value defines */ @@ -80,7 +81,9 @@ struct key_buffer { }; /* Hotkey list: linked list that holds all valid hoteys parsed from the - * config file and the corresponding command */ + * config file and the corresponding command + * TODO: re-implement hotkey_list as a hash table to make searching O(1) + */ union hotkey_main_data { struct key_buffer kb; @@ -95,6 +98,12 @@ struct hotkey_list_e { }; struct hotkey_list_e *hotkey_list = NULL; +/* TODO: add hotkey_range struct as a second check to avoid accessing the list + * struct { + * unsigned int min; + * unsigned int max; + * } hotkey_range; + */ unsigned long hotkey_size_mask = 0; char *ext_config_file = NULL; /* Global flags */ @@ -111,7 +120,7 @@ void int_handler (int signum); void exec_command (char *); void parse_config_file (void); void update_descriptors_list (int **, int *); -void remove_lock (void); +inline void remove_lock (void); void die (const char *, ...); void usage (void); int prepare_epoll (int *, int, int); @@ -121,6 +130,7 @@ const char * code_to_name (unsigned int); void hotkey_list_add (struct hotkey_list_e *, union hotkey_main_data *, char *, int); void hotkey_list_destroy (struct hotkey_list_e *); void hotkey_list_remove (struct hotkey_list_e *, struct hotkey_list_e *); +void replace (char **, const char *, const char *); int main (int argc, char *argv[]) { @@ -560,15 +570,14 @@ void hotkey_list_remove (struct hotkey_list_e *head, struct hotkey_list_e *elem) void parse_config_file (void) { wordexp_t result = {0}; - FILE *fd; + int config_file; /* normal, skip line, get matching, get keys, get command, output */ enum {NORM, LINE_SKIP, GET_TYPE, GET_KEYS, GET_CMD, LAST} parse_state = NORM; - enum {CONT, NEW_BL, LAST_BL, END} block_state = CONT; /* continue, new block, last block, end */ enum {HK_NORM = 0, HK_FUZZY = 1, ALIAS = -1} type; - int cmd_is_alias = 0; - int alloc_tmp = 0, alloc_size = 0; + int eof = 0; + int token_size = 0; int i_tmp = 0, linenum = 1; - char block[BLOCK_SIZE + 1] = {0}; + char *buffer; char *bb = NULL; char *keys = NULL; char *cmd = NULL; @@ -576,6 +585,7 @@ void parse_config_file (void) union hotkey_main_data dt = {0}; unsigned short us_tmp = 0; + /* Choose config file */ if (ext_config_file) { switch (wordexp(ext_config_file, &result, 0)) { case 0: @@ -589,9 +599,9 @@ void parse_config_file (void) die("Path not valid:"); } - fd = fopen(result.we_wordv[0], "r"); + config_file = open(result.we_wordv[0], O_RDONLY | O_NONBLOCK); wordfree(&result); - if (!fd) + if (config_file < 0) die("Error opening config file:"); free(ext_config_file); ext_config_file = NULL; @@ -609,231 +619,188 @@ void parse_config_file (void) die("Path not valid:"); } - fd = fopen(result.we_wordv[0], "r"); + config_file = open(result.we_wordv[0], O_RDONLY | O_NONBLOCK); wordfree(&result); - if (fd) + if (config_file >= 0) break; if (vflag) printf(yellow("config file not found at %s\n"), config_paths[i]); } - if (!fd) + if (!config_file) 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("mmap failed"); + close(config_file); + bb = buffer; + +// write(STDOUT_FILENO, bb, file_size); + hotkey_list_destroy(hotkey_list); hotkey_list = NULL; - while (block_state != END) { - int tmp = 0; - memset(block, 0, BLOCK_SIZE + 1); - tmp = fread(block, sizeof(char), BLOCK_SIZE, fd); - if (!tmp) + while (!eof) { + // FIXME: incorect line counting, specialli for multiline commands + switch (parse_state) { + // First state + case NORM: + // FIXME: never ending cycle of death + // remove whitespaces + while (isblank(*bb)) + bb++; + // get state + switch (*bb) { + case '\0': + eof = 1; + break; + case '\n': + case '#': + parse_state = LINE_SKIP; + break; + default: + parse_state = GET_TYPE; + break; + } break; - if (tmp < BLOCK_SIZE || feof(fd)) - block_state = LAST_BL; - else - block_state = CONT; - bb = block; - - while (block_state == CONT || block_state == LAST_BL) { - switch (parse_state) { - // First state - case NORM: - // remove whitespaces - while (isblank(*bb) && *bb) - bb++; - // get state - switch (*bb) { -#if defined(__X86_64__) || defined(__i386__) - case EOF: -#endif - case '\0': - // If it is the end of the last block exit - block_state = block_state == LAST_BL ? END : NEW_BL; - break; - case '\n': - case '#': - parse_state = LINE_SKIP; - break; - default: - parse_state = GET_TYPE; - break; - } + // Skip line (comment) + case LINE_SKIP: + // FIXME: check end of file + for (;*bb != '\n'; bb++); + bb++; + linenum++; + parse_state = NORM; + break; + // Get compairson method + case GET_TYPE: + switch (*bb) { + case '-': + type = HK_NORM; break; - // Skip line (comment) - case LINE_SKIP: - while (*bb != '\n' && *bb) - bb++; - if (*bb) { - bb++; - linenum++; - parse_state = NORM; - } else { - block_state = NEW_BL; - } + case '*': + type = HK_FUZZY; break; - // Get compairson method - case GET_TYPE: - switch (*bb) { - case '-': - type = HK_NORM; - break; - case '*': - type = HK_FUZZY; - break; - case '@': - type = ALIAS; - break; - default: - die("Error at line %d: " - "hotkey definition must start with '-', '*' or '@'", - linenum); - break; - } - bb++; - parse_state = GET_KEYS; + case '@': + type = ALIAS; break; - // Get keys - case GET_KEYS: - if (!keys) { - if (!(keys = malloc(alloc_size = (sizeof(char) * 64)))) - die("malloc for keys in parse_config_file():"); - memset(keys, 0, alloc_size); - } else if (alloc_tmp >= alloc_size) { - if (!(keys = realloc(keys, alloc_size = alloc_size * 2))) - die("realloc for keys in parse_config_file():"); - memset(&keys[alloc_size / 2], 0, alloc_size / 2); - } - - for (alloc_tmp = 0; bb[alloc_tmp] && - bb[alloc_tmp] != ':' && - bb[alloc_tmp] != '<' && - bb[alloc_tmp] != '\n' && - alloc_tmp < alloc_size; alloc_tmp++); - - if (!bb[alloc_tmp] || alloc_tmp == alloc_size) { - strncat(keys, bb, alloc_tmp); - bb += alloc_tmp; - if (block_state == LAST_BL) - die("Keys not finished before end of file"); - else - block_state = NEW_BL; - 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); - } + default: + die("Error at line %d: " + "hotkey definition must start with '-', '*' or '@'", + 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; + } + bb++; + parse_state = GET_KEYS; + break; + // Get keys + case GET_KEYS: + // FIXME: check end of file, token_size >= remaining_size + 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("Error at line %d: " + "no command specified, missing ':' after keys", + linenum); + keys = malloc(token_size + 1); + if (!keys) + die("malloc failed in keys"); + memcpy(keys, bb, token_size); + keys[token_size] = '\0'; + bb += token_size + 1; + parse_state = GET_CMD; + break; + // Get command + case GET_CMD: + // FIXME: check end of file, token_size >= remaining_size + for (token_size = 0; token_size < (file_size - !(bb - buffer)); token_size++) { + if (bb[token_size] == ':') break; - } else { - strncat(cmd, bb, alloc_tmp); - if (!(bb[alloc_tmp - 1] == '\\')) - parse_state = LAST; - bb += alloc_tmp + 1; - linenum++; + if (bb[token_size] == '\n' && bb[token_size - token_size ? 1 : 0] != '\\') 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 { + } + cmd = malloc(token_size + 1); + if (!cmd) + die("malloc failed in cmd"); + memcpy(cmd, bb, token_size); + cmd[token_size] = '\0'; + bb += token_size; + parse_state = LAST; + 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: " - "alias %s not found", linenum - 1, - cp_tmp); + "%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); + } - hotkey_list_add(hotkey_list, &dt, cp_tmp, type); + /* search the command in the known aliases and replace */ + struct hotkey_list_e *hkl = hotkey_list; + while (hkl && hkl->fuzzy == ALIAS) { + replace(&cmd, hkl->data.name, hkl->command); + hkl = hkl->next; + } - if (type != ALIAS) - key_buffer_reset(&dt.kb); - free(keys); - free(cmd); - cp_tmp = keys = cmd = NULL; - i_tmp = 0; - parse_state = NORM; - break; - default: - die("Unknown state in parse_config_file"); - break; + cp_tmp = cmd; + while (isblank(*cp_tmp)) + cp_tmp++; + if (*cp_tmp == '\0') + die("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("Unknown state in parse_config_file"); + break; - } } } + munmap(buffer, file_size); + for (struct hotkey_list_e *hkl = hotkey_list, *tmp; hkl; hkl = hkl->next) { if (hkl->fuzzy == ALIAS) { tmp = hkl; @@ -901,3 +868,42 @@ void usage (void) "\t-c file uses the specified file as config\n"); exit(EXIT_SUCCESS); } + +/* replaces every instance of m(match) with r(eplace) inside of s */ +void replace (char **s, const char *m, const char *r) +{ + 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)))) + die("bad realloc"); + 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))) + die("bad realloc"); + *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); +} diff --git a/makefile b/makefile index bc38549..5d2184a 100644 --- a/makefile +++ b/makefile @@ -1,6 +1,6 @@ CC ?= gcc CFLAGS = -Wall -Werror -pedantic --std=c99 -O2 -VERSION = 0.4 +VERSION = 0.5 PREFIX = /usr/local MANPREFIX = ${PREFIX}/share/man diff --git a/tests/makefile b/tests/makefile index 7f1661b..0e3925b 100644 --- a/tests/makefile +++ b/tests/makefile @@ -5,6 +5,10 @@ parse: parse.c parse_v2: parse_v2.c +replace: replace.c + +replace_v2: replace_v2.c + ioctl: ioctl.c evtest: evtest.c @@ -14,4 +18,4 @@ inotify: inotify.c struct_init: struct_init.c clean: - rm -f *.o parse parse_v2 ioctl inotify struct_init + rm -f *.o parse parse_v2 replace replace_v2 ioctl inotify struct_init diff --git a/tests/replace.c b/tests/replace.c new file mode 100644 index 0000000..bc89b28 --- /dev/null +++ b/tests/replace.c @@ -0,0 +1,43 @@ +#define _POSIX_C_SOURCE 200809L + +#include +#include +#include + +/* 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; +} diff --git a/tests/replace_v2.c b/tests/replace_v2.c new file mode 100644 index 0000000..5921113 --- /dev/null +++ b/tests/replace_v2.c @@ -0,0 +1,141 @@ +#define _POSIX_C_SOURCE 200809L + +#include +#include +#include +#include +#include + +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; +}