poe

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

чување 9f5ea057100f4dd7471d0c5b48e86da92a7466bd
родитељ 61f8bd03e533f48a6d2883f733254fc8c711468d
Аутор: Страхиња Радић <sr@strahinja.org>
Датум:   Thu, 19 Feb 2026 19:54:49 +0100

Continue WIP: portable wide literals; handle TODOs; general tidy up

Diffstat:
MTODO | 29+++++++++++++++--------------
MTODO.done | 9+++++++++
Mdraw.c | 115+++++++++++++++++++++++++------------------------------------------------------
Mdraw.h | 13+++----------
Mpo.c | 2558+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mpo.h | 93++++++++++++++++++++++++++++++++++---------------------------------------------
Mpoe.1.in | 26+++++++++++++++-----------
Mpoe.c | 371++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mpoe.h | 5+++--
Mutil.c | 135+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mutil.h | 32+++++++++++++++++++++++++-------
измењених датотека: 11, додавања: 1748(+), брисања: 1638(-)

diff --git a/TODO b/TODO @@ -1,26 +1,27 @@ TODO ==== -[ ] Support Yudit's nonstandard PO format (no empty lines between individual - entries) - [~] Tidy up basic logic everywhere (based on sled) - [ ] Check cleanness against sled, function-by-function - Last: save_msgstr - [ ] Investigate insert_char + [x] Check cleanness against sled, function-by-function + [x] draw.c + [x] poe.c + [x] po.c + [x] util.c + [x] 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 - [ ] Get rid of SAVE_* + [x] Tidy up return codes and error handling + [x] Get rid of SAVE_* [x] Introduce position_to (sled) + [x] Handle all TODO comments + [x] util.c: u32_match_msgid_ending + [x] Check L"" literals -[x] Add -B, -b, -L, -W, -w - [x] --no-backup - [x] --backup - [x] --dont-break-literals - [x] --no-wrap - [x] --wrap-width +[ ] Document exit statuses in poe.1.in + +[ ] Support Yudit's nonstandard PO format (no empty lines between individual + entries) < > Wrap long lines? Not if it would complicate the code too much. diff --git a/TODO.done b/TODO.done @@ -1,6 +1,15 @@ Done todos ========== +[x] Add - to the list of whitespace characters + +[x] Add -B, -b, -L, -W, -w + [x] --no-backup + [x] --backup + [x] --dont-break-literals + [x] --no-wrap + [x] --wrap-width + [x] More sophisticated "not PO file" detection - avoid opening "binary" (relatively speaking) files diff --git a/draw.c b/draw.c @@ -58,6 +58,7 @@ const char* errors[] = { [ERR_ILLEGAL_ON_FIRST] = "Not allowed on first entry", [ERR_SYNTAX] = "%s:%ld:%ld: Syntax error", [ERR_NO_PLURAL_FORMS] = "No Plural-Forms (add manually)", + [ERR_BAD_NPLURALS] = "Cannot read nplurals", [ERR_ERRNO_SET] = "System call/library function error"}; const char* actions[] = { [ACT_WRITING] = "Writing...", @@ -88,8 +89,8 @@ ssize_t buffer_length(uint32_t* buffer, const ssize_t max_index) { uint32_t* pbuf = buffer; - ssize_t len = 0; ssize_t i; + ssize_t len = 0; int w; if (!pbuf) @@ -97,7 +98,7 @@ buffer_length(uint32_t* buffer, const ssize_t max_index) for (i = 0; *pbuf && len < max_index; i++) { - pbuf = buffer + i; + pbuf = &buffer[i]; if (*pbuf == '\t') len += MIN(TAB_SIZE - (len % TAB_SIZE), max_index - len); else @@ -216,10 +217,10 @@ draw_box(const int startx, const int endx, const int starty, const int endy, int draw_help(const struct DrawState* state) { + int maxx, maxy; + int row = 0; int startx, endx, starty, endy; int w, h; - int row = 0; - int maxx, maxy; assert(state != NULL); w = state->help_width; @@ -254,13 +255,14 @@ int draw_editbox(const struct DrawState* state) { struct PoEntry* entry; + struct BufferLine* curline; char flags_buf[MAXFLAGSBUF]; char sbuf[LINE_DEFAULT]; ssize_t dlen, i; uint16_t fg, bg; + int maxx, maxy; int startx, endx, starty, endy; int w, h; - int maxx, maxy; assert(state != NULL); w = state->edit_width; @@ -294,8 +296,7 @@ draw_editbox(const struct DrawState* state) < state->info_rows_count; i++) { - struct BufferLine* curline - = &state->info_buffer[state->info_first_shown.row + i]; + curline = &state->info_buffer[state->info_first_shown.row + i]; if (!curline || !curline->text) return 1; dlen = display_length(curline->text, curline->length); @@ -318,13 +319,12 @@ draw_editbox(const struct DrawState* state) : colors[C_EDIT_MSGSTR_FOCUS].bg; draw_rect(startx + 1, endx - 1, endy - h / 2 - 1, endy - 2, fg, bg); - for (ssize_t i = 0; i < (h - 3) / 2 + 1 + for (i = 0; i < (h - 3) / 2 + 1 && (ssize_t)state->input_first_shown.row + i < state->input_rows_count; i++) { - struct BufferLine* curline - = &state->input_buffer[state->input_first_shown.row + i]; + curline = &state->input_buffer[state->input_first_shown.row + i]; if (!curline || !curline->text) return 1; dlen = display_length(curline->text, curline->length); @@ -370,9 +370,9 @@ draw_list_visible(const struct DrawState* state) { const struct PoEntry* current = NULL; ssize_t i = 0; + int cy = 0; int flags_len; int max_screen_entries = 0; - int cy = 0; assert(state != NULL); @@ -460,8 +460,9 @@ void draw_rect(const int startx, const int endx, const int starty, const int endy, const uint16_t fg, const uint16_t bg) { - for (int y = starty; y < endy; y++) - for (int x = startx; x < endx; x++) + int x, y; + for (y = starty; y < endy; y++) + for (x = startx; x < endx; x++) tb_set_cell(x, y, ' ', fg, bg); } @@ -470,9 +471,9 @@ draw_search(const struct DrawState* state) { ssize_t len; ssize_t dlen = 0; + int maxx, maxy; int startx, endx, starty, endy; int w, h; - int maxx, maxy; assert(state != NULL); w = state->search_width; @@ -510,7 +511,7 @@ draw_status(const struct DrawState* state) char* buf = NULL; ssize_t seg_size; int current_start; - int maxx; + int last, maxx; assert(state != NULL); maxx = state->maxx; @@ -545,7 +546,7 @@ draw_status(const struct DrawState* state) else while (pseg < status_segments + LEN(status_segments)) { - int last = pseg - status_segments + last = pseg - status_segments == LEN(status_segments) - 1; buf = calloc(limits[L_LINE_MAX], 1); if (!buf) @@ -706,8 +707,6 @@ void init_drawstate(struct DrawState* state, char* filename, char* prompt) { assert(state != NULL); - state->error = ERR_NONE; - state->action = ACT_IDLE; state->prompt = prompt; state->filename = filename; state->orig_real_filename = NULL; @@ -715,27 +714,23 @@ init_drawstate(struct DrawState* state, char* filename, char* prompt) state->backup_filename = NULL; state->rfn_len = 0; state->search = NULL; + state->error = ERR_NONE; + state->action = ACT_IDLE; state->input_buffer = NULL; state->buffer_size = 0; init_coord(&state->coord, 0, 0); init_coord(&state->first_shown, 0, 0); init_coord(&state->input_first_shown, 0, 0); - // state->input_column = 0; - // state->input_row = 0; - /*state->input_first_shown_column = 0; - state->input_first_shown_row = 0;*/ state->input_rows_count = 0; state->input_display_column = 0; state->input_changed_fuzzy = UNCHANGED; state->saved_column = -1; state->info_buffer = NULL; init_coord(&state->info_first_shown, 0, 0); - /*state->info_first_shown_column = 0; - state->info_first_shown_row = 0;*/ state->info_rows_count = 0; state->info_maxlen = 0; - state->help_width = calculate_help_width(state); state->edit_width = calculate_edit_width(state); + state->help_width = calculate_help_width(state); state->search_width = calculate_search_width(state); state->paste = NULL; state->paste_count = 0; @@ -751,15 +746,15 @@ init_drawstate(struct DrawState* state, char* filename, char* prompt) state->dirty_before_edit = 0; state->fuzzy_before_edit = 0; state->in_prompt = 0; - state->show_help = 0; state->show_edit = 0; + state->show_help = 0; state->edit_info_focused = 0; state->show_search = 0; state->recalculate_size = 1; state->maxx = 0; state->maxy = 0; - state->msgid_number = state->msgid_count > 0 ? 1 : 0; state->msgid_count = 0; + state->msgid_number = state->msgid_count > 0 ? 1 : 0; state->msgid_size = 0; state->fuzzy_count = 0; state->untranslated_count = 0; @@ -778,9 +773,10 @@ ssize_t input_length(uint32_t* buffer, const ssize_t max_index) { uint32_t* pbuf = buffer; - ssize_t len = 0; ssize_t i = 0; + ssize_t len = 0; + assert(buffer != NULL); for (i = 0; *pbuf && len < max_index; i++) { pbuf = buffer + i; @@ -796,9 +792,11 @@ input_length(uint32_t* buffer, const ssize_t max_index) size_t real_msgid_to_display_msgid(const struct DrawState* state) { - struct PoEntry* current = state->entries; - size_t ret = 1; + struct PoEntry* current; + size_t ret = 1; + assert((state != NULL) && (state->entries != NULL)); + current = state->entries; if (!state->msgid_count) return 0; @@ -824,49 +822,6 @@ recalculate_size(struct DrawState* state) state->search_width = calculate_search_width(state); } -struct DrawState* -set_nplurals(struct DrawState* state, struct PoEntry* entry) -{ - if (!entry || !entry->plural_forms) - return NULL; - - int read_number = 0; - uint32_t* ppf = entry->plural_forms; - char token[LINE_DEFAULT]; - char* ptoken = token; - - *ptoken = 0; - while (*ppf) - { - if (u32_starts_with(ppf, (uint32_t*)L"nplurals=")) - { - ppf += strlen("nplurals="); - read_number = 1; - } - else if (read_number && *ppf >= '0' && *ppf <= '9') - { - *ptoken++ = *ppf++; - *ptoken = 0; - } - else if (read_number && *ppf == ';') - { - *ptoken = 0; - ptoken = token; - break; - } - else - ppf++; - } - if (*ptoken) - { - state->nplurals = strtol(ptoken, NULL, 0); - if (errno) - return NULL; - } - - return state; -} - /* mc - (max cols) pack text and padding into this many columns * fc - (fill cols) this many columns from the left/right/center will be * filled @@ -928,6 +883,8 @@ u32_draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, * reaches zero the highlighting is turned * off */ + int j; + c = mc - p_l - p_r; if (align == LEFT) @@ -939,11 +896,11 @@ u32_draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, /* fill */ if (fill) - for (int j = 0; j < fc; j++) + for (j = 0; j < fc; j++) tb_set_cell(x + j, y, ' ', fg, bg); /* left padding */ - for (int j = 0; j < p_l; j++) + for (j = 0; j < p_l; j++) tb_set_cell(x + d + j, y, ' ', fg, bg); sc = 0; @@ -983,7 +940,7 @@ u32_draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, * */ if (ssc - (ssc % ts) + ts >= ds) { - for (int j = 0; + for (j = 0; j < ssc - (ssc % ts) + ts - ds; j++) tb_set_cell(x + d + p_l + sc + j, @@ -1016,7 +973,7 @@ u32_draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, * */ if (ssc + (ts - (ssc % ts)) >= ds + c) { - for (int j = 0; j < ds + c - ssc; j++) + for (j = 0; j < ds + c - ssc; j++) tb_set_cell(x + d + p_l + sc + j, y, ' ', cfg, cbg); sc = c; @@ -1036,7 +993,7 @@ u32_draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, * | ssc * ds * */ - for (int j = 0; j < ts - (ssc % ts); j++) + for (j = 0; j < ts - (ssc % ts); j++) tb_set_cell(x + d + p_l + sc + j, y, ' ', cfg, cbg); sc += ts - (ssc % ts); @@ -1070,6 +1027,6 @@ u32_draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, } /* right padding */ - for (int j = 0; j < p_r; j++) + for (j = 0; j < p_r; j++) tb_set_cell(x + d + p_l + c + j, y, ' ', fg, bg); } diff --git a/draw.h b/draw.h @@ -100,10 +100,6 @@ struct DrawState { struct Coord coord; /* buffer coordinates */ struct Coord first_shown; /* display coordinates */ struct Coord input_first_shown; /* display coordinates */ - /*int input_column; - int input_row;*/ - /*int input_first_shown_column; - int input_first_shown_row;*/ ssize_t input_rows_count; /* buffer parameter */ int input_display_column; /* display coordinate */ FlagChange input_changed_fuzzy; @@ -111,12 +107,10 @@ struct DrawState { struct BufferLine* info_buffer; /* information pane above * edit box */ struct Coord info_first_shown; /* display coordinates */ - /*int info_first_shown_column; - int info_first_shown_row;*/ int info_rows_count; int info_maxlen; - int help_width; /* display parameter */ int edit_width; /* display parameter */ + int help_width; /* display parameter */ int search_width; /* display parameter */ struct BufferLine* paste; ssize_t paste_count; @@ -132,15 +126,15 @@ struct DrawState { int dirty_before_edit; ssize_t fuzzy_before_edit; int in_prompt; - int show_help; int show_edit; + int show_help; int edit_info_focused; int show_search; int recalculate_size; int maxx; int maxy; - ssize_t msgid_number; /* "real" msgid (inc. obsolete msgids) */ ssize_t msgid_count; /* without obsolete msgids */ + ssize_t msgid_number; /* "real" msgid (inc. obsolete msgids) */ ssize_t msgid_size; ssize_t fuzzy_count; ssize_t untranslated_count; @@ -198,7 +192,6 @@ void init_drawstate(struct DrawState* state, char* filename, char* prompt); ssize_t input_length(uint32_t* buffer, const ssize_t max_index); size_t real_msgid_to_display_msgid(const struct DrawState* state); void recalculate_size(struct DrawState* state); -struct DrawState* set_nplurals(struct DrawState* state, struct PoEntry* entry); void u32_draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, const uint32_t* s, const int max_cols, const int fill_cols, const int padding_left, const int padding_right, diff --git a/po.c b/po.c @@ -17,12 +17,50 @@ #include "util.h" #include "version.h" +#define INC_COL() \ + do \ + { \ + if (!dstate->error) \ + (*col)++; \ + } while (0) + extern const char* errors[]; extern const char* flag_strings[]; 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); +extern int redraw_status(struct DrawState* state); + +const uint32_t empty_string[] = {0}; + +int +add_ref_comment(struct PoEntry* entry, const char* comment) +{ + char** ref_comments = NULL; + char* new_line = NULL; + size_t len = 0; + + assert((entry != NULL) && (comment != NULL)); + + entry->ref_comment_lines++; + + if (!entry->ref_comments) + ref_comments = calloc(entry->ref_comment_lines, sizeof(char*)); + else + ref_comments = realloc(entry->ref_comments, + entry->ref_comment_lines * sizeof(char*)); + if (!ref_comments) + return 1; + entry->ref_comments = ref_comments; + + len = strlen(comment) + 1; + new_line = calloc(len, 1); + if (!new_line) + return 1; + entry->ref_comments[entry->ref_comment_lines - 1] = new_line; + strncpy(new_line, comment, len); + return 0; +} char* format_flags(char* buffer, const size_t max, const struct PoEntry* entry, @@ -38,31 +76,6 @@ format_flags(char* buffer, const size_t max, const struct PoEntry* entry, } void -init_po_entry(struct PoEntry* entry) -{ - entry->flags = FL_NONE; - entry->obsolete = 0; - entry->warning = 0; - entry->msgid = NULL; - entry->msgid_size = 0; - entry->msgid_len = 0; - entry->msgid_plural = NULL; - entry->msgid_plural_size = 0; - entry->msgid_plural_len = 0; - entry->plural_forms = NULL; - entry->msgstr = NULL; - entry->msgstr_count = 0; - entry->msgstr_size = NULL; - entry->msgstr_len = NULL; - entry->comments = NULL; - entry->comment_lines = 0; - entry->extr_comments = NULL; - entry->extr_comment_lines = 0; - entry->ref_comments = NULL; - entry->ref_comment_lines = 0; -} - -void free_po_entry(struct PoEntry* entry) { if (!entry) @@ -107,453 +120,116 @@ free_po_entry(struct PoEntry* entry) } } -struct PoEntry* -set_flags(struct PoEntry* entry, const Flags flags) +void +init_po_entry(struct PoEntry* entry) { - if (!entry) - return NULL; - entry->flags = flags; - return entry; + entry->flags = FL_NONE; + entry->obsolete = 0; + entry->warning = 0; + entry->msgid = NULL; + entry->msgid_size = 0; + entry->msgid_len = 0; + entry->msgid_plural = NULL; + entry->msgid_plural_size = 0; + entry->msgid_plural_len = 0; + entry->plural_forms = NULL; + entry->plural_forms_size = 0; + entry->plural_forms_len = 0; + entry->msgstr = NULL; + entry->msgstr_count = 0; + entry->msgstr_size = NULL; + entry->msgstr_len = NULL; + entry->comments = NULL; + entry->comment_lines = 0; + entry->extr_comments = NULL; + entry->extr_comment_lines = 0; + entry->ref_comments = NULL; + entry->ref_comment_lines = 0; } int -set_msgid(struct DrawState* state, struct PoEntry* entry, const char* msgid, - int append) +load_file(struct DrawState* dstate, long* lineno, long* col) { - uint32_t umsgid[MAXMSGLINE]; - size_t msgid_len = 0; - assert(entry != NULL); - if (msgid) - msgid_len = u8_string_to_unicode(umsgid, msgid, MAXMSGLINE); - return u32_set_msgid(state, entry, umsgid, msgid_len, append); -} + struct PoEntry* newchunk = NULL; + struct PoEntry* entry = NULL; + struct PoEntry* current = NULL; + FILE* input = NULL; + char* input_line = NULL; + ParseState pstate = PS_NONE; + int has_plural_msgid = 0; + ssize_t input_size; + ssize_t non_obsolete_sofar = 0; + /* lines to tolerate the lack of msgid */ + int msgid_counter = PO_DETECTION_LINES; + int msgstr_index = -1; + int possible_nulbyte; -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_len = 0; - assert(entry != NULL); - if (msgid_plural) - msgid_plural_len = u8_string_to_unicode(umsgid_plural, - msgid_plural, MAXMSGLINE); - return u32_set_msgid_plural(state, entry, umsgid_plural, - msgid_plural_len, append); -} + assert((dstate != NULL) && (limits != NULL)); + input_size = limits[L_LINE_MAX]; + input_line = malloc(input_size); + if (!input_line) + goto load_file_alloc_error; + dstate->msgid_size = 0; + newchunk = calloc(ALLOC_DELTA, sizeof(struct PoEntry)); + if (!newchunk) + goto load_file_alloc_error; -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) - 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); -} + dstate->msgid_count = 0; + dstate->msgid_size = ALLOC_DELTA; + dstate->entries = newchunk; + entry = dstate->entries; + dstate->untranslated_count++; -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(state, entry, umsgstr, msgstr_index, append); -} + if (!(input = fopen(dstate->filename, "rt"))) + return 1; -int -u32_set_msgid(struct DrawState* state, struct PoEntry* entry, - const uint32_t* msgid, size_t msgid_len, int append) -{ - uint32_t* temp = NULL; + dstate->action = ACT_READING; + if (redraw_status(dstate)) + return 1; - 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) + possible_nulbyte = 0; + *lineno = 1; + while (!feof(input)) { - entry->msgid_size += MSGDELTA; - 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 (append) - U32_MEMCCPY(entry->msgid + entry->msgid_len - msgid_len, msgid, - entry->msgid_size - msgid_len, temp); - else - U32_MEMCCPY(entry->msgid, msgid, entry->msgid_size, temp); - return 0; + char* eol = NULL; + char* nulbyte = NULL; -u32_set_msgid_alloc_error: - state->error = ERR_CANT_ALLOC; - return 1; -} + if (!fgets(input_line, input_size, input)) + { + if (ferror(input)) + { + fclose(input); + return 1; + } + else + continue; + } -int -u32_set_msgid_plural(struct DrawState* state, struct PoEntry* entry, - const uint32_t* msgid_plural, size_t msgid_plural_len, int append) -{ - uint32_t* temp = NULL; + if (possible_nulbyte) + { + /* Definitely not EOF, NUL byte confirmed */ + dstate->error = ERR_NOTPO_FILE; + fclose(input); + free(input_line); + return 1; + } + eol = strchr(input_line, '\n'); + if (eol) + *eol = 0; + nulbyte = strchr(input_line, 0); - assert((state != NULL) && (entry != NULL)); - if (msgid_plural) - msgid_plural_len = u32_strlen(msgid_plural); - if (!entry->msgid_plural) - { - entry->msgid_plural_size = MSGDELTA; - 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; - 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 (append) - U32_MEMCCPY(entry->msgid_plural + entry->msgid_plural_len - - msgid_plural_len, - msgid_plural, - entry->msgid_plural_size - msgid_plural_len, temp); - else - U32_MEMCCPY(entry->msgid_plural, msgid_plural, - entry->msgid_plural_size, temp); - return 0; + /* We don't know if the NUL byte not being equal to + * (possibly NULL) eol is the result of EOF or not + */ + if (nulbyte && (nulbyte != eol)) + possible_nulbyte = 1; -u32_set_msgid_plural_alloc_error: - state->error = ERR_CANT_ALLOC; - return 1; -} + if (parse_po_line(input_line, dstate, &entry, &pstate, col, + &msgstr_index)) + goto load_file_cleanup; -int -u32_set_plural_forms(struct DrawState* state, struct PoEntry* entry, - const uint32_t* plural_forms, const size_t plural_forms_len, int append) -{ - uint32_t* temp = NULL; - - assert((state != NULL) && (entry != NULL)); - if (plural_forms_len >= MSGDELTA) - return 1; - if (!entry->plural_forms) - { - entry->plural_forms_size = MSGDELTA; - entry->plural_forms_len = 0; - entry->plural_forms = calloc(MAXMSGLINE, sizeof(uint32_t)); - if (!entry->plural_forms) - 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_MEMCCPY(entry->plural_forms + entry->plural_forms_size - - plural_forms_len, - plural_forms, - entry->plural_forms_size - plural_forms_len, temp); - else - 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; -} - -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; - - assert((state != NULL) && (entry != NULL)); - if (msgstr) - msgstr_len = u32_strlen(msgstr); - if (entry->msgstr_count == 0 || msgstr_index + 1 > entry->msgstr_count) - { - uint32_t** newmsgstr = NULL; - size_t* newmsgstr_size = NULL; - size_t* newmsgstr_len = NULL; - entry->msgstr_count = msgstr_index + 1; - if (!entry->msgstr) - { - 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) - state->untranslated_count--; - } - else - { - 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)); - 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; - } - if (!entry->msgstr[msgstr_index]) - { - entry->msgstr_size[msgstr_index] = MSGDELTA; - 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]) - { - entry->msgstr_size[msgstr_index] += MSGDELTA; - entry->msgstr[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 (append) - U32_MEMCCPY(entry->msgstr[msgstr_index] - + entry->msgstr_len[msgstr_index] - msgstr_len, - msgstr, entry->msgstr_size[msgstr_index] - msgstr_len, - temp); - else - 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* -u32_add_comment(struct PoEntry* entry, const uint32_t* comment) -{ - uint32_t** comments = NULL; - uint32_t* new_line = NULL; - size_t len = 0; - - if (!entry) - return NULL; - - entry->comment_lines++; - if (!entry->comments) - comments = calloc(entry->comment_lines, sizeof(uint32_t*)); - else - comments = realloc(entry->comments, - entry->comment_lines * sizeof(uint32_t*)); - if (!comments) - return NULL; - entry->comments = comments; - - len = u32_strlen(comment) + 1; - new_line = calloc(len, sizeof(uint32_t)); - if (!new_line) - return NULL; - entry->comments[entry->comment_lines - 1] = new_line; - u32_strncpy(new_line, comment, len); - return entry; -} - -struct PoEntry* -u32_add_extr_comment(struct PoEntry* entry, const uint32_t* extr_comment) -{ - uint32_t** extr_comments = NULL; - uint32_t* new_line = NULL; - size_t len = 0; - - if (!entry) - return NULL; - - entry->extr_comment_lines++; - if (!entry->extr_comments) - extr_comments - = calloc(entry->extr_comment_lines, sizeof(uint32_t*)); - else - extr_comments = realloc(entry->extr_comments, - entry->extr_comment_lines * sizeof(uint32_t*)); - if (!extr_comments) - return NULL; - entry->extr_comments = extr_comments; - - len = u32_strlen(extr_comment) + 1; - new_line = calloc(len, sizeof(uint32_t)); - if (!new_line) - return NULL; - entry->extr_comments[entry->extr_comment_lines - 1] = new_line; - u32_strncpy(new_line, extr_comment, len); - return entry; -} - -struct PoEntry* -add_ref_comment(struct PoEntry* entry, const char* comment) -{ - char** ref_comments = NULL; - char* new_line = NULL; - size_t len = 0; - - if (!entry) - return NULL; - - entry->ref_comment_lines++; - - if (!entry->ref_comments) - ref_comments = calloc(entry->ref_comment_lines, sizeof(char*)); - else - ref_comments = realloc(entry->ref_comments, - entry->ref_comment_lines * sizeof(char*)); - if (!ref_comments) - return NULL; - entry->ref_comments = ref_comments; - - len = strlen(comment) + 1; - new_line = calloc(len, 1); - if (!new_line) - return NULL; - entry->ref_comments[entry->ref_comment_lines - 1] = new_line; - strncpy(new_line, comment, len); - return entry; -} - -int -load_file(struct DrawState* dstate, long* lineno, long* col) -{ - struct PoEntry* newchunk = NULL; - struct PoEntry* entry = NULL; - struct PoEntry* current = NULL; - FILE* input = NULL; - char* input_line = NULL; - ParseState pstate = PS_NONE; - int has_plural_msgid = 0; - ssize_t input_size = limits[L_LINE_MAX]; - ssize_t non_obsolete_sofar = 0; - /* lines to tolerate the lack of msgid */ - int msgid_counter = PO_DETECTION_LINES; - int msgstr_index = -1; - int possible_nulbyte; - - assert(dstate != NULL); - input_line = malloc(input_size); - if (!input_line) - goto load_file_alloc_error; - dstate->msgid_size = 0; - newchunk = calloc(ALLOC_DELTA, sizeof(struct PoEntry)); - if (!newchunk) - goto load_file_alloc_error; - - dstate->msgid_count = 0; - dstate->msgid_size = ALLOC_DELTA; - dstate->entries = newchunk; - entry = dstate->entries; - dstate->untranslated_count++; - - if (!(input = fopen(dstate->filename, "rt"))) - return 1; - - dstate->action = ACT_READING; - redraw_status(dstate); - - possible_nulbyte = 0; - *lineno = 1; - while (!feof(input)) - { - char* eol = NULL; - char* nulbyte = NULL; - - if (!fgets(input_line, input_size, input)) - { - if (ferror(input)) - { - fclose(input); - return 1; - } - else - continue; - } - - if (possible_nulbyte) - { - /* Definitely not EOF, NUL byte confirmed */ - dstate->error = ERR_NOTPO_FILE; - fclose(input); - free(input_line); - return 1; - } - eol = strchr(input_line, '\n'); - if (eol) - *eol = 0; - nulbyte = strchr(input_line, 0); - - /* We don't know if the NUL byte not being equal to - * (possibly NULL) eol is the result of EOF or not - */ - if (nulbyte && (nulbyte != eol)) - possible_nulbyte = 1; - - if (parse_po_line(input_line, dstate, &entry, &pstate, col, - &msgstr_index)) - goto load_file_cleanup; - - if (entry && entry->msgid_plural) - has_plural_msgid = 1; + if (entry && entry->msgid_plural) + has_plural_msgid = 1; switch (dstate->error) { @@ -636,700 +312,159 @@ load_file_alloc_error: } int -save_comment(FILE* output, struct DrawState* state, - const struct PoEntry* current) -{ - char* u8_comment = NULL; - char* temp = NULL; - size_t c, len, size; - - assert((output != NULL) && (state != NULL) && (current != NULL)); - for (c = 0; c < current->comment_lines; c++) - { - len = u32_strlen(current->comments[c]); - size = len * UTF8REPMAX + 1; - temp = realloc(u8_comment, size); - if (!temp) - { - state->error = ERR_CANT_ALLOC; - return 1; - } - u8_comment = temp; - unicode_string_to_u8(u8_comment, current->comments[c], len); - fprintf(output, "#%s%s\n", current->obsolete ? "~ " : "", - u8_comment); - } - free(u8_comment); - return 0; -} - -int -save_file(struct DrawState* state) +parse_po_line(const char* line, struct DrawState* dstate, + struct PoEntry** current_entry, ParseState* pstate, long* col, + int* msgstr_index) { - FILE* output = NULL; - const struct PoEntry* current = NULL; - struct stat st; - int result = 0; - int saved_errno; - - assert(state != NULL); - - state->action = ACT_WRITING; - redraw_status(state); - errno = 0; - - current = state->entries; - if (!create_backups) - goto save_skip_backup_copy; - if (!backup_fail && !state->backup_possible) - goto save_skip_backup_copy; + char* ptoken = NULL; + const char* pline = line; + char* token = NULL; + size_t i = 0; + size_t len; - if ((unlink(state->backup_filename) < 0) && (errno != ENOENT)) - { - state->error = ERR_ERRNO_SET; - result = 1; - goto save_file_cleanup; - } + assert((line != NULL) && (limits != NULL)); + *col = 1; + token = malloc(limits[L_LINE_MAX]); + if (!token) + goto parse_po_line_alloc_error; + *token = 0; + ptoken = token; - if (cp(state->rfn_len > 0 ? state->real_filename : state->filename, - state->backup_filename)) + if (starts_with(line, "msgid_plural ")) { - if (backup_fail) - { - state->error = ERR_ERRNO_SET; - result = 1; - goto save_file_cleanup; - } - else - errno = 0; /* ignore errors */ + len = strlen("msgid_plural "); + *pstate &= ~PS_MSGID; + *pstate |= PS_MSGID_PLURAL; + pline += len; + *col += len; } - -save_skip_backup_copy: - if (!(output = fopen(state->rfn_len > 0 ? state->real_filename - : state->filename, - "wt"))) + else if (starts_with(line, "msgid ")) { - state->error = ERR_ERRNO_SET; - result = 1; - goto save_file_cleanup; + len = strlen("msgid "); + *pstate |= PS_MSGID; + pline += len; + *col += len; } - - if (!create_backups) - goto save_skip_backup_symlink; - - if (lstat(state->rfn_len > 0 ? state->real_filename : state->filename, - &st) - < 0) + else if (starts_with(line, "msgstr[")) { - state->error = ERR_ERRNO_SET; - result = 1; - goto save_file_cleanup; - } + len = strlen("msgstr["); + *pstate &= ~PS_MSGID; + *pstate &= ~PS_MSGID_PLURAL; + *pstate |= PS_MSGSTR_ARRAY; + (*msgstr_index)++; + pline += len; + *col += len; - if (!backup_fail && !state->backup_possible) - goto save_skip_backup_symlink; + while (*pline >= '0' && *pline <= '9') + { + pline++; + (*col)++; + } - if (state->rfn_len > 0) - { - if (unlink(state->filename) == -1) + if (starts_with(pline, "] ")) { - state->error = ERR_ERRNO_SET; - result = 1; - goto save_file_cleanup; + pline += 2; + (*col) += 2; } - if (symlink(state->orig_real_filename, state->filename) == -1) + else { - state->error = ERR_ERRNO_SET; - result = 1; - goto save_file_cleanup; + dstate->error = ERR_SYNTAX; + goto parse_po_cleanup; } } - chmod(state->rfn_len > 0 ? state->real_filename : state->filename, - st.st_mode); - -save_skip_backup_symlink: - /*if (state->rows_count == 1 && state->buffer[0].length == 0) - goto save_file_cleanup;*/ - - ; - -save_loop_entries_start: - if (current >= state->entries + state->real_msgid_count) - goto save_loop_entries_done; - - if (!current->obsolete && current->comment_lines > 0) - if (save_comment(output, state, current)) - goto save_file_error_cleanup; - if (current->extr_comment_lines > 0) - if (save_extr_comment(output, state, current)) - goto save_file_error_cleanup; - if (current->ref_comment_lines > 0) - if (save_ref_comment(output, state, current)) - goto save_file_error_cleanup; - if (save_flags(output, state, current)) - goto save_file_error_cleanup; - if (current->obsolete && current->comment_lines > 0) - if (save_comment(output, state, current)) - goto save_file_error_cleanup; - - if (current->obsolete) + else if (starts_with(line, "msgstr ")) { - if (current < state->entries + state->real_msgid_count - 1) - fprintf(output, "\n"); - current++; - goto save_loop_entries_start; + len = strlen("msgstr "); + *pstate &= ~PS_MSGID; + *pstate &= ~PS_MSGID_PLURAL; + *pstate |= PS_MSGSTR; + pline += len; + *col += len; } - - save_msgid(output, state, current); - if (current->msgid_plural) - if (save_msgid_plural(output, state, current)) - goto save_file_error_cleanup; - - if (save_msgstrs(output, state, current)) - goto save_file_error_cleanup; - - if (current < state->entries + state->real_msgid_count - 1) - fprintf(output, "\n"); - current++; - goto save_loop_entries_start; - - /*save_file_alloc_error: - if (output) - fclose(output); - return 1;*/ - -save_file_error_cleanup: - result = 1; - -save_loop_entries_done: -save_file_cleanup: - saved_errno = errno; - if (output) - fclose(output); - state->action = ACT_IDLE; - errno = saved_errno; - return result; -} - -int -save_flags(FILE* output, struct DrawState* state, const struct PoEntry* current) -{ - Flags flags; - - assert((output != NULL) && (state != NULL) && (current != NULL)); - flags = current->flags; - if (flags != FL_NONE) - fprintf(output, "#, "); - while (flags != FL_NONE) + else if (*line == 0) { - if (flags & FL_C_FORMAT) - { - flags &= ~FL_C_FORMAT; - fprintf(output, "%s", flag_strings[FL_C_FORMAT]); - } - else if (flags & FL_NO_C_FORMAT) - { - flags &= ~FL_NO_C_FORMAT; - fprintf(output, "%s", flag_strings[FL_NO_C_FORMAT]); - } - else if (flags & FL_FUZZY) - { - flags &= ~FL_FUZZY; - fprintf(output, "%s", flag_strings[FL_FUZZY]); - } - else - flags = FL_NONE; /* see po.h */ - - if (flags != FL_NONE) - fprintf(output, ", "); + *pstate &= ~PS_MSGID; + *pstate &= ~PS_MSGID_PLURAL; + *pstate &= ~PS_MSGSTR; + *pstate &= ~PS_MSGSTR_ARRAY; } - if (current->flags != FL_NONE) - fprintf(output, "\n"); - return 0; -} - -int -save_msgid(FILE* output, struct DrawState* state, const struct PoEntry* current) -{ - const uint32_t* pumsgid = NULL; - const uint32_t* old_pumsgid = NULL; - char* u8_msgid = NULL; - 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) + else if (*line != ' ' && *line != '\t' && *line != '"' && *line != '#') { - fprintf(output, "msgid \"\"\n"); - return 0; + dstate->error = ERR_SYNTAX; + goto parse_po_cleanup; } - num_lines = u32_lines_in_string(current->msgid, 1, wrap_width); - if ((num_lines > 1) - || u32_strlen(current->msgid) + strlen("msgid: \"") + 1 - > (size_t)literal_break_threshold) - { - pumsgid = current->msgid; - fprintf(output, "msgid %s", break_literals ? "\"\"\n" : ""); - while (*pumsgid) - { - u8_msgid = NULL; - old_pumsgid = pumsgid; - len = u32_next_line(current->msgid, &pumsgid, 1, - wrap_width); - if (len == 0) + while (*pline && i < strlen(line)) + { + switch (*pline) + { + case ' ': + case '\t': + if (*pstate & PS_COMMENT) { - first_line = 0; - continue; + *ptoken++ = *pline++; + INC_COL(); } - - size = len * UTF8REPMAX + 1; - - u8_msgid = malloc(size); - if (!u8_msgid) + else if (*pstate & PS_STRING) { - state->error = ERR_CANT_ALLOC; - return 1; + *ptoken++ = *pline++; + INC_COL(); } - - memset(u8_msgid, 0, size); - - unicode_string_to_u8(u8_msgid, old_pumsgid, len); - fprintf(output, "%s%s\"\n", - break_literals && first_line ? "" : "\"", - u8_msgid); - free(u8_msgid); - first_line = 0; - } - } - else - { - size = current->msgid_size * UTF8REPMAX + 1; - u8_msgid = malloc(size); - if (!u8_msgid) - { - state->error = ERR_CANT_ALLOC; - return 1; - } - - memset(u8_msgid, 0, size); - - unicode_string_to_u8(u8_msgid, current->msgid, - current->msgid_size); - fprintf(output, "msgid \"%s\"\n", u8_msgid); - free(u8_msgid); - } - return 0; -} - -int -save_msgid_plural(FILE* output, struct DrawState* state, - const struct PoEntry* current) -{ - const uint32_t* old_pumsgid = NULL; - const uint32_t* pumsgid = NULL; - 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) - + strlen("msgid_plural: \"") + 1 - > (size_t)literal_break_threshold) - { - pumsgid = current->msgid_plural; - fprintf(output, "msgid_plural %s", - break_literals ? "\"\"\n" : ""); - while (*pumsgid) - { - u8_msgid = NULL; - old_pumsgid = pumsgid; - len = u32_next_line(current->msgid_plural, &pumsgid, 1, - wrap_width); - if (len == 0) + else { - first_line = 0; - continue; + pline++; + INC_COL(); } - - size = len * UTF8REPMAX + 1; - u8_msgid = malloc(size); - if (!u8_msgid) + break; + case '#': + if (pline - line + 1 == 1) { - state->error = ERR_CANT_ALLOC; - return 1; + *pstate |= PS_COMMENT_TRAN; + pline++; + INC_COL(); } - - memset(u8_msgid, 0, size); - - unicode_string_to_u8(u8_msgid, old_pumsgid, len); - fprintf(output, "%s%s\"\n", - break_literals && first_line ? "" : "\"", - u8_msgid); - first_line = 0; - free(u8_msgid); - } - } - else - { - size = current->msgid_plural_size * UTF8REPMAX + 1; - u8_msgid = malloc(size); - if (!u8_msgid) - { - state->error = ERR_CANT_ALLOC; - return 1; - } - - memset(u8_msgid, 9, size); - - msgid_plural_len = u32_strlen(current->msgid_plural); - unicode_string_to_u8(u8_msgid, current->msgid_plural, - msgid_plural_len); - fprintf(output, "msgid_plural \"%s\"\n", u8_msgid); - free(u8_msgid); - } - return 0; -} - -int -save_msgstrs(FILE* output, struct DrawState* state, - const struct PoEntry* current) -{ - const uint32_t* pumsgstr = NULL; - const uint32_t* old_pumsgstr = NULL; - char now[MAXDATEBUF]; - char* u8_msgstr = NULL; - struct tm* now_tm = NULL; - time_t t; - ssize_t m, num_lines; - size_t len, size, msgstr_len; - int i; - int seen_generator = 0; - int seen_plural_forms = 0; - int first_entry, first_line; - - assert((output != NULL) && (state != NULL) && (current != NULL)); - first_entry = (current == state->entries) && !current->msgid; - if (!current->msgstr_count) - { - if (current->msgid_plural) - for (i = 0; i < state->nplurals; i++) - fprintf(output, "msgstr[%d] \"\"\n", i); - else - fprintf(output, "msgstr \"\"\n"); - return 0; - } - if (current->msgstr_count == 1) - { - t = time(NULL); - now_tm = localtime(&t); - strftime(now, MAXDATEBUF, "%F %H:%M%z", now_tm); - } - 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]) - + (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 %s", - break_literals ? "\"\"\n" : ""); - else - fprintf(output, "msgstr[%ld] %s", m, - break_literals ? "\"\"\n" : ""); - - save_msgstrs_pumsgstr_loop: - old_pumsgstr = pumsgstr; - len = u32_next_line(current->msgstr[m], &pumsgstr, 1, - wrap_width); - if (len == 0) - goto save_msgstrs_pumsgstr_done; - size = len * UTF8REPMAX + 1; - u8_msgstr = malloc(size); - if (!u8_msgstr) + else if (*pstate & (PS_STRING | PS_COMMENT)) { - state->error = ERR_CANT_ALLOC; - return 1; + *ptoken++ = *pline++; + INC_COL(); } - memset(u8_msgstr, 0, size); - - unicode_string_to_u8(u8_msgstr, old_pumsgstr, len); - if (first_entry - && starts_with(u8_msgstr, "X-Generator: ")) + else { - seen_generator = 1; - fprintf(output, "\"X-Generator: poe %s\\n\"\n", - VERSION); + dstate->error = ERR_SYNTAX; + goto parse_po_cleanup; } - else if (first_entry && plural_string - && starts_with(u8_msgstr, "Plural-Forms: ")) + break; + case '.': + if (pline - line + 1 == 2 && *pstate & PS_COMMENT_TRAN) { - seen_plural_forms = 1; - fprintf(output, "\"Plural-Forms: %s\\n\"\n", - plural_string); + *pstate &= ~PS_COMMENT_TRAN; + *pstate |= PS_COMMENT_EXTR; + pline++; + INC_COL(); + while (*pline == ' ' || *pline == '\t') + { + pline++; + INC_COL(); + } } else - fprintf(output, "%s%s\"\n", - break_literals && first_line ? "" : "\"", - u8_msgstr); - free(u8_msgstr); - first_line = 0; - if (*pumsgstr) - goto save_msgstrs_pumsgstr_loop; - - save_msgstrs_pumsgstr_done: - if (first_entry && !seen_generator) - fprintf(output, "\"X-Generator: poe %s\\n\"\n", - VERSION); - if (first_entry && plural_string && !seen_plural_forms) - fprintf(output, "\"Plural-Forms: %s\\n\"\n", - plural_string); - } - else - { - size = MAXMSGLINE * UTF8REPMAX + 1; - u8_msgstr = malloc(size); - if (!u8_msgstr) { - state->error = ERR_CANT_ALLOC; - return 1; - } - msgstr_len = u32_strlen(current->msgstr[m]); - unicode_string_to_u8(u8_msgstr, current->msgstr[m], - msgstr_len); - if (current->msgstr_count == 1) - fprintf(output, "msgstr \"%s\"\n", u8_msgstr); - else - fprintf(output, "msgstr[%ld] \"%s\"\n", m, - u8_msgstr); - free(u8_msgstr); - } - } - return 0; -} - -int -save_ref_comment(FILE* output, struct DrawState* state, - const struct PoEntry* current) -{ - size_t rc; - assert((output != NULL) && (state != NULL) && (current != NULL)); - for (rc = 0; rc < current->ref_comment_lines; rc++) - fprintf(output, "#: %s\n", current->ref_comments[rc]); - return 0; -} - -int -save_extr_comment(FILE* output, struct DrawState* state, - const struct PoEntry* current) -{ - char* u8_extr_comment = NULL; - char* temp = NULL; - size_t tc, len, size; - - assert((output != NULL) && (state != NULL) && (current != NULL)); - for (tc = 0; tc < current->extr_comment_lines; tc++) - { - len = u32_strlen(current->extr_comments[tc]); - size = len * UTF8REPMAX + 1; - - temp = realloc(u8_extr_comment, size); - if (!temp) - { - state->error = ERR_CANT_ALLOC; - return 1; - } - u8_extr_comment = temp; - unicode_string_to_u8(u8_extr_comment, - current->extr_comments[tc], len); - fprintf(output, "#.%s%s\n", u8_extr_comment[0] == 0 ? "" : " ", - u8_extr_comment); - } - free(u8_extr_comment); - return 0; -} - -#define INC_COL() \ - do \ - { \ - if (!dstate->error) \ - (*col)++; \ - } while (0) - -int -parse_po_line(const char* line, struct DrawState* dstate, - struct PoEntry** current_entry, ParseState* pstate, long* col, - int* msgstr_index) -{ - const char* pline = line; - char* token = NULL; - char* ptoken = NULL; - size_t i = 0; - size_t len; - - assert(line != NULL); - - *col = 1; - - token = malloc(limits[L_LINE_MAX]); - if (!token) - goto parse_po_line_alloc_error; - *token = 0; - ptoken = token; - - if (starts_with(line, "msgid_plural ")) - { - len = strlen("msgid_plural "); - *pstate &= ~PS_MSGID; - *pstate |= PS_MSGID_PLURAL; - pline += len; - *col += len; - } - else if (starts_with(line, "msgid ")) - { - len = strlen("msgid "); - *pstate |= PS_MSGID; - pline += len; - *col += len; - } - else if (starts_with(line, "msgstr[")) - { - len = strlen("msgstr["); - *pstate &= ~PS_MSGID; - *pstate &= ~PS_MSGID_PLURAL; - *pstate |= PS_MSGSTR_ARRAY; - (*msgstr_index)++; - pline += len; - *col += len; - - while (*pline >= '0' && *pline <= '9') - { - pline++; - (*col)++; - } - - if (starts_with(pline, "] ")) - { - pline += 2; - (*col) += 2; - } - else - { - dstate->error = ERR_SYNTAX; - goto parse_po_cleanup; - } - } - else if (starts_with(line, "msgstr ")) - { - len = strlen("msgstr "); - *pstate &= ~PS_MSGID; - *pstate &= ~PS_MSGID_PLURAL; - *pstate |= PS_MSGSTR; - pline += len; - *col += len; - } - else if (*line == 0) - { - *pstate &= ~PS_MSGID; - *pstate &= ~PS_MSGID_PLURAL; - *pstate &= ~PS_MSGSTR; - *pstate &= ~PS_MSGSTR_ARRAY; - } - else if (*line != ' ' && *line != '\t' && *line != '"' && *line != '#') - { - dstate->error = ERR_SYNTAX; - goto parse_po_cleanup; - } - - while (*pline && i < strlen(line)) - { - switch (*pline) - { - case ' ': - case '\t': - if (*pstate & PS_COMMENT) - { - *ptoken++ = *pline++; - INC_COL(); - } - else if (*pstate & PS_STRING) - { - *ptoken++ = *pline++; - INC_COL(); - } - else - { - pline++; - INC_COL(); - } - break; - case '#': - if (pline - line + 1 == 1) - { - *pstate |= PS_COMMENT_TRAN; - pline++; - INC_COL(); - } - else if (*pstate & (PS_STRING | PS_COMMENT)) - { - *ptoken++ = *pline++; - INC_COL(); - } - else - { - dstate->error = ERR_SYNTAX; - goto parse_po_cleanup; - } - break; - case '.': - if (pline - line + 1 == 2 && *pstate & PS_COMMENT_TRAN) - { - *pstate &= ~PS_COMMENT_TRAN; - *pstate |= PS_COMMENT_EXTR; - pline++; - INC_COL(); - while (*pline == ' ' || *pline == '\t') - { - pline++; - INC_COL(); - } - } - else - { - *ptoken++ = *pline++; - INC_COL(); - } - break; - case ',': - if (pline - line + 1 == 2 && *pstate & PS_COMMENT_TRAN) - { - *pstate &= ~PS_COMMENT_TRAN; - *pstate |= PS_COMMENT_FLAG; - pline++; - INC_COL(); - while (*pline == ' ' || *pline == '\t') - { - pline++; - INC_COL(); - } + *ptoken++ = *pline++; + INC_COL(); + } + break; + case ',': + if (pline - line + 1 == 2 && *pstate & PS_COMMENT_TRAN) + { + *pstate &= ~PS_COMMENT_TRAN; + *pstate |= PS_COMMENT_FLAG; + pline++; + INC_COL(); + while (*pline == ' ' || *pline == '\t') + { + pline++; + INC_COL(); + } } else { @@ -1451,134 +586,1103 @@ parse_po_line(const char* line, struct DrawState* dstate, *msgstr_index = -1; } - if (!*token) - goto parse_po_empty_token; + if (!*token) + goto parse_po_empty_token; + + if (*pstate & PS_MSGID) + { + 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) + { + if (starts_with(token, "Plural-Forms: ")) + { + if (set_plural_forms(dstate, *current_entry, + token + strlen("Plural-Forms: "), 0)) + goto parse_po_line_alloc_error; + if (set_nplurals(dstate, *current_entry)) + goto parse_po_cleanup; + } + } + + 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); + if (u32_add_comment(*current_entry, u32_token)) + goto parse_po_line_alloc_error; + 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); + if (u32_add_extr_comment(*current_entry, u32_token)) + goto parse_po_line_alloc_error; + free(u32_token); + } + else if (*pstate & PS_COMMENT_FLAG) + { + ptoken = token; + while (*ptoken) + { + 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 typedef Flags in po.h + * for rationale */ + while (*ptoken) + ptoken++; + } + } + } + else if (*pstate & PS_COMMENT_REFN) + { + if (add_ref_comment(*current_entry, token)) + goto parse_po_line_alloc_error; + } + 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)) + goto parse_po_line_alloc_error; + else + (*current_entry)->obsolete = 1; + free(u32_token); + } + *token = 0; + goto parse_po_cleanup; + +parse_po_empty_token: + if (*pstate & PS_COMMENT_TRAN) + { + if (u32_add_comment(*current_entry, empty_string)) + goto parse_po_line_alloc_error; + } + else if (*pstate & PS_COMMENT_EXTR) + { + if (u32_add_extr_comment(*current_entry, empty_string)) + goto parse_po_line_alloc_error; + } + +parse_po_cleanup: + /* one-line states */ + *pstate &= ~PS_COMMENT; + return !!dstate->error; + +parse_po_line_alloc_error: + dstate->error = ERR_CANT_ALLOC; + return 1; +} + +int +save_comment(FILE* output, struct DrawState* state, + const struct PoEntry* current) +{ + char* temp = NULL; + char* u8_comment = NULL; + ssize_t c, len, size; + + assert((output != NULL) && (state != NULL) && (current != NULL)); + for (c = 0; c < current->comment_lines; c++) + { + len = u32_strlen(current->comments[c]); + size = len * UTF8REPMAX + 1; + temp = realloc(u8_comment, size); + if (!temp) + { + state->error = ERR_CANT_ALLOC; + return 1; + } + u8_comment = temp; + unicode_string_to_u8(u8_comment, current->comments[c], len); + fprintf(output, "#%s%s\n", current->obsolete ? "~ " : "", + u8_comment); + } + free(u8_comment); + return 0; +} + +int +save_extr_comment(FILE* output, struct DrawState* state, + const struct PoEntry* current) +{ + char* temp = NULL; + char* u8_extr_comment = NULL; + ssize_t tc, len, size; + + assert((output != NULL) && (state != NULL) && (current != NULL)); + for (tc = 0; tc < current->extr_comment_lines; tc++) + { + len = u32_strlen(current->extr_comments[tc]); + size = len * UTF8REPMAX + 1; + + temp = realloc(u8_extr_comment, size); + if (!temp) + { + state->error = ERR_CANT_ALLOC; + return 1; + } + u8_extr_comment = temp; + unicode_string_to_u8(u8_extr_comment, + current->extr_comments[tc], len); + fprintf(output, "#.%s%s\n", u8_extr_comment[0] == 0 ? "" : " ", + u8_extr_comment); + } + free(u8_extr_comment); + return 0; +} + +int +save_file(struct DrawState* state) +{ + FILE* output = NULL; + const struct PoEntry* current = NULL; + struct stat st; + int result = 0; + int saved_errno; + + assert(state != NULL); + state->action = ACT_WRITING; + if (redraw_status(state)) + return 1; + errno = 0; + + current = state->entries; + if (!create_backups) + goto save_skip_backup_copy; + if (!backup_fail && !state->backup_possible) + goto save_skip_backup_copy; + + if ((unlink(state->backup_filename) < 0) && (errno != ENOENT)) + { + state->error = ERR_ERRNO_SET; + result = 1; + goto save_file_cleanup; + } + + if (cp(state->rfn_len > 0 ? state->real_filename : state->filename, + state->backup_filename)) + { + if (backup_fail) + { + state->error = ERR_ERRNO_SET; + result = 1; + goto save_file_cleanup; + } + else + errno = 0; /* ignore errors */ + } + +save_skip_backup_copy: + if (!(output = fopen(state->rfn_len > 0 ? state->real_filename + : state->filename, + "wt"))) + { + state->error = ERR_ERRNO_SET; + result = 1; + goto save_file_cleanup; + } + + if (!create_backups) + goto save_skip_backup_symlink; + + if (lstat(state->rfn_len > 0 ? state->real_filename : state->filename, + &st) + < 0) + { + state->error = ERR_ERRNO_SET; + result = 1; + goto save_file_cleanup; + } + + if (!backup_fail && !state->backup_possible) + goto save_skip_backup_symlink; + + if (state->rfn_len > 0) + { + if (unlink(state->filename) == -1) + { + state->error = ERR_ERRNO_SET; + result = 1; + goto save_file_cleanup; + } + if (symlink(state->orig_real_filename, state->filename) == -1) + { + state->error = ERR_ERRNO_SET; + result = 1; + goto save_file_cleanup; + } + } + chmod(state->rfn_len > 0 ? state->real_filename : state->filename, + st.st_mode); + +save_skip_backup_symlink: +save_loop_entries_start: + if (current >= state->entries + state->real_msgid_count) + goto save_loop_entries_done; + + if (!current->obsolete && current->comment_lines > 0) + if (save_comment(output, state, current)) + goto save_file_error_cleanup; + if (current->extr_comment_lines > 0) + if (save_extr_comment(output, state, current)) + goto save_file_error_cleanup; + if (current->ref_comment_lines > 0) + if (save_ref_comment(output, current)) + goto save_file_error_cleanup; + if (save_flags(output, current)) + goto save_file_error_cleanup; + if (current->obsolete && current->comment_lines > 0) + if (save_comment(output, state, current)) + goto save_file_error_cleanup; + + if (current->obsolete) + { + if (current < state->entries + state->real_msgid_count - 1) + fprintf(output, "\n"); + current++; + goto save_loop_entries_start; + } + + if (save_msgid(output, state, current)) + goto save_file_error_cleanup; + if (current->msgid_plural) + if (save_msgid_plural(output, state, current)) + goto save_file_error_cleanup; + + if (save_msgstrs(output, state, current)) + goto save_file_error_cleanup; + + if (current < state->entries + state->real_msgid_count - 1) + fprintf(output, "\n"); + current++; + goto save_loop_entries_start; + +save_file_error_cleanup: + result = 1; + +save_loop_entries_done: +save_file_cleanup: + saved_errno = errno; + if (output) + fclose(output); + state->action = ACT_IDLE; + errno = saved_errno; + return result; +} + +int +save_flags(FILE* output, const struct PoEntry* current) +{ + Flags flags; + + assert((output != NULL) && (current != NULL)); + flags = current->flags; + if (flags != FL_NONE) + fprintf(output, "#, "); + while (flags != FL_NONE) + { + if (flags & FL_C_FORMAT) + { + flags &= ~FL_C_FORMAT; + fprintf(output, "%s", flag_strings[FL_C_FORMAT]); + } + else if (flags & FL_NO_C_FORMAT) + { + flags &= ~FL_NO_C_FORMAT; + fprintf(output, "%s", flag_strings[FL_NO_C_FORMAT]); + } + else if (flags & FL_FUZZY) + { + flags &= ~FL_FUZZY; + fprintf(output, "%s", flag_strings[FL_FUZZY]); + } + else + flags = FL_NONE; /* see po.h, typedef Flags */ + + if (flags != FL_NONE) + fprintf(output, ", "); + } + if (current->flags != FL_NONE) + fprintf(output, "\n"); + return 0; +} + +int +save_msgid(FILE* output, struct DrawState* state, const struct PoEntry* current) +{ + const uint32_t* old_pumsgid = NULL; + const uint32_t* pumsgid = NULL; + char* u8_msgid = NULL; + size_t len = 0; + size_t num_lines; + size_t size; + + assert((output != NULL) && (state != NULL) && (current != NULL)); + if (!current->msgid) + { + fprintf(output, "msgid \"\"\n"); + return 0; + } + num_lines = u32_lines_in_string(current->msgid, 1, wrap_width); + if ((num_lines > 1) + || u32_strlen(current->msgid) + strlen("msgid: \"") + 1 + > (size_t)literal_break_threshold) + { + pumsgid = current->msgid; + fprintf(output, "msgid %s", break_literals ? "\"\"\n" : ""); + while (*pumsgid) + { + u8_msgid = NULL; + old_pumsgid = pumsgid; + + len = u32_next_line(current->msgid, &pumsgid, 1, + wrap_width); + if (len == 0) + continue; + + size = len * UTF8REPMAX + 1; + u8_msgid = malloc(size); + if (!u8_msgid) + { + state->error = ERR_CANT_ALLOC; + return 1; + } + memset(u8_msgid, 0, size); + + unicode_string_to_u8(u8_msgid, old_pumsgid, len); + fprintf(output, "\"%s\"\n", u8_msgid); + free(u8_msgid); + } + } + else + { + size = current->msgid_size * UTF8REPMAX + 1; + u8_msgid = malloc(size); + if (!u8_msgid) + { + state->error = ERR_CANT_ALLOC; + return 1; + } + + memset(u8_msgid, 0, size); + + unicode_string_to_u8(u8_msgid, current->msgid, + current->msgid_size); + fprintf(output, "msgid \"%s\"\n", u8_msgid); + free(u8_msgid); + } + return 0; +} + +int +save_msgid_plural(FILE* output, struct DrawState* state, + const struct PoEntry* current) +{ + const uint32_t* old_pumsgid = NULL; + const uint32_t* pumsgid = NULL; + char* u8_msgid = NULL; + size_t len = 0; + size_t msgid_plural_len, num_lines, size; + + 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) + + strlen("msgid_plural: \"") + 1 + > (size_t)literal_break_threshold) + { + pumsgid = current->msgid_plural; + fprintf(output, "msgid_plural %s", + break_literals ? "\"\"\n" : ""); + while (*pumsgid) + { + u8_msgid = NULL; + old_pumsgid = pumsgid; + len = u32_next_line(current->msgid_plural, &pumsgid, 1, + wrap_width); + if (len == 0) + continue; + + size = len * UTF8REPMAX + 1; + u8_msgid = malloc(size); + if (!u8_msgid) + { + state->error = ERR_CANT_ALLOC; + return 1; + } + + memset(u8_msgid, 0, size); + + unicode_string_to_u8(u8_msgid, old_pumsgid, len); + fprintf(output, "\"%s\"\n", u8_msgid); + free(u8_msgid); + } + } + else + { + size = current->msgid_plural_size * UTF8REPMAX + 1; + u8_msgid = malloc(size); + if (!u8_msgid) + { + state->error = ERR_CANT_ALLOC; + return 1; + } + + memset(u8_msgid, 9, size); + + msgid_plural_len = u32_strlen(current->msgid_plural); + unicode_string_to_u8(u8_msgid, current->msgid_plural, + msgid_plural_len); + fprintf(output, "msgid_plural \"%s\"\n", u8_msgid); + free(u8_msgid); + } + return 0; +} + +int +save_msgstrs(FILE* output, struct DrawState* state, + const struct PoEntry* current) +{ + struct tm* now_tm = NULL; + const uint32_t* old_pumsgstr = NULL; + const uint32_t* pumsgstr = NULL; + char now[MAXDATEBUF]; + char* u8_msgstr = NULL; + time_t t; + ssize_t m, num_lines; + size_t len, size, msgstr_len, llen; + int i; + int seen_generator = 0; + int seen_plural_forms = 0; + int first_entry; + + assert((output != NULL) && (state != NULL) && (current != NULL)); + first_entry = (current == state->entries) && !current->msgid; + if (!current->msgstr_count) + { + if (current->msgid_plural) + for (i = 0; i < state->nplurals; i++) + fprintf(output, "msgstr[%d] \"\"\n", i); + else + fprintf(output, "msgstr \"\"\n"); + return 0; + } + if (current->msgstr_count == 1) + { + t = time(NULL); + now_tm = localtime(&t); + strftime(now, MAXDATEBUF, "%F %H:%M%z", now_tm); + } + for (m = 0; m < current->msgstr_count; m++) + { + assert(current->msgstr); + num_lines = u32_lines_in_string(current->msgstr[m], 1, + wrap_width); + + llen = (current->msgstr_count == 1 ? strlen("msgstr: \"") + : strlen("msgstr[ ]: \"")) + + u32_strlen(current->msgstr[m]) + 1; + if ((num_lines > 1) || llen > (size_t)literal_break_threshold) + { + pumsgstr = current->msgstr[m]; + if (current->msgstr_count == 1) + fprintf(output, "msgstr %s", + break_literals ? "\"\"\n" : ""); + else + fprintf(output, "msgstr[%ld] %s", m, + break_literals ? "\"\"\n" : ""); + + save_msgstrs_pumsgstr_loop: + old_pumsgstr = pumsgstr; + len = u32_next_line(current->msgstr[m], &pumsgstr, 1, + wrap_width); + if (len == 0) + goto save_msgstrs_pumsgstr_done; + size = len * UTF8REPMAX + 1; + u8_msgstr = malloc(size); + if (!u8_msgstr) + { + state->error = ERR_CANT_ALLOC; + return 1; + } + memset(u8_msgstr, 0, size); + + unicode_string_to_u8(u8_msgstr, old_pumsgstr, len); + if (first_entry + && starts_with(u8_msgstr, "X-Generator: ")) + { + seen_generator = 1; + fprintf(output, "\"X-Generator: poe %s\\n\"\n", + VERSION); + } + else if (first_entry && plural_string + && starts_with(u8_msgstr, "Plural-Forms: ")) + { + seen_plural_forms = 1; + fprintf(output, "\"Plural-Forms: %s\\n\"\n", + plural_string); + } + else + fprintf(output, "\"%s\"\n", u8_msgstr); + free(u8_msgstr); + if (*pumsgstr) + goto save_msgstrs_pumsgstr_loop; + + save_msgstrs_pumsgstr_done: + if (first_entry && !seen_generator) + fprintf(output, "\"X-Generator: poe %s\\n\"\n", + VERSION); + if (first_entry && plural_string && !seen_plural_forms) + fprintf(output, "\"Plural-Forms: %s\\n\"\n", + plural_string); + } + else + { + msgstr_len = u32_strlen(current->msgstr[m]); + size = (msgstr_len + 1) * UTF8REPMAX; + u8_msgstr = malloc(size); + if (!u8_msgstr) + { + state->error = ERR_CANT_ALLOC; + return 1; + } + unicode_string_to_u8(u8_msgstr, current->msgstr[m], + msgstr_len); + if (current->msgstr_count == 1) + fprintf(output, "msgstr \"%s\"\n", u8_msgstr); + else + fprintf(output, "msgstr[%ld] \"%s\"\n", m, + u8_msgstr); + free(u8_msgstr); + } + } + return 0; +} + +int +save_ref_comment(FILE* output, const struct PoEntry* current) +{ + ssize_t rc; + assert((output != NULL) && (current != NULL)); + for (rc = 0; rc < current->ref_comment_lines; rc++) + fprintf(output, "#: %s\n", current->ref_comments[rc]); + return 0; +} + +int +set_flags(struct PoEntry* entry, const Flags flags) +{ + assert(entry != NULL); + entry->flags = flags; + return 0; +} + +int +set_msgid(struct DrawState* state, struct PoEntry* entry, const char* msgid, + int append) +{ + uint32_t* umsgid = NULL; + size_t msgid_len = 0; + int result = 0; + + assert(entry != NULL); + if (msgid) + msgid_len = strlen(msgid); + umsgid = calloc(msgid_len + 1, sizeof(uint32_t)); + if (!umsgid) + goto set_msgid_alloc_error; + if (msgid) + (void)u8_string_to_unicode(umsgid, msgid, msgid_len); + result = u32_set_msgid(state, entry, umsgid, msgid_len, append); + free(umsgid); + return result; + +set_msgid_alloc_error: + state->error = ERR_CANT_ALLOC; + return 1; +} + +int +set_msgid_plural(struct DrawState* state, struct PoEntry* entry, + const char* msgid_plural, int append) +{ + uint32_t* umsgid_plural = NULL; + size_t msgid_plural_len = 0; + int result = 0; + + assert(entry != NULL); + if (msgid_plural) + msgid_plural_len = strlen(msgid_plural); + umsgid_plural = calloc(msgid_plural_len + 1, sizeof(uint32_t)); + if (!umsgid_plural) + goto set_msgid_plural_alloc_error; + if (msgid_plural) + (void)u8_string_to_unicode(umsgid_plural, msgid_plural, + msgid_plural_len); + result = u32_set_msgid_plural(state, entry, umsgid_plural, + msgid_plural_len, append); + free(umsgid_plural); + return result; + +set_msgid_plural_alloc_error: + state->error = ERR_CANT_ALLOC; + return 1; +} + +int +set_msgstr(struct DrawState* state, struct PoEntry* entry, const char* msgstr, + int msgstr_index, int append) +{ + uint32_t* umsgstr = NULL; + size_t msgstr_len = 0; + int result = 0; + + assert(entry != NULL); + if (msgstr) + msgstr_len = strlen(msgstr); + umsgstr = calloc(msgstr_len + 1, sizeof(uint32_t)); + if (!umsgstr) + goto set_msgstr_alloc_error; + if (msgstr) + (void)u8_string_to_unicode(umsgstr, msgstr, msgstr_len); + result = u32_set_msgstr(state, entry, umsgstr, msgstr_index, append); + free(umsgstr); + return result; + +set_msgstr_alloc_error: + state->error = ERR_CANT_ALLOC; + return 1; +} + +int +set_nplurals(struct DrawState* state, struct PoEntry* entry) +{ + char token[LINE_DEFAULT]; + static uint32_t* unplurals = NULL; + uint32_t* ppf; + uint32_t* temp; + char* ptoken = token; + int read_number = 0; + int nplurals_len = 0; + + assert(state != NULL); + if (!entry || !entry->plural_forms) + return 1; + + nplurals_len = strlen("nplurals="); + ppf = entry->plural_forms; + *ptoken = 0; + while (*ppf) + { + if (!unplurals) + { + unplurals = calloc(nplurals_len + 1, sizeof(uint32_t)); + if (!unplurals) + goto set_nplurals_alloc_error; + U32_U8_MEMCCPY(unplurals, "nplurals=", nplurals_len, + temp); + } + if (u32_starts_with(ppf, unplurals)) + { + ppf += nplurals_len; + read_number = 1; + } + else if (read_number && *ppf >= ZERO && *ppf <= NINE) + { + *ptoken++ = *ppf++; + *ptoken = 0; + } + else if (read_number && *ppf == SEMICOL) + { + *ptoken = 0; + ptoken = token; + break; + } + else + ppf++; + } + if (*ptoken) + { + if (sscanf(ptoken, "%d", &state->nplurals) < 1) + { + state->error = ERR_BAD_NPLURALS; + return 1; + } + } + else + { + state->error = ERR_BAD_NPLURALS; + return 1; + } + + return 0; + +set_nplurals_alloc_error: + state->error = ERR_CANT_ALLOC; + return 1; +} + +int +set_plural_forms(struct DrawState* state, struct PoEntry* entry, + const char* plural_forms, int append) +{ + uint32_t* uplural_forms = NULL; + size_t plural_forms_len = 0; + int result = 0; + + assert(entry != NULL); + if (plural_forms) + plural_forms_len = strlen(plural_forms); + uplural_forms = calloc(plural_forms_len + 1, sizeof(uint32_t)); + if (!uplural_forms) + goto set_plural_forms_alloc_error; + if (plural_forms) + (void)u8_string_to_unicode(uplural_forms, plural_forms, + plural_forms_len); + result = u32_set_plural_forms(state, entry, uplural_forms, + plural_forms_len, append); + free(uplural_forms); + return result; + +set_plural_forms_alloc_error: + state->error = ERR_CANT_ALLOC; + return 1; +} + +int +u32_add_comment(struct PoEntry* entry, const uint32_t* comment) +{ + uint32_t** comments = NULL; + uint32_t* new_line = NULL; + size_t len = 0; + size_t size; + + assert((entry != NULL) && (comment != NULL)); + entry->comment_lines++; + if (!entry->comments) + comments = calloc(entry->comment_lines, sizeof(uint32_t*)); + else + comments = realloc(entry->comments, + entry->comment_lines * sizeof(uint32_t*)); + if (!comments) + return 1; + entry->comments = comments; + + len = u32_strlen(comment); + size = len + 1; + new_line = calloc(size, sizeof(uint32_t)); + if (!new_line) + return 1; + entry->comments[entry->comment_lines - 1] = new_line; + u32_strncpy(new_line, comment, size); + return 0; +} + +int +u32_add_extr_comment(struct PoEntry* entry, const uint32_t* extr_comment) +{ + uint32_t** extr_comments = NULL; + uint32_t* new_line = NULL; + size_t len = 0; + size_t size; + + assert((entry != NULL) && (extr_comment != NULL)); + entry->extr_comment_lines++; + if (!entry->extr_comments) + extr_comments + = calloc(entry->extr_comment_lines, sizeof(uint32_t*)); + else + extr_comments = realloc(entry->extr_comments, + entry->extr_comment_lines * sizeof(uint32_t*)); + if (!extr_comments) + return 1; + entry->extr_comments = extr_comments; + + len = u32_strlen(extr_comment); + size = len + 1; + new_line = calloc(size, sizeof(uint32_t)); + if (!new_line) + return 1; + entry->extr_comments[entry->extr_comment_lines - 1] = new_line; + u32_strncpy(new_line, extr_comment, size); + return 0; +} + +int +u32_set_msgid(struct DrawState* state, struct PoEntry* entry, + const uint32_t* msgid, ssize_t msgid_len, int append) +{ + 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; + 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 (append) + U32_MEMCCPY(entry->msgid + entry->msgid_len - msgid_len, msgid, + entry->msgid_size - msgid_len, temp); + else + U32_MEMCCPY(entry->msgid, msgid, entry->msgid_size, temp); + return 0; + +u32_set_msgid_alloc_error: + state->error = ERR_CANT_ALLOC; + return 1; +} - if (*pstate & PS_MSGID) +int +u32_set_msgid_plural(struct DrawState* state, struct PoEntry* entry, + const uint32_t* msgid_plural, ssize_t msgid_plural_len, int append) +{ + uint32_t* temp = NULL; + + assert((state != NULL) && (entry != NULL)); + if (msgid_plural) + msgid_plural_len = u32_strlen(msgid_plural); + if (!entry->msgid_plural) { - if (set_msgid(dstate, *current_entry, token, 1)) - goto parse_po_line_alloc_error; + entry->msgid_plural_size = MSGDELTA; + 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 (*pstate & PS_MSGID_PLURAL) + else if (entry->msgid_plural_size + msgid_plural_len + >= entry->msgid_plural_size) { - 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; + entry->msgid_plural_size += MSGDELTA; + 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; } - else if (*pstate & PS_MSGSTR) + entry->msgid_plural_len += msgid_plural_len; + if (append) + U32_MEMCCPY(entry->msgid_plural + entry->msgid_plural_len + - msgid_plural_len, + msgid_plural, + entry->msgid_plural_size - msgid_plural_len, temp); + else + 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; +} + +int +u32_set_msgstr(struct DrawState* state, struct PoEntry* entry, + const uint32_t* msgstr, int msgstr_index, int append) +{ + uint32_t** newmsgstr = NULL; + uint32_t* temp = NULL; + ssize_t* newmsgstr_len = NULL; + ssize_t* newmsgstr_size = NULL; + ssize_t msgstr_len = 0; + + assert((state != NULL) && (entry != NULL)); + if (msgstr) + msgstr_len = u32_strlen(msgstr); + if (entry->msgstr_count == 0 || msgstr_index + 1 > entry->msgstr_count) { - /* first entry */ - if (*current_entry == dstate->entries) + newmsgstr = NULL; + newmsgstr_size = NULL; + newmsgstr_len = NULL; + entry->msgstr_count = msgstr_index + 1; + if (!entry->msgstr) { - if (starts_with(token, "Plural-Forms: ")) - { - if (set_plural_forms(dstate, *current_entry, - token + strlen("Plural-Forms: "), 0)) - goto parse_po_line_alloc_error; - set_nplurals(dstate, *current_entry); - } + newmsgstr = calloc(entry->msgstr_count, + sizeof(uint32_t*)); + if (!newmsgstr) + goto u32_set_msgstr_alloc_error; + newmsgstr_size + = calloc(entry->msgstr_count, sizeof(ssize_t)); + if (!newmsgstr_size) + goto u32_set_msgstr_alloc_error; + newmsgstr_len + = calloc(entry->msgstr_count, sizeof(ssize_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) + state->untranslated_count--; } - - 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 + { + 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(ssize_t)); + if (!newmsgstr_size) + goto u32_set_msgstr_alloc_error; + newmsgstr_len = realloc(entry->msgstr_len, + entry->msgstr_count * sizeof(ssize_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; } - else if (*pstate & PS_COMMENT_TRAN) + if (!entry->msgstr[msgstr_index]) { - 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); + entry->msgstr_size[msgstr_index] = MSGDELTA; + 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 (*pstate & PS_COMMENT_EXTR) + else if (entry->msgstr_len[msgstr_index] + msgstr_len + >= entry->msgstr_size[msgstr_index]) { - 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); + entry->msgstr_size[msgstr_index] += MSGDELTA; + entry->msgstr[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; } - else if (*pstate & PS_COMMENT_FLAG) + entry->msgstr_len[msgstr_index] += msgstr_len; + if (append) + U32_MEMCCPY(entry->msgstr[msgstr_index] + + entry->msgstr_len[msgstr_index] - msgstr_len, + msgstr, entry->msgstr_size[msgstr_index] - msgstr_len, + temp); + else + 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; +} + +int +u32_set_plural_forms(struct DrawState* state, struct PoEntry* entry, + const uint32_t* plural_forms, const ssize_t plural_forms_len, + int append) +{ + uint32_t* temp = NULL; + + assert((state != NULL) && (entry != NULL)); + if (plural_forms_len >= MSGDELTA) + return 1; + if (!entry->plural_forms) { - ptoken = token; - while (*ptoken) - { - 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++; - } - } + entry->plural_forms_size = MSGDELTA; + entry->plural_forms_len = 0; + entry->plural_forms = calloc(MAXMSGLINE, sizeof(uint32_t)); + if (!entry->plural_forms) + goto u32_set_plural_forms_alloc_error; } - else if (*pstate & PS_COMMENT_REFN) - add_ref_comment(*current_entry, token); - else if (*pstate & PS_COMMENT_OBSO) + else if (entry->plural_forms_size + plural_forms_len + >= entry->plural_forms_size) { - 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); + 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; } - *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; - return !!dstate->error; + entry->plural_forms_len += plural_forms_len; + if (append) + U32_MEMCCPY(entry->plural_forms + entry->plural_forms_size + - plural_forms_len, + plural_forms, + entry->plural_forms_size - plural_forms_len, temp); + else + U32_MEMCCPY(entry->plural_forms, plural_forms, + entry->plural_forms_size, temp); + return 0; -parse_po_line_alloc_error: - dstate->error = ERR_CANT_ALLOC; +u32_set_plural_forms_alloc_error: + state->error = ERR_CANT_ALLOC; return 1; } @@ -1619,11 +1723,11 @@ update_warning(const uint32_t* msgid, const uint32_t* msgstr, int* warning) { if (!msgid || !msgstr || !warning) return; - if (u32_count_strings(msgid, (const uint32_t*)L"\\n") - != u32_count_strings(msgstr, (const uint32_t*)L"\\n") + if (u32_count_strings(msgid, bslash_en) + != u32_count_strings(msgstr, bslash_en) || (warn_count_dots - && u32_count_chars(msgid, '.') - != u32_count_chars(msgstr, '.'))) + && u32_count_chars(msgid, DOT) + != u32_count_chars(msgstr, DOT))) *warning = 1; else *warning = 0; diff --git a/po.h b/po.h @@ -5,24 +5,11 @@ #include <stdint.h> #include <stdlib.h> -#define MSGSTR_MAX 4096 -// #define ALLOC_DELTA 10 /* lines to tolerate the lack of msgid */ #define PO_DETECTION_LINES 24 struct DrawState; -enum { - SAVE_ERR_NONE = 0, - SAVE_ERR_CANT_ALLOC = 1, - SAVE_ERR_CANT_OPEN_FILE = 2, - SAVE_ERR_CANT_MOVE_FILE = 3, - SAVE_ERR_CANT_STAT_FILE = 4, - SAVE_ERR_CANT_READLINK = 5, - SAVE_ERR_CANT_SYMLINK = 6, - SAVE_ERR_CANT_UNLINK = 7 -}; - typedef enum { PS_NONE = 0, PS_COMMENT_TRAN = 1, /* "translator-comments" : # */ @@ -62,70 +49,70 @@ struct PoEntry { int obsolete; int warning; uint32_t* msgid; - size_t msgid_size; - size_t msgid_len; + ssize_t msgid_size; + ssize_t msgid_len; uint32_t* msgid_plural; size_t msgid_plural_size; size_t msgid_plural_len; uint32_t* plural_forms; - size_t plural_forms_size; - size_t plural_forms_len; + ssize_t plural_forms_size; + ssize_t plural_forms_len; uint32_t** msgstr; int msgstr_count; - size_t* msgstr_size; - size_t* msgstr_len; + ssize_t* msgstr_size; + ssize_t* msgstr_len; uint32_t** comments; - size_t comment_lines; + ssize_t comment_lines; uint32_t** extr_comments; - size_t extr_comment_lines; + ssize_t extr_comment_lines; char** ref_comments; - size_t ref_comment_lines; + ssize_t ref_comment_lines; }; +int add_ref_comment(struct PoEntry* entry, const char* comment); char* format_flags(char* buffer, const size_t max, const struct PoEntry* entry, const int draw_box); -void init_po_entry(struct PoEntry* entry); void free_po_entry(struct PoEntry* entry); -struct PoEntry* set_flags(struct PoEntry* entry, const Flags flags); -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); -int set_plural_forms(struct DrawState* state, struct PoEntry* entry, - const char* plural_forms, int append); -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); -struct PoEntry* add_ref_comment(struct PoEntry* entry, const char* comment); +void init_po_entry(struct PoEntry* entry); int load_file(struct DrawState* state, long* lineno, long* col); +int parse_po_line(const char* line, struct DrawState* dstate, + struct PoEntry** current_entry, ParseState* pstate, long* col, + int* msgstr_index); int save_comment(FILE* output, struct DrawState* state, const struct PoEntry* current); -int save_file(struct DrawState* state); -int save_flags(FILE* output, struct DrawState* state, +int save_extr_comment(FILE* output, struct DrawState* state, const struct PoEntry* current); +int save_file(struct DrawState* state); +int save_flags(FILE* output, const struct PoEntry* current); int save_msgid(FILE* output, struct DrawState* state, const struct PoEntry* current); int save_msgid_plural(FILE* output, struct DrawState* state, const struct PoEntry* current); int save_msgstrs(FILE* output, struct DrawState* state, const struct PoEntry* current); -int save_ref_comment(FILE* output, struct DrawState* state, - const struct PoEntry* current); -int save_extr_comment(FILE* output, struct DrawState* state, - const struct PoEntry* current); -int parse_po_line(const char* line, struct DrawState* dstate, - struct PoEntry** current_entry, ParseState* pstate, long* col, - int* msgstr_index); +int save_ref_comment(FILE* output, const struct PoEntry* current); +int set_flags(struct PoEntry* entry, const Flags flags); +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); +int set_msgstr(struct DrawState* state, struct PoEntry* entry, + const char* msgstr, int msgstr_index, int append); +int set_nplurals(struct DrawState* state, struct PoEntry* entry); +int set_plural_forms(struct DrawState* state, struct PoEntry* entry, + const char* plural_forms, int append); +int u32_add_comment(struct PoEntry* entry, const uint32_t* comment); +int u32_add_extr_comment(struct PoEntry* entry, + const uint32_t* extr_comment); +int u32_set_msgid(struct DrawState* state, struct PoEntry* entry, + const uint32_t* msgid, ssize_t msgid_size, int append); +int u32_set_msgid_plural(struct DrawState* state, struct PoEntry* entry, + const uint32_t* msgid_plural, ssize_t msgid_plural_size, int append); +int u32_set_msgstr(struct DrawState* state, struct PoEntry* entry, + const uint32_t* msgstr, int msgstr_index, int append); +int u32_set_plural_forms(struct DrawState* state, struct PoEntry* entry, + const uint32_t* plural_forms, const ssize_t plural_forms_len, + int append); void update_statistics(const struct PoEntry* entries, struct DrawState* state, struct PoEntry* entry); void update_warning(const uint32_t* msgid, const uint32_t* msgstr, int* warning); diff --git a/poe.1.in b/poe.1.in @@ -33,13 +33,15 @@ This option overrides the setting in 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 +By default (unless changed in +.Pa config.h Ns ), +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 +the closing quote) is greater than .Va literal_break_threshold , a line of the form .Bd -literal -offset indent @@ -203,20 +205,22 @@ Bugs can be reported using the ticket tracker at: .It UTF-8 character set is assumed. .It -CJK characters are unsupported/problematic. -This is a limitation of termbox library. -See -.Lk https://\:github.com/\:nsf/\:termbox -for more details. +RTL scripts are unsupported/problematic. +CJK scripts should work better after v2.0, which introduced the use of +.Xr wcwidth 3 . .It Only one file can be edited at a time, and no additional files can be loaded at runtime. This is a design decision. .It -Unless the define -.Dv CREATE_BACKUPS -is commented out before compilation, the directory of the edited file must be -writable by the user in order to write the changes to the file. +Unless the variable +.Va create_backups +is set to 0 before compilation, or the parameter +.Fl B +is used when invoking +.Nm , +the directory of the edited file must be writable by the user in order to write +the changes to the file. This is due to creating backups in order to avoid the potential data loss under specific circumstances (loss of power during write). .It diff --git a/poe.c b/poe.c @@ -26,9 +26,6 @@ " 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" @@ -39,6 +36,8 @@ #include "util.h" #include "version.h" +#define MAXCOPYBUF 8192 + extern const char* errors[]; extern const char* actions[]; extern const char* prompt_dirty; @@ -104,7 +103,7 @@ cancel_msgstr(struct DrawState* state) state->input_rows_count = 0; if (state->info_buffer) for (i = 0; i < state->info_rows_count; i++) - free_bufferline(state->info_buffer + i); + free_bufferline(&state->info_buffer[i]); free(state->info_buffer); state->info_buffer = NULL; state->info_rows_count = 0; @@ -145,8 +144,7 @@ copy_msgid_to_input(struct DrawState* state) 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; + ssize_t msgid_num_lines, lines_to_init, lines_to_free, i, len, newlen; assert(state != NULL); @@ -207,7 +205,7 @@ copy_msgid_to_input(struct DrawState* state) len = u32_next_line(current->msgid, &pmsgid, 0, 0); u32_strncpy(pbuffer->text, old_pmsgid, MIN(len + 1, LINE_DEFAULT)); - newlen = u32_decode_tabs(pbuffer->text, LINE_DEFAULT); + newlen = u32_decode_tabs(state, pbuffer->text, LINE_DEFAULT); pbuffer->length = newlen; pbuffer++; } @@ -219,8 +217,6 @@ copy_msgid_to_input(struct DrawState* state) return 0; } -#define MAXCOPYBUF 8192 - int cp(const char* from, const char* to) { @@ -362,7 +358,6 @@ edit_page_up(struct DrawState* state) static int erase_backwards(struct DrawState* state) { - const struct BufferLine* current_line; struct BufferLine* buffer_line; assert((state != NULL) && (state->input_buffer != NULL)); @@ -384,23 +379,7 @@ erase_backwards(struct DrawState* state) } buffer_line->text[i - 1] = 0; buffer_line->length--; - /* TODO: position_to */ - state->coord.col--; - if (state->input_first_shown.col > 0) - { - current_line = &state->input_buffer[state->coord.row]; - if (display_length(current_line->text, - current_line->length) - >= state->edit_width - 4) - state->input_first_shown.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->input_first_shown.col - = state->input_display_column; + position_to(state, state->coord.row, state->coord.col - 1); state->dirty = 1; } else if (state->coord.row > 0) @@ -850,7 +829,6 @@ insert_char(struct DrawState* state, uint32_t ch) len = buffer_line->length; size = buffer_line->size; - // TODO: realloc if (len + 1 > size) { size += LINE_ALLOC_DELTA; @@ -871,20 +849,7 @@ insert_char(struct DrawState* state, uint32_t ch) } 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); - } + position_to(state, state->coord.row, state->coord.col + 1); if (add_null) buffer_line->text[state->coord.col] = 0; state->dirty = 1; @@ -1100,7 +1065,7 @@ load_info(struct DrawState* state) { num_lines += msgid_plural_num_lines + 1; if (first->plural_forms) - num_lines += 3; + num_lines += 2; /* title + plural_forms */ } if (current->comments) num_lines += current->comment_lines + 1; @@ -1120,6 +1085,8 @@ load_info(struct DrawState* state) if (!newtext) goto load_info_alloc_error; pnewbuf->text = newtext; + pnewbuf->size = LINE_DEFAULT; + pnewbuf->size = 0; pnewbuf++; } @@ -1161,12 +1128,13 @@ 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; + ssize_t len, llen, size, 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; + (*lines)->length = llen + = ptext ? ptext - (*lines)->text : LINE_DEFAULT - 1; + (*lines)->size = LINE_DEFAULT; if (llen > state->info_maxlen) state->info_maxlen = llen; (*lines)->fg = colors[C_EDIT_MSGID_HEADING].fg; @@ -1176,15 +1144,21 @@ load_info_comments(struct DrawState* state, struct BufferLine** lines) { 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; + size = MAX(len + 2, LINE_DEFAULT); /* + NUL */ + ENSURE_SIZE((*lines)->text, ptext, (*lines)->size, size, size, + load_info_comments_alloc_error, uint32_t); + U32_MEMCCPY((*lines)->text, comment, len + 1, ptext); + llen = ptext ? ptext - (*lines)->text : (*lines)->size - 1; + (*lines)->length = llen; if (llen > state->info_maxlen) state->info_maxlen = llen; (*lines)++; } return 0; + +load_info_comments_alloc_error: + state->error = ERR_CANT_ALLOC; + return 1; } static int @@ -1193,13 +1167,13 @@ 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; + ssize_t len, llen, size, 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; + (*lines)->size = MAX(llen + 1, LINE_DEFAULT); if (llen > state->info_maxlen) state->info_maxlen = llen; (*lines)->fg = colors[C_EDIT_MSGID_HEADING].fg; @@ -1209,15 +1183,21 @@ load_info_extr_comments(struct DrawState* state, struct BufferLine** lines) { 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; + size = MAX(len + 2, LINE_DEFAULT); /* + NUL */ + ENSURE_SIZE((*lines)->text, ptext, (*lines)->size, size, size, + load_info_extr_comments_alloc_error, uint32_t); + U32_MEMCCPY((*lines)->text, extr_comment, len + 1, ptext); + len = ptext ? ptext - (*lines)->text : (*lines)->size - 1; + (*lines)->length = llen; if (llen > state->info_maxlen) state->info_maxlen = llen; (*lines)++; } return 0; + +load_info_extr_comments_alloc_error: + state->error = ERR_CANT_ALLOC; + return 1; } static int @@ -1227,7 +1207,7 @@ load_info_msgid(struct DrawState* state, struct BufferLine** lines) const uint32_t* pumsgid = NULL; const uint32_t* old_pumsgid = NULL; uint32_t* ptext = NULL; - ssize_t len, llen; + ssize_t len, llen, size; current = &state->entries[state->msgid_number - 1]; ptext = u32_u8_memccpy((*lines)->text, "msgid:", 0, LINE_DEFAULT); @@ -1242,15 +1222,21 @@ load_info_msgid(struct DrawState* state, struct BufferLine** lines) { 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); + size = MAX(len + 2, LINE_DEFAULT); /* + NUL */ + ENSURE_SIZE((*lines)->text, ptext, (*lines)->size, size, size, + load_info_msgid_alloc_error, uint32_t); + U32_MEMCCPY((*lines)->text, old_pumsgid, len + 1, ptext); + llen = u32_decode_tabs(state, (*lines)->text, len + 1); (*lines)->length = llen; if (llen > state->info_maxlen) state->info_maxlen = llen; (*lines)++; } return 0; + +load_info_msgid_alloc_error: + state->error = ERR_CANT_ALLOC; + return 1; } static int @@ -1258,10 +1244,10 @@ 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; + const uint32_t* pumsgid = NULL; uint32_t* ptext = NULL; - ssize_t len, llen; + ssize_t len, llen, size; first = &state->entries[0]; current = &state->entries[state->msgid_number - 1]; @@ -1276,10 +1262,12 @@ load_info_msgid_plural(struct DrawState* state, struct BufferLine** lines) 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); + len = u32_next_line(current->msgid_plural, &pumsgid, 0, 0); + size = MAX(len + 2, LINE_DEFAULT); + ENSURE_SIZE((*lines)->text, ptext, (*lines)->size, size, size, + load_info_msgid_plural_alloc_error, uint32_t); + U32_MEMCCPY((*lines)->text, old_pumsgid, len + 1, ptext); + llen = u32_decode_tabs(state, (*lines)->text, size); (*lines)->length = llen; if (llen > state->info_maxlen) state->info_maxlen = llen; @@ -1290,35 +1278,42 @@ load_info_msgid_plural(struct DrawState* state, struct BufferLine** lines) ptext = u32_u8_memccpy((*lines)->text, "Plural forms:", 0, LINE_DEFAULT); (*lines)->length = llen - = ptext ? ptext - (*lines)->text : LINE_DEFAULT; + = ptext ? ptext - (*lines)->text : LINE_DEFAULT - 1; + (*lines)->size = 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); + U32_SAFE_COPY((*lines)->text, ptext, (*lines)->size, + first->plural_forms, first->plural_forms_size, + load_info_msgid_plural_alloc_error); (*lines)->length = llen - = ptext ? ptext - (*lines)->text : LINE_DEFAULT; + = ptext ? ptext - (*lines)->text : (*lines)->size - 1; + //(*lines)->size = MAX(llen + 1, LINE_DEFAULT); if (llen > state->info_maxlen) state->info_maxlen = llen; (*lines)++; } return 0; + +load_info_msgid_plural_alloc_error: + state->error = ERR_CANT_ALLOC; + return 1; } 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; + char* ref_comment = NULL; + ssize_t len, llen, size, i; 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; + (*lines)->size = MAX(llen + 1, LINE_DEFAULT); if (llen > state->info_maxlen) state->info_maxlen = llen; (*lines)->fg = colors[C_EDIT_MSGID_HEADING].fg; @@ -1328,15 +1323,21 @@ load_info_ref_comments(struct DrawState* state, struct BufferLine** lines) { 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; + size = MAX(len + 2, LINE_DEFAULT); /* + NUL */ + ENSURE_SIZE((*lines)->text, ptext, (*lines)->size, size, size, + load_info_ref_comments_alloc_error, uint32_t); + U32_U8_MEMCCPY((*lines)->text, ref_comment, len + 1, ptext); + llen = ptext ? ptext - (*lines)->text : LINE_DEFAULT; + (*lines)->length = llen; if (llen > state->info_maxlen) state->info_maxlen = llen; (*lines)++; } return 0; + +load_info_ref_comments_alloc_error: + state->error = ERR_CANT_ALLOC; + return 1; } static int @@ -1345,12 +1346,11 @@ load_msgstr(struct DrawState* state) struct PoEntry* current; struct BufferLine* newbuf; struct BufferLine* pnewbuf; - const uint32_t* pumsgstr = NULL; const uint32_t* old_pumsgstr = NULL; + const uint32_t* pumsgstr = NULL; uint32_t* newtext = NULL; uint32_t* temp = NULL; - size_t num_lines, i, len, newlen; - ssize_t row; + ssize_t num_lines, i, len, newlen, size, row; int msgstr_index; assert((state != NULL) && (state->entries != NULL)); @@ -1360,10 +1360,10 @@ load_msgstr(struct DrawState* state) msgstr_index = state->msgstr_index; num_lines = current->msgstr_count == 0 ? 1 - : u32_lines_in_string(*(current->msgstr + msgstr_index), 0, 0); + : u32_lines_in_string(current->msgstr[msgstr_index], 0, 0); for (row = 0; row < state->input_rows_count; row++) - free_bufferline(state->input_buffer + row); + free_bufferline(&state->input_buffer[row]); free(state->input_buffer); newbuf = calloc(num_lines, sizeof(struct BufferLine)); @@ -1374,43 +1374,44 @@ load_msgstr(struct DrawState* state) for (i = 0; i < num_lines; i++) { init_bufferline(pnewbuf); - newtext = calloc(LINE_DEFAULT, sizeof(uint32_t)); + size = MAX(current->msgstr_size[msgstr_index], LINE_DEFAULT); + newtext = calloc(size, sizeof(uint32_t)); if (!newtext) goto load_msgstr_alloc_error; pnewbuf->text = newtext; - pnewbuf->size = LINE_DEFAULT; + pnewbuf->size = size; pnewbuf->length = 0; pnewbuf++; } - state->input_rows_count = num_lines; if (current->msgstr_count > 0) { - pumsgstr = *(current->msgstr + msgstr_index); - if (pumsgstr) + pumsgstr = current->msgstr[msgstr_index]; + if (!pumsgstr) { - pnewbuf = newbuf; - while (*pumsgstr) - { - old_pumsgstr = pumsgstr; - len = u32_next_line(current->msgstr[msgstr_index], - &pumsgstr, 0, 0); - U32_MEMCCPY(pnewbuf->text, old_pumsgstr, - MIN(len + 1, LINE_DEFAULT), temp); - newlen = u32_decode_tabs(pnewbuf->text, - LINE_DEFAULT); /* TODO: Check */ - pnewbuf->length = newlen; - pnewbuf->size = LINE_DEFAULT; - pnewbuf++; - } - } - else newbuf[0].length = 0; + goto load_msgstr_done; + } + pnewbuf = newbuf; + while (*pumsgstr) + { + old_pumsgstr = pumsgstr; + len = u32_next_line(current->msgstr[msgstr_index], + &pumsgstr, 0, 0); + size = MAX(len + 2, LINE_DEFAULT); /* + NUL */ + ENSURE_SIZE(pnewbuf->text, temp, pnewbuf->size, size, + size, load_msgstr_alloc_error, uint32_t); + U32_MEMCCPY(pnewbuf->text, old_pumsgstr, len + 1, temp); + newlen = u32_decode_tabs(state, pnewbuf->text, len + 1); + pnewbuf->length = newlen; + pnewbuf++; + } } else newbuf[0].length = 0; +load_msgstr_done: state->input_buffer = newbuf; return 0; @@ -1577,6 +1578,13 @@ static int move_right(struct DrawState* state) { assert(state != NULL); + if (state->edit_info_focused) + { + if (state->info_first_shown.col + state->edit_width - 3 + < state->info_maxlen + 1) + state->info_first_shown.col++; + return 0; + } position_to(state, state->coord.row, state->coord.col + 1); return 0; } @@ -2095,13 +2103,29 @@ prev_msgid(struct DrawState* state, const int delta) return ret; } +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--; + show_edit(state); + return 0; +} + int print_error(const int code, const char* msg, ...) { char* buf = NULL; va_list args; long bufsize; - assert(msg != NULL); + assert((msg != NULL) && (limits != NULL)); bufsize = limits[L_LINE_MAX] ? limits[L_LINE_MAX] : PRINT_BUF_MAX_FALLBACK; buf = malloc(bufsize); @@ -2119,41 +2143,28 @@ print_error(const int code, const char* msg, ...) static int quit_callback(struct DrawState* state, struct tb_event* ev) { + int result = 0; assert((state != NULL) && (ev != NULL)); if (ev->ch == 'y') - write_file(state); + result = write_file(state); state->prompt_callback = NULL; state->running = 0; - return 0; + return result; } -void +int redraw_status(struct DrawState* state) { + int result = 0; assert(state != NULL); if (state->recalculate_size) { recalculate_size(state); state->recalculate_size = 0; } - (void)draw_status(state); + result = draw_status(state); tb_present(); -} - -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--; - show_edit(state); - return 0; + return result; } static int @@ -2196,10 +2207,6 @@ save_msgstr(struct DrawState* state) 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));*/ } free_bufferline(pinput_buffer); } @@ -2207,24 +2214,35 @@ save_msgstr(struct DrawState* state) free(state->input_buffer); state->input_buffer = NULL; state->input_rows_count = 0; + assert(state->info_buffer != NULL); for (i = 0; i < state->info_rows_count; i++) - free_bufferline(state->info_buffer + i); + free_bufferline(&state->info_buffer[i]); free(state->info_buffer); 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)); + if (u32_encode_tabs(state, to_save, to_save_size * sizeof(uint32_t)) < + 0) + goto save_msgstr_error; current = &state->entries[state->msgid_number - 1]; - u32_match_msgid_ending(current->msgid, to_save, - to_save_size * sizeof(uint32_t)); + if (u32_match_msgid_ending(state, current->msgid, to_save, + to_save_size * sizeof(uint32_t)) < 0) + goto save_msgstr_error; u32_set_msgstr(state, current, to_save, state->msgstr_index, 0); update_warning(current->msgid, to_save, &current->warning); state->input_changed_fuzzy = UNCHANGED; +save_msgstr_error: + if (state->error == ERR_CANT_ALLOC) + goto save_msgstr_alloc_error; + tb_hide_cursor(); free(to_save); - return 0; + return !!state->error; + +save_msgstr_alloc_error: + return 1; } static int @@ -2252,6 +2270,7 @@ search_position_to(struct DrawState* state, const int column) static int show_edit(struct DrawState* state) { + assert((state != NULL) && (state->entries != NULL)); if (state->msgstr_index + 1 > state->entries[state->msgid_number - 1].msgstr_count) state->msgstr_index = 0; @@ -2278,6 +2297,7 @@ show_edit(struct DrawState* state) static int show_help(struct DrawState* state) { + assert(state != NULL); state->show_help = 1; state->error = ERR_NONE; return 0; @@ -2286,6 +2306,7 @@ show_help(struct DrawState* state) static int show_search(struct DrawState* state) { + assert(state != NULL); state->show_search = 1; state->search_column = 0; state->search_display_column = 0; @@ -2301,6 +2322,7 @@ show_search(struct DrawState* state) static int toggle_fuzzy(struct DrawState* state) { + assert((state != NULL) && (state->entries != NULL)); if (state->msgid_number == 1) { state->error = ERR_ILLEGAL_ON_FIRST; @@ -2333,6 +2355,7 @@ toggle_fuzzy(struct DrawState* state) static int toggle_info_focused(struct DrawState* state) { + assert(state != NULL); state->edit_info_focused = !state->edit_info_focused; return 0; } @@ -2343,9 +2366,13 @@ yank_input_to_paste_buffer(struct DrawState* state) struct BufferLine* ppaste = NULL; struct BufferLine* newbuf = NULL; struct BufferLine* pinput = NULL; + uint32_t* newtext = NULL; + uint32_t* 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) return 1; @@ -2358,7 +2385,7 @@ yank_input_to_paste_buffer(struct DrawState* state) if (!state->input_buffer || !(*state->input_buffer).text || !*(*state->input_buffer).text) { - for (ssize_t i = 0; i < state->paste_count; i++) + for (i = 0; i < state->paste_count; i++) free_bufferline(state->paste + i); free(state->paste); state->paste = NULL; @@ -2373,7 +2400,7 @@ yank_input_to_paste_buffer(struct DrawState* state) ? state->paste_count - state->input_rows_count : 0; - for (ssize_t i = 0; i < lines_to_free; i++) + for (i = 0; i < lines_to_free; i++) free_bufferline(state->paste + state->paste_count - i - 1); if (!state->paste) newbuf = calloc(state->input_rows_count, @@ -2381,68 +2408,52 @@ yank_input_to_paste_buffer(struct DrawState* state) else newbuf = realloc(state->paste, state->input_rows_count * sizeof(struct BufferLine)); - pinput = state->input_buffer; - if (!newbuf) - return 0; + goto yank_input_alloc_error; - for (ssize_t i = 0; i < lines_to_init; i++) + pinput = state->input_buffer; + for (i = 0; i < lines_to_init; i++) { - uint32_t* newtext; ppaste = newbuf + state->input_rows_count - 1 - i; init_bufferline(ppaste); newtext = calloc(LINE_DEFAULT, sizeof(uint32_t)); if (!newtext) - return 0; + goto yank_input_alloc_error; ppaste->text = newtext; + ppaste->size = LINE_DEFAULT; } if (pinput) - for (ssize_t i = 0; i < state->input_rows_count; i++) + for (i = 0; i < state->input_rows_count; i++) { - ppaste = newbuf + i; - u32_strncpy(ppaste->text, pinput->text, - MIN(pinput->length + 1, LINE_DEFAULT)); + ppaste = &newbuf[i]; + U32_SAFE_COPY(ppaste->text, temp, ppaste->size, + pinput->text, pinput->size, + yank_input_alloc_error); ppaste->length = pinput->length; pinput++; } state->paste_count = state->input_rows_count; state->paste = newbuf; - return 0; + +yank_input_alloc_error: + state->error = ERR_CANT_ALLOC; + return 1; } static int write_file(struct DrawState* state) { + int result; assert(state != NULL); - if (save_file(state)) + result = save_file(state); + if (result) { state->running = 0; - return 1; + return result; } - /* TODO: Check - int result = save_file(state->entries, state->real_msgid_count, - state->filename, state->nplurals); - switch (result) - { - case SAVE_ERR_CANT_ALLOC: - state->error = ERR_CANT_ALLOC; - state->running = 0; - break; - case SAVE_ERR_CANT_OPEN_FILE: - state->error = ERR_CANT_SAVE; - state->running = 0; - break; - case SAVE_ERR_CANT_MOVE_FILE: - state->error = ERR_ERRNO_SET; - state->running = 0; - break; - default: - state->error = ERR_NONE; - state->dirty = 0; - }*/ state->error = ERR_NONE; state->dirty = 0; return 0; @@ -2451,20 +2462,20 @@ write_file(struct DrawState* state) int main(int argc, char** argv) { - struct tb_event ev; struct DrawState state; + struct tb_event ev; struct stat st; char prompt[LINE_DEFAULT]; char* dir = NULL; char* filename = NULL; char* pdir = NULL; - char* temp = NULL; char* rfn = NULL; const char* saved_tb_error = NULL; + char* temp = NULL; char* wd = NULL; ssize_t filename_size = 0; - long lineno = 1; long col = 1; + long lineno = 1; int dir_access; int dir_access_errno; int optind = 1; @@ -2482,16 +2493,15 @@ main(int argc, char** argv) } #endif + /* Necessary in order for termbox to function properly + * See: https://github.com/termbox/termbox2/issues/73 + */ setlocale(LC_ALL, ""); - if (argc == 1) - return print_error(CUSTOM_ERROR_START + ERR_NO_CL_ARG, - "No filename given"); - main_options_loop: if (optind >= argc) return print_error(CUSTOM_ERROR_START + ERR_NO_CL_ARG, - "No filename given"); + errors[ERR_NO_CL_ARG]); if (!strcmp(argv[optind], "-h") || !strcmp(argv[optind], "--help")) { @@ -2596,7 +2606,7 @@ main_options_loop: * we need a dir, which needs to be allocated, and to allocate it we * need to know the value returned by fpathconf(dirfd, _PC_PATH_MAX)... */ - /* argv[1] is the filename; BTW, "filename" is a misnomer */ + /* argv[optind] is the filename; BTW, "filename" is a misnomer */ filename_size = strlen(argv[optind]) + 1; if (!(dir = malloc(filename_size))) { @@ -2625,7 +2635,8 @@ main_options_loop: { free(filename); free(dir); - return print_error(EINVAL, "No filename given"); + return print_error(CUSTOM_ERROR_START + ERR_NO_CL_ARG, + errors[ERR_NO_CL_ARG]); } init_drawstate(&state, filename, prompt); @@ -2833,8 +2844,6 @@ main_skip_backups: state.msgid_number = state.msgid_count > 0 ? 1 : 0; state.first_shown_msgid = state.msgid_number; - goto init_termbox; - init_termbox: if ((tbires = tb_init())) { diff --git a/poe.h b/poe.h @@ -34,7 +34,8 @@ static int insert_space(struct DrawState* state); static int insert_tab(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_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); @@ -80,7 +81,7 @@ static size_t prev_msgid(struct DrawState* state, const int delta); static int prev_plural(struct DrawState* state); 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); +int 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); diff --git a/util.c b/util.c @@ -8,30 +8,28 @@ #include <stdlib.h> #include <string.h> +#include "draw.h" #include "termbox.h" #include "po.h" #include "util.h" -/* Whitespace definition for determining word boundaries */ -#define B_WS " \v\t-" -/* Full word boundary delimiter definition */ -#define B_FULL " \v\t-()[]{},.;:/\\" +/* 32-bit string constants */ +const uint32_t bslash_en[] = {0x0000005c, 0x0000006e, 0}; +const uint32_t space_dot[] = {0x00000020, 0x0000002e, 0}; -enum { UTLERRNONE = 0, UTLERROVFL = 1 }; - -int utlerrno = UTLERRNONE; +extern int match_msgstr_ending; /* max - max Unicode characters to convert from UTF-8 * Returns: number of processed Unicode characters */ size_t u8_string_to_unicode(uint32_t* us, const char* s, const size_t max) { - uint32_t uch; uint32_t* pus = us; - int u8_len = 0; - size_t added = 0; + uint32_t uch; + size_t added = 0; + int u8_len = 0; - assert(s != NULL); + assert((s != NULL) && (us != NULL)); while (*s) { u8_len = tb_utf8_char_to_unicode(&uch, s); @@ -54,16 +52,16 @@ size_t unicode_string_to_u8(char* s, const uint32_t* us, const size_t max) { char ch[8]; - char* ps = NULL; + char* ps = NULL; const uint32_t* pus = us; + size_t added = 0; size_t len; - size_t added = 0; assert((s != NULL) && (us != NULL)); ps = s; while (*pus) { - len = tb_utf8_unicode_to_char(ch, *pus); + len = tb_utf8_unicode_to_char(ch, *pus); if (added + 1 > max) return added; memccpy(ps, ch, 0, len); @@ -75,62 +73,70 @@ unicode_string_to_u8(char* s, const uint32_t* us, const size_t max) return added; } -// TODO: Check -size_t -u32_match_msgid_ending(const uint32_t* msgid, uint32_t* msgstr, size_t max) +/* + * Returns: -1 on error + */ +ssize_t +u32_match_msgid_ending(struct DrawState* state, const uint32_t* msgid, + uint32_t* msgstr, size_t max) { - uint32_t* buf = NULL; - size_t buf_len = 0; - size_t msgid_len = 0; + uint32_t* buf = NULL; + ssize_t buf_len = 0; + ssize_t msgid_len = 0; + assert((state != NULL) && (msgstr != NULL)); buf = calloc(max + 1, sizeof(uint32_t)); - if (!buf || !msgid) + if (!buf) + goto u32_match_msgid_ending_alloc_error; + + if (!msgid) return 0; buf_len = u32_strncpy(buf, msgstr, (max + 1) * sizeof(uint32_t)); msgid_len = u32_strlen(msgid); - if (msgid_len > 1 - && u32_starts_with(msgid + msgid_len - 2, (uint32_t*)L"\\n")) + if (msgid_len > 1 && u32_starts_with(msgid + msgid_len - 2, bslash_en)) { if (!(buf_len > 1 - && u32_starts_with(buf + buf_len - 2, - (uint32_t*)L"\\n"))) + && u32_starts_with(buf + buf_len - 2, bslash_en))) { buf_len += 2; - buf[buf_len - 2] = '\\'; - buf[buf_len - 1] = 'n'; + buf[buf_len - 2] = BSLASH; + buf[buf_len - 1] = EN; buf[buf_len] = 0; } } -#ifdef MATCH_MSGSTR_ENDING - else if (msgid_len > 0 - && u32_strstr((uint32_t*)L" .", msgid + msgid_len - 1)) + else if (match_msgstr_ending && msgid_len > 0 + && u32_strstr(space_dot, msgid + msgid_len - 1)) { if (!(buf_len > 0 - && u32_strstr((uint32_t*)L" .", buf + buf_len - 1))) + && u32_strstr(space_dot, buf + buf_len - 1))) { buf_len++; buf[buf_len - 1] = msgid[msgid_len - 1]; buf[buf_len] = 0; } } - else if (buf_len > 1 - && u32_starts_with(buf + buf_len - 2, (uint32_t*)L"\\n")) + else if (match_msgstr_ending && buf_len > 1 + && u32_starts_with(buf + buf_len - 2, bslash_en)) { buf_len -= 2; buf[buf_len] = 0; } - else if (buf_len > 0 && u32_strstr((uint32_t*)L" .", buf + buf_len - 1)) + else if (match_msgstr_ending && buf_len > 0 + && u32_strstr(space_dot, buf + buf_len - 1)) { buf_len--; buf[buf_len] = 0; } -#endif u32_strncpy(msgstr, buf, max); return buf_len; + +u32_match_msgid_ending_alloc_error: + state->error = ERR_CANT_ALLOC; + return -1; } uint32_t* @@ -180,8 +186,8 @@ 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 result = 0; const uint32_t* ps = s; + size_t result = 0; assert(s != NULL); while (*ps) { @@ -195,9 +201,9 @@ 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 result = 0; const uint32_t* phaystack = haystack; size_t needle_len = 0; + size_t result = 0; assert((haystack != NULL) && (needle != NULL)); needle_len = u32_strlen(needle); @@ -213,18 +219,21 @@ u32_count_strings(const uint32_t* haystack, const uint32_t* needle) return result; } -size_t -u32_encode_tabs(uint32_t* s, size_t max) +/* + * Returns: -1 on error + */ +ssize_t +u32_encode_tabs(struct DrawState* state, uint32_t* s, size_t max) { - uint32_t* buf = NULL; + uint32_t* buf = NULL; uint32_t* pbuf = NULL; uint32_t* ps = s; size_t newlen = 0; - assert(s != NULL); + assert((state != NULL) && (s != NULL)); buf = calloc(max + 1, sizeof(uint32_t)); if (!buf) - return 0; + goto u32_encode_tabs_alloc_error; pbuf = buf; while (*ps) { @@ -245,22 +254,29 @@ u32_encode_tabs(uint32_t* s, size_t max) u32_strncpy(s, buf, max); free(buf); return newlen; + +u32_encode_tabs_alloc_error: + state->error = ERR_CANT_ALLOC; + return -1; } -size_t -u32_decode_tabs(uint32_t* s, size_t max) +/* + * Returns: -1 on error + */ +ssize_t +u32_decode_tabs(struct DrawState* state, uint32_t* s, size_t max) { - uint32_t* buf = NULL; + uint32_t* buf = NULL; uint32_t* pbuf = NULL; uint32_t* ps = s; size_t newlen = 0; - assert(s != NULL); + assert((state != NULL) && (s != NULL)); buf = calloc(max + 1, sizeof(uint32_t)); if (!buf) - return 0; + goto u32_decode_tabs_alloc_error; pbuf = buf; - while (*ps) + while (*ps && (size_t)(ps - s) < max) { if (*ps == '\\' && *(ps + 1) == 't') { @@ -278,6 +294,10 @@ u32_decode_tabs(uint32_t* s, size_t max) u32_strncpy(s, buf, max); free(buf); return newlen; + +u32_decode_tabs_alloc_error: + state->error = ERR_CANT_ALLOC; + return -1; } size_t @@ -326,6 +346,7 @@ const uint32_t* u32_strchr(const uint32_t* haystack, const uint32_t needle) { const uint32_t* ph = haystack; + assert(haystack != NULL); while (*ph) { if (*ph == needle) @@ -340,6 +361,7 @@ u32_strncpy(uint32_t* to, const uint32_t* from, size_t max) { const uint32_t* pfrom = from; size_t copied = 0; + assert((to != NULL) && (from != NULL)); /* pfrom - from < max - 1 ... except we can't reliably subtract with * size_t */ while (*pfrom && pfrom + 1 < max + from) @@ -360,6 +382,7 @@ u32_strncat(uint32_t* to, const uint32_t* from, size_t max) { const uint32_t* pfrom = from; size_t copied = 0; + assert((to != NULL) && (from != NULL)); while (*to && copied + 1 < max) { to++; @@ -379,6 +402,7 @@ u32_u8_strncpy(uint32_t* to, const char* from, size_t max) { uint32_t* ufrom = calloc(max + 1, sizeof(uint32_t)); size_t len; + assert(to != NULL); if (!ufrom) return 0; u8_string_to_unicode(ufrom, from, max); @@ -387,15 +411,18 @@ u32_u8_strncpy(uint32_t* to, const char* from, size_t max) return len; } -// TODO: Check -size_t +/* + * Returns: -1 on error + */ +ssize_t u8_u32_strncpy(char* to, const uint32_t* from, size_t max) { char* cfrom = NULL; size_t len; + assert(to != NULL); cfrom = calloc(max + 1, 1); if (!cfrom) - return 0; + return -1; unicode_string_to_u8(cfrom, from, max); len = strlen(cfrom); strncpy(to, cfrom, max); @@ -406,13 +433,13 @@ u8_u32_strncpy(char* to, const uint32_t* from, size_t max) } /* wrap - if >0, also wrap lines - * */ + */ size_t 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; @@ -426,7 +453,7 @@ u32_lines_in_string(const uint32_t* s, const int include_eol, const size_t wrap) } /*if (ps > s + 1 && *(ps - 2) == '\\' && *(ps - 1) == 'n') last_empty = 1;*/ - return result;// + last_empty; + return result; // + last_empty; } /* diff --git a/util.h b/util.h @@ -5,6 +5,25 @@ #include <stdint.h> #include <sys/types.h> +/* Whitespace definition for determining word boundaries */ +#define B_WS " \v\t-" +/* Full word boundary delimiter definition */ +#define B_FULL " \v\t-()[]{},.;:/\\" + +/* 32-bit char constants */ +#define BSLASH ((uint32_t)0x0000005c) +#define DOT ((uint32_t)0x0000002e) +#define EN ((uint32_t)0x0000005c) +#define NINE ((uint32_t)0x00000039) +#define SPACE ((uint32_t)0x00000020) +#define SEMICOL ((uint32_t)0x0000003b) +#define TAB ((uint32_t)0x00000009) +#define ZERO ((uint32_t)0x00000030) + +/* 32-bit string constants */ +extern const uint32_t bslash_en[]; +extern const uint32_t space_dot[]; + #define ABS(a) ((a < 0) ? (-a) : (a)) #define LEN(x) (sizeof(x) / sizeof(x[0])) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) @@ -112,6 +131,7 @@ enum { ERR_ILLEGAL_ON_FIRST, ERR_SYNTAX, ERR_NO_PLURAL_FORMS, + ERR_BAD_NPLURALS, ERR_ERRNO_SET }; @@ -127,19 +147,17 @@ enum { ACT_IDLE, ACT_WRITING, ACT_READING }; /* maximum chars for UTF-8 representations of Unicode chars, per Unicode char */ #define UTF8REPMAX 6 -extern int utlerrno; - 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); +ssize_t u32_match_msgid_ending(struct DrawState* state, 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); -size_t u32_decode_tabs(uint32_t* s, size_t max); +ssize_t u32_encode_tabs(struct DrawState* state, uint32_t* s, size_t max); +ssize_t u32_decode_tabs(struct DrawState* state, uint32_t* s, size_t max); size_t u8_strlen(const char* s); size_t u32_strlen(const uint32_t* s); const uint32_t* u32_strstr(const uint32_t* haystack, const uint32_t* needle); @@ -147,7 +165,7 @@ const uint32_t* u32_strchr(const uint32_t* haystack, const uint32_t needle); size_t u32_strncpy(uint32_t* to, const uint32_t* from, size_t max); size_t u32_strncat(uint32_t* to, const uint32_t* from, size_t max); size_t u32_u8_strncpy(uint32_t* to, const char* from, size_t max); -size_t u8_u32_strncpy(char* to, const uint32_t* from, size_t max); +ssize_t u8_u32_strncpy(char* to, const uint32_t* from, size_t max); size_t u32_lines_in_string(const uint32_t* s, const int include_eol, const size_t wrap); size_t u32_next_line(const uint32_t* s, const uint32_t** sptr,