URI: 
       tFree memory properly on exit - ledit - Text editor (WIP)
  HTML git clone git://lumidify.org/ledit.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/git/ledit.git (encrypted, but very slow)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit bf406bf2c7ad5025890a061780823576b65a67d7
   DIR parent 2e64aac7eba6791ed47514d37838b0d5d9d00cf1
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Sun, 24 Oct 2021 15:21:13 +0200
       
       Free memory properly on exit
       
       There are probably still a lot of things missing here.
       
       Diffstat:
         M buffer.c                            |      12 ++++++++++--
         M cache.c                             |       2 +-
         M keys_basic.c                        |      18 ++++++++++++++++++
         M keys_basic.h                        |       1 +
         M ledit.c                             |      13 ++++++-------
         M search.c                            |       9 ++++-----
         M search.h                            |       2 +-
         M txtbuf.c                            |       2 ++
         M util.c                              |       9 +++++++++
         M util.h                              |       1 +
         M window.c                            |      20 ++++++++++++++++++++
         M window.h                            |       1 +
       
       12 files changed, 74 insertions(+), 16 deletions(-)
       ---
   DIR diff --git a/buffer.c b/buffer.c
       t@@ -52,7 +52,6 @@ ledit_buffer_set_mode(ledit_buffer *buffer, enum ledit_mode mode) {
                ledit_change_mode_group(buffer->undo);
        }
        
       -/* FIXME: destroy basic_attrs somewhere */
        ledit_buffer *
        ledit_buffer_create(ledit_common *common, ledit_theme *theme, ledit_window *window) {
                if (basic_attrs == NULL) {
       t@@ -181,11 +180,18 @@ ledit_buffer_destroy(ledit_buffer *buffer) {
                ledit_cache_destroy(buffer->cache);
                ledit_undo_stack_destroy(buffer->undo);
                free(buffer->lines);
       -        if (buffer->filename) free(buffer->filename);
       +        if (buffer->filename)
       +                free(buffer->filename);
                free(buffer);
        }
        
        void
       +ledit_buffer_cleanup(void) {
       +        if (basic_attrs)
       +                pango_attr_list_unref(basic_attrs);
       +}
       +
       +void
        ledit_buffer_normalize_line(ledit_line *line) {
                if (line->gap < line->len) {
                        memmove(
       t@@ -231,6 +237,7 @@ ledit_buffer_set_line_selection(ledit_buffer *buffer, int line, int start_byte, 
                pango_attr_list_insert(list, attr2);
                #endif
                pango_layout_set_attributes(l->layout, list);
       +        pango_attr_list_unref(list);
                l->dirty = 1;
        }
        
       t@@ -256,6 +263,7 @@ ledit_buffer_set_line_cursor_attrs(ledit_buffer *buffer, int line, int index) {
                        pango_attr_list_insert(list, attr2);
                        #endif
                        pango_layout_set_attributes(l->layout, list);
       +                pango_attr_list_unref(list);
                } else {
                        pango_layout_set_attributes(l->layout, basic_attrs);
                }
   DIR diff --git a/cache.c b/cache.c
       t@@ -10,9 +10,9 @@
        
        ledit_cache *
        ledit_cache_create(ledit_common *common) {
       -        (void)common; /* FIXME: remove argument */
                /* FIXME: prevent overflow */
                ledit_cache *cache = ledit_malloc(sizeof(ledit_cache));
       +        cache->dpy = common->dpy;
                cache->entries = ledit_malloc(20 * sizeof(ledit_cache_pixmap));
                for (int i = 0; i < 20; i++) {
                        cache->entries[i].pixmap = None;
   DIR diff --git a/keys_basic.c b/keys_basic.c
       t@@ -62,6 +62,21 @@ static struct {
                struct key_stack_elem *stack;
        } key_stack = {0, 0, NULL};
        
       +void
       +basic_key_cleanup(void) {
       +        /* this should be safe since push_repetition_stack sets all new
       +           elements to NULL when resizing the stack */
       +        for (size_t i = 0; i < repetition_stack.alloc; i++) {
       +                free(repetition_stack.stack[i].key_text);
       +        }
       +        for (size_t i = 0; i < repetition_stack.tmp_alloc; i++) {
       +                free(repetition_stack.tmp_stack[i].key_text);
       +        }
       +        free(repetition_stack.stack);
       +        free(repetition_stack.tmp_stack);
       +        free(key_stack.stack);
       +}
       +
        /* No, this isn't actually a stack. So what? */
        static struct repetition_stack_elem *push_repetition_stack(void);
        static void finalize_repetition_stack(void);
       t@@ -146,6 +161,9 @@ push_repetition_stack(void) {
                            repetition_stack.tmp_stack,
                            new_alloc * sizeof(struct repetition_stack_elem)
                        );
       +                for (size_t i = repetition_stack.tmp_alloc; i < new_alloc; i++) {
       +                        repetition_stack.tmp_stack[i].key_text = NULL;
       +                }
                        repetition_stack.tmp_alloc = new_alloc;
                }
                e = &repetition_stack.tmp_stack[repetition_stack.tmp_len];
   DIR diff --git a/keys_basic.h b/keys_basic.h
       t@@ -1 +1,2 @@
       +void basic_key_cleanup(void);
        struct action basic_key_handler(ledit_buffer *buffer, XEvent *event, int lang_index);
   DIR diff --git a/ledit.c b/ledit.c
       t@@ -38,6 +38,7 @@
        #include "undo.h"
        #include "buffer.h"
        #include "action.h"
       +#include "search.h"
        #include "keys.h"
        #include "keys_basic.h"
        
       t@@ -230,14 +231,12 @@ setup(int argc, char *argv[]) {
        
        static void
        cleanup(void) {
       -        /* FIXME: cleanup everything else */
       -        /*
       -        ledit_cleanup_search();
       -        ledit_destroy_cache();
       -        ledit_destroy_undo_stack(buffer);
       -        ledit_destroy_buffer(buffer);
       -        */
       +        /* FIXME: check for other things to clean up */
       +        ledit_search_cleanup();
       +        basic_key_cleanup();
       +        ledit_buffer_destroy(buffer);
                ledit_window_destroy(window);
       +        ledit_theme_destroy(&common, theme);
                XCloseDisplay(common.dpy);
        }
        
   DIR diff --git a/search.c b/search.c
       t@@ -19,7 +19,6 @@
        #include "search.h"
        
        /* FIXME: make sure only whole utf8 chars are matched */
       -/* FIXME: clean this up */
        char *last_search = NULL;
        enum {
                FORWARD,
       t@@ -27,15 +26,15 @@ enum {
        } last_dir = FORWARD;
        
        void
       -ledit_set_search_forward(char *pattern) {
       -        last_dir = FORWARD;
       +ledit_search_cleanup(void) {
                free(last_search);
       -        last_search = ledit_strdup(pattern);
        }
        
        void
       -ledit_cleanup_search(void) {
       +ledit_set_search_forward(char *pattern) {
       +        last_dir = FORWARD;
                free(last_search);
       +        last_search = ledit_strdup(pattern);
        }
        
        void
   DIR diff --git a/search.h b/search.h
       t@@ -5,7 +5,7 @@ enum ledit_search_state {
                SEARCH_NO_PATTERN
        };
        
       -void ledit_cleanup_search(void);
       +void ledit_search_cleanup(void);
        void ledit_set_search_forward(char *pattern);
        void ledit_set_search_backward(char *pattern);
        enum ledit_search_state ledit_search_next(ledit_buffer *buffer, int *line_ret, int *byte_ret);
   DIR diff --git a/txtbuf.c b/txtbuf.c
       t@@ -32,6 +32,8 @@ txtbuf_shrink(txtbuf *buf) {
        
        void
        txtbuf_destroy(txtbuf *buf) {
       +        if (!buf)
       +                return;
                free(buf->text);
                free(buf);
        }
   DIR diff --git a/util.c b/util.c
       t@@ -1,3 +1,5 @@
       +#include <stdlib.h>
       +
        #include <X11/Xlib.h>
        #include <X11/Xutil.h>
        #include <pango/pangoxft.h>
       t@@ -39,3 +41,10 @@ ledit_draw_grow(ledit_window *window, ledit_draw *draw, int w, int h) {
                        XftDrawChange(draw->xftdraw, draw->pixmap);
                }
        }
       +
       +void
       +ledit_draw_destroy(ledit_window *window, ledit_draw *draw) {
       +        XFreePixmap(window->common->dpy, draw->pixmap);
       +        XftDrawDestroy(draw->xftdraw);
       +        free(draw);
       +}
   DIR diff --git a/util.h b/util.h
       t@@ -6,3 +6,4 @@ typedef struct {
        
        ledit_draw *ledit_draw_create(ledit_window *window, int w, int h);
        void ledit_draw_grow(ledit_window *window, ledit_draw *draw, int w, int h);
       +void ledit_draw_destroy(ledit_window *window, ledit_draw *draw);
   DIR diff --git a/window.c b/window.c
       t@@ -415,7 +415,27 @@ ledit_window_create(ledit_common *common, ledit_theme *theme) {
        void
        ledit_window_destroy(ledit_window *window) {
                /* FIXME: cleanup everything else */
       +        pango_font_description_free(window->font);
       +        g_object_unref(window->fontmap);
       +        g_object_unref(window->context);
       +        /* FIXME: is gc, etc. destroyed automatically when destroying window? */
       +        if (window->spotlist)
       +                XFree(window->spotlist);
                XDestroyWindow(window->common->dpy, window->xwin);
       +        g_object_unref(window->bb->mode);
       +        /*g_object_unref(window->bb->ruler);*/ /* FIXME: implement ruler */
       +        g_object_unref(window->bb->line);
       +        ledit_draw_destroy(window, window->bb->mode_draw);
       +        ledit_draw_destroy(window, window->bb->line_draw);
       +        free(window->bb->line_text);
       +        free(window->bb);
       +        free(window);
       +}
       +
       +void
       +ledit_window_cleanup(void) {
       +        txtbuf_destroy(xsel.primary);
       +        free(xsel.clipboard);
        }
        
        void
   DIR diff --git a/window.h b/window.h
       t@@ -42,6 +42,7 @@ typedef struct {
        
        ledit_window *ledit_window_create(ledit_common *common, ledit_theme *theme);
        void ledit_window_destroy(ledit_window *window);
       +void ledit_window_cleanup(void);
        
        /* FIXME: this is a bit confusing because there's a difference between editable
           text shown and non-editable message shown */