poe

Уређивач .po фајлова
git clone https://git.sr.ht/~strahinja/poe
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување bdab3f03d657a8149f169895901e6c5a61a34fab
родитељ a5c441d579f43728675954c4b729fc1729322918
Аутор: Страхиња Радић <contact@strahinja.org>
Датум:   Sat,  9 Mar 2024 20:56:39 +0100

Add pledge(2)/unveil(2); add macros for strlcat and strlcpy; copy code from sled dealing with symlinks

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

Diffstat:
MTODO | 2--
MTODO.done | 2++
Mdraw.c | 17++++++-----------
Mdraw.h | 4++++
Mpo.c | 21++++-----------------
Mpoe.c | 341+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mutil.h | 23++++++++++++++++++-----
измењених датотека: 7, додавања: 220(+), брисања: 190(-)

diff --git a/TODO b/TODO @@ -1,8 +1,6 @@ TODO ==== -[ ] Add pledge(2) and unveil(2) - [ ] Support Yudit's nonstandard PO format (no empty lines between individual entries) diff --git a/TODO.done b/TODO.done @@ -1,6 +1,8 @@ Done todos ========== +[x] Add pledge(2) and unveil(2) + [x] Switch to strlcpy, strlcat similar to sled; compare util.c and util.h to versions of those files from sled diff --git a/draw.c b/draw.c @@ -91,6 +91,9 @@ init_drawstate(struct DrawState* state, char* error, char* filename, { state->error = error; state->filename = filename; + state->real_filename = filename; + state->backup_filename = NULL; + state->rfn_len = 0; state->search = NULL; state->input_buffer = NULL; state->input_changed_fuzzy = UNCHANGED; @@ -132,6 +135,7 @@ init_drawstate(struct DrawState* state, char* error, char* filename, state->obsolete_at_end_count = 0; state->msgstr_index = 0; state->first_shown_msgid = state->msgid_number; + state->backup_possible = 1; } void @@ -614,18 +618,9 @@ draw_editbox(const struct DrawState* state) struct PoEntry* entry = state->entries + state->msgid_number - 1; size_t dlen = 0; - if (strlcpy(flags_buf, "[ ", MAXFLAGSBUF + 4) >= MAXFLAGSBUF + 4) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY("[ ", flags_buf, MAXFLAGSBUF + 4); format_flags(flags_buf + 2, MAXFLAGSBUF, entry); - if (strlcpy(flags_buf + 2 + MAXFLAGSBUF - 1, " ]", MAXFLAGSBUF + 4 - 2) - >= MAXFLAGSBUF + 4 - 2) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY(" ]", flags_buf + 2 + MAXFLAGSBUF - 1, MAXFLAGSBUF + 4 - 2); startx = w > maxx ? 0 : (maxx - w) / 2; endx = w > maxx ? maxx - 1 : startx + w; diff --git a/draw.h b/draw.h @@ -41,6 +41,9 @@ struct DrawState { char* error; char* prompt; char* filename; + char* real_filename; /* what symlink points to */ + char* backup_filename; + size_t rfn_len; uint32_t* search; struct BufferLine* input_buffer; /* edit box */ int input_column; @@ -89,6 +92,7 @@ struct DrawState { size_t real_msgid_count; /* msgid_count + obsolete_count */ int msgstr_index; /* with msgstr[] */ size_t first_shown_msgid; + int backup_possible; /* is saving backup possible? */ }; struct Key { diff --git a/po.c b/po.c @@ -486,13 +486,8 @@ load_file(struct DrawState* dstate, long* lineno, long* col) entry->obsolete = 1; #ifndef PLURAL_STRING if (has_plural_msgid && !dstate->entries->plural_forms) - if (strlcpy(dstate->error, errors[ERR_NO_PLURAL_FORMS], - MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY(errors[ERR_NO_PLURAL_FORMS], dstate->error, + MAXBUFLINE); #endif } @@ -555,16 +550,8 @@ save_file(const struct PoEntry* entries, const size_t real_msgid_count, if (stat(real_filename, &st) == -1) return SAVE_ERR_CANT_STAT_FILE; } - if (strlcpy(backup_filename, filename, MAXPATH) >= MAXPATH) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } - if (strlcat(backup_filename, backup_suffix, MAXPATH) >= MAXPATH) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY(filename, backup_filename, MAXPATH); + STRLCAT(backup_suffix, backup_filename, MAXPATH); if (rename(rfn_len > 0 ? real_filename : filename, backup_filename) == -1) diff --git a/poe.c b/poe.c @@ -31,6 +31,7 @@ extern const char* prompt_overwrite; extern const char* program_name; void cancel_callback(struct DrawState* state, struct tb_event* ev); +char* dirname(char* path); void goto_msgid(struct DrawState* state, const size_t msgid_number); void handle_key_event(struct tb_event* ev, struct DrawState* state); int insert_char(struct DrawState* state, uint32_t ch); @@ -57,6 +58,35 @@ cancel_callback(struct DrawState* state, struct tb_event* ev) state->prompt_callback = NULL; } +char* +dirname(char* path) +{ + static char* result = NULL; + static const char* dot = "."; + char* p = NULL; + char* slash = NULL; + + if (!path || !*path) + return NULL; + + free(result); + result = malloc(MAXPATH); + memset(result, 0, MAXPATH); + + STRLCPY(path, result, MAXPATH); + p = result + strlen(result) - 1; + if (*p == '/') + *p = 0; + + slash = strrchr(result, '/'); + if (!slash) + return (char*)dot; + + *slash = 0; + + return result; +} + void goto_msgid(struct DrawState* state, const size_t msgid_number) { @@ -120,12 +150,7 @@ handle_key_event(struct tb_event* ev, struct DrawState* state) if (state->show_help) { - if (strlcpy(state->error, errors[ERR_DLG_OPEN_QUIT], MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY(errors[ERR_DLG_OPEN_QUIT], state->error, MAXBUFLINE); return; } else if (state->show_edit) @@ -183,33 +208,11 @@ handle_key_event(struct tb_event* ev, struct DrawState* state) } if (state->edit_info_focused) - { - if (strlcpy(state->error, errors[ERR_DLG_FOCUS_EDIT], MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } - } + STRLCPY(errors[ERR_DLG_FOCUS_EDIT], state->error, MAXBUFLINE); else if (state->show_edit || state->show_search) - { - if (strlcpy(state->error, errors[ERR_UNKNOWN_KEY], MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } - } + STRLCPY(errors[ERR_UNKNOWN_KEY], state->error, MAXBUFLINE); else - { - if (strlcpy(state->error, errors[ERR_UNKNOWN_KEY_HELP], - MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } - } + STRLCPY(errors[ERR_UNKNOWN_KEY_HELP], state->error, MAXBUFLINE); } int @@ -688,12 +691,8 @@ cancel_close(struct DrawState* state) cancel_msgstr(state); else if (state->show_search) cancel_search(state); - else if (strlcpy(state->error, errors[ERR_EXIT_KEY], MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + else + STRLCPY(errors[ERR_EXIT_KEY], state->error, MAXBUFLINE); return 0; } @@ -704,12 +703,7 @@ cancel_msgstr(struct DrawState* state) if (!state->in_prompt && state->dirty) { state->in_prompt = 1; - if (strlcpy(state->prompt, prompt_cancel_dirty, MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY(prompt_cancel_dirty, state->prompt, MAXBUFLINE); state->prompt_callback = cancel_callback; return 0; } @@ -763,25 +757,14 @@ copy_msgid_to_input(struct DrawState* state) if (!state->in_prompt && *state->input_buffer->text) { state->in_prompt = 1; - if (strlcpy(state->prompt, prompt_overwrite, MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY(prompt_overwrite, state->prompt, MAXBUFLINE); state->prompt_callback = overwrite_copy_msgid_callback; return 0; } if (state->msgid_number == 1) { - if (strlcpy(state->error, errors[ERR_ILLEGAL_ON_FIRST], - MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY(errors[ERR_ILLEGAL_ON_FIRST], state->error, MAXBUFLINE); return 0; } @@ -943,13 +926,8 @@ erase_forward(struct DrawState* state) { if (!join_lines(state, state->input_row, 1)) { - if (strlcpy(state->error, errors[ERR_CANT_ALLOC], - MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY(errors[ERR_CANT_ALLOC], state->error, + MAXBUFLINE); state->running = 0; } } @@ -1149,23 +1127,13 @@ exit_program(struct DrawState* state) { if (state->show_edit) { - if (strlcpy(state->error, errors[ERR_DLG_OPEN], MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY(errors[ERR_DLG_OPEN], state->error, MAXBUFLINE); return 0; } if (state->dirty) { state->in_prompt = 1; - if (strlcpy(state->prompt, prompt_dirty, MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY(prompt_dirty, state->prompt, MAXBUFLINE); state->prompt_callback = quit_callback; } else @@ -1195,12 +1163,7 @@ insert_line(struct DrawState* state) state->input_rows_count * sizeof(struct BufferLine)); if (!newbuf) { - if (strlcpy(state->error, errors[ERR_CANT_ALLOC], MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY(errors[ERR_CANT_ALLOC], state->error, MAXBUFLINE); state->running = 0; return 0; } @@ -1210,12 +1173,7 @@ insert_line(struct DrawState* state) uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); if (!newtext) { - if (strlcpy(state->error, errors[ERR_CANT_ALLOC], MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY(errors[ERR_CANT_ALLOC], state->error, MAXBUFLINE); state->running = 0; return 0; } @@ -1796,25 +1754,14 @@ paste_paste_buffer_to_input(struct DrawState* state) if (!state->in_prompt && *state->input_buffer->text) { state->in_prompt = 1; - if (strlcpy(state->prompt, prompt_overwrite, MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY(prompt_overwrite, state->prompt, MAXBUFLINE); state->prompt_callback = overwrite_paste_callback; return 0; } if (state->msgid_number == 1) { - if (strlcpy(state->error, errors[ERR_ILLEGAL_ON_FIRST], - MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY(errors[ERR_ILLEGAL_ON_FIRST], state->error, MAXBUFLINE); return 0; } @@ -1939,13 +1886,8 @@ save_msgstr(struct DrawState* state) to_save = calloc(to_save_size, sizeof(uint32_t)); if (!to_save) { - if (strlcpy(state->error, errors[ERR_CANT_ALLOC], - MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY(errors[ERR_CANT_ALLOC], state->error, + MAXBUFLINE); return 0; } struct BufferLine* pinput_buffer = NULL; @@ -1992,12 +1934,7 @@ show_edit(struct DrawState* state) { if (!load_msgstr(state) || !load_info(state)) { - if (strlcpy(state->error, errors[ERR_CANT_ALLOC], MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY(errors[ERR_CANT_ALLOC], state->error, MAXBUFLINE); state->running = 0; return 0; } @@ -2045,13 +1982,7 @@ toggle_fuzzy(struct DrawState* state) { if (state->msgid_number == 1) { - if (strlcpy(state->error, errors[ERR_ILLEGAL_ON_FIRST], - MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY(errors[ERR_ILLEGAL_ON_FIRST], state->error, MAXBUFLINE); return 0; } if (state->msgid_count > 0) @@ -2093,13 +2024,7 @@ yank_input_to_paste_buffer(struct DrawState* state) if (state->msgid_number == 1) { - if (strlcpy(state->error, errors[ERR_ILLEGAL_ON_FIRST], - MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY(errors[ERR_ILLEGAL_ON_FIRST], state->error, MAXBUFLINE); return 0; } @@ -2175,30 +2100,15 @@ write_file(struct DrawState* state) switch (result) { case SAVE_ERR_CANT_ALLOC: - if (strlcpy(state->error, errors[ERR_CANT_ALLOC], MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY(errors[ERR_CANT_ALLOC], state->error, MAXBUFLINE); state->running = 0; break; case SAVE_ERR_CANT_OPEN_FILE: - if (strlcpy(state->error, errors[ERR_CANT_SAVE], MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY(errors[ERR_CANT_SAVE], state->error, MAXBUFLINE); state->running = 0; break; case SAVE_ERR_CANT_MOVE_FILE: - if (strlcpy(state->error, strerror(errno), MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY(strerror(errno), state->error, MAXBUFLINE); state->running = 0; break; default: @@ -2218,11 +2128,24 @@ main(int argc, char** argv) char filename[MAXPATH]; int recalculate_size = 1; struct stat st; + long lineno; + long col; + int result; + int dir_access; + int dir_access_errno; + char* dir = NULL; *error = 0; *prompt = 0; *filename = 0; +#ifdef __OpenBSD__ + if (pledge("stdio tty unveil rpath wpath cpath fattr", NULL) < 0) + { + perror("sled: pledge"); + exit(1); + } +#endif if (argc == 1) return print_error(1, "No filename given"); @@ -2281,9 +2204,117 @@ main(int argc, char** argv) init_drawstate(&state, error, filename, prompt); - long lineno = 1; - long col = 1; - int result = load_file(&state, &lineno, &col); +#ifdef __OpenBSD__ + if (unveil(state.filename, "rwc") < 0) + { + perror("sled: unveil"); + exit(1); + } +#endif + + dir = dirname(state.filename); +#ifdef __OpenBSD__ + if (unveil(dir, "rwc") < 0) + { + perror("sled: unveil"); + exit(1); + } +#endif +#ifdef CREATE_BACKUPS + errno = 0; + if (lstat(state.filename, &st) != -1) + { + if (S_ISLNK(st.st_mode)) + { + if (!(state.real_filename = calloc(MAXPATH, 1))) + return print_error(1, + "Allocation failed (out of memory?)"); + state.rfn_len = readlink(state.filename, + state.real_filename, MAXPATH); + if (state.rfn_len == -1) + return print_error(1, strerror(errno)); + state.real_filename[state.rfn_len] = 0; +#ifdef __OpenBSD__ + if (unveil(state.real_filename, "rwc") < 0) + { + perror("sled: unveil"); + exit(1); + } +#endif + if (stat(state.real_filename, &st) == -1) + return print_error(1, strerror(errno)); + } + if (!(state.backup_filename = calloc(MAXPATH, 1))) + return print_error(1, + "Allocation failed (out of memory?)"); + + /* Backup filename is the target file + suffix if the file to + * write is a symlink, otherwise file + suffix */ + STRLCPY(state.rfn_len > 0 ? state.real_filename : state.filename, + state.backup_filename, MAXPATH); + STRLCAT(backup_suffix, state.backup_filename, MAXPATH); +#ifdef __OpenBSD__ + if (unveil(state.backup_filename, "rwc") < 0) + { + perror("sled: unveil"); + exit(1); + } +#endif + } + else + return print_error(1, strerror(errno)); +#endif /* CREATE_BACKUPS */ + + if (state.rfn_len > 0) + { + dir = dirname(state.real_filename); +#ifdef __OpenBSD__ + if (unveil(dir, "rwc") < 0) + { + perror("sled: unveil"); + exit(1); + } +#endif + } + + dir_access = access(dir, W_OK | X_OK); + dir_access_errno = errno; +#ifdef BACKUP_FAIL_OK + state.backup_possible = dir_access != -1; +#else /* !BACKUP_FAIL_OK */ + if (dir_access == -1) + { + errno = dir_access_errno; + goto file_error; + } +#endif /* BACKUP_FAIL_OK */ + if (stat(state.filename, &st) == -1) + { + errno = dir_access_errno; + goto file_error; + } + + if (!S_ISREG(st.st_mode)) + return print_error(EINVAL, "Is not a regular file"); + +#ifdef __OpenBSD__ + if (unveil(NULL, NULL) < 0) + { + perror("sled: unveil"); + exit(1); + } +#endif + + if (access(state.filename, F_OK | R_OK | W_OK) == -1) + goto file_error; + + if (state.rfn_len > 0 + && access(state.real_filename, F_OK | R_OK | W_OK) == -1) + goto file_error; + + lineno = 1; + col = 1; + result = load_file(&state, &lineno, &col); switch (result) { case LOAD_ERR_CANT_ALLOC: @@ -2307,7 +2338,13 @@ main(int argc, char** argv) } state.msgid_number = state.msgid_count > 0 ? 1 : 0; state.first_shown_msgid = state.msgid_number; + goto init_termbox; +file_error: + perror(program_name); + return errno; + +init_termbox: tb_init(); *error = 0; state.maxx = 0; @@ -2327,13 +2364,7 @@ main(int argc, char** argv) state.maxy = tb_height(); if (!draw(&state)) - if (strlcpy(state.error, errors[ERR_CANT_ALLOC], - MAXBUFLINE) - >= MAXBUFLINE) - { - print_error(1, "strlcpy:%d: Overflow", __LINE__); - exit(1); - } + STRLCPY(errors[ERR_CANT_ALLOC], state.error, MAXBUFLINE); tb_present(); result = tb_poll_event(&ev); if (result == TB_OK && ev.type == TB_EVENT_RESIZE) diff --git a/util.h b/util.h @@ -8,6 +8,24 @@ #define LEN(x) (sizeof(x) / sizeof(x[0])) #define MAX(a, b) ((a > b) ? a : b) #define MIN(a, b) ((a < b) ? a : b) +#define STRLCAT(tail, head, totallen) \ + do \ + { \ + if (strlcat(head, tail, totallen) >= totallen) \ + { \ + print_error(1, "strlcat:%d: Overflow", __LINE__); \ + exit(1); \ + } \ + } while (0) +#define STRLCPY(from, to, tolen) \ + do \ + { \ + if (strlcpy(to, from, tolen) >= tolen) \ + { \ + print_error(1, "strlcpy:%d: Overflow", __LINE__); \ + exit(1); \ + } \ + } while (0) enum { ERR_UNKNOWN_KEY, @@ -36,13 +54,8 @@ enum { extern int utlerrno; -#ifndef strlcpy size_t strlcpy(char* dst, const char* src, size_t dsize); -#endif -#ifndef strlcat size_t strlcat(char* dst, const char* src, size_t dsize); -#endif - size_t u8_string_to_unicode(uint32_t* us, const char* s, const size_t max); size_t unicode_string_to_u8(char* s, const size_t s_size, const uint32_t* us, const size_t max);