poe

Уређивач .po фајлова
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување 165060e58bab530ae1f106c95546f4314b84d365
родитељ 1798c93a8167147bf3a00be0b1ba8f5910547a5a
Аутор: Страхиња Радић <contact@strahinja.org>
Датум:   Tue,  6 Jul 2021 12:22:40 +0200

Added F3, F4; added no-c-format flag; draw_help changed; added untranslated_count and fuzzy_count

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

Diffstat:
MTODO | 10++--------
Mconfig.def.h | 2+-
Mconfig.h | 2+-
Mdraw.c | 25+++++++++++++++++--------
Mdraw.h | 4+++-
Mpo.c | 204++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mpo.h | 42++++++++++++++++++++++++++----------------
Mpoe.1.in | 8+++++++-
Mpoe.c | 139++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Mutil.c | 2+-
Mutil.h | 2+-
измењених датотека: 11, додавања: 299(+), брисања: 141(-)

diff --git a/TODO b/TODO @@ -4,21 +4,15 @@ [x] Line editing with cursor [ ] Previous/next word? - [x] .po file save - [x] Save all read .po structure - [x] Edit first msgid's msgstr (in saved file, not in memory) - [ ] Yank/paste (entire current msgstr) [ ] Copy msgid (only) to msgstr + [ ] Handle #~ + [ ] Search (highlight=/, then navigate with next/prev) [ ] Sort by flag/column? - [/] ~Text selection?~ - No need. - - [/] ~Multiple page help dialog?~ - There's already a man page. - Legend ------ diff --git a/config.def.h b/config.def.h @@ -52,7 +52,7 @@ void format_msgs(char* result, size_t result_size, const char* format, static const struct StatusSegment status_segments[] = { { .format = "%s%s", .callback = format_filename, .fg = TB_BLACK, .bg = TB_WHITE, .alignment = LEFT }, - { .format = "%ld/%ld (%ld fuzzy)", + { .format = "%ld/%ld (%ld unt %ld fuzzy)", .callback = format_msgs, .fg = TB_WHITE | TB_BOLD, .bg = TB_BLUE, .alignment = CENTER }, { .format = "H=HELP, q=QUIT", .callback = NULL, diff --git a/config.h b/config.h @@ -52,7 +52,7 @@ void format_msgs(char* result, size_t result_size, const char* format, static const struct StatusSegment status_segments[] = { { .format = "%s%s", .callback = format_filename, .fg = TB_BLACK, .bg = TB_WHITE, .alignment = LEFT }, - { .format = "%ld/%ld (%ld fuzzy)", + { .format = "%ld/%ld (%ld unt %ld fuzzy)", .callback = format_msgs, .fg = TB_WHITE | TB_BOLD, .bg = TB_BLUE, .alignment = CENTER }, { .format = "H=HELP, q=QUIT", .callback = NULL, diff --git a/draw.c b/draw.c @@ -10,14 +10,17 @@ #include "config.h" #include "version.h" -static const char* help_title = "poe - .po File Editor"; +static const char* help_title = "Main view help"; static const char* help[] = { + "", "Esc - Close dialog, clear error", "Enter - Edit entry", "H or F1 - Show this screen", "/ or F7 - Incremental search (end = Enter)", "n or F3 - Find next", "N or F4 - Find previous", + "F3 - Go to next untranslated entry", + "F4 - Go to next fuzzy entry", "g or Home - Go to first entry", "C-B or PgUp - Go to previous page", "k or \u2191 - Go to previous entry", @@ -41,8 +44,9 @@ const char* errors[] = { [ERR_CANT_MOVE] = "Cannot move temporary file" }; const char* flag_strings[] = { - [FL_C_FORMAT] = "c-format", - [FL_FUZZY] = "fuzzy" + [FL_C_FORMAT] = "c-format", + [FL_NO_C_FORMAT] = "no-c-format", + [FL_FUZZY] = "fuzzy" }; const char* first_entry_msgid = "*** SPECIAL: file info ***"; const char* program_name = "poe"; @@ -100,6 +104,8 @@ init_drawstate(struct DrawState* state, char* error, char* filename, state->show_edit = 0; state->edit_info_focused = 0; state->msgid_number = state->msgid_count > 0 ? 1 : 0; + state->fuzzy_count = 0; + state->untranslated_count = 0; state->msgstr_index = 0; state->first_shown_msgid = state->msgid_number; } @@ -316,8 +322,8 @@ draw_help(const struct DrawState* state) return NULL; while (row < LEN(help)) { - if (!draw_string(startx+1, starty+3+row, DLG_FG, DLG_BG, - help[row], w-2, w-2, 2, 2, LEFT)) + if (!draw_string(startx+1, starty+1+row, DLG_FG, DLG_BG, + help[row], w-2, w-2, 2, 2, LEFT)) return NULL; row++; } @@ -491,14 +497,18 @@ draw_list_visible(const struct DrawState* state) if (!state) return NULL; - const struct PoEntry* current = state->entries + - state->first_shown_msgid-1; + if (state->msgid_count == 0) + return state; + + const struct PoEntry* current = NULL; int max_screen_entries = state->maxy-1; int cy = 0; size_t i = state->first_shown_msgid; while (i <= state->msgid_count && cy < max_screen_entries) { + current = state->entries + i-1; + int sel = i == state->msgid_number; char flags_buf[MAXFLAGSBUF]; @@ -546,7 +556,6 @@ draw_list_visible(const struct DrawState* state) state->msgstr_index*/), state->maxx/2, state->maxx/2, 2, 2, LEFT); - current++; i++; cy++; } diff --git a/draw.h b/draw.h @@ -13,7 +13,7 @@ #define EDIT_WIDTH 70 #define EDIT_HEIGHT 20 #define HELP_WIDTH 50 -#define HELP_HEIGHT 22 +#define HELP_HEIGHT 21 #define TAB_SIZE 8 typedef enum { @@ -61,6 +61,8 @@ struct DrawState { size_t msgid_number; size_t msgid_count; size_t msgid_size; + size_t fuzzy_count; + size_t untranslated_count; int msgstr_index; /* with msgstr[] */ size_t first_shown_msgid; }; diff --git a/po.c b/po.c @@ -14,10 +14,11 @@ extern const char* flag_strings[]; char* format_flags(char* buffer, const size_t max, const struct PoEntry* entry) { - snprintf(buffer, max, "%c%c%c", - entry->flags & FL_C_FORMAT ? 'C' : ' ', - entry->flags & FL_FUZZY ? 'F' : ' ', - entry->msgstr_count > 1 ? 'P' : ' '); + snprintf(buffer, max, "%c%c%c%c", + entry->flags & FL_C_FORMAT ? 'C' : ' ', + entry->flags & FL_FUZZY ? 'F' : ' ', + entry->msgstr_count > 1 ? 'P' : ' ', + entry->flags & FL_NO_C_FORMAT ? 'N' : ' '); return buffer; } @@ -122,13 +123,13 @@ set_plural_forms(struct PoEntry* entry, const char* plural_forms, int append) struct PoEntry* set_msgstr(struct PoEntry* entry, const char* msgstr, int msgstr_index, - int append) + int append, struct DrawState* state) { uint32_t umsgstr[MAXMSG]; if (!entry) return NULL; u8_string_to_unicode(umsgstr, msgstr, MAXMSG); - return u32_set_msgstr(entry, umsgstr, msgstr_index, append); + return u32_set_msgstr(entry, umsgstr, msgstr_index, append, state); } struct PoEntry* @@ -189,7 +190,7 @@ u32_set_plural_forms(struct PoEntry* entry, const uint32_t* plural_forms, struct PoEntry* u32_set_msgstr(struct PoEntry* entry, const uint32_t* msgstr, int msgstr_index, - int append) + int append, struct DrawState* state) { if (!entry) return NULL; @@ -198,7 +199,13 @@ u32_set_msgstr(struct PoEntry* entry, const uint32_t* msgstr, int msgstr_index, uint32_t** newmsgstr = NULL; entry->msgstr_count = msgstr_index + 1; if (!entry->msgstr) + { newmsgstr = calloc(entry->msgstr_count, sizeof(uint32_t*)); + /* 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*)); @@ -304,25 +311,24 @@ add_ref_comment(struct PoEntry* entry, const char* comment) } int -load_file(struct PoEntry** entries, size_t* entries_size, - size_t* entries_count, const char* filename, long* lineno, - long* col, char* error) +load_file(struct DrawState* dstate, long* lineno, long* col) { struct PoEntry* newchunk = calloc(ALLOC_DELTA, sizeof(struct PoEntry)); struct PoEntry* entry = NULL; - ParseState state = PS_NONE; + ParseState pstate = PS_NONE; - *entries_size = 0; + dstate->msgid_size = 0; if (!newchunk) return LOAD_ERR_CANT_ALLOC; - *entries_count = 0; - *entries_size = ALLOC_DELTA; - *entries = newchunk; - entry = *entries; + dstate->msgid_count = 0; + dstate->msgid_size = ALLOC_DELTA; + dstate->entries = newchunk; + entry = dstate->entries; + dstate->untranslated_count++; FILE* input = NULL; - if (!(input = fopen(filename, "rt"))) + if (!(input = fopen(dstate->filename, "rt"))) return LOAD_ERR_CANT_OPEN_FILE; char input_line[MAXBUFLINE]; /* lines to tolerate the lack of msgid */ @@ -338,12 +344,11 @@ load_file(struct PoEntry** entries, size_t* entries_size, *eol = 0; int result; - result = parse_po_line(input_line, entries, entries_size, - entries_count, &entry, &state, + result = parse_po_line(input_line, dstate, &entry, &pstate, col, &msgstr_index); if (result == PARSE_ERR_NONE) { - if (state & PS_MSGID) + if (pstate & PS_MSGID) msgid_counter = -1; else if (msgid_counter != -1) msgid_counter--; @@ -352,19 +357,26 @@ load_file(struct PoEntry** entries, size_t* entries_size, return LOAD_ERR_NOT_PO_FILE; } else if (result == PARSE_ERR_SYNTAX) - sprintf(error, "%s:%ld:%ld: Syntax error", - filename, *lineno, *col); + sprintf(dstate->error, "%s:%ld:%ld: Syntax error", + dstate->filename, *lineno, *col); else { - if (*entries) - free(*entries); + if (dstate->entries) + { + free(dstate->entries); + dstate->entries = NULL; + } return LOAD_PARSE_ERR(result); } (*lineno)++; } fclose(input); - if (*entries_count > 1) - (*entries_count)++; + + /* when the .po file is not a standard text file (no LF at end) */ + if (dstate->msgid_count > 1 && entry->msgid) + dstate->msgid_count++; + else + dstate->untranslated_count--; /* file has < PO_DETECTION_LINES */ if (msgid_counter > 0) @@ -439,12 +451,21 @@ save_file(const struct PoEntry* entries, const size_t msgid_count, 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, ", "); } @@ -561,13 +582,6 @@ save_file(const struct PoEntry* entries, const size_t msgid_count, struct tm* now_tm = localtime(&t); strftime(now, MAXDATEBUF, "%F %H:%M%z", now_tm); - /* - *if (now_tm) - *{ - * free(now_tm); - * now_tm = NULL; - *} - */ struct BufferLine* msgstr_split = split(*current->msgstr); @@ -610,7 +624,7 @@ save_file(const struct PoEntry* entries, const size_t msgid_count, u8_msgstr, last ? "" : "\\n"); } - if (!seen_generator) + if (first_entry && !seen_generator) fprintf(output, "\"X-Generator: poe %s\"\n", VERSION); @@ -701,9 +715,9 @@ save_file(const struct PoEntry* entries, const size_t msgid_count, } int -parse_po_line(const char* line, struct PoEntry** entries, size_t* entries_size, - size_t* entries_count, struct PoEntry** current_entry, - ParseState* state, long* col, int* msgstr_index) +parse_po_line(const char* line, struct DrawState* dstate, + struct PoEntry** current_entry, ParseState* pstate, long* col, + int* msgstr_index) { size_t i = 0; *col = 1; @@ -718,33 +732,33 @@ parse_po_line(const char* line, struct PoEntry** entries, size_t* entries_size, if (starts_with(line, "msgid ")) { size_t len = strlen("msgid "); - *state |= PS_MSGID; + *pstate |= PS_MSGID; pline += len; *col += len; } else if (starts_with(line, "msgid_plural ")) { size_t len = strlen("msgid_plural "); - *state &= ~PS_MSGID; - *state |= PS_MSGID_PLURAL; + *pstate &= ~PS_MSGID; + *pstate |= PS_MSGID_PLURAL; pline += len; *col += len; } else if (starts_with(line, "msgstr ")) { size_t len = strlen("msgstr "); - *state &= ~PS_MSGID; - *state &= ~PS_MSGID_PLURAL; - *state |= PS_MSGSTR; + *pstate &= ~PS_MSGID; + *pstate &= ~PS_MSGID_PLURAL; + *pstate |= PS_MSGSTR; pline += len; *col += len; } else if (starts_with(line, "msgstr[")) { size_t len = strlen("msgstr["); - *state &= ~PS_MSGID; - *state &= ~PS_MSGID_PLURAL; - *state |= PS_MSGSTR_ARRAY; + *pstate &= ~PS_MSGID; + *pstate &= ~PS_MSGID_PLURAL; + *pstate |= PS_MSGSTR_ARRAY; (*msgstr_index)++; pline += len; *col += len; @@ -765,10 +779,10 @@ parse_po_line(const char* line, struct PoEntry** entries, size_t* entries_size, } else if (*line == 0) { - *state &= ~PS_MSGID; - *state &= ~PS_MSGID_PLURAL; - *state &= ~PS_MSGSTR; - *state &= ~PS_MSGSTR_ARRAY; + *pstate &= ~PS_MSGID; + *pstate &= ~PS_MSGID_PLURAL; + *pstate &= ~PS_MSGSTR; + *pstate &= ~PS_MSGSTR_ARRAY; } else if (*line != ' ' && *line != '\t' && *line != '"' && *line != '#') return PARSE_ERR_SYNTAX; @@ -780,7 +794,7 @@ parse_po_line(const char* line, struct PoEntry** entries, size_t* entries_size, case '#': if (*col == 1) { - *state |= PS_COMMENT; + *pstate |= PS_COMMENT_TRAN; pline++; (*col)++; } @@ -791,10 +805,10 @@ parse_po_line(const char* line, struct PoEntry** entries, size_t* entries_size, } break; case '.': - if (*col == 2 && *state & PS_COMMENT) + if (*col == 2 && *pstate & PS_COMMENT_TRAN) { - *state &= ~PS_COMMENT; - *state |= PS_COMMENT_TRANS; + *pstate &= ~PS_COMMENT_TRAN; + *pstate |= PS_COMMENT_EXTR; pline++; (*col)++; if (*pline == ' ') @@ -812,10 +826,10 @@ parse_po_line(const char* line, struct PoEntry** entries, size_t* entries_size, } break; case ',': - if (*col == 2 && *state & PS_COMMENT) + if (*col == 2 && *pstate & PS_COMMENT_TRAN) { - *state &= ~PS_COMMENT; - *state |= PS_COMMENT_FLAG; + *pstate &= ~PS_COMMENT_TRAN; + *pstate |= PS_COMMENT_FLAG; pline++; (*col)++; if (*pline == ' ') @@ -833,10 +847,10 @@ parse_po_line(const char* line, struct PoEntry** entries, size_t* entries_size, } break; case ':': - if (*col == 2 && *state & PS_COMMENT) + if (*col == 2 && *pstate & PS_COMMENT_TRAN) { - *state &= ~PS_COMMENT; - *state |= PS_COMMENT_REF; + *pstate &= ~PS_COMMENT_TRAN; + *pstate |= PS_COMMENT_REFN; pline++; (*col)++; if (*pline == ' ') @@ -856,8 +870,8 @@ parse_po_line(const char* line, struct PoEntry** entries, size_t* entries_size, case '"': /* skip beginning and end quote */ if ((ptoken == token || *(pline+1) == 0) - && !(*state & (PS_COMMENT | - PS_COMMENT_TRANS))) + && !(*pstate & (PS_COMMENT_TRAN + | PS_COMMENT_EXTR))) { pline++; (*col)++; @@ -873,20 +887,21 @@ parse_po_line(const char* line, struct PoEntry** entries, size_t* entries_size, (*col)++; } } - if (*state == PS_NONE) + if (*pstate == PS_NONE) { struct PoEntry* newchunk = NULL; (*current_entry)++; - (*entries_count)++; - if (*current_entry == *entries + *entries_size) + dstate->msgid_count++; + dstate->untranslated_count++; + if (*current_entry == dstate->entries + dstate->msgid_size) { - *entries_size += ALLOC_DELTA; - newchunk = realloc(*entries, *entries_size * + dstate->msgid_size += ALLOC_DELTA; + newchunk = realloc(dstate->entries, dstate->msgid_size * sizeof(struct PoEntry)); if (!newchunk) return PARSE_ERR_ALLOC; - *entries = newchunk; - *current_entry = *entries + *entries_size - + dstate->entries = newchunk; + *current_entry = dstate->entries + dstate->msgid_size - ALLOC_DELTA; } init_po_entry(*current_entry); @@ -896,33 +911,35 @@ parse_po_line(const char* line, struct PoEntry** entries, size_t* entries_size, { *ptoken = 0; - if (*state & PS_MSGID) + if (*pstate & PS_MSGID) set_msgid(*current_entry, token, 1); - else if (*state & PS_MSGID_PLURAL) + else if (*pstate & PS_MSGID_PLURAL) set_msgid_plural(*current_entry, token, 1); - else if (*state & PS_MSGSTR) + else if (*pstate & PS_MSGSTR) { - if (*current_entry == *entries && starts_with(token, + if (*current_entry == dstate->entries && + starts_with(token, "Plural-Forms: ")) set_plural_forms(*current_entry, token + strlen("Plural-Forms: "), 0); - set_msgstr(*current_entry, token, 0, 1); + set_msgstr(*current_entry, token, 0, 1, dstate); } - else if (*state & PS_MSGSTR_ARRAY) - set_msgstr(*current_entry, token, *msgstr_index, 1); - else if (*state & PS_COMMENT) + else if (*pstate & PS_MSGSTR_ARRAY) + set_msgstr(*current_entry, token, *msgstr_index, 1, + dstate); + else if (*pstate & PS_COMMENT_TRAN) { uint32_t u32_token[MAXCOMMENTLINE]; u8_string_to_unicode(u32_token, token, MAXCOMMENTLINE); u32_add_comment(*current_entry, u32_token); } - else if (*state & PS_COMMENT_TRANS) + else if (*pstate & PS_COMMENT_EXTR) { uint32_t u32_token[MAXCOMMENTLINE]; u8_string_to_unicode(u32_token, token, MAXCOMMENTLINE); u32_add_trans_comment(*current_entry, u32_token); } - else if (*state & PS_COMMENT_FLAG) + else if (*pstate & PS_COMMENT_FLAG) { ptoken = token; while (*ptoken) @@ -935,6 +952,14 @@ parse_po_line(const char* line, struct PoEntry** entries, size_t* entries_size, 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])) { (*current_entry)->flags |= FL_FUZZY; @@ -944,22 +969,31 @@ parse_po_line(const char* line, struct PoEntry** entries, size_t* entries_size, else if (starts_with(ptoken, ", ")) ptoken += 2; else - return PARSE_ERR_SYNTAX; + { + /* Let this slip, ignore everything + * instead; see po.h for rationale */ + /*return PARSE_ERR_SYNTAX;*/ + + while (*ptoken && *ptoken != ' ' + && *ptoken != ',' + && *ptoken != '\t') + ptoken++; + } } } - else if (*state & PS_COMMENT_REF) + else if (*pstate & PS_COMMENT_REFN) add_ref_comment(*current_entry, token); *token = 0; } - else if (*state & PS_COMMENT) + else if (*pstate & PS_COMMENT_TRAN) u32_add_comment(*current_entry, (uint32_t*)L""); - else if (*state & PS_COMMENT_TRANS) + else if (*pstate & PS_COMMENT_EXTR) u32_add_trans_comment(*current_entry, (uint32_t*)L""); /* one-line states */ - *state &= ~(PS_COMMENT | PS_COMMENT_TRANS | PS_COMMENT_FLAG | - PS_COMMENT_REF); + *pstate &= ~(PS_COMMENT_TRAN | PS_COMMENT_EXTR | PS_COMMENT_FLAG | + PS_COMMENT_REFN); return PARSE_ERR_NONE; } diff --git a/po.h b/po.h @@ -6,10 +6,12 @@ #define MSGSTR_MAX 4096 #define MAXCOMMENTLINE 512 #define ALLOC_DELTA 10 -#define PO_DETECTION_LINES 15 +#define PO_DETECTION_LINES 24 #define LOAD_PARSE_ERR(e) (LOAD_ERR_PARSE_PREFIX + (e)) +struct DrawState; + enum { PARSE_ERR_NONE = 0, PARSE_ERR_INVAL = 1, @@ -37,20 +39,31 @@ enum { typedef enum { PS_NONE = 0, - PS_COMMENT = 1, - PS_COMMENT_TRANS = 1 << 1, - PS_COMMENT_FLAG = 1 << 2, - PS_COMMENT_REF = 1 << 3, + PS_COMMENT_TRAN = 1, /* "translator-comments" */ + PS_COMMENT_EXTR = 1 << 1, /* "extracted-comments" */ + PS_COMMENT_FLAG = 1 << 2, /* "flag" */ + PS_COMMENT_REFN = 1 << 3, /* "reference" */ PS_MSGID = 1 << 4, PS_MSGID_PLURAL = 1 << 5, PS_MSGSTR = 1 << 6, PS_MSGSTR_ARRAY = 1 << 7 } ParseState; +/* Complete list at: + * + * https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html + * + * - JS, awk and sh using gettext, seriously? + * "Let's just shove every programming and scripting language there is into + * flags!" /s + * Where are the flags for Pascal and Ada, by the way, hmm? [SR] + * + * - Including just the sane values, will strip the rest. */ typedef enum { - FL_NONE = 0, - FL_C_FORMAT = 1, - FL_FUZZY = 1 << 1, + FL_NONE = 0, + FL_FUZZY = 1, + FL_C_FORMAT = 1 << 1, + FL_NO_C_FORMAT = 1 << 2 } Flags; struct PoEntry { @@ -79,7 +92,7 @@ struct PoEntry* set_msgid_plural(struct PoEntry* entry, struct PoEntry* set_plural_forms(struct PoEntry* entry, const char* plural_forms, int append); struct PoEntry* set_msgstr(struct PoEntry* entry, const char* msgstr, - int msgstr_index, int append); + int msgstr_index, int append, struct DrawState* state); struct PoEntry* u32_set_msgid(struct PoEntry* entry, const uint32_t* msgid, int append); struct PoEntry* u32_set_msgid_plural(struct PoEntry* entry, @@ -87,18 +100,15 @@ struct PoEntry* u32_set_msgid_plural(struct PoEntry* entry, struct PoEntry* u32_set_plural_forms(struct PoEntry* entry, const uint32_t* plural_forms, int append); struct PoEntry* u32_set_msgstr(struct PoEntry* entry, const uint32_t* msgstr, - int msgstr_index, int append); + int msgstr_index, int append, struct DrawState* state); struct PoEntry* u32_add_comment(struct PoEntry* entry, const uint32_t* comment); struct PoEntry* u32_add_trans_comment(struct PoEntry* entry, const uint32_t* trans_comment); struct PoEntry* add_ref_comment(struct PoEntry* entry, const char* comment); -int load_file(struct PoEntry** entries, size_t* entries_size, - size_t* entries_count, const char* filename, long* lineno, - long* col, char* error); +int load_file(struct DrawState* state, long* lineno, long* col); int save_file(const struct PoEntry* entries, const size_t msgid_count, const char* filename); -int parse_po_line(const char* line, struct PoEntry** entries, - size_t* entries_size, size_t* entries_count, - struct PoEntry** current_entry, ParseState* state, long* col, +int parse_po_line(const char* line, struct DrawState* dstate, + struct PoEntry** current_entry, ParseState* pstate, long* col, int* msgstr_index); diff --git a/poe.1.in b/poe.1.in @@ -71,6 +71,8 @@ _ \fC/\fP or \fCF7\fP@Incremental search (end input with Enter) \fCn\fP or \fCF3\fP@Find next \fCN\fP or \fCF4\fP@Find previous +\fCF3\fP@Go to next untranslated entry +\fCF4\fP@Go to next fuzzy entry \fCg\fP or \fCHome\fP@Go to first entry \fCC-B\fP or \fCPgUp\fP@Go to previous page \fCk\fP or \fC\[ua]\fP@Go to previous entry @@ -144,13 +146,17 @@ for more details. . .IP \(bu Only one file can be edited at a time, and no additional files can be loaded at -runtime. This is a design decision. +runtime. This is a design decision. . .IP \(bu The directory of the edited file must be writeable 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). . +.IP \(bu +Only the flags \fIfuzzy\fP, \fIc-format\fP and \fIno-c-format\fP are supported. +Other flags will be stripped on save. This is a design decision. +. .SH BUGS . .LP diff --git a/poe.c b/poe.c @@ -33,6 +33,8 @@ struct BufferLine* load_info(struct DrawState* state); struct BufferLine* load_msgstr(struct DrawState* state); void save_msgstr(struct DrawState* state); void cancel_msgstr(struct DrawState* state); +void next_untranslated(struct DrawState* state); +void next_fuzzy(struct DrawState* state); void move_up(struct DrawState* state); void move_down(struct DrawState* state); void move_start(struct DrawState* state); @@ -100,20 +102,14 @@ void format_msgs(char* result, size_t result_size, const char* format, const struct DrawState* state) { - int fuzzy_count = 0; - if (!result) return; - if (state && state->msgid_count) - for (size_t i = 0; i < state->msgid_count; i++) - if (state->entries[i].flags & FL_FUZZY) - fuzzy_count++; - snprintf(result, result_size, format, state ? state->msgid_number : 0, state ? state->msgid_count : 0, - fuzzy_count); + state ? state->untranslated_count : 0, + state ? state->fuzzy_count : 0); } void @@ -418,7 +414,7 @@ save_msgstr(struct DrawState* state) state->info_buffer = NULL; u32_encode_tabs(to_save, MAXBUFLINE); u32_set_msgstr(&state->entries[state->msgid_number-1], to_save, - state->msgstr_index, 0); + state->msgstr_index, 0, state); //state->dirty = 1; } @@ -438,6 +434,105 @@ cancel_msgstr(struct DrawState* state) state->dirty = state->dirty_before_edit; } +void +next_untranslated(struct DrawState* state) +{ + if (state->msgid_count == 0) + return; + + struct PoEntry* current = NULL; + for (size_t e = state->msgid_number; e < state->msgid_count; e++) + { + current = state->entries + e; + if (!current->msgstr || !*current->msgstr || + u32_strlen(*current->msgstr) == 0) + { + if (state->show_edit) + { + save_msgstr(state); + state->dirty_before_edit = state->dirty; + } + + state->msgid_number = e + 1; + if (state->msgid_number < (state->maxy-1)/2) + state->first_shown_msgid = + state->msgid_count > 0 ? 1 : 0; + else if (state->msgid_count - state->msgid_number < + (state->maxy-1)/2) + state->first_shown_msgid = + state->msgid_count - (state->maxy-2); + else + state->first_shown_msgid = state->msgid_number + - (state->maxy-1)/2; + + if (state->show_edit) + { + state->msgstr_index = 0; + load_msgstr(state); + load_info(state); + state->input_row = 0; + state->input_first_shown_row = 0; + state->input_first_shown_column = 0; + state->info_first_shown_row = 0; + state->info_first_shown_column = 0; + move_start(state); + state->error[0] = 0; + } + + return; + } + } +} + +void +next_fuzzy(struct DrawState* state) +{ + if (state->msgid_count == 0) + return; + + struct PoEntry* current = NULL; + for (size_t e = state->msgid_number; e < state->msgid_count; e++) + { + current = state->entries + e; + if (current->flags & FL_FUZZY) + { + if (state->show_edit) + { + save_msgstr(state); + state->dirty_before_edit = state->dirty; + } + + state->msgid_number = e + 1; + if (state->msgid_number < (state->maxy-1)/2) + state->first_shown_msgid = + state->msgid_count > 0 ? 1 : 0; + else if (state->msgid_count - state->msgid_number < + (state->maxy-1)/2) + state->first_shown_msgid = + state->msgid_count - (state->maxy-2); + else + state->first_shown_msgid = state->msgid_number + - (state->maxy-1)/2; + + if (state->show_edit) + { + state->msgstr_index = 0; + load_msgstr(state); + load_info(state); + state->input_row = 0; + state->input_first_shown_row = 0; + state->input_first_shown_column = 0; + state->info_first_shown_row = 0; + state->info_first_shown_column = 0; + move_start(state); + state->error[0] = 0; + } + + return; + } + } +} + void move_up(struct DrawState* state) { @@ -699,6 +794,10 @@ toggle_fuzzy(struct DrawState* state) if (state->msgid_count > 0) { state->entries[state->msgid_number-1].flags ^= FL_FUZZY; + if (state->entries[state->msgid_number-1].flags & FL_FUZZY) + state->fuzzy_count++; + else + state->fuzzy_count--; state->dirty = 1; } } @@ -1008,6 +1107,10 @@ handle_editbox_key_event(struct tb_event* ev, struct DrawState* state) cancel_msgstr(state); tb_set_cursor(TB_HIDE_CURSOR, TB_HIDE_CURSOR); } + else if (ev->key == TB_KEY_F3) + next_untranslated(state); + else if (ev->key == TB_KEY_F4) + next_fuzzy(state); else if (ev->key == TB_KEY_F6) state->edit_info_focused = !state->edit_info_focused; else if (ev->key == TB_KEY_HOME @@ -1021,10 +1124,6 @@ handle_editbox_key_event(struct tb_event* ev, struct DrawState* state) if (state->info_first_shown_row + (EDIT_HEIGHT-3)/2 < state->info_rows_count-1) state->info_first_shown_row += (EDIT_HEIGHT-3)/2; - /* - *else - * move_end(state); - */ } else { @@ -1194,6 +1293,10 @@ handle_key_event(struct tb_event* ev, struct DrawState* state) state->show_help = 1; state->error[0] = 0; } + else if (ev->key == TB_KEY_F3) + next_untranslated(state); + else if (ev->key == TB_KEY_F4) + next_fuzzy(state); else if (ev->key == TB_KEY_HOME || ev->ch == 'g') move_list_start(state); else @@ -1239,11 +1342,11 @@ main(int argc, char** argv) return print_error(ENOENT, "No such file `%s'", filename); fclose(input); + init_drawstate(&state, error, filename, prompt); + long lineno = 1; long col = 1; - int result = load_file(&state.entries, &state.msgid_size, - &state.msgid_count, filename, &lineno, &col, - error); + int result = load_file(&state, &lineno, &col); switch (result) { case LOAD_ERR_CANT_ALLOC: @@ -1268,12 +1371,12 @@ main(int argc, char** argv) default: return print_error(1, "Unknown error (%d)", result); } + state.msgid_number = state.msgid_count > 0 ? 1 : 0; + state.first_shown_msgid = state.msgid_number; tb_init(); error[0] = 0; - init_drawstate(&state, error, filename, prompt); - while (state.running) { tb_clear(); diff --git a/util.c b/util.c @@ -21,7 +21,7 @@ u8_string_to_unicode(uint32_t* us, const char* s, const size_t max) while (s && *s && u8_len != TB_EOF) { u8_len = tb_utf8_char_to_unicode(&uch, s); - if (added + 2 > max) + if (added + 1 > max) break; *pus++ = uch; if (u8_len != TB_EOF) diff --git a/util.h b/util.h @@ -17,7 +17,7 @@ enum { #define MAXBUFLINE 4096 #define MAXDATEBUF 80 -#define MAXFLAGSBUF 3+1 +#define MAXFLAGSBUF (4+1) #define MAXMSG 1024 #define MAXPATH 1024