ledit

Text editor (WIP)
git clone git://lumidify.org/ledit.git (fast, but not encrypted)
git clone https://lumidify.org/git/ledit.git (encrypted, but very slow)
Log | Files | Refs | README | LICENSE

commit 86dd70c41a66c874b49092caa1986ecedd36f254
parent 36a12616e748e39265af475dbaee5e8356cc984a
Author: lumidify <nobody@lumidify.org>
Date:   Wed, 22 Sep 2021 20:17:12 +0200

Add basic file loading and writing functionality

Diffstat:
Mbuffer.c | 103++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mbuffer.h | 8++++++--
Mcommands.c | 9+++++++--
Mledit.c | 13++++++++++++-
4 files changed, 122 insertions(+), 11 deletions(-)

diff --git a/buffer.c b/buffer.c @@ -1,8 +1,10 @@ /* FIXME: shrink buffers when text length less than a fourth of the size */ /* FIXME: also cache PangoLayouts since keeping them around isn't really of much use? */ +#include <stdio.h> #include <string.h> #include <assert.h> +#include <limits.h> #include <X11/Xlib.h> #include <X11/Xutil.h> @@ -44,6 +46,7 @@ ledit_create_buffer(ledit_common_state *state) { ledit_buffer *buffer = ledit_malloc(sizeof(ledit_buffer)); buffer->state = state; buffer->lines = NULL; + buffer->filename = NULL; buffer->lines_num = 0; buffer->lines_cap = 0; buffer->cur_line = 0; @@ -62,6 +65,84 @@ ledit_create_buffer(ledit_common_state *state) { return buffer; } +/* FIXME: don't generate extra blank line at end! */ +/* WARNING: errstr must be copied as soon as possible! */ +int +ledit_load_file_into_buffer(ledit_buffer *buffer, char *filename, int line, char **errstr) { + long len; + int off = 0; + ledit_line *ll; + char *file_contents; + FILE *file; + + file = fopen(filename, "r"); + if (!file) goto error; + if (fseek(file, 0, SEEK_END)) goto errorclose; + len = ftell(file); + if (len < 0) goto errorclose; + if (fseek(file, 0, SEEK_SET)) goto errorclose; + + ll = ledit_get_line(buffer, line); + file_contents = ledit_malloc(len + 2); + /* mimic nvi (or at least the openbsd version) - if the line + is empty, insert directly, otherwise insert after the line */ + if (ll->len > 0) { + off = 1; + file_contents[0] = '\n'; + } + clearerr(file); + fread(file_contents + off, 1, (size_t)len, file); + if (ferror(file)) goto errorclose; + file_contents[len + off] = '\0'; + /* don't generate extra newline at end */ + if (len + off > 1 && file_contents[len + off - 1 == '\n']) { + file_contents[len + off - 1] = '\0'; + len--; + } + if (fclose(file)) goto error; + + ledit_insert_text_with_newlines( + buffer, line, ll->len, file_contents, len + off, NULL, NULL + ); + free(file_contents); + return 0; +error: + if (*errstr) + *errstr = strerror(errno); + return 1; +errorclose: + if (*errstr) + *errstr = strerror(errno); + fclose(file); + return 1; +} + +/* FIXME: allow to write only certain lines */ +int +ledit_write_buffer_to_file(ledit_buffer *buffer, char *filename, char **errstr) { + FILE *file; + ledit_line *ll; + file = fopen(filename, "w"); + if (!file) goto error; + clearerr(file); + for (int i = 0; i < buffer->lines_num; i++) { + ll = ledit_get_line(buffer, i); + ledit_normalize_line(ll); + if (fprintf(file, "%s\n", ll->text) < 0) goto errorclose; + } + if (fclose(file)) goto error; + return 0; +error: + if (*errstr) + *errstr = strerror(errno); + return 1; +errorclose: + if (*errstr) + *errstr = strerror(errno); + fclose(file); + return 1; +} + void ledit_destroy_buffer(ledit_buffer *buffer) { ledit_line *l; @@ -71,6 +152,7 @@ ledit_destroy_buffer(ledit_buffer *buffer) { free(l->text); } free(buffer->lines); + if (buffer->filename) free(buffer->filename); free(buffer); } @@ -84,6 +166,7 @@ ledit_normalize_line(ledit_line *line) { ); line->gap = line->len; } + /* FIXME: check if enough space, just to be sure */ line->text[line->len] = '\0'; } @@ -338,8 +421,8 @@ ledit_insert_text_final(ledit_buffer *buffer, int line_index, int index, char *t /* FIXME: this isn't optimized like the standard version, but whatever */ static char * -strchr_len(char *text, char c, int len) { - for (int i = 0; i < len; i++) { +strchr_len(char *text, char c, long len) { + for (long i = 0; i < len; i++) { if (text[i] == c) return text + i; } @@ -347,11 +430,18 @@ strchr_len(char *text, char c, int len) { } /* FIXME: make these functions that call recalc* also be final as described above */ +/* FIXME: Sort out integer types. + -> len is long here mainly because that's what ftell(3) returns and it sort of + makes sense since a file can be very long (although ledit probably won't work + with such long files anyways). The individual lines have to use int anyways + because of pango. + Maybe len isn't needed anyways? It might be possible to enforce that text + just always has to be null-terminated. */ void ledit_insert_text_with_newlines( ledit_buffer *buffer, int line_index, int index, - char *text, int len, + char *text, long len, int *end_line_ret, int *end_char_ret) { int end; ledit_insert_text_with_newlines_base( @@ -366,15 +456,16 @@ ledit_insert_text_with_newlines( ledit_recalc_from_line(buffer, line_index); } +/* FIXME: Check for integer overflow when casting to int */ void ledit_insert_text_with_newlines_base( ledit_buffer *buffer, int line_index, int index, - char *text, int len, + char *text, long len, int *end_line_ret, int *end_char_ret) { if (len == -1) len = strlen(text); - int rem_len = len; + long rem_len = len; char *cur, *last = text; int cur_line = line_index; int cur_index = index; @@ -384,8 +475,8 @@ ledit_insert_text_with_newlines_base( ledit_insert_text_base(buffer, cur_line, cur_index, last, cur - last); cur_index = 0; cur_line++; - last = cur + 1; rem_len -= cur - last + 1; + last = cur + 1; } /* FIXME: check how legal this casting between pointers and ints is */ ledit_insert_text_base(buffer, cur_line, cur_index, last, text + len - last); diff --git a/buffer.h b/buffer.h @@ -25,9 +25,11 @@ typedef struct { char h_dirty; /* whether height needs to be recalculated still */ } ledit_line; +/* TODO: advisory lock on file? also check if modification date changed before writing */ struct ledit_buffer { ledit_common_state *state; /* general state, e.g. display, window, etc. */ ledit_line *lines; /* array of lines */ + char *filename; int lines_cap; /* number of lines allocated in array */ int lines_num; /* number of used lines */ int cur_line; /* current line */ @@ -44,6 +46,8 @@ struct ledit_buffer { }; ledit_buffer *ledit_create_buffer(ledit_common_state *state); +int ledit_load_file_into_buffer(ledit_buffer *buffer, char *filename, int line, char **errstr); +int ledit_write_buffer_to_file(ledit_buffer *buffer, char *filename, char **errstr); void ledit_destroy_buffer(ledit_buffer *buffer); void ledit_normalize_line(ledit_line *line); void ledit_set_line_selection(ledit_buffer *buffer, int line, int start_byte, int end_byte); @@ -80,7 +84,7 @@ void ledit_insert_text_base(ledit_buffer *buffer, int line_index, int index, cha void ledit_insert_text_with_newlines_base( ledit_buffer *buffer, int line_index, int index, - char *text, int len, + char *text, long len, int *end_line_ret, int *end_char_ret ); void ledit_append_line_base(ledit_buffer *buffer, int line_index, int text_index); @@ -105,7 +109,7 @@ void ledit_insert_text(ledit_buffer *buffer, int line_index, int index, char *te void ledit_insert_text_with_newlines( ledit_buffer *buffer, int line_index, int index, - char *text, int len, + char *text, long len, int *end_line_ret, int *end_char_ret ); void ledit_append_line(ledit_buffer *buffer, int line_index, int text_index); diff --git a/commands.c b/commands.c @@ -1,5 +1,6 @@ /* FIXME: Parse commands properly and allow combinations of commands */ #include <ctype.h> +#include <stdlib.h> #include <X11/Xlib.h> #include <X11/Xutil.h> #include <pango/pangoxft.h> @@ -16,7 +17,10 @@ handle_write(ledit_buffer *buffer, char *cmd, int l1, int l2) { (void)cmd; (void)l1; (void)l2; - printf("write\n"); + /* FIXME: Implement properly; handle error */ + char *errstr; + if (buffer->filename) + ledit_write_buffer_to_file(buffer, buffer->filename, &errstr); return 0; } @@ -26,7 +30,8 @@ handle_quit(ledit_buffer *buffer, char *cmd, int l1, int l2) { (void)cmd; (void)l1; (void)l2; - printf("quit\n"); + /* FIXME: Implement */ + exit(1); return 0; } diff --git a/ledit.c b/ledit.c @@ -37,6 +37,7 @@ #include "cache.h" #include "util.h" #include "undo.h" +#include "commands.h" enum key_type { KEY_NONE = 0, @@ -1021,7 +1022,17 @@ setup(int argc, char *argv[]) { buffer = ledit_create_buffer(&state); /* FIXME: move this to create_buffer */ ledit_init_undo_stack(buffer); - set_mode(INSERT); + /* FIXME: Support multiple buffers/files */ + if (argc > 1) { + char *load_err; + if (ledit_load_file_into_buffer(buffer, argv[1], 0, &load_err)) { + fprintf(stderr, "Error opening file '%s': %s\n", argv[1], load_err); + exit(1); + } + buffer->filename = ledit_strdup(argv[1]); + } + set_mode(NORMAL); + ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index); key_stack.len = key_stack.alloc = 0; key_stack.stack = NULL;