sled

Једноставни уређивач текста
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување 611564963cf23a4a662c7555473d3df71b7d8b7b
родитељ 99f581a62d270044e8ba9fb6791fb680a22f0b97
Аутор: Страхиња Радић <contact@strahinja.org>
Датум:   Wed, 23 Feb 2022 13:38:53 +0100

Finish copy/paste WIP; more thorough directory access testing

Signed-off-by: Страхиња Радић <contact@strahinja.org>

Diffstat:
MREADME | 6------
MTODO | 17++---------------
Mconfig.def.h | 26+++++++++++++++++---------
Mconfig.h | 26+++++++++++++++++---------
Msled.1.in | 5++---
Msled.c | 135++++++++++++++++++++++++++++++++++++-------------------------------------------
измењених датотека: 6, додавања: 99(+), брисања: 116(-)

diff --git a/README b/README @@ -1,12 +1,6 @@ sled - Simple text editor ========================= -Currently not yet functional (getting there though!). In the process of reusing -code from poe[1]. - -[1]: https://sr.ht/~strahinja/poe - - Q: Why can't sled open the file "read only" even if modification is disallowed? A: A text editor's purpose should be to *edit* text files. For just *viewing* text files, there's less(1). This avoids the situation when opening a file diff --git a/TODO b/TODO @@ -3,25 +3,12 @@ [x] Copy/paste? + [ ] Overwrite mode + [ ] Investigate DrawState.first_shown_column [ ] Add vi V - [x] Add vi H/L - - [x] Allow creating files (error out if directory not writeable, only - create file when saving) - - [x] Add handling of terminal resize event - - [/] Test for binary when opening? (Why?) - - [x] Fix goto line #0 - - [x] Fix moving up in empty file - - [x] Write operation; create backup files - < > Search? < > Replace? diff --git a/config.def.h b/config.def.h @@ -23,6 +23,14 @@ static const char* backup_suffix = "~"; #define SEL_BG TB_WHITE #define SHADOW_FG TB_BLACK #define SHADOW_BG TB_BLACK | TB_BOLD +#define STATUS_1_FG TB_WHITE | TB_BOLD +#define STATUS_1_BG TB_BLUE +#define STATUS_2_FG TB_YELLOW | TB_BOLD +#define STATUS_2_BG TB_BLUE +#define STATUS_3_FG TB_WHITE | TB_BOLD +#define STATUS_3_BG TB_BLUE +#define STATUS_4_FG TB_WHITE +#define STATUS_4_BG TB_BLUE #define TEXT_FG TB_WHITE #define TEXT_BG TB_DEFAULT #define TITLE_FG TB_BLACK @@ -198,7 +206,7 @@ static const struct Key command_keys[] = { { .ev = { .ch = 'L' }, .callback = move_screen_bottom }, { .ev = { .ch = 'l' }, .callback = move_right }, { .ev = { .ch = 'M' }, .callback = move_screen_middle }, - { .ev = { .ch = 'p' }, .callback = paste_from_paste }, + { .ev = { .ch = 'P' }, .callback = paste_from_paste }, { .ev = { .ch = 'q' }, .callback = exit_program }, { .ev = { .ch = 'v' }, .callback = toggle_selection }, { .ev = { .ch = 'W' }, .callback = move_prev_word }, @@ -219,23 +227,23 @@ void format_position(char* result, size_t result_size, const char* format, static const struct StatusSegment status_segments[] = { {.format = "%s%s", .callback = format_filename, - .fg = TB_WHITE, - .bg = TB_BLUE, + .fg = STATUS_1_FG, + .bg = STATUS_1_BG, .alignment = LEFT}, { .format = "%s", .callback = format_mode, - .fg = TB_YELLOW | TB_BOLD, - .bg = TB_BLUE, + .fg = STATUS_2_FG, + .bg = STATUS_2_BG, .alignment = CENTER}, {.format = "%5ld:%5ld (%3ld%%)", .callback = format_position, - .fg = TB_WHITE | TB_BOLD, - .bg = TB_BLUE, + .fg = STATUS_3_FG, + .bg = STATUS_3_BG, .alignment = CENTER}, {.format = "F1=HELP, C-Q=QUIT", .callback = NULL, - .fg = TB_WHITE, - .bg = TB_BLUE, + .fg = STATUS_4_FG, + .bg = STATUS_4_BG, .alignment = RIGHT}, }; /* clang-format on */ diff --git a/config.h b/config.h @@ -23,6 +23,14 @@ static const char* backup_suffix = "~"; #define SEL_BG TB_WHITE #define SHADOW_FG TB_BLACK #define SHADOW_BG TB_BLACK | TB_BOLD +#define STATUS_1_FG TB_WHITE | TB_BOLD +#define STATUS_1_BG TB_BLUE +#define STATUS_2_FG TB_YELLOW | TB_BOLD +#define STATUS_2_BG TB_BLUE +#define STATUS_3_FG TB_WHITE | TB_BOLD +#define STATUS_3_BG TB_BLUE +#define STATUS_4_FG TB_WHITE +#define STATUS_4_BG TB_BLUE #define TEXT_FG TB_WHITE #define TEXT_BG TB_DEFAULT #define TITLE_FG TB_BLACK @@ -198,7 +206,7 @@ static const struct Key command_keys[] = { { .ev = { .ch = 'L' }, .callback = move_screen_bottom }, { .ev = { .ch = 'l' }, .callback = move_right }, { .ev = { .ch = 'M' }, .callback = move_screen_middle }, - { .ev = { .ch = 'p' }, .callback = paste_from_paste }, + { .ev = { .ch = 'P' }, .callback = paste_from_paste }, { .ev = { .ch = 'q' }, .callback = exit_program }, { .ev = { .ch = 'v' }, .callback = toggle_selection }, { .ev = { .ch = 'W' }, .callback = move_prev_word }, @@ -219,23 +227,23 @@ void format_position(char* result, size_t result_size, const char* format, static const struct StatusSegment status_segments[] = { {.format = "%s%s", .callback = format_filename, - .fg = TB_WHITE, - .bg = TB_BLUE, + .fg = STATUS_1_FG, + .bg = STATUS_1_BG, .alignment = LEFT}, { .format = "%s", .callback = format_mode, - .fg = TB_YELLOW | TB_BOLD, - .bg = TB_BLUE, + .fg = STATUS_2_FG, + .bg = STATUS_2_BG, .alignment = CENTER}, {.format = "%5ld:%5ld (%3ld%%)", .callback = format_position, - .fg = TB_WHITE | TB_BOLD, - .bg = TB_BLUE, + .fg = STATUS_3_FG, + .bg = STATUS_3_BG, .alignment = CENTER}, {.format = "F1=HELP, C-Q=QUIT", .callback = NULL, - .fg = TB_WHITE, - .bg = TB_BLUE, + .fg = STATUS_4_FG, + .bg = STATUS_4_BG, .alignment = RIGHT}, }; /* clang-format on */ diff --git a/sled.1.in b/sled.1.in @@ -216,9 +216,8 @@ Only one file can be edited at a time, and no additional files can be loaded at runtime. This is a design decision. . .IP \(bu -The directory of the edited file must be writable by the user in order to write -the changes to the file. This is due to creating backups in order to avoid the -potential data loss under specific circumstances (loss of power during write). +If backup creation is enabled (\fCCREATE_BACKUPS\fP in \fIconfig.h\fP), the +directory of the edited file must be writable by the user to open the file. . .SH BUGS . diff --git a/sled.c b/sled.c @@ -230,12 +230,19 @@ load_file(struct DrawState* state, long* lineno, long* col) struct BufferLine* line = NULL; FILE* input = NULL; char input_line[MAXBUFLINE]; + uint32_t* newtext = NULL; newchunk = calloc(ALLOC_DELTA, sizeof(struct BufferLine)); if (!newchunk) return LOAD_ERR_CANT_ALLOC; for (size_t row = 0; row < ALLOC_DELTA; row++) + { init_bufferline(newchunk + row); + newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); + if (!newtext) + return LOAD_ERR_CANT_ALLOC; + newchunk[row].text = newtext; + } strcpy(state->action, actions[ACT_READING]); redraw_status(state); @@ -250,8 +257,7 @@ load_file(struct DrawState* state, long* lineno, long* col) while (!feof(input)) { - char* eol = NULL; - uint32_t* newtext = NULL; + char* eol = NULL; if (!fgets(input_line, MAXBUFLINE, input)) continue; @@ -259,8 +265,7 @@ load_file(struct DrawState* state, long* lineno, long* col) if (eol) *eol = 0; - line = state->buffer + state->rows_count; - if (line == state->buffer + state->buffer_size) + if (state->rows_count == state->buffer_size) { state->buffer_size += ALLOC_DELTA; newchunk = realloc(state->buffer, @@ -270,17 +275,17 @@ load_file(struct DrawState* state, long* lineno, long* col) state->buffer = newchunk; for (size_t row = state->buffer_size - ALLOC_DELTA; row < state->buffer_size; row++) + { init_bufferline(state->buffer + row); + newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); + if (!newtext) + return LOAD_ERR_CANT_ALLOC; + state->buffer[row].text = newtext; + } line = state->buffer + state->buffer_size - ALLOC_DELTA; } - if (!line->text) - { - newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); - if (!newtext) - return LOAD_ERR_CANT_ALLOC; - line->length = u32_u8_strcpy(newtext, input_line); - line->text = newtext; - } + line = state->buffer + state->rows_count; + line->length = u32_u8_strcpy(line->text, input_line); state->rows_count++; } fclose(input); @@ -365,6 +370,7 @@ yank_to_paste(struct DrawState* state) size_t ser = 0; size_t sec = 0; struct BufferLine* newbuf = NULL; + size_t initialize_start; if (!SEL_NOT_EMPTY(state)) return 0; @@ -387,19 +393,22 @@ yank_to_paste(struct DrawState* state) state->paste_count = ser - ssr + 1; if (!state->paste_size) { - state->paste_size = ALLOC_DELTA; + initialize_start = 0; + while (state->paste_count > state->paste_size) + state->paste_size += ALLOC_DELTA; newbuf = calloc(state->paste_size, sizeof(struct BufferLine)); } else if (state->paste_count > state->paste_size) { - state->paste_size += ALLOC_DELTA; + initialize_start = state->paste_size; + while (state->paste_count > state->paste_size) + state->paste_size += ALLOC_DELTA; newbuf = realloc(state->paste, state->paste_size * sizeof(struct BufferLine)); } else newbuf = state->paste; - for (size_t row = state->paste_size - ALLOC_DELTA; - row < state->paste_size; row++) + for (size_t row = initialize_start; row < state->paste_size; row++) { uint32_t* newtext = NULL; init_bufferline(newbuf + row); @@ -434,17 +443,10 @@ yank_to_paste(struct DrawState* state) int paste_from_paste(struct DrawState* state) { - /* Selection start row/col */ - size_t ssr = 0; - size_t ssc = 0; - /* Selection end row/col */ - size_t ser = 0; - size_t sec = 0; uint32_t* newtext = NULL; struct BufferLine* line = NULL; struct BufferLine* newbuf = NULL; - size_t dlen; - uint32_t* rest = NULL; + uint32_t* rest = NULL; if (!state->paste_count) return 0; @@ -466,8 +468,8 @@ paste_from_paste(struct DrawState* state) state->buffer_size += ALLOC_DELTA; newbuf = realloc(state->buffer, state->buffer_size * sizeof(struct BufferLine)); - for (size_t row = 0; - row < state->buffer_size - old_size; row++) + for (size_t row = old_size; row < state->buffer_size; + row++) { init_bufferline(newbuf + row); newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); @@ -491,9 +493,11 @@ paste_from_paste(struct DrawState* state) { u32_strcat(newtext, line->text + state->column); line->length = u32_strcpy(line->text, newtext); + state->column += state->paste[state->paste_count - 1].length; } else { + state->rows_count += state->paste_count - 1; rest = calloc(MAXBUFLINE, sizeof(uint32_t)); if (!rest) { @@ -502,13 +506,12 @@ paste_from_paste(struct DrawState* state) } u32_strcpy(rest, line->text + state->column); line->length = u32_strcpy(line->text, newtext); - /* TODO: */ for (size_t row = state->rows_count - state->row - (state->paste_count - 2) - 1; row > 0; row--) { line = state->buffer + state->row + state->paste_count - - 2 + row - 1; + - 2 + row; line->length = u32_strcpy(line->text, state->buffer[state->row + row - 1].text); } @@ -523,37 +526,10 @@ paste_from_paste(struct DrawState* state) line = state->buffer + state->row + state->paste_count - 1; u32_strcpy(newtext, state->paste[state->paste_count - 1].text); u32_strcat(newtext, rest); - line->length = u32_strcpy(line->text, newtext); - } - /*for (size_t row = state->paste_size - ALLOC_DELTA; - row < state->paste_size; row++) - { - uint32_t* newtext = NULL; - init_bufferline(newbuf + row); - newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); - if (!newtext) - strcpy(state->error, errors[ERR_CANT_ALLOC]); - newbuf[row].text = newtext; - newbuf[row].length = 0; + line->length = u32_strcpy(line->text, newtext); + state->column = state->paste[state->paste_count - 1].length; + state->row += state->paste_count - 1; } - state->paste = newbuf; - if (state->paste_count == 0) - state->paste[0].length = u32_strncpy(state->paste[0].text, - state->buffer[ssr].text + ssc, sec - ssc + 1); - else - { - state->paste[0].length = u32_strcpy(state->paste[0].text, - state->buffer[ssr].text + ssc); - for (size_t row = 1; row < state->paste_count - 1; row++) - state->paste[row].length - = u32_strcpy(state->paste[row].text, - state->buffer[ssr + row].text); - state->paste[state->paste_count - 1].length - = u32_strncpy(state->paste[state->paste_count - 1].text, - state->buffer[ser].text, sec + 1); - }*/ - state->row += state->paste_count - 1; - state->column += state->paste[state->paste_count - 1].length; state->display_column = display_length(state, state->buffer[state->row].text, state->column); @@ -562,6 +538,8 @@ paste_from_paste(struct DrawState* state) if (state->first_shown_column + state->maxx - 1 < state->display_column) state->first_shown_column = state->display_column - state->maxx + 1; + if (state->first_shown_row + (state->maxy - 2) < state->row) + state->first_shown_row = state->row - (state->maxy - 2); state->dirty = 1; paste_from_paste_cleanup: @@ -1921,7 +1899,11 @@ main(int argc, char** argv) long lineno = 1; long col = 1; int result; + int dir_access; + int dir_access_errno; int recalculate_size = 1; + char dirname[MAXPATH]; + char* slash = NULL; *error = 0; *action = 0; @@ -1952,26 +1934,24 @@ main(int argc, char** argv) init_drawstate(&state, error, action, filename, prompt); - if (stat(filename, &st) == -1) + strcpy(dirname, filename); + if ((slash = strrchr(dirname, '/'))) + *slash = 0; + else { - char dirname[MAXPATH]; - char* slash = NULL; - - if (errno != ENOENT) - goto file_error; + *dirname = '.'; + *(dirname + 1) = 0; + } - strcpy(dirname, filename); - if ((slash = strrchr(dirname, '/'))) - *slash = 0; - else + dir_access = access(dirname, W_OK); + dir_access_errno = errno; + if (stat(filename, &st) == -1) + { + if (errno != ENOENT || dir_access == -1) { - *dirname = '.'; - *(dirname + 1) = 0; - } - - if (access(dirname, F_OK | R_OK | W_OK) == -1) + errno = dir_access_errno; goto file_error; - + } result = create_file(&state); switch (result) { @@ -1982,6 +1962,13 @@ main(int argc, char** argv) } goto init_termbox; } +#ifdef CREATE_BACKUPS + if (dir_access == -1) + { + errno = dir_access_errno; + goto file_error; + } +#endif /* CREATE_BACKUPS */ if (access(filename, F_OK | R_OK | W_OK) == -1) goto file_error;