From 25b289ccfb02fd9a5f4d03120c661f4a4a805bfe Mon Sep 17 00:00:00 2001 From: kamkow1 Date: Thu, 5 Mar 2026 16:29:59 +0100 Subject: [PATCH] CE edit command --- ce/ce.c | 50 ++++++++++++---- ce/edit.c | 140 +++++++++++++++++++++++++++++++++++++++++++++ ce/edit.h | 6 ++ ce/gapbuffer.c | 40 +++++++++---- ce/gapbuffer.h | 23 ++++++-- ce/interp.c | 47 +++++++++++++++ ce/src.mk | 6 +- ce/walloc.h | 16 ++++++ libarena/arena.h | 2 +- libstring/string.c | 14 +++++ libstring/string.h | 2 + 11 files changed, 316 insertions(+), 30 deletions(-) create mode 100644 ce/edit.c create mode 100644 ce/edit.h create mode 100644 ce/walloc.h diff --git a/ce/ce.c b/ce/ce.c index 0e3a8a7..d34127b 100644 --- a/ce/ce.c +++ b/ce/ce.c @@ -7,6 +7,7 @@ #include "strbuf.h" #include #include +#include #include #include #include @@ -17,9 +18,35 @@ #include #include -#define LINE_INIT_MAX 40 -#define LINE_TAIL_BATCH_MAX 1024 -#define PROMPT "$ " +#define LINE_INIT_MAX 40 +#define PROMPT "$ " + +void* wmalloc (void* ctx, size_t size) { + (void)ctx; + return malloc (size); +} + +void* wrealloc (void* ctx, void* mem, size_t old, size_t new) { + (void)ctx, (void)old; + return realloc (mem, new); +} + +void wfree (void* ctx, void* mem) { + (void)ctx; + free (mem); +} + +void* warena_malloc (void* ctx, size_t size) { + struct arena* a = ctx; + return arena_malloc (a, size); +} + +void* warena_realloc (void* ctx, void* mem, size_t old, size_t new) { + struct arena* a = ctx; + return arena_realloc (a, mem, old, new); +} + +void warena_free (void* ctx, void* mem) { (void)ctx, (void)mem; } struct edit_line { struct gapbuffer gb; @@ -44,7 +71,7 @@ void app_main (void) { const char* render = NULL; while (interp_is_running ()) { - gapbuffer_init (&edit_line.gb, LINE_INIT_MAX); + gapbuffer_init (&warena_malloc, &arena, &edit_line.gb, LINE_INIT_MAX); edit_line.cursor = 0; mprintf (PROMPT); @@ -66,7 +93,8 @@ void app_main (void) { mprintf (ANSIQ_CUR_LEFT (1)); - char* tail = gapbuffer_string_at (&edit_line.gb, edit_line.cursor); + char* tail = + gapbuffer_string_at (&warena_malloc, &arena, &edit_line.gb, edit_line.cursor); mprintf ("%s" ANSIQ_SCR_CLR2LEND, tail); int move_back = strlen (tail); @@ -78,7 +106,8 @@ void app_main (void) { if (edit_line.cursor < gapbuffer_length (&edit_line.gb)) { edit_line.gb.gap_end++; - char* tail = gapbuffer_string_at (&edit_line.gb, edit_line.cursor); + char* tail = + gapbuffer_string_at (&warena_malloc, &arena, &edit_line.gb, edit_line.cursor); mprintf ("%s" ANSIQ_SCR_CLR2LEND, tail); int move_back = strlen (tail); @@ -100,13 +129,14 @@ void app_main (void) { break; default: if (isprint (ch)) { - gapbuffer_insert (&edit_line.gb, ch); + gapbuffer_insert (&warena_realloc, &arena, &edit_line.gb, ch); edit_line.cursor++; if (edit_line.cursor == gapbuffer_length (&edit_line.gb)) { mprintf ("%c", ch); } else { - char* tail = gapbuffer_string_at (&edit_line.gb, edit_line.cursor - 1); + char* tail = + gapbuffer_string_at (&warena_malloc, &arena, &edit_line.gb, edit_line.cursor - 1); size_t move_back = strlen (tail) - 1; mprintf ("%s" ANSIQ_DYN_CUR_LEFT, tail, move_back); } @@ -117,12 +147,12 @@ void app_main (void) { mprintf ("\n"); - render = gapbuffer_get_string (&edit_line.gb); + render = gapbuffer_get_string (&warena_malloc, &arena, &edit_line.gb); if (render != NULL) exec_line (render); - gapbuffer_fini (&edit_line.gb); + gapbuffer_fini (&warena_free, &arena, &edit_line.gb); arena_reset (&arena); } diff --git a/ce/edit.c b/ce/edit.c new file mode 100644 index 0000000..ffd6946 --- /dev/null +++ b/ce/edit.c @@ -0,0 +1,140 @@ +#include "edit.h" +#include "arena_alloc.h" +#include "gapbuffer.h" +#include "mprintf.h" +#include "self.h" +#include "walloc.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct edit_line { + struct list_node_link lines_link; + struct gapbuffer gb; +}; + +struct cursor { + size_t col; + size_t line; +}; + +struct editor { + struct list_node_link* lines; + struct edit_line* current_line; + struct cursor cursor; +}; + +static struct editor editor; + +static bool prepare_lines_cb (void* ctx, const char* start, size_t len) { + struct edit_line* line = malloc (sizeof (*line)); + + if (line == NULL) + return false; + + memset (line, 0, sizeof (*line)); + gapbuffer_init (&wmalloc, NULL, &line->gb, len); + + for (size_t chr = 0; chr < len; chr++) + gapbuffer_insert (&wrealloc, NULL, &line->gb, start[chr]); + + list_append (editor.lines, &line->lines_link); + + if (editor.current_line == NULL) + editor.current_line = line; + + return true; +} + +static void prepare_lines (const char* text) { strtokenize (text, '\n', NULL, &prepare_lines_cb); } + +void edit_start (const char* text) { + prepare_lines (text); + struct arena temp_arena; + + mprintf (ANSIQ_SCR_SAVE ANSIQ_SCR_CLR_ALL ANSIQ_CUR_INVISIBLE); + + for (;;) { + const size_t backbuffer_max = 128 * 1024; + char* backbuffer = arena_malloc (&temp_arena, backbuffer_max); + memset (backbuffer, 0, backbuffer_max); + + strncat (backbuffer, ANSIQ_CUR_HOME ANSIQ_SCR_CLR_ALL, backbuffer_max); + + struct list_node_link *line_link, *tmp_line_link; + list_foreach (editor.lines, line_link, tmp_line_link) { + struct edit_line* line = list_entry (line_link, struct edit_line, lines_link); + + strncat (backbuffer, ANSIQ_SCR_CLR_LINE, backbuffer_max); + + char* render = gapbuffer_get_string (&warena_malloc, &temp_arena, &line->gb); + strncat (backbuffer, render, backbuffer_max); + strncat (backbuffer, "\n", backbuffer_max); + } + + char tmp[128]; + snprintf (tmp, sizeof (tmp), ANSIQ_DYN_CUR_SET ANSIQ_CUR_VISIBLE, (int)editor.cursor.line + 1, + (int)editor.cursor.col + 1); + strncat (backbuffer, tmp, backbuffer_max); + + mail_send (e_pgid, backbuffer, strlen (backbuffer)); + + uint8_t ch = 0; + mail_receive (&ch, 1); + + gapbuffer_move (&editor.current_line->gb, editor.cursor.col); + + switch (ch) { + case '\b': + if (editor.cursor.col > 0) { + editor.cursor.col--; + gapbuffer_backspace (&editor.current_line->gb); + } + break; + case KB_DELETE: + if (editor.cursor.col < gapbuffer_length (&editor.current_line->gb)) { + editor.current_line->gb.gap_end++; + } + break; + case KB_LEFT: + if (editor.cursor.col > 0) { + editor.cursor.col--; + } + break; + case KB_RIGHT: + if (editor.cursor.col < gapbuffer_length (&editor.current_line->gb)) { + editor.cursor.col++; + } + break; + default: + if (isprint (ch)) { + gapbuffer_insert (&wrealloc, NULL, &editor.current_line->gb, ch); + editor.cursor.col++; + } + break; + } + + arena_reset (&temp_arena); + } + + mprintf (ANSIQ_CUR_VISIBLE ANSIQ_SCR_RESTORE); + + arena_destroy (&temp_arena); + + struct list_node_link *line_link, *tmp_line_link; + list_foreach (editor.lines, line_link, tmp_line_link) { + struct edit_line* line = list_entry (line_link, struct edit_line, lines_link); + free (line->gb.buffer); + free (line); + } +} diff --git a/ce/edit.h b/ce/edit.h new file mode 100644 index 0000000..e9b7c06 --- /dev/null +++ b/ce/edit.h @@ -0,0 +1,6 @@ +#ifndef _EDIT_H +#define _EDIT_H + +void edit_start (const char* text); + +#endif // _EDIT_H diff --git a/ce/gapbuffer.c b/ce/gapbuffer.c index e7248b4..7015cc7 100644 --- a/ce/gapbuffer.c +++ b/ce/gapbuffer.c @@ -1,11 +1,12 @@ #include "gapbuffer.h" #include "arena_alloc.h" +#include #include #include #include -void gapbuffer_init (struct gapbuffer* gb, size_t capacity) { - gb->buffer = arena_malloc (&arena, capacity); +void gapbuffer_init (gb_malloc_func_t mallocfn, void* ctx, struct gapbuffer* gb, size_t capacity) { + gb->buffer = mallocfn (ctx, capacity); if (gb->buffer == NULL) return; @@ -16,13 +17,16 @@ void gapbuffer_init (struct gapbuffer* gb, size_t capacity) { gb->gap_end = capacity; } -void gapbuffer_fini (struct gapbuffer* gb) { memset (gb, 0, sizeof (*gb)); } +void gapbuffer_fini (gb_free_func_t freefn, void* ctx, struct gapbuffer* gb) { + freefn (ctx, gb->buffer); + memset (gb, 0, sizeof (*gb)); +} -void gapbuffer_grow (struct gapbuffer* gb) { +void gapbuffer_grow (gb_realloc_func_t reallocfn, void* ctx, struct gapbuffer* gb) { size_t old_size = gb->size; size_t new_size = gb->size * 2; - char* new = arena_realloc (&arena, gb->buffer, old_size, new_size); + char* new = reallocfn (ctx, gb->buffer, old_size, new_size); if (new == NULL) return; @@ -55,9 +59,9 @@ void gapbuffer_move (struct gapbuffer* gb, size_t pos) { } } -void gapbuffer_insert (struct gapbuffer* gb, char c) { +void gapbuffer_insert (gb_realloc_func_t reallocfn, void* ctx, struct gapbuffer* gb, char c) { if (gb->gap_start == gb->gap_end) - gapbuffer_grow (gb); + gapbuffer_grow (reallocfn, ctx, gb); gb->buffer[gb->gap_start] = c; gb->gap_start++; @@ -68,10 +72,10 @@ void gapbuffer_backspace (struct gapbuffer* gb) { gb->gap_start--; } -char* gapbuffer_get_string (struct gapbuffer* gb) { +char* gapbuffer_get_string (gb_malloc_func_t mallocfn, void* ctx, struct gapbuffer* gb) { size_t size = gb->size - (gb->gap_end - gb->gap_start); - char* str = arena_malloc (&arena, size + 1); + char* str = mallocfn (ctx, size + 1); if (str == NULL) return NULL; @@ -86,7 +90,7 @@ char* gapbuffer_get_string (struct gapbuffer* gb) { size_t gapbuffer_length (struct gapbuffer* gb) { return gb->size - (gb->gap_end - gb->gap_start); } -char* gapbuffer_string_at (struct gapbuffer* gb, size_t pos) { +char* gapbuffer_string_at (gb_malloc_func_t mallocfn, void* ctx, struct gapbuffer* gb, size_t pos) { size_t total_size = gapbuffer_length (gb); if (pos >= total_size) @@ -94,7 +98,7 @@ char* gapbuffer_string_at (struct gapbuffer* gb, size_t pos) { size_t tail_size = total_size - pos; - char* res = arena_malloc (&arena, tail_size + 1); + char* res = mallocfn (ctx, tail_size + 1); if (res == NULL) return NULL; @@ -124,3 +128,17 @@ char* gapbuffer_string_at (struct gapbuffer* gb, size_t pos) { res[written] = '\0'; return res; } + +char gapbuffer_at (struct gapbuffer* gb, size_t index) { + if (index < gb->gap_start) + return gb->buffer[index]; + else + return gb->buffer[index + (gb->gap_end - gb->gap_start)]; +} + +void gapbuffer_delete_at (struct gapbuffer* gb, size_t index) { + gapbuffer_move (gb, index); + + if (gb->gap_end < gb->size) + gb->gap_end++; +} diff --git a/ce/gapbuffer.h b/ce/gapbuffer.h index d266468..d865286 100644 --- a/ce/gapbuffer.h +++ b/ce/gapbuffer.h @@ -1,6 +1,7 @@ #ifndef _GAPBUFFER_H #define _GAPBUFFER_H +#include #include struct gapbuffer { @@ -10,22 +11,32 @@ struct gapbuffer { size_t gap_end; }; -void gapbuffer_init (struct gapbuffer* gb, size_t capacity); +typedef void* (*gb_malloc_func_t) (void*, size_t); -void gapbuffer_fini (struct gapbuffer* gb); +typedef void* (*gb_realloc_func_t) (void*, void*, size_t, size_t); + +typedef void (*gb_free_func_t) (void*, void*); + +void gapbuffer_init (gb_malloc_func_t mallocfn, void* ctx, struct gapbuffer* gb, size_t capacity); + +void gapbuffer_fini (gb_free_func_t freefn, void* ctx, struct gapbuffer* gb); void gapbuffer_move (struct gapbuffer* gb, size_t pos); -void gapbuffer_insert (struct gapbuffer* gb, char c); +void gapbuffer_insert (gb_realloc_func_t reallocfn, void* ctx, struct gapbuffer* gb, char c); void gapbuffer_backspace (struct gapbuffer* gb); -void gapbuffer_grow (struct gapbuffer* gb); +void gapbuffer_grow (gb_realloc_func_t reallocfn, void* ctx, struct gapbuffer* gb); -char* gapbuffer_get_string (struct gapbuffer* gb); +char* gapbuffer_get_string (gb_malloc_func_t mallocfn, void* ctx, struct gapbuffer* gb); size_t gapbuffer_length (struct gapbuffer* gb); -char* gapbuffer_string_at (struct gapbuffer* gb, size_t pos); +char* gapbuffer_string_at (gb_malloc_func_t mallocfn, void* ctx, struct gapbuffer* gb, size_t pos); + +char gapbuffer_at (struct gapbuffer* gb, size_t index); + +void gapbuffer_delete_at (struct gapbuffer* gb, size_t index); #endif // _GAPBUFFER_H diff --git a/ce/interp.c b/ce/interp.c index 5a5c23b..64ffb6d 100644 --- a/ce/interp.c +++ b/ce/interp.c @@ -1,12 +1,14 @@ #include "interp.h" #include "arena_alloc.h" #include "context.h" +#include "edit.h" #include "parser.h" #include "self.h" #include #include #include #include +#include #include #include #include @@ -189,6 +191,48 @@ static void ls (struct context* context, const char* path_string) { volume_close (); } +static void edit (struct context* context, const char* path_string) { + struct desc desc; + char volume[VOLUME_MAX]; + const char* path; + int ret; + + if (!path_parse (path_string, volume, &path)) { + cprintf (context, "ERROR bad path '%s'\n", path_string); + return; + } + + if ((ret = volume_open (volume)) < 0) { + cprintf (context, "ERROR could not open volume '%s': %s\n", volume, str_status[-ret]); + return; + } + + if ((ret = describe (path, &desc)) < 0) { + cprintf (context, "ERROR could not describe '%s': %s\n", path, str_status[-ret]); + volume_close (); + return; + } + + char* str = malloc (desc.size + 1); + + if (str == NULL) { + volume_close (); + return; + } + + memset (str, 0, desc.size); + + if ((ret = read_file (path, 0, (uint8_t*)str, desc.size)) < 0) { + free (str); + volume_close (); + return; + } + + edit_start (str); + + volume_close (); +} + static void quit1 (struct context* context) { cprintf (context, "Goodbye!\n"); interp_shutdown (); @@ -203,6 +247,7 @@ static void help (struct context* context) { cprintf (context, "mkfile \n"); cprintf (context, "mkdir \n"); cprintf (context, "rm \n"); + cprintf (context, "edit \n"); cprintf (context, "quit\n"); } @@ -239,6 +284,8 @@ static void execute_cmd (struct ast_cmd* cmd, struct context* context) { mkdir (context, cmd->args, cmd->arg_count); } else if (strcmp (cmd->name, "rm") == 0) { rm (context, cmd->args, cmd->arg_count); + } else if (strcmp (cmd->name, "edit") == 0) { + edit (context, cmd->args[0]); } else { char volume[VOLUME_MAX]; const char* path; diff --git a/ce/src.mk b/ce/src.mk index bfed501..7fe6778 100644 --- a/ce/src.mk +++ b/ce/src.mk @@ -6,7 +6,8 @@ c += ce.c \ interp.c \ mprintf.c \ self.c \ - gapbuffer.c + gapbuffer.c \ + edit.c o += ce.o \ arena_alloc.o \ @@ -16,4 +17,5 @@ o += ce.o \ interp.o \ mprintf.o \ self.o \ - gapbuffer.o + gapbuffer.o \ + edit.o diff --git a/ce/walloc.h b/ce/walloc.h new file mode 100644 index 0000000..076f776 --- /dev/null +++ b/ce/walloc.h @@ -0,0 +1,16 @@ +#ifndef _WALLOC_H +#define _WALLOC_H + +void* wmalloc (void* ctx, size_t size); + +void* wrealloc (void* ctx, void* mem, size_t old, size_t new); + +void wfree (void* ctx, void* mem); + +void* warena_malloc (void* ctx, size_t size); + +void* warena_realloc (void* ctx, void* mem, size_t old, size_t new); + +void warena_free (void* ctx, void* mem); + +#endif // _WALLOC_H diff --git a/libarena/arena.h b/libarena/arena.h index 74dad6a..574bc1c 100644 --- a/libarena/arena.h +++ b/libarena/arena.h @@ -4,7 +4,7 @@ #include #include -#define ARENA_CHUNK_CAPACITY (8 * 1024) +#define ARENA_CHUNK_CAPACITY (1024 * 1024) struct arena_chunk { struct arena_chunk* next; diff --git a/libstring/string.c b/libstring/string.c index 3f3b02c..e78eea5 100644 --- a/libstring/string.c +++ b/libstring/string.c @@ -140,3 +140,17 @@ void* memmove (void* dest, const void* src, unsigned int n) { } return dest; } + +char* strncat (char* dest, const char* src, size_t n) { + char* ptr = dest; + while (*ptr != '\0') + ptr++; + + while (n > 0 && *src != '\0') { + *ptr++ = *src++; + n--; + } + + *ptr = '\0'; + return dest; +} diff --git a/libstring/string.h b/libstring/string.h index ae42eb7..ca1537c 100644 --- a/libstring/string.h +++ b/libstring/string.h @@ -31,6 +31,8 @@ int strcmp (const char* s1, const char* s2); /* concatinate strings */ char* strcat (char* dest, const char* src); +char* strncat (char* dest, const char* src, size_t n); + int isalnum (int c); int isalpha (int c);