diff --git a/config.c b/config.c index 59476f43..142a0995 100644 --- a/config.c +++ b/config.c @@ -23,6 +23,7 @@ #include "tokenize.h" #include "util.h" #include "wayland.h" +#include "xmalloc.h" static const uint32_t default_foreground = 0xdcdccc; static const uint32_t default_background = 0x111111; @@ -98,9 +99,7 @@ static_assert(ALEN(search_binding_action_map) == BIND_ACTION_SEARCH_COUNT, #define LOG_AND_NOTIFY_ERR(fmt, ...) \ LOG_ERR(fmt, ## __VA_ARGS__); \ { \ - int len = snprintf(NULL, 0, fmt, ## __VA_ARGS__); \ - char *text = malloc(len + 1); \ - snprintf(text, len + 1, fmt, ## __VA_ARGS__); \ + char *text = xasprintf(fmt, ## __VA_ARGS__); \ struct user_notification notif = { \ .kind = USER_NOTIFICATION_ERROR, \ .text = text, \ @@ -111,9 +110,7 @@ static_assert(ALEN(search_binding_action_map) == BIND_ACTION_SEARCH_COUNT, #define LOG_AND_NOTIFY_WARN(fmt, ...) \ LOG_WARN(fmt, ## __VA_ARGS__); \ { \ - int len = snprintf(NULL, 0, fmt, ## __VA_ARGS__); \ - char *text = malloc(len + 1); \ - snprintf(text, len + 1, fmt, ## __VA_ARGS__); \ + char *text = xasprintf(fmt, ## __VA_ARGS__); \ struct user_notification notif = { \ .kind = USER_NOTIFICATION_WARNING, \ .text = text, \ @@ -128,7 +125,7 @@ static_assert(ALEN(search_binding_action_map) == BIND_ACTION_SEARCH_COUNT, { \ int len = snprintf(NULL, 0, fmt, ## __VA_ARGS__); \ int errno_len = snprintf(NULL, 0, ": %s", strerror(_errno)); \ - char *text = malloc(len + errno_len + 1); \ + char *text = xmalloc(len + errno_len + 1); \ snprintf(text, len + errno_len + 1, fmt, ## __VA_ARGS__); \ snprintf(&text[len], errno_len + 1, ": %s", strerror(_errno)); \ struct user_notification notif = { \ @@ -154,7 +151,7 @@ get_shell(void) } LOG_DBG("user's shell: %s", shell); - return strdup(shell); + return xstrdup(shell); } static char * @@ -169,9 +166,7 @@ get_config_path_user_config(void) const char *home_dir = passwd->pw_dir; LOG_DBG("user's home directory: %s", home_dir); - int len = snprintf(NULL, 0, "%s/.config/footrc", home_dir); - char *path = malloc(len + 1); - snprintf(path, len + 1, "%s/.config/footrc", home_dir); + char *path = xasprintf("%s/.config/footrc", home_dir); return path; } @@ -182,9 +177,7 @@ get_config_path_xdg(void) if (xdg_config_home == NULL) return NULL; - int len = snprintf(NULL, 0, "%s/footrc", xdg_config_home); - char *path = malloc(len + 1); - snprintf(path, len + 1, "%s/footrc", xdg_config_home); + char *path = xasprintf("%s/footrc", xdg_config_home); return path; } @@ -268,12 +261,12 @@ parse_section_main(const char *key, const char *value, struct config *conf, { if (strcmp(key, "term") == 0) { free(conf->term); - conf->term = strdup(value); + conf->term = xstrdup(value); } else if (strcmp(key, "shell") == 0) { free(conf->shell); - conf->shell = strdup(value); + conf->shell = xstrdup(value); } else if (strcmp(key, "login-shell") == 0) { @@ -282,12 +275,12 @@ parse_section_main(const char *key, const char *value, struct config *conf, else if (strcmp(key, "title") == 0) { free(conf->title); - conf->title = strdup(value); + conf->title = xstrdup(value); } else if (strcmp(key, "app-id") == 0) { free(conf->app_id); - conf->app_id = strdup(value); + conf->app_id = xstrdup(value); } else if (strcmp(key, "geometry") == 0) { @@ -335,7 +328,7 @@ parse_section_main(const char *key, const char *value, struct config *conf, } else if (strcmp(key, "font") == 0) { - char *copy = strdup(value); + char *copy = xstrdup(value); for (const char *font = strtok(copy, ","); font != NULL; font = strtok(NULL, ",")) { /* Trim spaces, strictly speaking not necessary, but looks nice :) */ while (*font != '\0' && isspace(*font)) @@ -361,9 +354,7 @@ parse_section_main(const char *key, const char *value, struct config *conf, LOG_WARN("deprecated: [default]: scrollback: use 'scrollback.lines' instead'"); const char *fmt = "%s:%d: \033[1mdefault.scrollback\033[21m, use \033[1mscrollback.lines\033[21m instead"; - int len = snprintf(NULL, 0, fmt, path, lineno); - char *text = malloc(len + 1); - snprintf(text, len + 1, fmt, path, lineno); + char *text = xasprintf(fmt, path, lineno); struct user_notification deprecation = { .kind = USER_NOTIFICATION_DEPRECATED, @@ -434,7 +425,7 @@ parse_section_scrollback(const char *key, const char *value, struct config *conf return false; } - conf->scrollback.indicator.text = calloc(len + 1, sizeof(wchar_t)); + conf->scrollback.indicator.text = xcalloc(len + 1, sizeof(wchar_t)); mbstowcs(conf->scrollback.indicator.text, value, len); } } @@ -529,7 +520,7 @@ parse_section_cursor(const char *key, const char *value, struct config *conf, conf->cursor.blink = str_to_bool(value); else if (strcmp(key, "color") == 0) { - char *value_copy = strdup(value); + char *value_copy = xstrdup(value); const char *text = strtok(value_copy, " "); const char *cursor = strtok(NULL, " "); @@ -663,7 +654,7 @@ verify_key_combo(struct config *conf, const char *combo, const char *path, unsig { /* Check regular key bindings */ tll_foreach(conf->bindings.key, it) { - char *copy = strdup(it->item.key); + char *copy = xstrdup(it->item.key); for (char *save = NULL, *collision = strtok_r(copy, " ", &save); collision != NULL; @@ -686,7 +677,7 @@ verify_key_combo(struct config *conf, const char *combo, const char *path, unsig /* Check scrollback search bindings */ tll_foreach(conf->bindings.search, it) { - char *copy = strdup(it->item.key); + char *copy = xstrdup(it->item.key); for (char *save = NULL, *collision = strtok_r(copy, " ", &save); collision != NULL; @@ -815,7 +806,7 @@ parse_section_key_bindings( free(it->item.pipe.cmd); free(it->item.pipe.argv); - it->item.key = strdup(value); + it->item.key = xstrdup(value); it->item.pipe.cmd = pipe_cmd; it->item.pipe.argv = pipe_argv; already_added = true; @@ -826,7 +817,7 @@ parse_section_key_bindings( if (!already_added) { struct config_key_binding_normal binding = { .action = action, - .key = strdup(value), + .key = xstrdup(value), .pipe = { .cmd = pipe_cmd, .argv = pipe_argv, @@ -877,7 +868,7 @@ parse_section_search_bindings( free(it->item.key); - it->item.key = strdup(value); + it->item.key = xstrdup(value); already_added = true; break; } @@ -886,7 +877,7 @@ parse_section_search_bindings( if (!already_added) { struct config_key_binding_search binding = { .action = action, - .key = strdup(value), + .key = xstrdup(value), }; tll_push_back(conf->bindings.search, binding); } @@ -1206,18 +1197,14 @@ get_server_socket_path(void) { const char *xdg_runtime = getenv("XDG_RUNTIME_DIR"); if (xdg_runtime == NULL) - return strdup("/tmp/foot.sock"); + return xstrdup("/tmp/foot.sock"); const char *wayland_display = getenv("WAYLAND_DISPLAY"); if (wayland_display == NULL) { - char *path = malloc(strlen(xdg_runtime) + 1 + strlen("foot.sock") + 1); - sprintf(path, "%s/foot.sock", xdg_runtime); - return path; + return xasprintf("%s/foot.sock", xdg_runtime); } - char *path = malloc(strlen(xdg_runtime) + 1 + strlen("foot-.sock") + strlen(wayland_display) + 1); - sprintf(path, "%s/foot-%s.sock", xdg_runtime, wayland_display); - return path; + return xasprintf("%s/foot-%s.sock", xdg_runtime, wayland_display); } bool @@ -1226,10 +1213,10 @@ config_load(struct config *conf, const char *conf_path, bool errors_are_fatal) bool ret = false; *conf = (struct config) { - .term = strdup("foot"), + .term = xstrdup("foot"), .shell = get_shell(), - .title = strdup("foot"), - .app_id = strdup("foot"), + .title = xstrdup("foot"), + .app_id = xstrdup("foot"), .width = 700, .height = 500, .pad_x = 2, @@ -1303,15 +1290,15 @@ config_load(struct config *conf, const char *conf_path, bool errors_are_fatal) .notifications = tll_init(), }; - struct config_key_binding_normal scrollback_up = {BIND_ACTION_SCROLLBACK_UP, strdup("Shift+Page_Up")}; - struct config_key_binding_normal scrollback_down = {BIND_ACTION_SCROLLBACK_DOWN, strdup("Shift+Page_Down")}; - struct config_key_binding_normal clipboard_copy = {BIND_ACTION_CLIPBOARD_COPY, strdup("Control+Shift+C")}; - struct config_key_binding_normal clipboard_paste = {BIND_ACTION_CLIPBOARD_PASTE, strdup("Control+Shift+V")}; - struct config_key_binding_normal search_start = {BIND_ACTION_SEARCH_START, strdup("Control+Shift+R")}; - struct config_key_binding_normal font_size_up = {BIND_ACTION_FONT_SIZE_UP, strdup("Control+plus Control+equal Control+KP_Add")}; - struct config_key_binding_normal font_size_down = {BIND_ACTION_FONT_SIZE_DOWN, strdup("Control+minus Control+KP_Subtract")}; - struct config_key_binding_normal font_size_reset = {BIND_ACTION_FONT_SIZE_RESET, strdup("Control+0 Control+KP_0")}; - struct config_key_binding_normal spawn_terminal = {BIND_ACTION_SPAWN_TERMINAL, strdup("Control+Shift+N")}; + struct config_key_binding_normal scrollback_up = {BIND_ACTION_SCROLLBACK_UP, xstrdup("Shift+Page_Up")}; + struct config_key_binding_normal scrollback_down = {BIND_ACTION_SCROLLBACK_DOWN, xstrdup("Shift+Page_Down")}; + struct config_key_binding_normal clipboard_copy = {BIND_ACTION_CLIPBOARD_COPY, xstrdup("Control+Shift+C")}; + struct config_key_binding_normal clipboard_paste = {BIND_ACTION_CLIPBOARD_PASTE, xstrdup("Control+Shift+V")}; + struct config_key_binding_normal search_start = {BIND_ACTION_SEARCH_START, xstrdup("Control+Shift+R")}; + struct config_key_binding_normal font_size_up = {BIND_ACTION_FONT_SIZE_UP, xstrdup("Control+plus Control+equal Control+KP_Add")}; + struct config_key_binding_normal font_size_down = {BIND_ACTION_FONT_SIZE_DOWN, xstrdup("Control+minus Control+KP_Subtract")}; + struct config_key_binding_normal font_size_reset = {BIND_ACTION_FONT_SIZE_RESET, xstrdup("Control+0 Control+KP_0")}; + struct config_key_binding_normal spawn_terminal = {BIND_ACTION_SPAWN_TERMINAL, xstrdup("Control+Shift+N")}; tll_push_back(conf->bindings.key, scrollback_up); tll_push_back(conf->bindings.key, scrollback_down); @@ -1326,22 +1313,22 @@ config_load(struct config *conf, const char *conf_path, bool errors_are_fatal) struct mouse_binding primary_paste = {BIND_ACTION_PRIMARY_PASTE, BTN_MIDDLE, 1}; tll_push_back(conf->bindings.mouse, primary_paste); - struct config_key_binding_search search_cancel = {BIND_ACTION_SEARCH_CANCEL, strdup("Control+g Escape")}; - struct config_key_binding_search search_commit = {BIND_ACTION_SEARCH_COMMIT, strdup("Return")}; - struct config_key_binding_search search_find_prev = {BIND_ACTION_SEARCH_FIND_PREV, strdup("Control+r")}; - struct config_key_binding_search search_find_next = {BIND_ACTION_SEARCH_FIND_NEXT, strdup("Control+s")}; - struct config_key_binding_search search_edit_left = {BIND_ACTION_SEARCH_EDIT_LEFT, strdup("Left Control+b")}; - struct config_key_binding_search search_edit_left_word = {BIND_ACTION_SEARCH_EDIT_LEFT_WORD, strdup("Control+Left Mod1+b")}; - struct config_key_binding_search search_edit_right = {BIND_ACTION_SEARCH_EDIT_RIGHT, strdup("Right Control+f")}; - struct config_key_binding_search search_edit_right_word = {BIND_ACTION_SEARCH_EDIT_RIGHT_WORD, strdup("Control+Right Mod1+f")}; - struct config_key_binding_search search_edit_home = {BIND_ACTION_SEARCH_EDIT_HOME, strdup("Home Control+a")}; - struct config_key_binding_search search_edit_end = {BIND_ACTION_SEARCH_EDIT_END, strdup("End Control+e")}; - struct config_key_binding_search search_del_prev = {BIND_ACTION_SEARCH_DELETE_PREV, strdup("BackSpace")}; - struct config_key_binding_search search_del_prev_word = {BIND_ACTION_SEARCH_DELETE_PREV_WORD, strdup("Mod1+BackSpace Control+BackSpace")}; - struct config_key_binding_search search_del_next = {BIND_ACTION_SEARCH_DELETE_NEXT, strdup("Delete")}; - struct config_key_binding_search search_del_next_word = {BIND_ACTION_SEARCH_DELETE_NEXT_WORD, strdup("Mod1+d Control+Delete")}; - struct config_key_binding_search search_ext_word = {BIND_ACTION_SEARCH_EXTEND_WORD, strdup("Control+w")}; - struct config_key_binding_search search_ext_word_ws = {BIND_ACTION_SEARCH_EXTEND_WORD_WS, strdup("Control+Shift+W")}; + struct config_key_binding_search search_cancel = {BIND_ACTION_SEARCH_CANCEL, xstrdup("Control+g Escape")}; + struct config_key_binding_search search_commit = {BIND_ACTION_SEARCH_COMMIT, xstrdup("Return")}; + struct config_key_binding_search search_find_prev = {BIND_ACTION_SEARCH_FIND_PREV, xstrdup("Control+r")}; + struct config_key_binding_search search_find_next = {BIND_ACTION_SEARCH_FIND_NEXT, xstrdup("Control+s")}; + struct config_key_binding_search search_edit_left = {BIND_ACTION_SEARCH_EDIT_LEFT, xstrdup("Left Control+b")}; + struct config_key_binding_search search_edit_left_word = {BIND_ACTION_SEARCH_EDIT_LEFT_WORD, xstrdup("Control+Left Mod1+b")}; + struct config_key_binding_search search_edit_right = {BIND_ACTION_SEARCH_EDIT_RIGHT, xstrdup("Right Control+f")}; + struct config_key_binding_search search_edit_right_word = {BIND_ACTION_SEARCH_EDIT_RIGHT_WORD, xstrdup("Control+Right Mod1+f")}; + struct config_key_binding_search search_edit_home = {BIND_ACTION_SEARCH_EDIT_HOME, xstrdup("Home Control+a")}; + struct config_key_binding_search search_edit_end = {BIND_ACTION_SEARCH_EDIT_END, xstrdup("End Control+e")}; + struct config_key_binding_search search_del_prev = {BIND_ACTION_SEARCH_DELETE_PREV, xstrdup("BackSpace")}; + struct config_key_binding_search search_del_prev_word = {BIND_ACTION_SEARCH_DELETE_PREV_WORD, xstrdup("Mod1+BackSpace Control+BackSpace")}; + struct config_key_binding_search search_del_next = {BIND_ACTION_SEARCH_DELETE_NEXT, xstrdup("Delete")}; + struct config_key_binding_search search_del_next_word = {BIND_ACTION_SEARCH_DELETE_NEXT_WORD, xstrdup("Mod1+d Control+Delete")}; + struct config_key_binding_search search_ext_word = {BIND_ACTION_SEARCH_EXTEND_WORD, xstrdup("Control+w")}; + struct config_key_binding_search search_ext_word_ws = {BIND_ACTION_SEARCH_EXTEND_WORD_WS, xstrdup("Control+Shift+W")}; tll_push_back(conf->bindings.search, search_cancel); tll_push_back(conf->bindings.search, search_commit); diff --git a/macros.h b/macros.h new file mode 100644 index 00000000..c8990858 --- /dev/null +++ b/macros.h @@ -0,0 +1,80 @@ +#pragma once + +#define VERCMP(x, y, cx, cy) ((cx > x) || ((cx == x) && (cy >= y))) + +#if defined(__GNUC__) && defined(__GNUC_MINOR__) + #define GNUC_AT_LEAST(x, y) VERCMP(x, y, __GNUC__, __GNUC_MINOR__) +#else + #define GNUC_AT_LEAST(x, y) 0 +#endif + +#ifdef __has_attribute + #define HAS_ATTRIBUTE(x) __has_attribute(x) +#else + #define HAS_ATTRIBUTE(x) 0 +#endif + +#ifdef __has_builtin + #define HAS_BUILTIN(x) __has_builtin(x) +#else + #define HAS_BUILTIN(x) 0 +#endif + +#if GNUC_AT_LEAST(3, 0) || HAS_ATTRIBUTE(malloc) + #define MALLOC __attribute__((__malloc__)) +#else + #define MALLOC +#endif + +#if GNUC_AT_LEAST(3, 0) || HAS_ATTRIBUTE(format) + #define PRINTF(x) __attribute__((__format__(__printf__, (x), (x + 1)))) + #define VPRINTF(x) __attribute__((__format__(__printf__, (x), 0))) +#else + #define PRINTF(x) + #define VPRINTF(x) +#endif + +#if (GNUC_AT_LEAST(3, 0) || HAS_BUILTIN(__builtin_expect)) && defined(__OPTIMIZE__) + #define likely(x) __builtin_expect(!!(x), 1) + #define unlikely(x) __builtin_expect(!!(x), 0) +#else + #define likely(x) (x) + #define unlikely(x) (x) +#endif + +#if GNUC_AT_LEAST(3, 3) || HAS_ATTRIBUTE(nonnull) + #define NONNULL_ARGS __attribute__((__nonnull__)) + #define NONNULL_ARG(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else + #define NONNULL_ARGS + #define NONNULL_ARG(...) +#endif + +#if GNUC_AT_LEAST(3, 4) || HAS_ATTRIBUTE(warn_unused_result) + #define WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) +#else + #define WARN_UNUSED_RESULT +#endif + +#if GNUC_AT_LEAST(4, 3) || HAS_ATTRIBUTE(cold) + #define COLD __attribute__((__cold__)) +#else + #define COLD +#endif + +#if GNUC_AT_LEAST(5, 0) || HAS_ATTRIBUTE(returns_nonnull) + #define RETURNS_NONNULL __attribute__((__returns_nonnull__)) +#else + #define RETURNS_NONNULL +#endif + +#define XMALLOC MALLOC RETURNS_NONNULL WARN_UNUSED_RESULT +#define XSTRDUP XMALLOC NONNULL_ARGS + +#if __STDC_VERSION__ >= 201112L + #define NORETURN _Noreturn +#elif GNUC_AT_LEAST(3, 0) + #define NORETURN __attribute__((__noreturn__)) +#else + #define NORETURN +#endif diff --git a/meson.build b/meson.build index 8c89b6a2..19d91156 100644 --- a/meson.build +++ b/meson.build @@ -110,6 +110,7 @@ executable( 'grid.c', 'grid.h', 'input.c', 'input.h', 'log.c', 'log.h', + 'macros.h', 'main.c', 'misc.c', 'misc.h', 'osc.c', 'osc.h', @@ -129,6 +130,7 @@ executable( 'user-notification.h', 'vt.c', 'vt.h', 'wayland.c', 'wayland.h', + 'xmalloc.c', 'xmalloc.h', wl_proto_src + wl_proto_headers, version, dependencies: [math, threads, pixman, wayland_client, wayland_cursor, xkb, fontconfig, tllist, fcft], diff --git a/terminal.h b/terminal.h index 9d9ed1be..1b41848d 100644 --- a/terminal.h +++ b/terminal.h @@ -13,12 +13,10 @@ //#include "config.h" #include "fdm.h" +#include "macros.h" #include "reaper.h" #include "wayland.h" -#define likely(c) __builtin_expect(!!(c), 1) -#define unlikely(c) __builtin_expect(!!(c), 0) - /* * Note: we want the cells to be as small as possible. Larger cells * means fewer scrollback lines (or performance drops due to cache diff --git a/xmalloc.c b/xmalloc.c new file mode 100644 index 00000000..aecb5787 --- /dev/null +++ b/xmalloc.c @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include "xmalloc.h" + +static NORETURN COLD void +fatal_error(const char *msg, int err) +{ + errno = err; + perror(msg); + abort(); +} + +static void * +check_alloc(void *alloc) +{ + if (unlikely(alloc == NULL)) { + fatal_error(__func__, ENOMEM); + } + return alloc; +} + +void * +xmalloc(size_t size) +{ + if (unlikely(size == 0)) { + size = 1; + } + return check_alloc(malloc(size)); +} + +void * +xcalloc(size_t nmemb, size_t size) +{ + if (unlikely(nmemb == 0 || size == 0)) { + size = 1; + } + return check_alloc(calloc(nmemb, size)); +} + +char * +xstrdup(const char *str) +{ + return check_alloc(strdup(str)); +} + +static VPRINTF(2) int +xvasprintf_(char **strp, const char *format, va_list ap) +{ + va_list ap2; + va_copy(ap2, ap); + int n = vsnprintf(NULL, 0, format, ap2); + if (unlikely(n < 0)) { + fatal_error("vsnprintf", EILSEQ); + } + va_end(ap2); + *strp = xmalloc(n + 1); + return vsnprintf(*strp, n + 1, format, ap); +} + +static VPRINTF(1) char * +xvasprintf(const char *format, va_list ap) +{ + char *str; + xvasprintf_(&str, format, ap); + return str; +} + +char * +xasprintf(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + char *str = xvasprintf(format, ap); + va_end(ap); + return str; +} diff --git a/xmalloc.h b/xmalloc.h new file mode 100644 index 00000000..d4e48f79 --- /dev/null +++ b/xmalloc.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include +#include "macros.h" + +void *xmalloc(size_t size) XMALLOC; +void *xcalloc(size_t nmemb, size_t size) XMALLOC; +char *xstrdup(const char *str) XSTRDUP; +char *xasprintf(const char *format, ...) PRINTF(1) XMALLOC;