poe

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

чување 61f8bd03e533f48a6d2883f733254fc8c711468d
родитељ 8c6c68719557ac042e43fda7974ecdd5478ad8ae
Аутор: Страхиња Радић <sr@strahinja.org>
Датум:   Tue, 17 Feb 2026 21:16:11 +0100

Continue WIP; str* -> memccpy; Add -B -b -L -W -w

Diffstat:
MTODO | 19+++++++++++++++----
Mconfig.def.h | 39++++++++++++++++++++++++++++++---------
Mdraw.c | 49++++++++++++++++++++++++++++++++++++++++---------
Mdraw.h | 13+++++++------
Mlimit.h | 4+++-
Mpo.c | 551+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mpo.h | 52+++++++++++++++++-----------------------------------
Mpoe.1.in | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mpoe.c | 1474++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mpoe.h | 26+++++++++++++++++++-------
Mtermbox.h | 4++++
Mutil.c | 60++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Mutil.h | 123+++++++++++++++++++++++++++++++++++--------------------------------------------
измењених датотека: 13, додавања: 1477(+), брисања: 994(-)

diff --git a/TODO b/TODO @@ -4,12 +4,23 @@ TODO [ ] Support Yudit's nonstandard PO format (no empty lines between individual entries) -[ ] Tidy up basic logic everywhere (based on sled) +[~] Tidy up basic logic everywhere (based on sled) [ ] Check cleanness against sled, function-by-function - Last: erase_search_prev_word - [ ] str* -> memccpy + Last: save_msgstr + [ ] Investigate insert_char + [ ] Revise dirty <-> dirty_before_edit and the interaction with keys + [x] Copy join_lines from sled's simple_join_lines + [x] str* -> memccpy [ ] Tidy up return codes and error handling - [ ] Introduce position_to (sled) + [ ] Get rid of SAVE_* + [x] Introduce position_to (sled) + +[x] Add -B, -b, -L, -W, -w + [x] --no-backup + [x] --backup + [x] --dont-break-literals + [x] --no-wrap + [x] --wrap-width < > Wrap long lines? Not if it would complicate the code too much. diff --git a/config.def.h b/config.def.h @@ -69,11 +69,12 @@ static const struct Color colors[] = { extern int backup_fail; extern char* backup_suffix; +extern int break_literals; extern int create_backups; +extern int literal_break_threshold; extern int match_msgstr_ending; extern char* plural_string; extern int warn_count_dots; -extern int wrap_first_msgstr; extern int wrap_width; #ifdef POE_C_GLOBALS @@ -83,7 +84,14 @@ extern int wrap_width; * directory permissions? */ int backup_fail = 1; char* backup_suffix = "~"; +int break_literals = 1; /* break long literals into an empty string + a + * literal on a separate line */ int create_backups = 1; /* comment out to not create backups */ +int literal_break_threshold + = 80; /* if "msgid: \"" (or msgstr, etc) + literal are + * longer than this, the string will be split + * into an empty string + a literal on a + * separate line */ int match_msgstr_ending = 1; /* correct the ending of msgstr to * match msgid if ' ', '.' or '\\n'? */ /* Uncomment/comment the appropriate definition to insert this after @@ -94,8 +102,7 @@ char* plural_string = NULL; /*char* plural_string = "nplurals=3; plural=(n%10==1 && n%100!=11 ?" " 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);";*/ int warn_count_dots = 0; -int wrap_first_msgstr = 0; -int wrap_width = 70; +int wrap_width = 77; /* set to 0 to turn off wrap */ /* Always processed */ static const struct Key top_level_keys[] = { @@ -114,9 +121,11 @@ static const struct Key edit_keys[] = { .callback = move_right }, { .ev = { .key = TB_KEY_ARROW_UP }, .callback = move_up }, - { .ev = { .key = TB_KEY_BACKSPACE }, + { .ev = { .key = TB_KEY_BACKSPACE, + .mod = TB_MOD_CTRL }, .callback = erase_backwards }, - { .ev = { .key = TB_KEY_BACKSPACE2 }, + { .ev = { .key = TB_KEY_BACKSPACE2, + .mod = TB_MOD_CTRL }, .callback = erase_backwards }, { .ev = { .key = TB_KEY_CTRL_A, .mod = TB_MOD_CTRL }, .callback = move_start }, @@ -147,7 +156,9 @@ static const struct Key edit_keys[] = { { .ev = { .key = TB_KEY_DELETE }, .callback = erase_forward }, { .ev = { .key = TB_KEY_END }, .callback = move_end }, - { .ev = { .key = TB_KEY_ENTER }, + /* Enter = C-M, hence the TB_MOD_CTRL; tb doesn't make a difference + * between C-Enter and Enter... */ + { .ev = { .key = TB_KEY_ENTER, .mod = TB_MOD_CTRL }, .callback = insert_line }, { .ev = { .key = TB_KEY_F5 }, .callback = copy_msgid_to_input }, { .ev = { .key = TB_KEY_F6 }, .callback = toggle_info_focused }, @@ -158,7 +169,10 @@ static const struct Key edit_keys[] = { { .ev = { .key = TB_KEY_PGUP }, .callback = edit_page_up }, { .ev = { .key = TB_KEY_SPACE }, .callback = insert_space }, - { .ev = { .key = TB_KEY_TAB }, .callback = insert_tab }, + /* Tab = C-I, hence the TB_MOD_CTRL; tb doesn't make a difference + * between C-I and Tab... */ + { .ev = { .key = TB_KEY_TAB, .mod = TB_MOD_CTRL }, + .callback = insert_tab }, }; /* Search box open */ @@ -167,9 +181,11 @@ static const struct Key search_keys[] = { .callback = move_search_left }, { .ev = { .key = TB_KEY_ARROW_RIGHT }, .callback = move_search_right }, - { .ev = { .key = TB_KEY_BACKSPACE, .mod = TB_MOD_CTRL }, + { .ev = { .key = TB_KEY_BACKSPACE, + .mod = TB_MOD_CTRL }, .callback = erase_search_backwards }, - { .ev = { .key = TB_KEY_BACKSPACE2, .mod = TB_MOD_CTRL }, + { .ev = { .key = TB_KEY_BACKSPACE2, + .mod = TB_MOD_CTRL }, .callback = erase_search_backwards }, { .ev = { .key = TB_KEY_CTRL_A, .mod = TB_MOD_CTRL }, .callback = move_search_start }, @@ -205,6 +221,11 @@ static const struct Key search_keys[] = { .callback = insert_search_tab }, }; +/* Help dialog */ +static const struct Key help_keys[] = { + { .ev = { .ch = 'q' }, .callback = exit_program }, +}; + /* Main window */ static const struct Key main_keys[] = { { .ev = { .key = TB_KEY_ARROW_DOWN }, diff --git a/draw.c b/draw.c @@ -40,6 +40,8 @@ static const char* help[] = { static const char* editbox_title = "Edit translation"; const char* errors[] = { [ERR_NO_CL_ARG] = "No filename given", + [ERR_NO_CL_ARG_PARAM] = "Option requires an argument: %s", + [ERR_BAD_CL_ARG] = "Invalid argument: %s", [ERR_NOTPO_FILE] = "Not a PO file", [ERR_NOTREG_FILE] = "'%s' is not a regular file", [ERR_TB_ERROR] = "termbox error", @@ -48,7 +50,7 @@ const char* errors[] = { [ERR_EXIT_KEY] = "Press q or C-Q to quit", [ERR_DLG_OPEN] = "Press Esc to close dialog or q/C-Q to quit", [ERR_DLG_OPEN_QUIT] = "Press Esc to close dialog first", - [ERR_DLG_FOCUS_EDIT] = "Switch to edit box first with F6", + [ERR_DLG_FOCUS_EDIT] = "Switch to edit box with F6 or press Esc", [ERR_EMPTY_FILE] = "No msgids to edit", [ERR_CANT_ALLOC] = "Memory allocation failed", [ERR_CANT_SAVE] = "File is not writeable", @@ -79,6 +81,35 @@ assign_coord(struct Coord* dest, const struct Coord src) init_coord(dest, src.col, src.row); } +/* Calculates buffer length from a display length. + * max_index = the display index up to which the length is calculated + */ +ssize_t +buffer_length(uint32_t* buffer, const ssize_t max_index) +{ + uint32_t* pbuf = buffer; + ssize_t len = 0; + ssize_t i; + int w; + + if (!pbuf) + return 0; + + for (i = 0; *pbuf && len < max_index; i++) + { + pbuf = buffer + i; + if (*pbuf == '\t') + len += MIN(TAB_SIZE - (len % TAB_SIZE), max_index - len); + else + { + w = wcwidth((wchar_t)*pbuf); + if (w > 0) + len += MIN(w, max_index - len); + } + } + return i; +} + int calculate_edit_width(const struct DrawState* state) { @@ -224,8 +255,8 @@ draw_editbox(const struct DrawState* state) { struct PoEntry* entry; char flags_buf[MAXFLAGSBUF]; - char sbuf[MAXBUFLINE]; - ssize_t dlen; + char sbuf[LINE_DEFAULT]; + ssize_t dlen, i; uint16_t fg, bg; int startx, endx, starty, endy; int w, h; @@ -237,7 +268,6 @@ draw_editbox(const struct DrawState* state) maxx = state->maxx; maxy = state->maxy; entry = state->entries + state->msgid_number - 1; - dlen = 0; format_flags(flags_buf, MAXFLAGSBUF, entry, 1); @@ -259,7 +289,7 @@ draw_editbox(const struct DrawState* state) : colors[C_EDIT_MSGID].bg; draw_rect(startx + 1, endx - 1, starty + 1, endy - h / 2 - 1, fg, bg); - for (ssize_t i = 0; i < (h - 3) / 2 + 1 + for (i = 0; i < (h - 3) / 2 + 1 && (ssize_t)state->info_first_shown.row + i < state->info_rows_count; i++) @@ -313,13 +343,14 @@ draw_editbox(const struct DrawState* state) - state->input_first_shown.row); if (entry->msgstr_count > 1) - snprintf(sbuf, MAXBUFLINE, "%d[%d]:%d[%d]/%ld (plural: %d/%d)", + snprintf(sbuf, LINE_DEFAULT, + "%d[%d]:%d[%d]/%ld (plural: %d/%d)", state->input_display_column, state->input_first_shown.col, state->coord.row, state->input_first_shown.row, state->input_rows_count, state->msgstr_index + 1, entry->msgstr_count); else - snprintf(sbuf, MAXBUFLINE, "%d[%d]:%d[%d]/%ld", + snprintf(sbuf, LINE_DEFAULT, "%d[%d]:%d[%d]/%ld", state->input_display_column, state->input_first_shown.col, state->coord.row, state->input_first_shown.row, state->input_rows_count); @@ -702,7 +733,7 @@ init_drawstate(struct DrawState* state, char* filename, char* prompt) /*state->info_first_shown_column = 0; state->info_first_shown_row = 0;*/ state->info_rows_count = 0; - state->info_rows_maxlen = 0; + state->info_maxlen = 0; state->help_width = calculate_help_width(state); state->edit_width = calculate_edit_width(state); state->search_width = calculate_search_width(state); @@ -801,7 +832,7 @@ set_nplurals(struct DrawState* state, struct PoEntry* entry) int read_number = 0; uint32_t* ppf = entry->plural_forms; - char token[MAXBUFLINE]; + char token[LINE_DEFAULT]; char* ptoken = token; *ptoken = 0; diff --git a/draw.h b/draw.h @@ -26,7 +26,7 @@ #define SEARCH_WIDTH 20 #define SEARCH_HEIGHT 4 #define TAB_SIZE 8 -#define MAXSEARCH (512 + 1) +#define MAXSEARCH 512 typedef enum { LEFT, CENTER, RIGHT } Alignment; @@ -96,7 +96,7 @@ struct DrawState { int error; int action; struct BufferLine* input_buffer; /* edit box */ - ssize_t buffer_size; /* buffer parameter */ + ssize_t buffer_size; /* buffer parameter */ struct Coord coord; /* buffer coordinates */ struct Coord first_shown; /* display coordinates */ struct Coord input_first_shown; /* display coordinates */ @@ -114,10 +114,10 @@ struct DrawState { /*int info_first_shown_column; int info_first_shown_row;*/ int info_rows_count; - int info_rows_maxlen; - int help_width; /* display parameter */ - int edit_width; /* display parameter */ - int search_width; /* display parameter */ + int info_maxlen; + int help_width; /* display parameter */ + int edit_width; /* display parameter */ + int search_width; /* display parameter */ struct BufferLine* paste; ssize_t paste_count; ssize_t paste_size; @@ -166,6 +166,7 @@ struct StatusSegment { }; void assign_coord(struct Coord* dest, const struct Coord src); +ssize_t buffer_length(uint32_t* buffer, const ssize_t max_index); int calculate_edit_width(const struct DrawState* state); int calculate_help_width(const struct DrawState* state); int calculate_search_width(const struct DrawState* state); diff --git a/limit.h b/limit.h @@ -20,7 +20,9 @@ extern long limits[]; /* initial line size (not length) */ #define LINE_DEFAULT 85 /* maximum size of field in input dialog */ -#define MAXINPUT (512 + 1) +#define MAXINPUT 512 +/* fallback buffer size before we determine limits */ +#define PRINT_BUF_MAX_FALLBACK 2048 /* maximum chars for UTF-8 representations of Unicode chars, per Unicode char */ #define UTF8REPMAX 6 diff --git a/po.c b/po.c @@ -20,8 +20,6 @@ extern const char* errors[]; extern const char* flag_strings[]; -// TODO: Check ifdef PLURAL_ ... - extern int cp(const char* from, const char* to); extern int print_error(const int code, const char* msg, ...); extern void redraw_status(struct DrawState* state); @@ -118,94 +116,102 @@ set_flags(struct PoEntry* entry, const Flags flags) return entry; } -struct PoEntry* -set_msgid(struct PoEntry* entry, const char* msgid, int append) +int +set_msgid(struct DrawState* state, struct PoEntry* entry, const char* msgid, + int append) { uint32_t umsgid[MAXMSGLINE]; - size_t msgid_size = 0; + size_t msgid_len = 0; assert(entry != NULL); if (msgid) - msgid_size = u8_string_to_unicode(umsgid, msgid, MAXMSGLINE); - return u32_set_msgid(entry, umsgid, msgid_size, append); + msgid_len = u8_string_to_unicode(umsgid, msgid, MAXMSGLINE); + return u32_set_msgid(state, entry, umsgid, msgid_len, append); } -struct PoEntry* -set_msgid_plural(struct PoEntry* entry, const char* msgid_plural, int append) +int +set_msgid_plural(struct DrawState* state, struct PoEntry* entry, + const char* msgid_plural, int append) { uint32_t umsgid_plural[MAXMSGLINE]; - size_t msgid_plural_size = 0; + size_t msgid_plural_len = 0; assert(entry != NULL); if (msgid_plural) - msgid_plural_size = u8_string_to_unicode(umsgid_plural, + msgid_plural_len = u8_string_to_unicode(umsgid_plural, msgid_plural, MAXMSGLINE); - return u32_set_msgid_plural(entry, umsgid_plural, msgid_plural_size, - append); + return u32_set_msgid_plural(state, entry, umsgid_plural, + msgid_plural_len, append); } -struct PoEntry* -set_plural_forms(struct PoEntry* entry, const char* plural_forms, int append) +int +set_plural_forms(struct DrawState* state, struct PoEntry* entry, + const char* plural_forms, int append) { uint32_t uplural_forms[MAXMSGLINE]; + size_t plural_forms_len = 0; assert(entry != NULL); if (plural_forms) - u8_string_to_unicode(uplural_forms, plural_forms, MAXMSGLINE); - return u32_set_plural_forms(entry, uplural_forms, append); + plural_forms_len = u8_string_to_unicode(uplural_forms, + plural_forms, MAXMSGLINE); + return u32_set_plural_forms(state, entry, uplural_forms, + plural_forms_len, append); } -struct PoEntry* -set_msgstr(struct PoEntry* entry, const char* msgstr, int msgstr_index, - int append, struct DrawState* state) +int +set_msgstr(struct DrawState* state, struct PoEntry* entry, const char* msgstr, + int msgstr_index, int append) { uint32_t umsgstr[MAXMSGLINE]; assert(entry != NULL); umsgstr[0] = 0; if (msgstr) u8_string_to_unicode(umsgstr, msgstr, MAXMSGLINE); - return u32_set_msgstr(entry, umsgstr, msgstr_index, append, state); + return u32_set_msgstr(state, entry, umsgstr, msgstr_index, append); } -struct PoEntry* -u32_set_msgid(struct PoEntry* entry, const uint32_t* msgid, size_t msgid_size, - int append) +int +u32_set_msgid(struct DrawState* state, struct PoEntry* entry, + const uint32_t* msgid, size_t msgid_len, int append) { - size_t msgid_len = 0; - /* TODO: Unused? */ - UNUSED(msgid_size); - if (!entry) - return NULL; - if (msgid) - msgid_len = u32_strlen(msgid); + uint32_t* temp = NULL; + + assert((state != NULL) && (entry != NULL)); if (!entry->msgid) { entry->msgid_size = MSGDELTA; entry->msgid_len = 0; entry->msgid = calloc(entry->msgid_size, sizeof(uint32_t)); + if (!entry->msgid) + goto u32_set_msgid_alloc_error; } else if (entry->msgid_len + msgid_len >= entry->msgid_size) { entry->msgid_size += MSGDELTA; - entry->msgid = realloc(entry->msgid, + temp = realloc(entry->msgid, entry->msgid_size * sizeof(uint32_t)); + if (!temp) + goto u32_set_msgid_alloc_error; + entry->msgid = temp; } entry->msgid_len += msgid_len; - if (!entry->msgid) - return NULL; if (append) - u32_strncat(entry->msgid, msgid, entry->msgid_size); + U32_MEMCCPY(entry->msgid + entry->msgid_len - msgid_len, msgid, + entry->msgid_size - msgid_len, temp); else - u32_strncpy(entry->msgid, msgid, entry->msgid_size); - return entry; + U32_MEMCCPY(entry->msgid, msgid, entry->msgid_size, temp); + return 0; + +u32_set_msgid_alloc_error: + state->error = ERR_CANT_ALLOC; + return 1; } -struct PoEntry* -u32_set_msgid_plural(struct PoEntry* entry, const uint32_t* msgid_plural, - size_t msgid_plural_size, int append) +int +u32_set_msgid_plural(struct DrawState* state, struct PoEntry* entry, + const uint32_t* msgid_plural, size_t msgid_plural_len, int append) { - size_t msgid_plural_len = 0; - /* TODO: Unused? */ - UNUSED(msgid_plural_size); - if (!entry) - return NULL; + uint32_t* temp = NULL; + + assert((state != NULL) && (entry != NULL)); if (msgid_plural) msgid_plural_len = u32_strlen(msgid_plural); if (!entry->msgid_plural) @@ -214,52 +220,86 @@ u32_set_msgid_plural(struct PoEntry* entry, const uint32_t* msgid_plural, entry->msgid_plural_len = 0; entry->msgid_plural = calloc(entry->msgid_plural_size, sizeof(uint32_t)); + if (!entry->msgid_plural) + goto u32_set_msgid_plural_alloc_error; } else if (entry->msgid_plural_size + msgid_plural_len >= entry->msgid_plural_size) { entry->msgid_plural_size += MSGDELTA; - entry->msgid_plural = realloc(entry->msgid_plural, + temp = realloc(entry->msgid_plural, entry->msgid_plural_size * sizeof(uint32_t)); + if (!temp) + goto u32_set_msgid_plural_alloc_error; + entry->msgid_plural = temp; } entry->msgid_plural_len += msgid_plural_len; - if (!entry->msgid_plural) - return NULL; if (append) - u32_strncat(entry->msgid_plural, msgid_plural, - entry->msgid_plural_size); + U32_MEMCCPY(entry->msgid_plural + entry->msgid_plural_len + - msgid_plural_len, + msgid_plural, + entry->msgid_plural_size - msgid_plural_len, temp); else - u32_strncpy(entry->msgid_plural, msgid_plural, - entry->msgid_plural_size); - return entry; + U32_MEMCCPY(entry->msgid_plural, msgid_plural, + entry->msgid_plural_size, temp); + return 0; + +u32_set_msgid_plural_alloc_error: + state->error = ERR_CANT_ALLOC; + return 1; } -struct PoEntry* -u32_set_plural_forms(struct PoEntry* entry, const uint32_t* plural_forms, - int append) +int +u32_set_plural_forms(struct DrawState* state, struct PoEntry* entry, + const uint32_t* plural_forms, const size_t plural_forms_len, int append) { - if (!entry) - return NULL; + uint32_t* temp = NULL; + + assert((state != NULL) && (entry != NULL)); + if (plural_forms_len >= MSGDELTA) + return 1; if (!entry->plural_forms) { - entry->plural_forms = calloc(MAXMSGLINE, sizeof(uint32_t)); + entry->plural_forms_size = MSGDELTA; + entry->plural_forms_len = 0; + entry->plural_forms = calloc(MAXMSGLINE, sizeof(uint32_t)); if (!entry->plural_forms) - return NULL; + goto u32_set_plural_forms_alloc_error; + } + else if (entry->plural_forms_size + plural_forms_len + >= entry->plural_forms_size) + { + entry->plural_forms_size += MSGDELTA; + temp = realloc(entry->plural_forms, + entry->plural_forms_size * sizeof(uint32_t)); + if (!temp) + goto u32_set_plural_forms_alloc_error; + entry->plural_forms = temp; } + entry->plural_forms_len += plural_forms_len; if (append) - u32_strncat(entry->plural_forms, plural_forms, MAXMSGLINE); + U32_MEMCCPY(entry->plural_forms + entry->plural_forms_size + - plural_forms_len, + plural_forms, + entry->plural_forms_size - plural_forms_len, temp); else - u32_strncpy(entry->plural_forms, plural_forms, MAXMSGLINE); - return entry; + U32_MEMCCPY(entry->plural_forms, plural_forms, + entry->plural_forms_size, temp); + return 0; + +u32_set_plural_forms_alloc_error: + state->error = ERR_CANT_ALLOC; + return 1; } -struct PoEntry* -u32_set_msgstr(struct PoEntry* entry, const uint32_t* msgstr, int msgstr_index, - int append, struct DrawState* state) +int +u32_set_msgstr(struct DrawState* state, struct PoEntry* entry, + const uint32_t* msgstr, int msgstr_index, int append) { + uint32_t* temp = NULL; size_t msgstr_len = 0; - if (!entry) - return NULL; + + assert((state != NULL) && (entry != NULL)); if (msgstr) msgstr_len = u32_strlen(msgstr); if (entry->msgstr_count == 0 || msgstr_index + 1 > entry->msgstr_count) @@ -272,10 +312,16 @@ u32_set_msgstr(struct PoEntry* entry, const uint32_t* msgstr, int msgstr_index, { newmsgstr = calloc(entry->msgstr_count, sizeof(uint32_t*)); + if (!newmsgstr) + goto u32_set_msgstr_alloc_error; newmsgstr_size = calloc(entry->msgstr_count, sizeof(size_t)); + if (!newmsgstr_size) + goto u32_set_msgstr_alloc_error; newmsgstr_len = calloc(entry->msgstr_count, sizeof(size_t)); + if (!newmsgstr_len) + goto u32_set_msgstr_alloc_error; /* when the .po file is not a standard text file (no LF * at end) */ if (state->untranslated_count > 0) @@ -283,32 +329,33 @@ u32_set_msgstr(struct PoEntry* entry, const uint32_t* msgstr, int msgstr_index, } else { - newmsgstr = realloc(entry->msgstr, - entry->msgstr_count * sizeof(uint32_t*)); + newmsgstr = realloc(entry->msgstr, + entry->msgstr_count * sizeof(uint32_t*)); + if (!newmsgstr) + goto u32_set_msgstr_alloc_error; newmsgstr_size = realloc(entry->msgstr_size, entry->msgstr_count * sizeof(size_t)); - newmsgstr_len = realloc(entry->msgstr_len, - entry->msgstr_count * sizeof(size_t)); - } - if (!newmsgstr || !newmsgstr_size || !newmsgstr_len) - { - /*free(newmsgstr); - free(newmsgstr_len); - free(newmsgstr_size);*/ - return NULL; + if (!newmsgstr_size) + goto u32_set_msgstr_alloc_error; + newmsgstr_len = realloc(entry->msgstr_len, + entry->msgstr_count * sizeof(size_t)); + if (!newmsgstr_len) + goto u32_set_msgstr_alloc_error; } newmsgstr[msgstr_index] = NULL; - entry->msgstr = newmsgstr; - entry->msgstr_size = newmsgstr_size; - entry->msgstr_len = newmsgstr_len; + entry->msgstr = newmsgstr; + entry->msgstr_size = newmsgstr_size; + entry->msgstr_len = newmsgstr_len; } if (!entry->msgstr[msgstr_index]) { entry->msgstr_size[msgstr_index] = MSGDELTA; - entry->msgstr_len[msgstr_index] = 0; + entry->msgstr_len[msgstr_index] = 0; entry->msgstr[msgstr_index] = calloc(entry->msgstr_size[msgstr_index], sizeof(uint32_t)); + if (!entry->msgstr[msgstr_index]) + goto u32_set_msgstr_alloc_error; } else if (entry->msgstr_len[msgstr_index] + msgstr_len >= entry->msgstr_size[msgstr_index]) @@ -318,17 +365,23 @@ u32_set_msgstr(struct PoEntry* entry, const uint32_t* msgstr, int msgstr_index, = realloc(entry->msgstr[msgstr_index], entry->msgstr_size[msgstr_index] * sizeof(uint32_t)); + if (!entry->msgstr[msgstr_index]) + goto u32_set_msgstr_alloc_error; } entry->msgstr_len[msgstr_index] += msgstr_len; - if (!entry->msgstr[msgstr_index]) - return NULL; if (append) - u32_strncat(entry->msgstr[msgstr_index], msgstr, - entry->msgstr_size[msgstr_index]); + U32_MEMCCPY(entry->msgstr[msgstr_index] + + entry->msgstr_len[msgstr_index] - msgstr_len, + msgstr, entry->msgstr_size[msgstr_index] - msgstr_len, + temp); else - u32_strncpy(entry->msgstr[msgstr_index], msgstr, - entry->msgstr_size[msgstr_index]); - return entry; + U32_MEMCCPY(entry->msgstr[msgstr_index], msgstr, + entry->msgstr_size[msgstr_index], temp); + return 0; + +u32_set_msgstr_alloc_error: + state->error = ERR_CANT_ALLOC; + return 1; } struct PoEntry* @@ -466,7 +519,15 @@ load_file(struct DrawState* dstate, long* lineno, long* col) char* nulbyte = NULL; if (!fgets(input_line, input_size, input)) - continue; + { + if (ferror(input)) + { + fclose(input); + return 1; + } + else + continue; + } if (possible_nulbyte) { @@ -488,8 +549,8 @@ load_file(struct DrawState* dstate, long* lineno, long* col) possible_nulbyte = 1; if (parse_po_line(input_line, dstate, &entry, &pstate, col, - &msgstr_index)) - return 1; + &msgstr_index)) + goto load_file_cleanup; if (entry && entry->msgid_plural) has_plural_msgid = 1; @@ -563,6 +624,8 @@ load_file(struct DrawState* dstate, long* lineno, long* col) } load_file_cleanup: + if (dstate->error == ERR_CANT_ALLOC) + goto load_file_alloc_error; fclose(input); dstate->action = ACT_IDLE; free(input_line); @@ -696,18 +759,18 @@ save_loop_entries_start: if (!current->obsolete && current->comment_lines > 0) if (save_comment(output, state, current)) - return 1; + goto save_file_error_cleanup; if (current->extr_comment_lines > 0) if (save_extr_comment(output, state, current)) - return 1; + goto save_file_error_cleanup; if (current->ref_comment_lines > 0) if (save_ref_comment(output, state, current)) - return 1; + goto save_file_error_cleanup; if (save_flags(output, state, current)) - return 1; + goto save_file_error_cleanup; if (current->obsolete && current->comment_lines > 0) if (save_comment(output, state, current)) - return 1; + goto save_file_error_cleanup; if (current->obsolete) { @@ -720,10 +783,10 @@ save_loop_entries_start: save_msgid(output, state, current); if (current->msgid_plural) if (save_msgid_plural(output, state, current)) - return 1; + goto save_file_error_cleanup; if (save_msgstrs(output, state, current)) - return 1; + goto save_file_error_cleanup; if (current < state->entries + state->real_msgid_count - 1) fprintf(output, "\n"); @@ -735,6 +798,9 @@ save_loop_entries_start: fclose(output); return 1;*/ +save_file_error_cleanup: + result = 1; + save_loop_entries_done: save_file_cleanup: saved_errno = errno; @@ -791,6 +857,7 @@ save_msgid(FILE* output, struct DrawState* state, const struct PoEntry* current) size_t len = 0; size_t num_lines; size_t size; + int first_line = 1; assert((output != NULL) && (state != NULL) && (current != NULL)); if (!current->msgid) @@ -799,22 +866,24 @@ save_msgid(FILE* output, struct DrawState* state, const struct PoEntry* current) return 0; } num_lines = u32_lines_in_string(current->msgid, 1, wrap_width); - if ((num_lines > 1) - && (u32_strlen(current->msgid) > SAVE_MULTILINE_TRESHOLD)) + || u32_strlen(current->msgid) + strlen("msgid: \"") + 1 + > (size_t)literal_break_threshold) { pumsgid = current->msgid; - fprintf(output, "msgid \"\"\n"); + fprintf(output, "msgid %s", break_literals ? "\"\"\n" : ""); while (*pumsgid) { u8_msgid = NULL; old_pumsgid = pumsgid; - len = 0; len = u32_next_line(current->msgid, &pumsgid, 1, wrap_width); if (len == 0) + { + first_line = 0; continue; + } size = len * UTF8REPMAX + 1; @@ -828,8 +897,11 @@ save_msgid(FILE* output, struct DrawState* state, const struct PoEntry* current) memset(u8_msgid, 0, size); unicode_string_to_u8(u8_msgid, old_pumsgid, len); - fprintf(output, "\"%s\"\n", u8_msgid); + fprintf(output, "%s%s\"\n", + break_literals && first_line ? "" : "\"", + u8_msgid); free(u8_msgid); + first_line = 0; } } else @@ -861,23 +933,29 @@ save_msgid_plural(FILE* output, struct DrawState* state, char* u8_msgid = NULL; size_t len = 0; size_t msgid_plural_len, num_lines, size; + int first_line = 1; assert((output != NULL) && (state != NULL) && (current != NULL)); num_lines = u32_lines_in_string(current->msgid_plural, 1, wrap_width); if ((num_lines > 1) - && (u32_strlen(current->msgid_plural) > SAVE_MULTILINE_TRESHOLD)) + || u32_strlen(current->msgid_plural) + + strlen("msgid_plural: \"") + 1 + > (size_t)literal_break_threshold) { pumsgid = current->msgid_plural; - fprintf(output, "msgid_plural \"\"\n"); + fprintf(output, "msgid_plural %s", + break_literals ? "\"\"\n" : ""); while (*pumsgid) { - len = 0; u8_msgid = NULL; old_pumsgid = pumsgid; len = u32_next_line(current->msgid_plural, &pumsgid, 1, wrap_width); if (len == 0) + { + first_line = 0; continue; + } size = len * UTF8REPMAX + 1; u8_msgid = malloc(size); @@ -890,7 +968,10 @@ save_msgid_plural(FILE* output, struct DrawState* state, memset(u8_msgid, 0, size); unicode_string_to_u8(u8_msgid, old_pumsgid, len); - fprintf(output, "\"%s\"\n", u8_msgid); + fprintf(output, "%s%s\"\n", + break_literals && first_line ? "" : "\"", + u8_msgid); + first_line = 0; free(u8_msgid); } } @@ -930,7 +1011,7 @@ save_msgstrs(FILE* output, struct DrawState* state, int i; int seen_generator = 0; int seen_plural_forms = 0; - int first_entry; + int first_entry, first_line; assert((output != NULL) && (state != NULL) && (current != NULL)); first_entry = (current == state->entries) && !current->msgid; @@ -951,20 +1032,31 @@ save_msgstrs(FILE* output, struct DrawState* state, } for (m = 0; m < current->msgstr_count; m++) { + assert(current->msgstr); num_lines = u32_lines_in_string(current->msgstr[m], 1, wrap_width); if ((num_lines > 1) - && (u32_strlen(current->msgstr[m]) - > SAVE_MULTILINE_TRESHOLD)) + || u32_strlen(current->msgstr[m]) + + (current->msgstr_count == 1 + ? strlen("msgst" + "r: " + "\"") + : strlen("msgst" + "r[ " + "]: " + "\"")) + + 1 + > (size_t)literal_break_threshold) { pumsgstr = current->msgstr[m]; if (current->msgstr_count == 1) - fprintf(output, "msgstr \"\"\n"); + fprintf(output, "msgstr %s", + break_literals ? "\"\"\n" : ""); else - fprintf(output, "msgstr[%ld] \"\"\n", m); + fprintf(output, "msgstr[%ld] %s", m, + break_literals ? "\"\"\n" : ""); save_msgstrs_pumsgstr_loop: - len = 0; old_pumsgstr = pumsgstr; len = u32_next_line(current->msgstr[m], &pumsgstr, 1, wrap_width); @@ -995,8 +1087,11 @@ save_msgstrs(FILE* output, struct DrawState* state, plural_string); } else - fprintf(output, "\"%s\"\n", u8_msgstr); + fprintf(output, "%s%s\"\n", + break_literals && first_line ? "" : "\"", + u8_msgstr); free(u8_msgstr); + first_line = 0; if (*pumsgstr) goto save_msgstrs_pumsgstr_loop; @@ -1281,15 +1376,17 @@ parse_po_line(const char* line, struct DrawState* dstate, } break; case '\\': - if (*pstate & PS_COMMENT) + if ((*pstate & PS_STRING) && *(pline + 1) == '"') { *ptoken++ = *pline++; INC_COL(); + /* Skip parsing '"' */ + *ptoken++ = *pline++; + INC_COL(); } - else if ((*pstate & PS_STRING) && *(pstate + 1)) + else { *ptoken++ = *pline++; - *ptoken++ = *pline++; INC_COL(); } break; @@ -1353,129 +1450,131 @@ parse_po_line(const char* line, struct DrawState* dstate, init_po_entry(*current_entry); *msgstr_index = -1; } - if (*token) + + if (!*token) + goto parse_po_empty_token; + + if (*pstate & PS_MSGID) { - if (*pstate & PS_MSGID) - set_msgid(*current_entry, token, 1); - else if (*pstate & PS_MSGID_PLURAL) - { - set_msgid_plural(*current_entry, token, 1); - if (*current_entry != dstate->entries) - for (int i = 0; i < dstate->nplurals; i++) - if (!set_msgstr(*current_entry, NULL, i, - 0, dstate)) - goto parse_po_line_alloc_error; - } - else if (*pstate & PS_MSGSTR) + if (set_msgid(dstate, *current_entry, token, 1)) + goto parse_po_line_alloc_error; + } + else if (*pstate & PS_MSGID_PLURAL) + { + if (set_msgid_plural(dstate, *current_entry, token, 1)) + goto parse_po_line_alloc_error; + if (*current_entry != dstate->entries) + for (int i = 0; i < dstate->nplurals; i++) + if (set_msgstr(dstate, *current_entry, NULL, i, + 0)) + goto parse_po_line_alloc_error; + } + else if (*pstate & PS_MSGSTR) + { + /* first entry */ + if (*current_entry == dstate->entries) { - /* first entry */ - if (*current_entry == dstate->entries) + if (starts_with(token, "Plural-Forms: ")) { - if (starts_with(token, "Plural-Forms: ")) - { - set_plural_forms(*current_entry, - token + strlen("Plural-Forms: "), - 0); - set_nplurals(dstate, *current_entry); - } + if (set_plural_forms(dstate, *current_entry, + token + strlen("Plural-Forms: "), 0)) + goto parse_po_line_alloc_error; + set_nplurals(dstate, *current_entry); } - - if (!set_msgstr(*current_entry, token, 0, 1, dstate)) - goto parse_po_line_alloc_error; - } - else if (*pstate & PS_MSGSTR_ARRAY) - { - if (!set_msgstr(*current_entry, token, *msgstr_index, 1, - dstate)) - goto parse_po_line_alloc_error; - } - else if (*pstate & PS_COMMENT_TRAN) - { - uint32_t* u32_token = NULL; - size_t len = strlen(token) + 1; - u32_token = calloc(len, sizeof(uint32_t)); - if (!u32_token) - goto parse_po_line_alloc_error; - u8_string_to_unicode(u32_token, token, len); - u32_add_comment(*current_entry, u32_token); - free(u32_token); - } - else if (*pstate & PS_COMMENT_EXTR) - { - uint32_t* u32_token = NULL; - size_t len = strlen(token) + 1; - u32_token = calloc(len, sizeof(uint32_t)); - if (!u32_token) - goto parse_po_line_alloc_error; - u8_string_to_unicode(u32_token, token, len); - u32_add_extr_comment(*current_entry, u32_token); - free(u32_token); } - else if (*pstate & PS_COMMENT_FLAG) + + if (set_msgstr(dstate, *current_entry, token, 0, 1)) + goto parse_po_line_alloc_error; + } + else if (*pstate & PS_MSGSTR_ARRAY) + { + if (set_msgstr(dstate, *current_entry, token, *msgstr_index, 1)) + goto parse_po_line_alloc_error; + } + else if (*pstate & PS_COMMENT_TRAN) + { + uint32_t* u32_token = NULL; + size_t len = strlen(token) + 1; + u32_token = calloc(len, sizeof(uint32_t)); + if (!u32_token) + goto parse_po_line_alloc_error; + u8_string_to_unicode(u32_token, token, len); + u32_add_comment(*current_entry, u32_token); + free(u32_token); + } + else if (*pstate & PS_COMMENT_EXTR) + { + uint32_t* u32_token = NULL; + size_t len = strlen(token) + 1; + u32_token = calloc(len, sizeof(uint32_t)); + if (!u32_token) + goto parse_po_line_alloc_error; + u8_string_to_unicode(u32_token, token, len); + u32_add_extr_comment(*current_entry, u32_token); + free(u32_token); + } + else if (*pstate & PS_COMMENT_FLAG) + { + ptoken = token; + while (*ptoken) { - ptoken = token; - while (*ptoken) + int first = *current_entry == dstate->entries; + if (starts_with(ptoken, flag_strings[FL_C_FORMAT])) { - int first = *current_entry == dstate->entries; - if (starts_with(ptoken, - flag_strings[FL_C_FORMAT])) - { - (*current_entry)->flags |= FL_C_FORMAT; - ptoken += strlen( - flag_strings[FL_C_FORMAT]); - } - else if (starts_with(ptoken, - flag_strings[FL_NO_C_FORMAT])) - { - (*current_entry)->flags - |= FL_NO_C_FORMAT; - ptoken += strlen( - flag_strings[FL_NO_C_FORMAT]); - } - else if (starts_with(ptoken, - flag_strings[FL_FUZZY]) - && !first) - { - (*current_entry)->flags |= FL_FUZZY; - dstate->fuzzy_count++; - ptoken += strlen(flag_strings[FL_FUZZY]); - } - else if (*ptoken == ',' && *(ptoken + 1) == ' ') - ptoken += 2; - else - { - /* Let this slip, ignore everything - * instead; see po.h for rationale */ - while (*ptoken) - ptoken++; - } + (*current_entry)->flags |= FL_C_FORMAT; + ptoken += strlen(flag_strings[FL_C_FORMAT]); + } + else if (starts_with(ptoken, + flag_strings[FL_NO_C_FORMAT])) + { + (*current_entry)->flags |= FL_NO_C_FORMAT; + ptoken += strlen(flag_strings[FL_NO_C_FORMAT]); + } + else if (starts_with(ptoken, flag_strings[FL_FUZZY]) + && !first) + { + (*current_entry)->flags |= FL_FUZZY; + dstate->fuzzy_count++; + ptoken += strlen(flag_strings[FL_FUZZY]); + } + else if (*ptoken == ',' && *(ptoken + 1) == ' ') + ptoken += 2; + else + { + /* Let this slip, ignore + * everything instead; see po.h + * for rationale */ + while (*ptoken) + ptoken++; } } - else if (*pstate & PS_COMMENT_REFN) - add_ref_comment(*current_entry, token); - else if (*pstate & PS_COMMENT_OBSO) - { - uint32_t* u32_token = NULL; - size_t len = strlen(token) + 1; - u32_token = calloc(len, sizeof(uint32_t)); - if (!u32_token) - goto parse_po_line_alloc_error; - u8_string_to_unicode(u32_token, token, len); - if (u32_add_comment(*current_entry, u32_token)) - (*current_entry)->obsolete = 1; - free(u32_token); - } - *token = 0; } - else if (*pstate & PS_COMMENT_TRAN) + else if (*pstate & PS_COMMENT_REFN) + add_ref_comment(*current_entry, token); + else if (*pstate & PS_COMMENT_OBSO) + { + uint32_t* u32_token = NULL; + size_t len = strlen(token) + 1; + u32_token = calloc(len, sizeof(uint32_t)); + if (!u32_token) + goto parse_po_line_alloc_error; + u8_string_to_unicode(u32_token, token, len); + if (u32_add_comment(*current_entry, u32_token)) + (*current_entry)->obsolete = 1; + free(u32_token); + } + *token = 0; + goto parse_po_cleanup; + +parse_po_empty_token: + if (*pstate & PS_COMMENT_TRAN) u32_add_comment(*current_entry, (uint32_t*)L""); else if (*pstate & PS_COMMENT_EXTR) u32_add_extr_comment(*current_entry, (uint32_t*)L""); +parse_po_cleanup: /* one-line states */ *pstate &= ~PS_COMMENT; - -parse_po_cleanup: return !!dstate->error; parse_po_line_alloc_error: diff --git a/po.h b/po.h @@ -7,32 +7,11 @@ #define MSGSTR_MAX 4096 // #define ALLOC_DELTA 10 +/* lines to tolerate the lack of msgid */ #define PO_DETECTION_LINES 24 -/* If msgid/msgstr is longer, it will be saved as multiple lines */ -#define SAVE_MULTILINE_TRESHOLD 70 - -// #define LOAD_PARSE_ERR(e) (LOAD_ERR_PARSE_PREFIX + (e)) struct DrawState; -/*enum { - PARSE_ERR_NONE = 0, - PARSE_ERR_INVAL = 1, - PARSE_ERR_ALLOC = 2, - PARSE_ERR_SYNTAX = 3 -}; - -enum { - LOAD_ERR_NONE = 0, - LOAD_ERR_CANT_ALLOC = 1, - LOAD_ERR_CANT_OPEN_FILE = 2, - LOAD_ERR_NOT_PO_FILE = 3, - LOAD_ERR_PARSE_PREFIX = 100, - LOAD_ERR_P_INVAL = LOAD_PARSE_ERR(PARSE_ERR_INVAL), - LOAD_ERR_P_ALLOC = LOAD_PARSE_ERR(PARSE_ERR_ALLOC), - LOAD_ERR_P_SYNTAX = LOAD_PARSE_ERR(PARSE_ERR_SYNTAX) -};*/ - enum { SAVE_ERR_NONE = 0, SAVE_ERR_CANT_ALLOC = 1, @@ -89,6 +68,8 @@ struct PoEntry { size_t msgid_plural_size; size_t msgid_plural_len; uint32_t* plural_forms; + size_t plural_forms_size; + size_t plural_forms_len; uint32_t** msgstr; int msgstr_count; size_t* msgstr_size; @@ -106,21 +87,22 @@ char* format_flags(char* buffer, const size_t max, const struct PoEntry* entry, void init_po_entry(struct PoEntry* entry); void free_po_entry(struct PoEntry* entry); struct PoEntry* set_flags(struct PoEntry* entry, const Flags flags); -struct PoEntry* set_msgid(struct PoEntry* entry, const char* msgid, int append); -struct PoEntry* set_msgid_plural(struct PoEntry* entry, +int set_msgid(struct DrawState* state, struct PoEntry* entry, const char* msgid, int append); +int set_msgid_plural(struct DrawState* state, struct PoEntry* entry, const char* msgid_plural, int append); -struct PoEntry* set_plural_forms(struct PoEntry* entry, +int set_plural_forms(struct DrawState* state, struct PoEntry* entry, const char* plural_forms, int append); -struct PoEntry* set_msgstr(struct PoEntry* entry, const char* msgstr, - int msgstr_index, int append, struct DrawState* state); -struct PoEntry* u32_set_msgid(struct PoEntry* entry, const uint32_t* msgid, - size_t msgid_size, int append); -struct PoEntry* u32_set_msgid_plural(struct PoEntry* entry, - const uint32_t* msgid_plural, size_t msgid_plural_size, int append); -struct PoEntry* u32_set_plural_forms(struct PoEntry* entry, - const uint32_t* plural_forms, int append); -struct PoEntry* u32_set_msgstr(struct PoEntry* entry, const uint32_t* msgstr, - int msgstr_index, int append, struct DrawState* state); +int set_msgstr(struct DrawState* state, struct PoEntry* entry, + const char* msgstr, int msgstr_index, int append); +int u32_set_msgid(struct DrawState* state, struct PoEntry* entry, + const uint32_t* msgid, size_t msgid_size, int append); +int u32_set_msgid_plural(struct DrawState* state, + struct PoEntry* entry, const uint32_t* msgid_plural, + size_t msgid_plural_size, int append); +int u32_set_plural_forms(struct DrawState* state, struct PoEntry* entry, + const uint32_t* plural_forms, const size_t plural_forms_len, int append); +int u32_set_msgstr(struct DrawState* state, struct PoEntry* entry, + const uint32_t* msgstr, int msgstr_index, int append); struct PoEntry* u32_add_comment(struct PoEntry* entry, const uint32_t* comment); struct PoEntry* u32_add_extr_comment(struct PoEntry* entry, const uint32_t* extr_comment); diff --git a/poe.1.in b/poe.1.in @@ -11,20 +11,73 @@ .Nm .Fl h | \-help | Fl V | \-full\-version | Fl v | \-version .Nm +.Op Fl B | \-no\-backup +.Op Fl b | \-backup +.Op Fl L | \-dont\-break\-literals +.Op Fl W | \-no\-wrap +.Op Fl w Ar cols No | Fl \-wrap\-width= Ns Ar cols .Ar filename.po .Sh DESCRIPTION .Nm is a .po (GNU gettext) file editor with a terminal UI. .Bl -tag -width Ds +.It Fl B , \-no\-backup +Disables backups. +This option overrides the setting in +.Pa config.h . +.It Fl b , \-backup +Enables backups. +This option overrides the setting in +.Pa config.h . .It Fl h , \-help Show option summary. +.It Fl L , \-dont\-break\-literals +Don't break long literals into an empty string and a literal on a separate line. +By default, if the length of the text +.Bd -literal -offset indent +msgid: \(dq +.Ed +.Pp +(or msgid_plural:, msgstr:, etc) plus the length of the literal, plus one (for +the closing quote) are greater than +.Va literal_break_threshold , +a line of the form +.Bd -literal -offset indent +msgid: \(dqSome really long literal string\(dq +.Ed +.Pp +will be broken into two lines: +.Bd -literal -offset indent +msgid: \(dq\(dq +\(dqSome really long literal string\(dq +.Ed +.Pp +This option prevents that. +It overrides the setting in +.Pa config.h . .It Fl V , \-full\-version Show full version information: like .Fl v , -followed by copyright notice and defines set in -.Pa config.h . +followed by copyright notice and the state of options. .It Fl v , \-version Show version information: program version and source git repository commit date. +.It Fl W , \-no\-wrap +Sets +.Va wrap_width +to 0, effectively turning off wrapping of +.Ic msgid Ns s, +.Ic msgid_plural Ns s, +.Ic msgstr Ns s +and +.Ic msgstr[nn] Ns s +on save. +.It Fl w Ar cols , Fl \-wrap\-width= Ns Ar cols +Sets +.Va wrap_width +to the specified value. +If +.Ad cols +is negative, it will be set to zero, effectively turning off wrapping on save. .El .Ss Key bindings Main window key bindings are as follows: diff --git a/poe.c b/poe.c @@ -14,9 +14,6 @@ #include <sys/stat.h> #include <unistd.h> -#define TB_OPT_V1_COMPAT -#define TB_IMPL - #define COPYRIGHT \ (" This program is licensed under the terms of GNU GPL v3" \ " or (at your option)\n" \ @@ -29,12 +26,16 @@ " LICENSE.termbox.\n") #define POE_C_GLOBALS #define TB_IMPL +#define BSLNEWLINE ((uint32_t*){'\\', 'n'}) +#define SPACE ((uint32_t)0x0020) +#define TAB ((uint32_t)0x0009) +/* Order is important! */ #include "draw.h" +#include "poe.h" #include "config.h" #include "limit.h" #include "po.h" -#include "poe.h" #include "util.h" #include "version.h" @@ -79,13 +80,16 @@ cancel_close(struct DrawState* state) static int cancel_msgstr(struct DrawState* state) { + char* temp = NULL; ssize_t i; assert(state != NULL); - if (!state->in_prompt && state->dirty) + if (!state->in_prompt + && (state->dirty + || (state->show_edit && state->dirty_before_edit))) { state->in_prompt = 1; - STRLCPY(prompt_cancel_dirty, state->prompt, MAXBUFLINE); + MEMCCPY(state->prompt, prompt_cancel_dirty, LINE_DEFAULT, temp); state->prompt_callback = cancel_callback; return 0; } @@ -140,18 +144,22 @@ copy_msgid_to_input(struct DrawState* state) const uint32_t* pmsgid; const uint32_t* old_pmsgid; uint32_t* newtext; + char* temp = NULL; ssize_t msgid_num_lines, lines_to_init, lines_to_free, i; size_t len, newlen; assert(state != NULL); if (state->edit_info_focused) + { + state->error = ERR_DLG_FOCUS_EDIT; return 0; + } if (!state->in_prompt && *state->input_buffer->text) { state->in_prompt = 1; - STRLCPY(prompt_overwrite, state->prompt, MAXBUFLINE); + MEMCCPY(state->prompt, prompt_overwrite, LINE_DEFAULT, temp); state->prompt_callback = overwrite_copy_msgid_callback; return 0; } @@ -186,7 +194,7 @@ copy_msgid_to_input(struct DrawState* state) { pbuffer = newbuf + msgid_num_lines - 1 - i; init_bufferline(pbuffer); - newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); + newtext = calloc(LINE_DEFAULT, sizeof(uint32_t)); if (!newtext) return 0; pbuffer->text = newtext; @@ -195,12 +203,11 @@ copy_msgid_to_input(struct DrawState* state) pbuffer = newbuf; while (pmsgid && *pmsgid) { - len = 0; - newlen = 0; old_pmsgid = pmsgid; len = u32_next_line(current->msgid, &pmsgid, 0, 0); - u32_strncpy(pbuffer->text, old_pmsgid, MIN(len + 1, MAXBUFLINE)); - newlen = u32_decode_tabs(pbuffer->text, MAXBUFLINE); + u32_strncpy(pbuffer->text, old_pmsgid, + MIN(len + 1, LINE_DEFAULT)); + newlen = u32_decode_tabs(pbuffer->text, LINE_DEFAULT); pbuffer->length = newlen; pbuffer++; } @@ -361,7 +368,10 @@ erase_backwards(struct DrawState* state) assert((state != NULL) && (state->input_buffer != NULL)); buffer_line = &state->input_buffer[state->coord.row]; if (state->edit_info_focused) + { + state->error = ERR_DLG_FOCUS_EDIT; return 1; + } if (state->coord.col > 0) { @@ -411,13 +421,16 @@ erase_forward(struct DrawState* state) assert((state != NULL) && (state->input_buffer != NULL)); buffer_line = &state->input_buffer[state->coord.row]; if (state->edit_info_focused) + { + state->error = ERR_DLG_FOCUS_EDIT; return 1; + } assert(buffer_line->text != NULL); if (buffer_line->text[state->coord.col]) { i = state->coord.col; - while (i < MAXBUFLINE - 2 && buffer_line->text[i + 1]) + while (i < LINE_DEFAULT - 2 && buffer_line->text[i + 1]) { buffer_line->text[i] = buffer_line->text[i + 1]; i++; @@ -428,10 +441,11 @@ erase_forward(struct DrawState* state) } else if (state->coord.row < state->input_rows_count - 1) { - if (!join_lines(state, state->coord.row, 1)) + if (join_lines(state)) { state->error = ERR_CANT_ALLOC; state->running = 0; + return 1; } } return 0; @@ -446,7 +460,10 @@ erase_prev_word(struct DrawState* state) assert(state != NULL); if (state->edit_info_focused) + { + state->error = ERR_DLG_FOCUS_EDIT; return 1; + } i = state->coord.col; len = state->input_buffer[state->coord.row].length; pline = state->input_buffer[state->coord.row].text; @@ -491,19 +508,7 @@ erase_search_backwards(struct DrawState* state) i++; } while (state->search[i]); state->search[i - 1] = 0; - state->search_column--; - if (state->search_column < state->search_first_shown_column) - { - if (state->search_column > state->search_width - 4) - state->search_first_shown_column - = state->search_column - - state->search_width + 4; - else - state->search_first_shown_column = 0; - } - state->search_display_column - = display_length(state->search, state->search_column); - + search_position_to(state, state->search_column - 1); return next_match(state); } return 0; @@ -553,29 +558,16 @@ erase_search_prev_word(struct DrawState* state) for (j = i; j < len - d; j++) pline[j] = pline[j + d]; - len -= state->search_column - i; - pline[len] = 0; - state->search_column = i; - state->search_display_column - = display_length(state->search, state->search_column); - - if (state->search_column < state->search_first_shown_column) - { - if (state->search_column > state->search_width - 4) - state->search_first_shown_column = state->search_column - - state->search_width + 4; - else - state->search_first_shown_column = 0; - } - state->search_display_column - = display_length(state->search, state->search_column); - + len -= d; + pline[len] = 0; + search_position_to(state, i); return next_match(state); } static int erase_search_to_end(struct DrawState* state) { + assert(state != NULL); state->search[state->search_column] = 0; move_search_end(state); return next_match(state); @@ -584,10 +576,14 @@ erase_search_to_end(struct DrawState* state) static int erase_search_to_start(struct DrawState* state) { - size_t i = state->search_column; - size_t len = u32_strlen(state->search); + size_t i, len; + + assert((state != NULL) && (state->search != NULL)); + i = state->search_column; + len = u32_strlen(state->search); do { + assert(i < MAXSEARCH); state->search[i - state->search_column] = state->search[i]; i++; } while (state->search[i]); @@ -601,10 +597,15 @@ erase_to_end(struct DrawState* state) { struct BufferLine* buffer_line = NULL; + assert((state != NULL) && (state->input_buffer != NULL)); if (state->edit_info_focused) + { + state->error = ERR_DLG_FOCUS_EDIT; return 1; + } buffer_line = &state->input_buffer[state->coord.row]; + assert(buffer_line->text != NULL); buffer_line->text[state->coord.col] = 0; buffer_line->length = state->coord.col; move_end(state); @@ -619,15 +620,20 @@ erase_to_start(struct DrawState* state) ssize_t i; ssize_t len; + assert((state != NULL) && (state->input_buffer != NULL)); i = state->coord.col; buffer_line = &state->input_buffer[state->coord.row]; len = buffer_line->length; if (state->edit_info_focused) + { + state->error = ERR_DLG_FOCUS_EDIT; return 1; + } do { + assert(i <= len); buffer_line->text[i - state->coord.col] = buffer_line->text[i]; i++; } while (buffer_line->text[i]); @@ -641,29 +647,29 @@ erase_to_start(struct DrawState* state) static int exit_program(struct DrawState* state) { - if (state->show_edit) - { - state->error = ERR_DLG_OPEN; - return 0; - } - if (state->dirty) + char* temp = NULL; + + assert(state != NULL); + if (state->dirty || (state->show_edit && state->dirty_before_edit)) { state->in_prompt = 1; - STRLCPY(prompt_dirty, state->prompt, MAXBUFLINE); + MEMCCPY(state->prompt, prompt_dirty, LINE_DEFAULT, temp); state->prompt_callback = quit_callback; } else state->running = 0; - //*state->error = 0; + state->error = ERR_NONE; return 0; } static void goto_msgid(struct DrawState* state, const ssize_t msgid_number) { - ssize_t last_non_obsolete - = state->real_msgid_count - state->obsolete_at_end_count; + ssize_t last_non_obsolete; + assert(state != NULL); + last_non_obsolete + = state->real_msgid_count - state->obsolete_at_end_count; if (msgid_number > state->real_msgid_count) return; @@ -685,6 +691,7 @@ static void handle_key_event(struct tb_event* ev, struct DrawState* state) { const struct Key* current = NULL; + size_t key; assert((state != NULL) && (ev != NULL)); state->error = ERR_NONE; @@ -706,17 +713,11 @@ handle_key_event(struct tb_event* ev, struct DrawState* state) /* Remember to also edit the following condition if you * reassign motion keys */ - /* TODO: Condition in sled also contains: - && ev->key != TB_KEY_PGDN && ev->key != TB_KEY_PGUP - && ev->key != TB_KEY_CTRL_B && ev->key != TB_KEY_CTRL_F - && !(IN(state->mode, M_COMMAND) && strchr("jkgGHLM", ev->ch))) - Check? - */ if (ev->key != TB_KEY_ARROW_DOWN && ev->key != TB_KEY_ARROW_UP && state->show_edit) state->saved_column = -1; - for (size_t key = 0; key < LEN(top_level_keys); key++) + for (key = 0; key < LEN(top_level_keys); key++) { current = top_level_keys + key; if (((current->ev.mod == ev->mod) @@ -731,9 +732,9 @@ handle_key_event(struct tb_event* ev, struct DrawState* state) if (state->show_edit) { - for (size_t key = 0; key < LEN(edit_keys); key++) + for (key = 0; key < LEN(edit_keys); key++) { - current = edit_keys + key; + current = &edit_keys[key]; if (((current->ev.mod == ev->mod) && (current->ev.key == ev->key) && !ev->ch) || (current->ev.ch @@ -744,7 +745,12 @@ handle_key_event(struct tb_event* ev, struct DrawState* state) return; } } - if (!state->edit_info_focused && ev->ch) + if (state->edit_info_focused) + { + state->error = ERR_DLG_FOCUS_EDIT; + return; + } + if (ev->ch) { insert_char(state, ev->ch); return; @@ -753,9 +759,9 @@ handle_key_event(struct tb_event* ev, struct DrawState* state) if (state->show_search) { - for (size_t key = 0; key < LEN(search_keys); key++) + for (key = 0; key < LEN(search_keys); key++) { - current = search_keys + key; + current = &search_keys[key]; if (((current->ev.mod == ev->mod) && (current->ev.key == ev->key) && !ev->ch) || (current->ev.ch @@ -775,13 +781,33 @@ handle_key_event(struct tb_event* ev, struct DrawState* state) if (state->show_help) { + for (key = 0; key < LEN(help_keys); key++) + { + current = help_keys + key; + if (((current->ev.mod == ev->mod) + && (current->ev.key == ev->key) && !ev->ch) + || (current->ev.ch + && (current->ev.ch == ev->ch))) + if (current->callback) + { + current->callback(state); + return; + } + } + state->error = ERR_DLG_OPEN; return; } - for (size_t key = 0; key < LEN(main_keys); key++) + if (state->show_search || state->show_edit) + { + state->error = ERR_DLG_OPEN_QUIT; + return; + } + + for (key = 0; key < LEN(main_keys); key++) { - current = main_keys + key; + current = &main_keys[key]; if (((current->ev.mod == ev->mod) && (current->ev.key == ev->key) && !ev->ch) || (current->ev.ch && (current->ev.ch == ev->ch))) @@ -803,6 +829,7 @@ handle_key_event(struct tb_event* ev, struct DrawState* state) static int hide_search(struct DrawState* state) { + assert(state != NULL); state->show_search = 0; tb_hide_cursor(); return 0; @@ -812,91 +839,118 @@ static int insert_char(struct DrawState* state, uint32_t ch) { struct BufferLine* buffer_line; - ssize_t len; - assert(state != NULL); + uint32_t* temp = NULL; + ssize_t i, len, size; + int add_null; + + assert((state != NULL) && (state->input_buffer != NULL)); if (state->edit_info_focused) return 1; buffer_line = &state->input_buffer[state->coord.row]; len = buffer_line->length; - if (state->coord.col < MAXBUFLINE - 1) + size = buffer_line->size; + + // TODO: realloc + if (len + 1 > size) { - ssize_t i = len + 1; - int add_null = buffer_line->text[state->coord.col] == 0; - while (i < MAXBUFLINE - 2 && i > state->coord.col) - { - buffer_line->text[i] = buffer_line->text[i - 1]; - i--; - } - buffer_line->text[state->coord.col++] = ch; - buffer_line->length++; - if (state->coord.col == MAXBUFLINE) - state->coord.col = MAXBUFLINE - 1; + size += LINE_ALLOC_DELTA; + buffer_line->size = size; + temp = realloc(buffer_line->text, size * sizeof(uint32_t)); + if (!temp) + goto insert_char_alloc_error; + buffer_line->text = temp; + } + + i = len; + add_null = buffer_line->text[state->coord.col] == 0; + assert(buffer_line->text != NULL); + while (i + 1 <= size && i > state->coord.col) + { + buffer_line->text[i] = buffer_line->text[i - 1]; + i--; + } + buffer_line->text[state->coord.col] = ch; + buffer_line->length++; + state->coord.col++; + state->input_display_column + = display_length(state->input_buffer[state->coord.row].text, + state->coord.col); + if (state->input_display_column - state->input_first_shown.col + > state->edit_width - 4) + { + state->input_first_shown.col + = state->input_display_column - state->edit_width + 4; state->input_display_column = display_length(state->input_buffer[state->coord.row] .text, state->coord.col); - if (state->input_display_column - state->input_first_shown.col - > state->edit_width - 4) - { - state->input_first_shown.col - = state->input_display_column - - state->edit_width + 4; - state->input_display_column = display_length( - state->input_buffer[state->coord.row].text, - state->coord.col); - } - if (add_null) - buffer_line->text[state->coord.col] = 0; } + if (add_null) + buffer_line->text[state->coord.col] = 0; state->dirty = 1; return 0; + +insert_char_alloc_error: + state->error = ERR_CANT_ALLOC; + return 1; } static int insert_line(struct DrawState* state) { struct BufferLine* newbuf = NULL; + struct BufferLine* line = NULL; + uint32_t* newtext = NULL; + uint32_t* ptext = NULL; + ssize_t i; if (state->edit_info_focused) + { + state->error = ERR_DLG_FOCUS_EDIT; return 1; + } - state->input_rows_count++; + assert(state != NULL); newbuf = realloc(state->input_buffer, - state->input_rows_count * sizeof(struct BufferLine)); + (state->input_rows_count + 1) * sizeof(struct BufferLine)); if (!newbuf) - { - state->error = ERR_CANT_ALLOC; - state->running = 0; - return 0; - } - + goto insert_line_alloc_error; state->input_buffer = newbuf; - init_bufferline(&state->input_buffer[state->input_rows_count - 1]); - uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); + line = &state->input_buffer[state->input_rows_count]; + init_bufferline(line); + newtext = calloc(LINE_DEFAULT, sizeof(uint32_t)); if (!newtext) + goto insert_line_alloc_error; + line->text = newtext; + line->size = LINE_DEFAULT; + state->input_rows_count++; + for (i = state->input_rows_count - 1; i > state->coord.row + 1; i--) { - state->error = ERR_CANT_ALLOC; - state->running = 0; - return 0; - } - state->input_buffer[state->input_rows_count - 1].text = newtext; - for (ssize_t i = state->input_rows_count - 1; i > state->coord.row + 1; - i--) - { - u32_strncpy(state->input_buffer[i].text, - state->input_buffer[i - 1].text, MAXBUFLINE); + U32_SAFE_COPY(state->input_buffer[i].text, ptext, + state->input_buffer[i].size, + state->input_buffer[i - 1].text, + state->input_buffer[i].size, insert_line_alloc_error); state->input_buffer[i].length = state->input_buffer[i - 1].length; } - u32_strncpy(state->input_buffer[state->coord.row + 1].text, - state->input_buffer[state->coord.row].text + state->coord.col, - MAXBUFLINE); + ENSURE_SIZE(state->input_buffer[state->coord.row + 1].text, ptext, + state->input_buffer[state->coord.row + 1].size, LINE_DEFAULT, + LINE_DEFAULT, insert_line_alloc_error, uint32_t); + *state->input_buffer[state->coord.row + 1].text = 0; + if (state->coord.col < state->input_buffer[state->coord.row].length) + U32_SAFE_COPY(state->input_buffer[state->coord.row + 1].text, + ptext, state->input_buffer[state->coord.row + 1].size, + state->input_buffer[state->coord.row].text + + state->coord.col, + state->input_buffer[state->coord.row].size, + insert_line_alloc_error); state->input_buffer[state->coord.row + 1].length - = u32_strlen(state->input_buffer[state->coord.row + 1].text); + = state->input_buffer[state->coord.row].length + - state->coord.col; state->input_buffer[state->coord.row].text[state->coord.col] = 0; - state->input_buffer[state->coord.row].length - = u32_strlen(state->input_buffer[state->coord.row].text); + state->input_buffer[state->coord.row].length = state->coord.col; + state->saved_column = -1; state->coord.row++; if (state->coord.row > state->input_first_shown.row + (EDIT_HEIGHT - 3) / 2) @@ -904,19 +958,25 @@ insert_line(struct DrawState* state) = state->coord.row - (EDIT_HEIGHT - 3) / 2; move_start(state); state->dirty = 1; - return 0; + +insert_line_alloc_error: + state->error = ERR_CANT_ALLOC; + return 1; } -static void +static int insert_search_char(struct DrawState* state, uint32_t ch) { - ssize_t len = u32_strlen(state->search); - if (state->search_column < MAXSEARCH - 1) + ssize_t len, i; + int add_null; + assert(state != NULL); + len = u32_strlen(state->search); + if (state->search_column + 1 < MAXSEARCH) { - ssize_t i = len + 1; - int add_null = state->search[state->search_column] == 0; - while (i < MAXSEARCH - 2 && i > state->search_column) + i = len + 1; + add_null = state->search[state->search_column] == 0; + while (i + 2 < MAXSEARCH && i > state->search_column) { state->search[i] = state->search[i - 1]; i--; @@ -942,91 +1002,97 @@ insert_search_char(struct DrawState* state, uint32_t ch) next_match(state); } + return 0; } static int insert_search_space(struct DrawState* state) { - insert_search_char(state, (uint32_t)L' '); - return 0; + return insert_search_char(state, SPACE); } static int insert_search_tab(struct DrawState* state) { - insert_search_char(state, (uint32_t)L'\t'); - return 0; + return insert_search_char(state, TAB); } static int insert_space(struct DrawState* state) { - insert_char(state, (uint32_t)L' '); - return 0; + return insert_char(state, SPACE); } static int insert_tab(struct DrawState* state) { - insert_char(state, (uint32_t)L'\t'); - return 0; + return insert_char(state, TAB); } -static struct DrawState* -join_lines(struct DrawState* state, size_t first_line, int forward) +static int +join_lines(struct DrawState* state) { - state->coord.col = state->input_buffer[first_line].length; - state->input_display_column - = display_length(state->input_buffer[state->coord.row].text, - state->coord.col); - u32_strncat(state->input_buffer[first_line].text, - state->input_buffer[first_line + 1].text, MAXBUFLINE); - state->input_buffer[first_line].length - = u32_strlen(state->input_buffer[first_line].text); - if (!forward) - { - state->coord.row--; - if (state->coord.row < 0) - state->coord.row = 0; - } - for (ssize_t i = first_line + 1; i < state->input_rows_count - 1; i++) - { - u32_strncpy(state->input_buffer[i].text, - state->input_buffer[i + 1].text, MAXBUFLINE); + struct BufferLine* first_line = NULL; + struct BufferLine* next_line = NULL; + uint32_t* ptext = NULL; + ssize_t i; + + assert((state != NULL) && (state->input_buffer != NULL)); + if (state->coord.row + 1 == state->input_rows_count) + return 1; + + first_line = &state->input_buffer[state->coord.row]; + next_line = &state->input_buffer[state->coord.row + 1]; + ENSURE_SIZE(first_line->text, ptext, first_line->size, + first_line->length + next_line->length + 1, + first_line->length + next_line->length + LINE_ALLOC_DELTA, + join_lines_alloc_error, uint32_t); + U32_MEMCCPY(first_line->text + first_line->length, next_line->text, + next_line->length + 1, ptext); + first_line->length = u32_strlen(first_line->text); + for (i = state->coord.row + 1; i + 1 < state->input_rows_count; i++) + { + U32_SAFE_COPY(state->input_buffer[i].text, ptext, + state->input_buffer[i].size, + state->input_buffer[i + 1].text, + state->input_buffer[i + 1].size, + join_lines_alloc_error); state->input_buffer[i].length = state->input_buffer[i + 1].length; } - free_bufferline(state->input_buffer + state->input_rows_count - 1); + free_bufferline(&state->input_buffer[state->input_rows_count - 1]); state->input_rows_count--; - struct BufferLine* newbuf = realloc(state->input_buffer, - state->input_rows_count * sizeof(struct BufferLine)); - if (!newbuf) - return NULL; - state->input_buffer = newbuf; - state->dirty = 1; + state->dirty = 1; + return 0; - return state; +join_lines_alloc_error: + state->error = ERR_CANT_ALLOC; + return 1; } -static struct BufferLine* +static int load_info(struct DrawState* state) { - struct PoEntry* first = &state->entries[0]; - struct PoEntry* current = &state->entries[state->msgid_number - 1]; + struct PoEntry* first = NULL; + struct PoEntry* current = NULL; struct BufferLine* newbuf = NULL; struct BufferLine* pnewbuf = NULL; - const uint32_t* pumsgid = NULL; - ssize_t msgid_num_lines = u32_lines_in_string(current->msgid, 0, 0); - ssize_t msgid_plural_num_lines + uint32_t* newtext = NULL; + ssize_t msgid_num_lines, msgid_plural_num_lines, num_lines, i; + + assert((state != NULL) && (state->entries != NULL)); + first = &state->entries[0]; + current = &state->entries[state->msgid_number - 1]; + msgid_num_lines = u32_lines_in_string(current->msgid, 0, 0); + msgid_plural_num_lines = u32_lines_in_string(current->msgid_plural, 0, 0); - ssize_t num_lines = 0; - ssize_t newlen = 0; + num_lines = 0; - for (ssize_t i = 0; i < state->info_rows_count; i++) - free_bufferline(state->info_buffer + i); + for (i = 0; i < state->info_rows_count; i++) + free_bufferline(&state->info_buffer[i]); free(state->info_buffer); - state->info_rows_maxlen = 0; + state->info_maxlen = 0; if (current->msgid) num_lines += msgid_num_lines + 1; @@ -1044,15 +1110,15 @@ load_info(struct DrawState* state) num_lines += current->ref_comment_lines + 1; newbuf = calloc(num_lines, sizeof(struct BufferLine)); if (!newbuf) - return NULL; + goto load_info_alloc_error; pnewbuf = newbuf; - for (ssize_t i = 0; i < num_lines; i++) + for (i = 0; i < num_lines; i++) { init_bufferline(pnewbuf); - uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); + newtext = calloc(LINE_DEFAULT, sizeof(uint32_t)); if (!newtext) - return NULL; + goto load_info_alloc_error; pnewbuf->text = newtext; pnewbuf++; } @@ -1060,155 +1126,231 @@ load_info(struct DrawState* state) state->info_rows_count = num_lines; pnewbuf = newbuf; - newlen = 0; - pumsgid = NULL; + if (current->msgid) - { - pumsgid = current->msgid; - newlen = u32_u8_strncpy(pnewbuf->text, "msgid:", MAXBUFLINE); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg; - pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg; - pnewbuf++; - while (*pumsgid) - { - size_t len = 0; - const uint32_t* old_pumsgid = pumsgid; - - len = u32_next_line(current->msgid, &pumsgid, 0, 0); - u32_strncpy(pnewbuf->text, old_pumsgid, - MIN(len + 1, MAXBUFLINE)); - newlen = u32_decode_tabs(pnewbuf->text, MAXBUFLINE); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf++; - } - } + if (load_info_msgid(state, &pnewbuf)) + return 1; + if (current->msgid_plural) - { - newlen = u32_u8_strncpy(pnewbuf->text, - "msgid_plural:", MAXBUFLINE); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg; - pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg; - pnewbuf++; - pumsgid = current->msgid_plural; - while (*pumsgid) - { - size_t len = 0; - const uint32_t* old_pumsgid = pumsgid; - - len = u32_next_line(current->msgid_plural, &pumsgid, 0, - 0); - u32_strncpy(pnewbuf->text, old_pumsgid, - MIN(len + 1, MAXBUFLINE)); - newlen = u32_decode_tabs(pnewbuf->text, MAXBUFLINE); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf++; - } - if (first->plural_forms) - { - newlen = u32_u8_strncpy(pnewbuf->text, - "Plural forms:", MAXBUFLINE); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg; - pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg; - pnewbuf++; - newlen = u32_strncpy(pnewbuf->text, first->plural_forms, - MAXBUFLINE); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf++; - } - } + if (load_info_msgid_plural(state, &pnewbuf)) + return 1; + if (current->comments) - { - newlen = u32_u8_strncpy(pnewbuf->text, "Comments:", MAXBUFLINE); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg; - pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg; - pnewbuf++; - for (size_t i = 0; i < current->comment_lines; i++) - { - uint32_t* comment = current->comments[i]; - size_t len = u32_strlen(comment); - newlen = u32_strncpy(pnewbuf->text, comment, - MIN(len + 1, MAXBUFLINE)); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf++; - } - } + if (load_info_comments(state, &pnewbuf)) + return 1; + if (current->extr_comments) - { - newlen = u32_u8_strncpy(pnewbuf->text, - "Translation comments:", MAXBUFLINE); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg; - pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg; - pnewbuf++; - for (size_t i = 0; i < current->extr_comment_lines; i++) - { - uint32_t* extr_comment = current->extr_comments[i]; - size_t len = u32_strlen(extr_comment); - newlen = u32_strncpy(pnewbuf->text, extr_comment, - MIN(len + 1, MAXBUFLINE)); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf++; - } - } + if (load_info_extr_comments(state, &pnewbuf)) + return 1; + if (current->ref_comments) - { - newlen = u32_u8_strncpy(pnewbuf->text, - "References:", MAXBUFLINE); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg; - pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg; - pnewbuf++; - for (size_t i = 0; i < current->ref_comment_lines; i++) - { - char* ref_comment = current->ref_comments[i]; - size_t len = strlen(ref_comment); - newlen = u32_u8_strncpy(pnewbuf->text, ref_comment, - MIN(len + 1, MAXBUFLINE)); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf++; - } - } + if (load_info_ref_comments(state, &pnewbuf)) + return 1; state->info_buffer = newbuf; + return 0; + +load_info_alloc_error: + state->error = ERR_CANT_ALLOC; + return 1; +} + +static int +load_info_comments(struct DrawState* state, struct BufferLine** lines) +{ + struct PoEntry* current = NULL; + uint32_t* comment = NULL; + uint32_t* ptext = NULL; + ssize_t len, llen; + size_t i; + + current = &state->entries[state->msgid_number - 1]; + ptext = u32_u8_memccpy((*lines)->text, "Comments:", 0, LINE_DEFAULT); + (*lines)->length = llen = ptext ? ptext - (*lines)->text : LINE_DEFAULT; + if (llen > state->info_maxlen) + state->info_maxlen = llen; + (*lines)->fg = colors[C_EDIT_MSGID_HEADING].fg; + (*lines)->bg = colors[C_EDIT_MSGID_HEADING].bg; + (*lines)++; + for (i = 0; i < current->comment_lines; i++) + { + comment = current->comments[i]; + len = u32_strlen(comment); + U32_MEMCCPY((*lines)->text, comment, MIN(len + 1, LINE_DEFAULT), + ptext); + (*lines)->length = llen + = ptext ? ptext - (*lines)->text : LINE_DEFAULT; + if (llen > state->info_maxlen) + state->info_maxlen = llen; + (*lines)++; + } + return 0; +} + +static int +load_info_extr_comments(struct DrawState* state, struct BufferLine** lines) +{ + struct PoEntry* current = NULL; + uint32_t* extr_comment = NULL; + uint32_t* ptext = NULL; + ssize_t len, llen; + size_t i; + + current = &state->entries[state->msgid_number - 1]; + ptext = u32_u8_memccpy((*lines)->text, "Translation comments:", 0, + LINE_DEFAULT); + (*lines)->length = llen = ptext ? ptext - (*lines)->text : LINE_DEFAULT; + if (llen > state->info_maxlen) + state->info_maxlen = llen; + (*lines)->fg = colors[C_EDIT_MSGID_HEADING].fg; + (*lines)->bg = colors[C_EDIT_MSGID_HEADING].bg; + (*lines)++; + for (i = 0; i < current->extr_comment_lines; i++) + { + extr_comment = current->extr_comments[i]; + len = u32_strlen(extr_comment); + U32_MEMCCPY((*lines)->text, extr_comment, + MIN(len + 1, LINE_DEFAULT), ptext); + (*lines)->length = llen + = ptext ? ptext - (*lines)->text : LINE_DEFAULT; + if (llen > state->info_maxlen) + state->info_maxlen = llen; + (*lines)++; + } + return 0; +} + +static int +load_info_msgid(struct DrawState* state, struct BufferLine** lines) +{ + struct PoEntry* current = NULL; + const uint32_t* pumsgid = NULL; + const uint32_t* old_pumsgid = NULL; + uint32_t* ptext = NULL; + ssize_t len, llen; + + current = &state->entries[state->msgid_number - 1]; + ptext = u32_u8_memccpy((*lines)->text, "msgid:", 0, LINE_DEFAULT); + (*lines)->length = llen = ptext ? ptext - (*lines)->text : LINE_DEFAULT; + if (llen > state->info_maxlen) + state->info_maxlen = llen; + (*lines)->fg = colors[C_EDIT_MSGID_HEADING].fg; + (*lines)->bg = colors[C_EDIT_MSGID_HEADING].bg; + (*lines)++; + pumsgid = current->msgid; + while (*pumsgid) + { + old_pumsgid = pumsgid; + len = u32_next_line(current->msgid, &pumsgid, 0, 0); + U32_MEMCCPY((*lines)->text, old_pumsgid, + MIN(len + 1, LINE_DEFAULT), ptext); + llen = u32_decode_tabs((*lines)->text, LINE_DEFAULT); + (*lines)->length = llen; + if (llen > state->info_maxlen) + state->info_maxlen = llen; + (*lines)++; + } + return 0; +} + +static int +load_info_msgid_plural(struct DrawState* state, struct BufferLine** lines) +{ + struct PoEntry* current = NULL; + struct PoEntry* first = NULL; + const uint32_t* pumsgid = NULL; + const uint32_t* old_pumsgid = NULL; + uint32_t* ptext = NULL; + ssize_t len, llen; + + first = &state->entries[0]; + current = &state->entries[state->msgid_number - 1]; + ptext = u32_u8_memccpy((*lines)->text, "msgid_plural:", 0, LINE_DEFAULT); + (*lines)->length = llen = ptext ? ptext - (*lines)->text : LINE_DEFAULT; + if (llen > state->info_maxlen) + state->info_maxlen = llen; + (*lines)->fg = colors[C_EDIT_MSGID_HEADING].fg; + (*lines)->bg = colors[C_EDIT_MSGID_HEADING].bg; + (*lines)++; + pumsgid = current->msgid_plural; + while (*pumsgid) + { + old_pumsgid = pumsgid; + len = u32_next_line(current->msgid_plural, &pumsgid, 0, 0); + U32_MEMCCPY((*lines)->text, old_pumsgid, + MIN(len + 1, LINE_DEFAULT), ptext); + llen = u32_decode_tabs((*lines)->text, LINE_DEFAULT); + (*lines)->length = llen; + if (llen > state->info_maxlen) + state->info_maxlen = llen; + (*lines)++; + } + if (first->plural_forms) + { + ptext = u32_u8_memccpy((*lines)->text, "Plural forms:", 0, + LINE_DEFAULT); + (*lines)->length = llen + = ptext ? ptext - (*lines)->text : LINE_DEFAULT; + if (llen > state->info_maxlen) + state->info_maxlen = llen; + (*lines)->fg = colors[C_EDIT_MSGID_HEADING].fg; + (*lines)->bg = colors[C_EDIT_MSGID_HEADING].bg; + (*lines)++; + ptext = u32_memccpy((*lines)->text, first->plural_forms, 0, + LINE_DEFAULT); + (*lines)->length = llen + = ptext ? ptext - (*lines)->text : LINE_DEFAULT; + if (llen > state->info_maxlen) + state->info_maxlen = llen; + (*lines)++; + } + return 0; +} + +static int +load_info_ref_comments(struct DrawState* state, struct BufferLine** lines) +{ + struct PoEntry* current = NULL; + char* ref_comment = NULL; + uint32_t* ptext = NULL; + ssize_t len, llen; + size_t i; - return state->info_buffer; + current = &state->entries[state->msgid_number - 1]; + ptext = u32_u8_memccpy((*lines)->text, "References:", 0, LINE_DEFAULT); + (*lines)->length = llen = ptext ? ptext - (*lines)->text : LINE_DEFAULT; + if (llen > state->info_maxlen) + state->info_maxlen = llen; + (*lines)->fg = colors[C_EDIT_MSGID_HEADING].fg; + (*lines)->bg = colors[C_EDIT_MSGID_HEADING].bg; + (*lines)++; + for (i = 0; i < current->ref_comment_lines; i++) + { + ref_comment = current->ref_comments[i]; + len = strlen(ref_comment); + ptext = u32_u8_memccpy((*lines)->text, ref_comment, 0, + MIN(len + 1, LINE_DEFAULT)); + (*lines)->length = llen + = ptext ? ptext - (*lines)->text : LINE_DEFAULT; + if (llen > state->info_maxlen) + state->info_maxlen = llen; + (*lines)++; + } + return 0; } -static struct BufferLine* +static int load_msgstr(struct DrawState* state) { struct PoEntry* current; struct BufferLine* newbuf; struct BufferLine* pnewbuf; - size_t num_lines; + const uint32_t* pumsgstr = NULL; + const uint32_t* old_pumsgstr = NULL; + uint32_t* newtext = NULL; + uint32_t* temp = NULL; + size_t num_lines, i, len, newlen; + ssize_t row; int msgstr_index; assert((state != NULL) && (state->entries != NULL)); @@ -1220,22 +1362,24 @@ load_msgstr(struct DrawState* state) ? 1 : u32_lines_in_string(*(current->msgstr + msgstr_index), 0, 0); - for (ssize_t i = 0; i < state->input_rows_count; i++) - free_bufferline(state->input_buffer + i); + for (row = 0; row < state->input_rows_count; row++) + free_bufferline(state->input_buffer + row); free(state->input_buffer); newbuf = calloc(num_lines, sizeof(struct BufferLine)); if (!newbuf) - return NULL; + goto load_msgstr_alloc_error; pnewbuf = newbuf; - for (size_t i = 0; i < num_lines; i++) + for (i = 0; i < num_lines; i++) { init_bufferline(pnewbuf); - uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); + newtext = calloc(LINE_DEFAULT, sizeof(uint32_t)); if (!newtext) - return NULL; - pnewbuf->text = newtext; + goto load_msgstr_alloc_error; + pnewbuf->text = newtext; + pnewbuf->size = LINE_DEFAULT; + pnewbuf->length = 0; pnewbuf++; } @@ -1243,23 +1387,21 @@ load_msgstr(struct DrawState* state) if (current->msgstr_count > 0) { - const uint32_t* pumsgstr = *(current->msgstr + msgstr_index); + pumsgstr = *(current->msgstr + msgstr_index); if (pumsgstr) { pnewbuf = newbuf; while (*pumsgstr) { - size_t len = 0; - size_t newlen = 0; - const uint32_t* old_pumsgstr = pumsgstr; - len = u32_next_line(*(current->msgstr - + msgstr_index), + old_pumsgstr = pumsgstr; + len = u32_next_line(current->msgstr[msgstr_index], &pumsgstr, 0, 0); - u32_strncpy(pnewbuf->text, old_pumsgstr, - MIN(len + 1, MAXBUFLINE)); + U32_MEMCCPY(pnewbuf->text, old_pumsgstr, + MIN(len + 1, LINE_DEFAULT), temp); newlen = u32_decode_tabs(pnewbuf->text, - MAXBUFLINE); + LINE_DEFAULT); /* TODO: Check */ pnewbuf->length = newlen; + pnewbuf->size = LINE_DEFAULT; pnewbuf++; } } @@ -1270,14 +1412,17 @@ load_msgstr(struct DrawState* state) newbuf[0].length = 0; state->input_buffer = newbuf; - return state->input_buffer; + return 0; + +load_msgstr_alloc_error: + state->error = ERR_CANT_ALLOC; + return 1; } static int move_down(struct DrawState* state) { - ssize_t dlen; - + assert(state != NULL); if (state->edit_info_focused) { if (state->info_first_shown.row + (EDIT_HEIGHT - 3) / 2 @@ -1285,94 +1430,46 @@ move_down(struct DrawState* state) state->info_first_shown.row++; return 0; } - - if (state->coord.row < state->input_rows_count - 1) - state->coord.row++; - if (state->input_first_shown.row + (EDIT_HEIGHT - 3) / 2 - < state->coord.row) - state->input_first_shown.row - = state->coord.row - (EDIT_HEIGHT - 3) / 2; - if (state->saved_column == -1) - state->saved_column = state->input_display_column; - dlen = display_length(state->input_buffer[state->coord.row].text, - state->input_buffer[state->coord.row].length); - if (dlen < state->saved_column) - state->input_display_column = dlen; - else - state->input_display_column = state->saved_column; - state->coord.col - = input_length(state->input_buffer[state->coord.row].text, - state->input_display_column); - if (state->input_first_shown.col > state->input_display_column) - state->input_first_shown.col = state->input_display_column; - if (state->input_first_shown.col + state->edit_width - 4 - < state->input_display_column) - state->input_first_shown.col - = state->input_display_column - state->edit_width + 4; - state->input_display_column - = display_length(state->input_buffer[state->coord.row].text, - state->coord.col); + position_to(state, state->coord.row + 1, state->coord.col); return 0; } static int move_end(struct DrawState* state) { - ssize_t len; - ssize_t dlen; - + assert((state != NULL) && (state->input_buffer != NULL)); if (state->edit_info_focused) { - if (state->info_rows_count > (EDIT_HEIGHT - 3) / 2) - state->info_first_shown.row = state->info_rows_count - - (EDIT_HEIGHT - 3) / 2; - state->info_first_shown.col = 0; + if (state->info_maxlen < state->edit_width - 3) + state->info_first_shown.col = 0; + else + state->info_first_shown.col + = state->info_maxlen - state->edit_width + 3; return 0; } - - len = state->input_buffer[state->coord.row].length; - state->coord.col = len; - dlen = display_length(state->input_buffer[state->coord.row].text, - state->coord.col); - state->input_display_column = dlen; - if (dlen > state->edit_width - 4) - state->input_first_shown.col = dlen - state->edit_width + 4; - else - state->input_first_shown.col = 0; + position_to(state, state->coord.row, + state->input_buffer[state->coord.row].length); return 0; } static int move_left(struct DrawState* state) { + assert((state != NULL) && (state->input_buffer != NULL)); if (state->edit_info_focused) - { - if (state->info_first_shown.col > 0) - state->info_first_shown.col--; - return 0; - } - - if (state->coord.col == 0) - return 0; - - if (state->input_buffer[state->coord.row].text[state->coord.col - 1] - == '\t') - state->input_display_column - = display_length(state->input_buffer[state->coord.row] - .text, - state->coord.col - 1); - else - state->input_display_column--; - state->coord.col--; - - if (state->input_display_column < state->input_first_shown.col) - state->input_first_shown.col = state->input_display_column; + { + if (state->info_first_shown.col > 0) + state->info_first_shown.col--; + return 0; + } + position_to(state, state->coord.row, state->coord.col - 1); return 0; } static int move_list_down(struct DrawState* state) { + assert(state != NULL); goto_msgid(state, next_msgid(state, 1)); return 0; } @@ -1380,6 +1477,7 @@ move_list_down(struct DrawState* state) static int move_list_end(struct DrawState* state) { + assert(state != NULL); goto_msgid(state, next_msgid(state, state->real_msgid_count - state->msgid_number)); @@ -1389,6 +1487,7 @@ move_list_end(struct DrawState* state) static int move_list_page_down(struct DrawState* state) { + assert(state != NULL); state->error = ERR_NONE; goto_msgid(state, next_msgid(state, state->maxy - 2)); return 0; @@ -1397,6 +1496,7 @@ move_list_page_down(struct DrawState* state) static int move_list_page_up(struct DrawState* state) { + assert(state != NULL); state->error = ERR_NONE; goto_msgid(state, prev_msgid(state, state->maxy - 2)); return 0; @@ -1405,6 +1505,7 @@ move_list_page_up(struct DrawState* state) static int move_list_start(struct DrawState* state) { + assert(state != NULL); goto_msgid(state, 1); return 0; } @@ -1412,6 +1513,7 @@ move_list_start(struct DrawState* state) static int move_list_up(struct DrawState* state) { + assert(state != NULL); goto_msgid(state, prev_msgid(state, 1)); return 0; } @@ -1419,12 +1521,16 @@ move_list_up(struct DrawState* state) static int move_next_word(struct DrawState* state) { - int i; uint32_t* pline; ssize_t len; + int i; + assert(state != NULL); if (state->edit_info_focused) + { + state->error = ERR_DLG_FOCUS_EDIT; return 1; + } i = state->coord.col; pline = state->input_buffer[state->coord.row].text; @@ -1434,25 +1540,23 @@ move_next_word(struct DrawState* state) i++; while (i < len && u32_is_word_boundary(pline[i], 0)) i++; - state->coord.col = i; - state->input_display_column - = display_length(state->input_buffer[state->coord.row].text, - state->coord.col); - if (state->input_display_column - state->input_first_shown.col - > state->edit_width - 4) - state->input_first_shown.col - = state->input_display_column - state->edit_width + 4; + + position_to(state, state->coord.row, i); return 0; } static int move_prev_word(struct DrawState* state) { - int i; uint32_t* pline; + int i; + assert((state != NULL) && (state->input_buffer != NULL)); if (state->edit_info_focused) + { + state->error = ERR_DLG_FOCUS_EDIT; return 1; + } i = state->coord.col; pline = state->input_buffer[state->coord.row].text; @@ -1465,113 +1569,64 @@ move_prev_word(struct DrawState* state) && !u32_is_word_boundary(pline[i - 1], 0)) i--; - state->coord.col = i; - state->input_display_column - = display_length(state->input_buffer[state->coord.row].text, - state->coord.col); - if (state->input_display_column < state->input_first_shown.col) - state->input_first_shown.col = state->input_display_column; + position_to(state, state->coord.row, i); return 0; } static int move_right(struct DrawState* state) { - ssize_t len; - - if (state->edit_info_focused) - { - if (state->info_first_shown.col + state->edit_width - 3 - < state->info_rows_maxlen + 1) - state->info_first_shown.col++; - return 0; - } - - len = state->input_buffer[state->coord.row].length; - if (state->coord.col == len) - return 0; - - if (state->input_buffer[state->coord.row].text[state->coord.col] == '\t') - state->input_display_column - = display_length(state->input_buffer[state->coord.row] - .text, - state->coord.col + 1); - else - state->input_display_column++; - state->coord.col++; - - if (state->input_display_column - state->input_first_shown.col - > state->edit_width - 4) - state->input_first_shown.col - = state->input_display_column - state->edit_width + 4; + assert(state != NULL); + position_to(state, state->coord.row, state->coord.col + 1); return 0; } static int move_search_end(struct DrawState* state) { - ssize_t len = u32_strlen(state->search); - state->search_column = len; - ssize_t dlen = display_length(state->search, state->search_column); - state->search_display_column = dlen; - if (dlen > state->search_width - 4) - state->search_first_shown_column - = dlen - state->search_width + 4; - else - state->search_first_shown_column = 0; + assert(state != NULL); + search_position_to(state, u32_strlen(state->search)); return 0; } static int move_search_left(struct DrawState* state) { + assert((state != NULL) && (state->search != NULL)); if (state->search_column == 0) - return 0; - - if (state->search[state->search_column - 1] == '\t') - state->search_display_column = display_length(state->search, - state->search_column - 1); - else - state->search_display_column--; - state->search_column--; - - if (state->search_display_column < state->search_first_shown_column) - state->search_first_shown_column = state->search_display_column; + return 1; + search_position_to(state, state->search_column - 1); return 0; } static int move_search_next_word(struct DrawState* state) { - int i = state->search_column; - uint32_t* pline = state->search; - ssize_t len = u32_strlen(state->search); + uint32_t* pline = NULL; + ssize_t len; + int i; + assert((state != NULL) && (state->search != NULL)); + i = state->search_column; + pline = state->search; + len = u32_strlen(state->search); while (i < len && !u32_is_word_boundary(pline[i], 0)) i++; while (i < len && u32_is_word_boundary(pline[i], 0)) i++; - state->search_column = i; - state->search_display_column - = display_length(state->search, state->search_column); - - if (state->search_display_column - state->search_first_shown_column - > state->search_width - 4) - { - state->search_first_shown_column = state->search_display_column - - state->search_width + 4; - state->search_display_column - = display_length(state->search, state->search_column); - } + search_position_to(state, i); return 0; } static int move_search_prev_word(struct DrawState* state) { - int i = state->search_column; - uint32_t* pline = state->search; + uint32_t* pline = NULL; + int i; + assert((state != NULL) && (state->search != NULL)); + i = state->search_column; + pline = state->search; if (i > 0 && u32_is_word_boundary(pline[i - 1], 0)) i--; while (i > 0 && u32_is_word_boundary(pline[i], 0)) @@ -1579,106 +1634,52 @@ move_search_prev_word(struct DrawState* state) while (i > 0 && !u32_is_word_boundary(pline[i], 0) && !u32_is_word_boundary(pline[i - 1], 0)) i--; - - state->search_column = i; - state->search_display_column - = display_length(state->search, state->search_column); - - if (state->search_column < state->search_first_shown_column) - { - if (state->search_column > state->search_width - 4) - state->search_first_shown_column = state->search_column - - state->search_width + 4; - else - state->search_first_shown_column = 0; - } - state->search_display_column - = display_length(state->search, state->search_column); + search_position_to(state, i); return 0; } static int move_search_right(struct DrawState* state) { - ssize_t len = u32_strlen(state->search); - if (state->search_column == len) - return 0; - - if (state->search[state->search_column] == '\t') - state->search_display_column = display_length(state->search, - state->search_column + 1); - else - state->search_display_column++; - state->search_column++; - - if (state->search_display_column - state->search_first_shown_column - > state->search_width - 4) - state->search_first_shown_column = state->search_display_column - - state->search_width + 4; + assert(state != NULL); + search_position_to(state, state->search_display_column + 1); return 0; } static int move_search_start(struct DrawState* state) { - state->search_column = 0; - state->search_display_column = 0; - state->search_first_shown_column = 0; + assert(state != NULL); + search_position_to(state, 0); return 0; } static int move_start(struct DrawState* state) { + assert(state != NULL); if (state->edit_info_focused) { state->info_first_shown.row = 0; state->info_first_shown.col = 0; return 0; } - - state->coord.col = 0; - state->input_display_column = 0; - state->input_first_shown.col = 0; + position_to(state, state->coord.row, 0); return 0; } static int move_up(struct DrawState* state) { - ssize_t dlen; - + assert(state != NULL); if (state->edit_info_focused) { if (state->info_first_shown.row > 0) state->info_first_shown.row--; return 0; } - - if (state->coord.row > 0) - state->coord.row--; - if (state->input_first_shown.row > state->coord.row) - state->input_first_shown.row = state->coord.row; - if (state->saved_column == -1) - state->saved_column = state->input_display_column; - dlen = display_length(state->input_buffer[state->coord.row].text, - state->input_buffer[state->coord.row].length); - if (dlen < state->saved_column) - state->input_display_column = dlen; - else - state->input_display_column = state->saved_column; - state->coord.col - = input_length(state->input_buffer[state->coord.row].text, - state->input_display_column); - if (state->input_first_shown.col > state->input_display_column) - state->input_first_shown.col = state->input_display_column; - if (state->input_first_shown.col + state->edit_width - 4 - < state->input_display_column) - state->input_first_shown.col - = state->input_display_column - state->edit_width + 4; - state->input_display_column - = display_length(state->input_buffer[state->coord.row].text, - state->coord.col); + position_to(state, state->coord.row > 1 ? state->coord.row - 1 : 0, + state->coord.col); return 0; } @@ -1686,17 +1687,19 @@ static int next_fuzzy(struct DrawState* state) { struct PoEntry* current = NULL; + ssize_t e; + int saved_show_edit; + assert(state != NULL); if (state->msgid_count == 0) - return 0; + return 1; - state->error = ERR_NONE; - for (ssize_t e = state->msgid_number; e < state->msgid_count; e++) + for (e = state->msgid_number; e < state->msgid_count; e++) { current = state->entries + e; if (current->flags & FL_FUZZY) { - int saved_show_edit = state->show_edit; + saved_show_edit = state->show_edit; if (saved_show_edit) { if (state->dirty) @@ -1723,12 +1726,13 @@ static int next_match(struct DrawState* state) { struct PoEntry* current = NULL; + ssize_t e; + assert(state != NULL); if (state->msgid_count == 0 || !state->search || !*state->search) - return 0; + return 1; - state->error = ERR_NONE; - for (ssize_t e = state->msgid_number; e < state->real_msgid_count; e++) + for (e = state->msgid_number; e < state->real_msgid_count; e++) { current = state->entries + e; if (current->obsolete) @@ -1750,8 +1754,12 @@ static int next_plural(struct DrawState* state) { struct PoEntry* current = NULL; + assert((state != NULL) && (state->entries != NULL)); if (state->edit_info_focused) + { + state->error = ERR_DLG_FOCUS_EDIT; return 1; + } current = &state->entries[state->msgid_number - 1]; save_msgstr(state); if (state->msgstr_index + 1 < current->msgstr_count) @@ -1764,19 +1772,18 @@ static int next_untranslated(struct DrawState* state) { struct PoEntry* current = NULL; - + ssize_t e; + int saved_show_edit; + assert((state != NULL) && (state->entries != NULL)); if (state->msgid_count == 0) - return 0; - - state->error = ERR_NONE; - - for (ssize_t e = state->msgid_number; e < state->msgid_count; e++) + return 1; + for (e = state->msgid_number; e < state->msgid_count; e++) { current = state->entries + e; if (!current->msgstr || !*current->msgstr || u32_strlen(*current->msgstr) == 0) { - int saved_show_edit = state->show_edit; + saved_show_edit = state->show_edit; if (saved_show_edit) { if (state->dirty) @@ -1802,24 +1809,25 @@ next_untranslated(struct DrawState* state) static size_t next_msgid(struct DrawState* state, const int delta) { - size_t ret = state->msgid_number; - size_t start = ret; - size_t last_non_obsolete + size_t ret, start, last_non_obsolete; + assert((state != NULL) && (state->entries != NULL)); + ret = state->msgid_number; + start = ret; + last_non_obsolete = state->real_msgid_count - state->obsolete_at_end_count; - while (ret < last_non_obsolete + 1) { ret++; if (!state->entries[ret - 1].obsolete && ret >= start + delta) break; } - return ret == last_non_obsolete + 1 ? last_non_obsolete : ret; } static int overwrite_copy_msgid_callback(struct DrawState* state, struct tb_event* ev) { + assert((state != NULL) && (ev != NULL)); if (ev->ch == 'y') copy_msgid_to_input(state); state->prompt_callback = NULL; @@ -1829,6 +1837,7 @@ overwrite_copy_msgid_callback(struct DrawState* state, struct tb_event* ev) static int overwrite_paste_callback(struct DrawState* state, struct tb_event* ev) { + assert((state != NULL) && (ev != NULL)); if (ev->ch == 'y') paste_paste_buffer_to_input(state); state->prompt_callback = NULL; @@ -1841,16 +1850,24 @@ paste_paste_buffer_to_input(struct DrawState* state) struct BufferLine* pinput = NULL; struct BufferLine* newbuf = NULL; struct BufferLine* ppaste = NULL; + uint32_t* newtext = NULL; + uint32_t* utemp = NULL; + char* temp = NULL; ssize_t lines_to_init = 0; ssize_t lines_to_free = 0; + ssize_t i; + assert(state != NULL); if (state->edit_info_focused) + { + state->error = ERR_DLG_FOCUS_EDIT; return 1; + } if (!state->in_prompt && *state->input_buffer->text) { state->in_prompt = 1; - STRLCPY(prompt_overwrite, state->prompt, MAXBUFLINE); + MEMCCPY(state->prompt, prompt_overwrite, LINE_DEFAULT, temp); state->prompt_callback = overwrite_paste_callback; return 0; } @@ -1858,16 +1875,18 @@ paste_paste_buffer_to_input(struct DrawState* state) if (state->msgid_number == 1) { state->error = ERR_ILLEGAL_ON_FIRST; - return 0; + return 1; } if (!state->paste || !(*state->paste).text || !*(*state->paste).text) { /* leave one row */ - for (ssize_t i = 1; i < state->input_rows_count; i++) + for (i = 1; i < state->input_rows_count; i++) free_bufferline(state->input_buffer + i); - state->input_buffer = realloc(state->input_buffer, - sizeof(struct BufferLine)); + state->input_buffer = realloc(state->input_buffer, + sizeof(struct BufferLine)); + if (!state->input_buffer) + goto paste_paste_buffer_alloc_error; *(state->input_buffer->text) = 0; init_coord(&state->coord, 0, 0); state->input_display_column = 0; @@ -1884,7 +1903,7 @@ paste_paste_buffer_to_input(struct DrawState* state) ? state->input_rows_count - state->paste_count : 0; - for (ssize_t i = 0; i < lines_to_free; i++) + for (i = 0; i < lines_to_free; i++) free_bufferline( state->input_buffer + state->input_rows_count - i - 1); newbuf = realloc(state->input_buffer, @@ -1892,25 +1911,24 @@ paste_paste_buffer_to_input(struct DrawState* state) ppaste = state->paste; if (!newbuf) - return 0; + goto paste_paste_buffer_alloc_error; - for (ssize_t i = 0; i < lines_to_init; i++) + for (i = 0; i < lines_to_init; i++) { - uint32_t* newtext; pinput = newbuf + state->paste_count - 1 - i; init_bufferline(pinput); - newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); + newtext = calloc(LINE_DEFAULT, sizeof(uint32_t)); if (!newtext) - return 0; + goto paste_paste_buffer_alloc_error; pinput->text = newtext; } if (ppaste) - for (ssize_t i = 0; i < state->paste_count; i++) + for (i = 0; i < state->paste_count; i++) { pinput = newbuf + i; - u32_strncpy(pinput->text, ppaste->text, - MIN(ppaste->length + 1, MAXBUFLINE)); + U32_MEMCCPY(pinput->text, ppaste->text, + MIN(ppaste->length + 1, LINE_DEFAULT), utemp); pinput->length = ppaste->length; ppaste++; } @@ -1918,20 +1936,132 @@ paste_paste_buffer_to_input(struct DrawState* state) state->input_rows_count = state->paste_count; state->input_buffer = newbuf; state->dirty = 1; - return 0; + +paste_paste_buffer_alloc_error: + state->error = ERR_CANT_ALLOC; + return 1; +} + +/* + * state->saved_column - display coordinate + * row, column - buffer coordinates (not display) + */ +static void +position_to(struct DrawState* state, const int row, const int column) +{ + ssize_t dlen; + + assert(state != NULL); + if (row != state->coord.row) + { + state->coord.row = row; + if ((state->coord.row + 1) > state->input_rows_count) + state->coord.row = state->input_rows_count - 1; + if (state->coord.row < state->input_first_shown.row) + state->input_first_shown.row = state->coord.row; + if (state->input_first_shown.row + (EDIT_HEIGHT - 3) / 2 + < state->coord.row) + state->input_first_shown.row + = state->coord.row - (EDIT_HEIGHT - 3) / 2; + if (state->saved_column == -1) + { + state->saved_column = state->input_display_column; + state->coord.col = column; + } + else + state->coord.col = buffer_length( + state->input_buffer[state->coord.row].text, + state->saved_column); + if (state->coord.col < 0) + state->coord.col = 0; + else if (state->coord.col + > (int)state->input_buffer[state->coord.row].length) + state->coord.col + = state->input_buffer[state->coord.row].length; + dlen = display_length(state->input_buffer[state->coord.row].text, + state->coord.col); + if (dlen < state->saved_column) + state->input_display_column = dlen; + else + state->input_display_column = state->saved_column; + } + else + { + state->coord.col = column; + if (state->coord.col < 0) + { + if (state->coord.row > 0) + { + state->coord.row--; + if (state->input_first_shown.row > 0 + && state->input_first_shown.row + > state->coord.row) + state->input_first_shown.row--; + state->coord.col + = state->input_buffer[state->coord.row] + .length; + } + else + state->coord.col = 0; + } + else if (state->coord.col + > state->input_buffer[state->coord.row].length) + { + if ((state->coord.row + 1) < state->input_rows_count) + { + state->coord.row++; + if (state->input_first_shown.row + + (state->maxy - 2) + < state->coord.row + && state->input_first_shown.row + 1 + < state->input_rows_count) + state->input_first_shown.row++; + state->coord.col = 0; + } + else + state->coord.col + = state->input_buffer[state->coord.row] + .length; + } + state->input_display_column + = display_length(state->input_buffer[state->coord.row] + .text, + state->coord.col); + state->saved_column = state->input_display_column; + } + if (state->input_first_shown.col > state->input_display_column) + state->input_first_shown.col = state->input_display_column; + if (state->input_first_shown.col + state->edit_width - 4 + < state->input_display_column) + { + int index_first; + int fsc = state->input_first_shown.col; + index_first + = buffer_length(state->input_buffer[state->coord.row] + .text, + fsc); + while (fsc + state->edit_width < state->input_display_column + 4) + { + index_first++; + fsc = display_length(state->input_buffer[state->coord.row] + .text, + index_first); + } + state->input_first_shown.col = fsc; + } } static int prev_match(struct DrawState* state) { struct PoEntry* current = NULL; + ssize_t e; + assert(state != NULL); if (state->msgid_count == 0 || !state->search || !*state->search) - return 0; - - state->error = ERR_NONE; - for (ssize_t e = state->msgid_number - 1; e > 0; e--) + return 1; + for (e = state->msgid_number - 1; e > 0; e--) { current = state->entries + e - 1; if ((current->msgid && *current->msgid @@ -1953,34 +2083,43 @@ prev_msgid(struct DrawState* state, const int delta) ssize_t ret = state->msgid_number; ssize_t start = ret; + assert((state != NULL) && (state->entries != NULL)); if (start < delta) return state->real_msgid_count ? 1 : 0; - while (ret > 1) { ret--; if (!state->entries[ret - 1].obsolete && ret <= start - delta) break; } - return ret; } int print_error(const int code, const char* msg, ...) { - char buf[MAXBUFLINE]; + char* buf = NULL; va_list args; + long bufsize; + assert(msg != NULL); + bufsize = limits[L_LINE_MAX] ? limits[L_LINE_MAX] + : PRINT_BUF_MAX_FALLBACK; + buf = malloc(bufsize); + if (!buf) + return ERR_CANT_ALLOC; va_start(args, msg); - vsnprintf(buf, sizeof(buf), msg, args); + if (vsnprintf(buf, bufsize, msg, args) >= bufsize) + buf[bufsize - 1] = 0; va_end(args); fprintf(stderr, "%s: %s\n", PROGRAM_NAME, buf); + free(buf); return code; } static int quit_callback(struct DrawState* state, struct tb_event* ev) { + assert((state != NULL) && (ev != NULL)); if (ev->ch == 'y') write_file(state); state->prompt_callback = NULL; @@ -2004,8 +2143,12 @@ redraw_status(struct DrawState* state) static int prev_plural(struct DrawState* state) { + assert(state != NULL); if (state->edit_info_focused) + { + state->error = ERR_DLG_FOCUS_EDIT; return 1; + } save_msgstr(state); if (state->msgstr_index > 0) state->msgstr_index--; @@ -2017,10 +2160,14 @@ static int save_msgstr(struct DrawState* state) { struct BufferLine* pinput_buffer; + struct PoEntry* current = NULL; uint32_t* to_save = NULL; + uint32_t* temp = NULL; + uint32_t* temp2 = NULL; size_t to_save_size = 0; - struct PoEntry* current = NULL; + ssize_t i; + assert(state != NULL); state->show_edit = 0; state->edit_info_focused = 0; @@ -2031,21 +2178,28 @@ save_msgstr(struct DrawState* state) if (!to_save) { state->error = ERR_CANT_ALLOC; - return 0; + return 1; } pinput_buffer = NULL; - for (ssize_t i = 0; i < state->input_rows_count; i++) + for (i = 0; i < state->input_rows_count; i++) { - pinput_buffer = state->input_buffer + i; + pinput_buffer = &state->input_buffer[i]; if (i == 0) - u32_strncpy(to_save, pinput_buffer->text, - to_save_size * sizeof(uint32_t)); - else + U32_MEMCCPY(to_save, pinput_buffer->text, + to_save_size * sizeof(uint32_t), temp); + else if (temp) { - u32_strncat(to_save, (uint32_t*)L"\\n", + U32_U8_MEMCCPY((temp - 1), "\\n", + to_save_size * sizeof(uint32_t), temp2); + if (temp2) + U32_MEMCCPY((temp2 - 1), + pinput_buffer->text, + to_save_size * sizeof(uint32_t), + temp); + /*u32_strncat(to_save, BSLNEWLINE , to_save_size * sizeof(uint32_t)); u32_strncat(to_save, pinput_buffer->text, - to_save_size * sizeof(uint32_t)); + to_save_size * sizeof(uint32_t));*/ } free_bufferline(pinput_buffer); } @@ -2053,17 +2207,18 @@ save_msgstr(struct DrawState* state) free(state->input_buffer); state->input_buffer = NULL; state->input_rows_count = 0; - for (ssize_t i = 0; i < state->info_rows_count; i++) + for (i = 0; i < state->info_rows_count; i++) free_bufferline(state->info_buffer + i); free(state->info_buffer); - state->dirty = (state->dirty_before_edit || state->dirty); + state->dirty = (state->dirty_before_edit || state->dirty); + // state->dirty_before_edit = 0; state->info_rows_count = 0; state->info_buffer = NULL; u32_encode_tabs(to_save, to_save_size * sizeof(uint32_t)); current = &state->entries[state->msgid_number - 1]; u32_match_msgid_ending(current->msgid, to_save, to_save_size * sizeof(uint32_t)); - u32_set_msgstr(current, to_save, state->msgstr_index, 0, state); + u32_set_msgstr(state, current, to_save, state->msgstr_index, 0); update_warning(current->msgid, to_save, &current->warning); state->input_changed_fuzzy = UNCHANGED; @@ -2073,17 +2228,38 @@ save_msgstr(struct DrawState* state) } static int +search_position_to(struct DrawState* state, const int column) +{ + ssize_t len; + + assert((state != NULL) && (state->search != NULL)); + len = u32_strlen(state->search); + state->search_column = column; + + if (state->search_column > len) + state->search_column = len; + state->search_display_column + = display_length(state->search, state->search_column); + if (state->search_first_shown_column > state->search_display_column) + state->search_first_shown_column = state->search_display_column; + if (state->search_first_shown_column + (state->search_width - 4) + < state->search_column) + state->search_first_shown_column = state->search_display_column + - (state->search_width - 4); + return 0; +} + +static int show_edit(struct DrawState* state) { if (state->msgstr_index + 1 > state->entries[state->msgid_number - 1].msgstr_count) state->msgstr_index = 0; - if (!load_msgstr(state) || !load_info(state)) + if (load_msgstr(state) || load_info(state)) { - state->error = ERR_CANT_ALLOC; state->running = 0; - return 0; + return 1; } state->dirty_before_edit = state->dirty; @@ -2215,7 +2391,7 @@ yank_input_to_paste_buffer(struct DrawState* state) uint32_t* newtext; ppaste = newbuf + state->input_rows_count - 1 - i; init_bufferline(ppaste); - newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); + newtext = calloc(LINE_DEFAULT, sizeof(uint32_t)); if (!newtext) return 0; ppaste->text = newtext; @@ -2226,7 +2402,7 @@ yank_input_to_paste_buffer(struct DrawState* state) { ppaste = newbuf + i; u32_strncpy(ppaste->text, pinput->text, - MIN(pinput->length + 1, MAXBUFLINE)); + MIN(pinput->length + 1, LINE_DEFAULT)); ppaste->length = pinput->length; pinput++; } @@ -2287,8 +2463,8 @@ main(int argc, char** argv) const char* saved_tb_error = NULL; char* wd = NULL; ssize_t filename_size = 0; - long lineno; - long col; + long lineno = 1; + long col = 1; int dir_access; int dir_access_errno; int optind = 1; @@ -2312,8 +2488,8 @@ main(int argc, char** argv) return print_error(CUSTOM_ERROR_START + ERR_NO_CL_ARG, "No filename given"); - // main_options_loop: - if (optind == argc) +main_options_loop: + if (optind >= argc) return print_error(CUSTOM_ERROR_START + ERR_NO_CL_ARG, "No filename given"); @@ -2321,7 +2497,10 @@ main(int argc, char** argv) { printf("Usage:\t%s -h | --help | -V | --full-version | -v |" " --version\n" - "\t%s filename.po\n", + "\t%s [-B | --no-backup] [-b | --backup]" + " [-L | --dont-break-literals]" + " [-W | --no-wrap] [-w <cols> | --wrap-width=<cols>]" + " filename.po\n", PROGRAM_NAME, PROGRAM_NAME); return 0; } @@ -2337,26 +2516,80 @@ main(int argc, char** argv) printf("%s %s, committed on %s\n", PROGRAM_NAME, VERSION, DATE); puts(COPYRIGHT); printf("Features set in config.h:\n"); - printf(" backup_fail:\t\t%s\n", backup_fail ? "set" : "unset"); + printf(" backup_fail:\t\t\t%s\n", + backup_fail ? "set" : "unset"); if (backup_suffix) - printf(" backup_suffix:\t\"%s\"\n", backup_suffix); + printf(" backup_suffix:\t\t\"%s\"\n", backup_suffix); else - printf(" backup_suffix:\tunset\n"); - printf(" create_backups:\t%s\n", + printf(" backup_suffix:\t\tunset\n"); + printf(" break_literals:\t\t%s\n", + break_literals ? "set" : "unset"); + printf(" create_backups:\t\t%s\n", create_backups ? "set" : "unset"); - printf(" match_msgstr_ending:\t%s\n", + printf(" literal_break_threshold:\t%d\n", + literal_break_threshold); + printf(" match_msgstr_ending:\t\t%s\n", match_msgstr_ending ? "set" : "unset"); if (plural_string) - printf(" plural_string:\t\"%s\"\n", plural_string); + printf(" plural_string:\t\t\"%s\"\n", plural_string); else - printf(" plural_string:\tunset\n"); - printf(" warn_count_dots:\t%s\n", + printf(" plural_string:\t\tunset\n"); + printf(" warn_count_dots:\t\t%s\n", warn_count_dots ? "set" : "unset"); - printf(" wrap_first_msgstr:\t%s\n", - wrap_first_msgstr ? "set" : "unset"); - printf(" wrap_width:\t\t%d\n", wrap_width); + printf(" wrap_width:\t\t\t%d\n", wrap_width); return 0; } + else if (!strcmp(argv[optind], "-B") + || !strcmp(argv[optind], "--no-backup")) + { + create_backups = 0; + optind++; + goto main_options_loop; + } + else if (!strcmp(argv[optind], "-b") + || !strcmp(argv[optind], "--backup")) + { + create_backups = 1; + optind++; + goto main_options_loop; + } + else if (!strcmp(argv[optind], "-L") + || !strcmp(argv[optind], "--dont-break-literals")) + { + break_literals = 0; + optind++; + goto main_options_loop; + } + else if (!strcmp(argv[optind], "-W") + || !strcmp(argv[optind], "--no-wrap")) + { + wrap_width = 0; + optind++; + goto main_options_loop; + } + else if (!strcmp(argv[optind], "-w") + || !strcmp(argv[optind], "--wrap-width=")) + { + optind++; + if (sscanf(argv[optind], "%d", &wrap_width) < 1) + { + if (*argv[optind] == '-') + state.error = ERR_NO_CL_ARG_PARAM; + else + state.error = ERR_BAD_CL_ARG; + goto main_done; + } + if (wrap_width < 0) + wrap_width = 0; + optind++; + goto main_options_loop; + } + else if (starts_with(argv[optind], "--") + || starts_with(argv[optind], "-")) + { + state.error = ERR_BAD_CL_ARG; + goto main_done; + } /* * Chicken and egg problem: to get fpathconf(dirfd, _PC_PATH_MAX) @@ -2603,7 +2836,6 @@ main_skip_backups: goto init_termbox; init_termbox: - tbires = 0; if ((tbires = tb_init())) { saved_tb_error = tb_strerror(tbires); @@ -2658,6 +2890,12 @@ main_done: case ERR_NOTREG_FILE: print_error(1, errors[state.error], state.filename); break; + case ERR_NO_CL_ARG_PARAM: + print_error(1, errors[state.error], argv[optind - 1]); + break; + case ERR_BAD_CL_ARG: + print_error(1, errors[state.error], argv[optind]); + break; case ERR_SYNTAX: print_error(1, errors[state.error], state.filename, lineno, col); break; diff --git a/poe.h b/poe.h @@ -27,15 +27,22 @@ static void handle_key_event(struct tb_event* ev, struct DrawState* state); static int hide_search(struct DrawState* state); static int insert_char(struct DrawState* state, uint32_t ch); static int insert_line(struct DrawState* state); -static void insert_search_char(struct DrawState* state, uint32_t ch); +static int insert_search_char(struct DrawState* state, uint32_t ch); static int insert_search_space(struct DrawState* state); static int insert_search_tab(struct DrawState* state); static int insert_space(struct DrawState* state); static int insert_tab(struct DrawState* state); -static struct DrawState* join_lines(struct DrawState* state, size_t first_line, - int forward); -static struct BufferLine* load_info(struct DrawState* state); -static struct BufferLine* load_msgstr(struct DrawState* state); +static int join_lines(struct DrawState* state); +static int load_info(struct DrawState* state); +static int load_info_comments(struct DrawState* state, struct BufferLine** lines); +static int load_info_extr_comments(struct DrawState* state, + struct BufferLine** lines); +static int load_info_msgid(struct DrawState* state, struct BufferLine** lines); +static int load_info_msgid_plural(struct DrawState* state, + struct BufferLine** lines); +static int load_info_ref_comments(struct DrawState* state, + struct BufferLine** lines); +static int load_msgstr(struct DrawState* state); static int move_down(struct DrawState* state); static int move_end(struct DrawState* state); static int move_left(struct DrawState* state); @@ -61,9 +68,13 @@ static int next_match(struct DrawState* state); static int next_plural(struct DrawState* state); static int next_untranslated(struct DrawState* state); static size_t next_msgid(struct DrawState* state, const int delta); -static int overwrite_copy_msgid_callback(struct DrawState* state, struct tb_event* ev); -static int overwrite_paste_callback(struct DrawState* state, struct tb_event* ev); +static int overwrite_copy_msgid_callback(struct DrawState* state, + struct tb_event* ev); +static int overwrite_paste_callback(struct DrawState* state, + struct tb_event* ev); static int paste_paste_buffer_to_input(struct DrawState* state); +static void position_to(struct DrawState* state, const int row, + const int column); static int prev_match(struct DrawState* state); static size_t prev_msgid(struct DrawState* state, const int delta); static int prev_plural(struct DrawState* state); @@ -71,6 +82,7 @@ int print_error(const int code, const char* msg, ...); static int quit_callback(struct DrawState* state, struct tb_event* ev); void redraw_status(struct DrawState* state); static int save_msgstr(struct DrawState* state); +static int search_position_to(struct DrawState* state, const int column); static int show_edit(struct DrawState* state); static int show_help(struct DrawState* state); static int show_search(struct DrawState* state); diff --git a/termbox.h b/termbox.h @@ -2345,7 +2345,9 @@ static int bytebuf_flush(struct bytebuf *b, int fd); static int bytebuf_reserve(struct bytebuf *b, size_t sz); static int bytebuf_free(struct bytebuf *b); static int tb_iswprint_ex(uint32_t ch, int *width); +#ifdef TB_OPT_EGC static int tb_cluster_width(uint32_t *ch, size_t nch); +#endif int tb_init(void) { return tb_init_file("/dev/tty"); @@ -4335,6 +4337,7 @@ int tb_wcwidth(uint32_t ch) { return w; } +#ifdef TB_OPT_EGC static int tb_cluster_width(uint32_t *ch, size_t nch) { int wmax = -1; int vs15 = 0, vs16 = 0, ri = 0, zwj = 0; @@ -4356,6 +4359,7 @@ static int tb_cluster_width(uint32_t *ch, size_t nch) { } return wmax; } +#endif static int tb_iswprint_ex(uint32_t ch, int *w) { #ifdef TB_OPT_LIBC_WCHAR diff --git a/util.c b/util.c @@ -13,9 +13,9 @@ #include "util.h" /* Whitespace definition for determining word boundaries */ -#define B_WS " \v\t" +#define B_WS " \v\t-" /* Full word boundary delimiter definition */ -#define B_FULL " \v\t()[]{},.;:/\\" +#define B_FULL " \v\t-()[]{},.;:/\\" enum { UTLERRNONE = 0, UTLERROVFL = 1 }; @@ -84,7 +84,7 @@ u32_match_msgid_ending(const uint32_t* msgid, uint32_t* msgstr, size_t max) size_t msgid_len = 0; buf = calloc(max + 1, sizeof(uint32_t)); - if (!buf) + if (!buf || !msgid) return 0; buf_len = u32_strncpy(buf, msgstr, (max + 1) * sizeof(uint32_t)); @@ -133,6 +133,50 @@ u32_match_msgid_ending(const uint32_t* msgid, uint32_t* msgstr, size_t max) return buf_len; } +uint32_t* +u32_memccpy(uint32_t* dst, const uint32_t* src, uint32_t c, size_t len) +{ + size_t copied = 0; + assert((dst != NULL) && (src != NULL)); + while ((*dst++ = *src++) != c && copied < len) + copied++; + if (copied == len) + return NULL; + return dst; +} + +/* len is the max length of src in bytes */ +uint32_t* +u32_u8_memccpy(uint32_t* dst, const char* src, uint32_t c, size_t len) +{ + const char* psrc = src; + uint32_t uch; + size_t copied = 0; + int u8_len; + + assert((dst != NULL) && (src != NULL)); + do + { + if (!*psrc) + { + *dst++ = 0; + break; + } + u8_len = tb_utf8_char_to_unicode(&uch, psrc); + if (u8_len < 0) + { + /* error, replace uch with "error char" */ + uch = 0xFFFD; + u8_len = -u8_len; + } + psrc += u8_len; + copied += u8_len; + if (copied >= len) + return NULL; + } while ((*dst++ = uch) != c && copied < len); + return dst; +} + size_t u32_count_chars(const uint32_t* s, const uint32_t ch) { @@ -368,7 +412,7 @@ u32_lines_in_string(const uint32_t* s, const int include_eol, const size_t wrap) { size_t result = 1; const uint32_t* ps = NULL; - int last_empty = 0; + //int last_empty = 0; if (!s) return result; @@ -380,9 +424,9 @@ u32_lines_in_string(const uint32_t* s, const int include_eol, const size_t wrap) if (*ps) result++; } - if (ps > s + 1 && *(ps - 2) == '\\' && *(ps - 1) == 'n') - last_empty = 1; - return result + last_empty; + /*if (ps > s + 1 && *(ps - 2) == '\\' && *(ps - 1) == 'n') + last_empty = 1;*/ + return result;// + last_empty; } /* @@ -432,7 +476,7 @@ u32_next_line(const uint32_t* s, const uint32_t** next_line, if (!u32_is_word_boundary(*ps, 1) && u32_is_word_boundary(*(ps + 1), 1)) { - last_nonblank = ps - start; + last_nonblank = ps + 1 - start; have_last_nonblank = 1; } diff --git a/util.h b/util.h @@ -5,78 +5,60 @@ #include <stdint.h> #include <sys/types.h> -#define ABS(a) ((a < 0) ? (-a) : (a)) -#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); \ - } \ +#define ABS(a) ((a < 0) ? (-a) : (a)) +#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 MEMCCPY(to, from, tolen, temp) \ + do \ + { \ + temp = memccpy((to), (from), 0, (tolen)); \ + if (!(temp)) \ + (to)[(tolen) > 0 ? (tolen) - 1 : 0] = 0; \ } 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) -#define MEMCCPY(to, from, tolen, temp) \ - do \ - { \ - temp = memccpy(to, from, 0, tolen); \ - if (!temp) \ - to[tolen > 0 ? tolen - 1 : 0] = 0; \ - } while (0) -#define MEMCCPY_EXT(to, fallbackto, from, tolen, temp) \ - do \ - { \ - temp = memccpy(to, from, 0, tolen); \ - if (!temp) \ - fallbackto[tolen > 0 ? tolen - 1 : 0] = 0; \ +#define MEMCCPY_EXT(to, fallbackto, from, tolen, temp) \ + do \ + { \ + temp = memccpy((to), (from), 0, (tolen)); \ + if (!(temp)) \ + (fallbackto)[(tolen) > 0 ? (tolen) - 1 : 0] = 0; \ } while (0) -#define U32_MEMCCPY(to, from, tolen, temp) \ - do \ - { \ - temp = u32_memccpy(to, from, 0L, tolen); \ - if (!temp) \ - to[tolen > 0 ? tolen - 1 : 0] = 0; \ +#define U32_MEMCCPY(to, from, tolen, temp) \ + do \ + { \ + (temp) = u32_memccpy((to), (from), 0L, (tolen)); \ + if (!(temp)) \ + (to)[(tolen) > 0 ? (tolen) - 1 : 0] = 0; \ } while (0) -#define U32_U8_MEMCCPY(to, from, tolen, temp) \ - do \ - { \ - temp = u32_u8_memccpy(to, from, 0L, tolen); \ - if (!temp) \ - to[tolen > 0 ? tolen - 1 : 0] = 0; \ +#define U32_U8_MEMCCPY(to, from, tolen, temp) \ + do \ + { \ + (temp) = u32_u8_memccpy((to), (from), 0L, (tolen)); \ + if (!(temp)) \ + (to)[(tolen) > 0 ? (tolen) - 1 : 0] = 0; \ } while (0) -#define U32_MEMCCPY_EXT(to, fallbackto, from, tolen, fbtolen, temp) \ - do \ - { \ - temp = u32_memccpy(to, from, 0L, tolen); \ - if (!temp) \ - fallbackto[fbtolen > 0 ? fbtolen - 1 : 0] = 0; \ +#define U32_MEMCCPY_EXT(to, fallbackto, from, tolen, fbtolen, temp) \ + do \ + { \ + temp = u32_memccpy((to), (from), 0L, (tolen)); \ + if (!temp) \ + (fallbackto)[(fbtolen) > 0 ? (fbtolen) - 1 : 0] = 0; \ } while (0) -#define ENSURE_SIZE(to, temp, tosize, testsize, fromsize, label, type) \ - do \ - { \ - if (!(to) || (tosize) < (testsize)) \ - { \ - temp = realloc(to, (fromsize) * sizeof(type)); \ - if (!temp) \ - { \ - state->running = 0; \ - goto label; \ - } \ - tosize = fromsize; \ - to = temp; \ - } \ +#define ENSURE_SIZE(to, temp, tosize, testsize, fromsize, label, type) \ + do \ + { \ + if (!(to) || (tosize) < (testsize)) \ + { \ + temp = realloc((to), (fromsize) * sizeof(type)); \ + if (!temp) \ + { \ + state->running = 0; \ + goto label; \ + } \ + tosize = (fromsize); \ + to = temp; \ + } \ } while (0) /* * - Make sure the size of the destination string is large enough to copy, and @@ -112,6 +94,8 @@ enum { ERR_NONE, ERR_NO_CL_ARG, + ERR_NO_CL_ARG_PARAM, + ERR_BAD_CL_ARG, ERR_NOTPO_FILE, ERR_NOTREG_FILE, ERR_TB_ERROR, @@ -133,7 +117,7 @@ enum { enum { ACT_IDLE, ACT_WRITING, ACT_READING }; -#define MAXBUFLINE 4096 +// #define MAXBUFLINE 4096 #define MAXCOPYBUF 8192 #define MAXDATEBUF 80 #define MAXFLAGSBUF (2 + 4 + 2 + 1) @@ -145,12 +129,13 @@ enum { ACT_IDLE, ACT_WRITING, ACT_READING }; extern int utlerrno; -size_t strlcpy(char* dst, const char* src, size_t dsize); -size_t strlcat(char* dst, const char* src, size_t dsize); 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 uint32_t* us, const size_t max); size_t u32_match_msgid_ending(const uint32_t* msgid, uint32_t* msgstr, size_t max); +uint32_t* u32_memccpy(uint32_t* dst, const uint32_t* src, uint32_t c, + size_t len); +uint32_t* u32_u8_memccpy(uint32_t* dst, const char* src, uint32_t c, size_t len); size_t u32_count_chars(const uint32_t* s, const uint32_t ch); size_t u32_count_strings(const uint32_t* haystack, const uint32_t* needle); size_t u32_encode_tabs(uint32_t* s, size_t max);