poe

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

чување c40777e5210834012214e707bdd78bd45de743a9
родитељ 5b21322848c163934256ec0962cbb5dbf1d81f7b
Аутор: Страхиња Радић <contact@strahinja.org>
Датум:   Thu, 10 Feb 2022 13:52:30 +0100

Finish WIP: add PoEntry.warning and draw logic

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

Diffstat:
MTODO | 6+++---
Mconfig.def.h | 133+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mconfig.h | 133+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mdraw.c | 27+++++++++++++++------------
Mpo.c | 26++++++++++++++++++++++++++
Mpo.h | 2++
Mpoe.1.in | 17+++++++++++++++++
Mpoe.c | 14++++++++------
Mutil.c | 35++++++++++++++++++++++++++++-------
Mutil.h | 3++-
измењених датотека: 10, додавања: 247(+), брисања: 149(-)

diff --git a/TODO b/TODO @@ -1,11 +1,11 @@ TODO ==== - [~] Investigate newlines loading and saving + [x] Investigate newlines loading and saving [x] Fix line counting when \n at end of string [x] Add heuristics to match the ending newline or dot - [ ] Soft-warn when the number of newlines doesn't match - (different color?) + [x] Soft-warn when the number of newlines doesn't match + (Different color? Yes.) < > Wrap long lines? Not if it would complicate the code too much. diff --git a/config.def.h b/config.def.h @@ -1,70 +1,83 @@ /* See the file LICENSE for copyright and license details. */ -#define SAVE_WRAP_WIDTH 70 +#define SAVE_WRAP_WIDTH 70 #define WRAP_FIRST_MSGSTR 0 +#define WARN_COUNT_DOTS 0 -#define DLG_FG TB_BLACK -#define DLG_BG TB_WHITE -#define EDIT_MSGID_FG TB_BLACK | TB_BOLD -#define EDIT_MSGID_BG TB_WHITE -#define EDIT_MSGID_FOCUS_FG TB_BLACK -#define EDIT_MSGID_FOCUS_BG TB_WHITE -#define EDIT_MSGID_HEADING_FG TB_WHITE | TB_BOLD -#define EDIT_MSGID_HEADING_BG TB_WHITE -#define EDIT_MSGSTR_FG TB_BLACK | TB_BOLD -#define EDIT_MSGSTR_BG TB_BLACK -#define EDIT_MSGSTR_FOCUS_FG TB_YELLOW | TB_BOLD -#define EDIT_MSGSTR_FOCUS_BG TB_BLACK -#define ERROR_FG TB_WHITE | TB_BOLD -#define ERROR_BG TB_RED -#define FLAG_FG TB_YELLOW | TB_BOLD -#define FLAG_BG TB_DEFAULT -#define LIST_FLAGS_FG TB_YELLOW | TB_BOLD -#define LIST_FLAGS_BG TB_DEFAULT -#define LIST_FLAGS_SEL_FG TB_WHITE | TB_BOLD -#define LIST_FLAGS_SEL_BG TB_BLUE -#define LIST_MSGID_FG TB_WHITE -#define LIST_MSGID_BG TB_DEFAULT -#define LIST_MSGID_SEL_FG TB_WHITE -#define LIST_MSGID_SEL_BG TB_BLUE -#define LIST_MSGID_SPECIAL_FG TB_YELLOW | TB_BOLD -#define LIST_MSGID_SPECIAL_BG TB_DEFAULT -#define LIST_MSGID_SPECIAL_SEL_FG TB_WHITE | TB_BOLD -#define LIST_MSGID_SPECIAL_SEL_BG TB_BLUE -#define LIST_MSGSTR_FG TB_GREEN -#define LIST_MSGSTR_BG TB_DEFAULT -#define LIST_MSGSTR_SEL_FG TB_WHITE | TB_BOLD -#define LIST_MSGSTR_SEL_BG TB_BLUE -#define LIST_SEL_FG TB_WHITE -#define LIST_SEL_BG TB_BLUE -#define MSGID_FG TB_GREEN -#define MSGID_BG TB_DEFAULT -#define MSGSTR_FG TB_WHITE -#define MSGSTR_BG TB_DEFAULT -#define PROMPT_FG TB_YELLOW | TB_BOLD -#define PROMPT_BG TB_MAGENTA -#define SEARCH_FG TB_BLACK -#define SEARCH_BG TB_WHITE -#define SEARCH_EDIT_FG TB_WHITE | TB_BOLD -#define SEARCH_EDIT_BG TB_BLACK -#define SEARCH_HL_FG TB_WHITE | TB_BOLD -#define SEARCH_HL_BG TB_GREEN -#define SHADOW_FG TB_BLACK -#define SHADOW_BG TB_BLACK | TB_BOLD -#define TITLE_FG TB_BLACK -#define TITLE_BG TB_WHITE +#define DLG_FG TB_BLACK +#define DLG_BG TB_WHITE +#define EDIT_MSGID_FG TB_BLACK | TB_BOLD +#define EDIT_MSGID_BG TB_WHITE +#define EDIT_MSGID_FOCUS_FG TB_BLACK +#define EDIT_MSGID_FOCUS_BG TB_WHITE +#define EDIT_MSGID_HEADING_FG TB_WHITE | TB_BOLD +#define EDIT_MSGID_HEADING_BG TB_WHITE +#define EDIT_MSGSTR_FG TB_BLACK | TB_BOLD +#define EDIT_MSGSTR_BG TB_BLACK +#define EDIT_MSGSTR_FOCUS_FG TB_YELLOW | TB_BOLD +#define EDIT_MSGSTR_FOCUS_BG TB_BLACK +#define ERROR_FG TB_WHITE | TB_BOLD +#define ERROR_BG TB_RED +#define FLAG_FG TB_YELLOW | TB_BOLD +#define FLAG_BG TB_DEFAULT +#define LIST_FLAGS_FG TB_YELLOW | TB_BOLD +#define LIST_FLAGS_BG TB_DEFAULT +#define LIST_FLAGS_SEL_FG TB_WHITE | TB_BOLD +#define LIST_FLAGS_SEL_BG TB_BLUE +#define LIST_MSGID_FG TB_WHITE +#define LIST_MSGID_BG TB_DEFAULT +#define LIST_MSGID_SEL_FG TB_WHITE +#define LIST_MSGID_SEL_BG TB_BLUE +#define LIST_MSGID_SPECIAL_FG TB_YELLOW | TB_BOLD +#define LIST_MSGID_SPECIAL_BG TB_DEFAULT +#define LIST_MSGID_SPECIAL_SEL_FG TB_WHITE | TB_BOLD +#define LIST_MSGID_SPECIAL_SEL_BG TB_BLUE +#define LIST_MSGSTR_FG TB_GREEN +#define LIST_MSGSTR_BG TB_DEFAULT +#define LIST_MSGSTR_SEL_FG TB_WHITE | TB_BOLD +#define LIST_MSGSTR_SEL_BG TB_BLUE +#define LIST_MSGSTR_WARN_FG TB_RED | TB_BOLD +#define LIST_MSGSTR_WARN_BG TB_DEFAULT +#define LIST_MSGSTR_SEL_WARN_FG TB_RED | TB_BOLD +#define LIST_MSGSTR_SEL_WARN_BG TB_BLUE +#define LIST_SEL_FG TB_WHITE +#define LIST_SEL_BG TB_BLUE +#define MSGID_FG TB_GREEN +#define MSGID_BG TB_DEFAULT +#define MSGSTR_FG TB_WHITE +#define MSGSTR_BG TB_DEFAULT +#define PROMPT_FG TB_YELLOW | TB_BOLD +#define PROMPT_BG TB_MAGENTA +#define SEARCH_FG TB_BLACK +#define SEARCH_BG TB_WHITE +#define SEARCH_EDIT_FG TB_WHITE | TB_BOLD +#define SEARCH_EDIT_BG TB_BLACK +#define SEARCH_HL_FG TB_WHITE | TB_BOLD +#define SEARCH_HL_BG TB_GREEN +#define SHADOW_FG TB_BLACK +#define SHADOW_BG TB_BLACK | TB_BOLD +#define TITLE_FG TB_BLACK +#define TITLE_BG TB_WHITE void format_filename(char* result, size_t result_size, const char* format, - const struct DrawState* state); + const struct DrawState* state); void format_msgs(char* result, size_t result_size, const char* format, - const struct DrawState* state); + const struct DrawState* state); static const struct StatusSegment status_segments[] = { - { .format = "%s%s", .callback = format_filename, - .fg = TB_BLACK, .bg = TB_WHITE, .alignment = LEFT }, - { .format = "%ld/%ld (%ldu/%ldf/%ldo)", - .callback = format_msgs, - .fg = TB_WHITE | TB_BOLD, .bg = TB_BLUE, .alignment = CENTER }, - { .format = "H=HELP, q=QUIT", .callback = NULL, - .fg = TB_BLACK, .bg = TB_WHITE, .alignment = RIGHT }, + {.format = "%s%s", + .callback = format_filename, + .fg = TB_BLACK, + .bg = TB_WHITE, + .alignment = LEFT}, + {.format = "%ld/%ld (%ldu/%ldf/%ldo)", + .callback = format_msgs, + .fg = TB_WHITE | TB_BOLD, + .bg = TB_BLUE, + .alignment = CENTER}, + {.format = "H=HELP, q=QUIT", + .callback = NULL, + .fg = TB_BLACK, + .bg = TB_WHITE, + .alignment = RIGHT}, }; diff --git a/config.h b/config.h @@ -1,70 +1,83 @@ /* See the file LICENSE for copyright and license details. */ -#define SAVE_WRAP_WIDTH 70 +#define SAVE_WRAP_WIDTH 70 #define WRAP_FIRST_MSGSTR 0 +#define WARN_COUNT_DOTS 0 -#define DLG_FG TB_BLACK -#define DLG_BG TB_WHITE -#define EDIT_MSGID_FG TB_BLACK | TB_BOLD -#define EDIT_MSGID_BG TB_WHITE -#define EDIT_MSGID_FOCUS_FG TB_BLACK -#define EDIT_MSGID_FOCUS_BG TB_WHITE -#define EDIT_MSGID_HEADING_FG TB_WHITE | TB_BOLD -#define EDIT_MSGID_HEADING_BG TB_WHITE -#define EDIT_MSGSTR_FG TB_BLACK | TB_BOLD -#define EDIT_MSGSTR_BG TB_BLACK -#define EDIT_MSGSTR_FOCUS_FG TB_YELLOW | TB_BOLD -#define EDIT_MSGSTR_FOCUS_BG TB_BLACK -#define ERROR_FG TB_WHITE | TB_BOLD -#define ERROR_BG TB_RED -#define FLAG_FG TB_YELLOW | TB_BOLD -#define FLAG_BG TB_DEFAULT -#define LIST_FLAGS_FG TB_YELLOW | TB_BOLD -#define LIST_FLAGS_BG TB_DEFAULT -#define LIST_FLAGS_SEL_FG TB_WHITE | TB_BOLD -#define LIST_FLAGS_SEL_BG TB_BLUE -#define LIST_MSGID_FG TB_WHITE -#define LIST_MSGID_BG TB_DEFAULT -#define LIST_MSGID_SEL_FG TB_WHITE -#define LIST_MSGID_SEL_BG TB_BLUE -#define LIST_MSGID_SPECIAL_FG TB_YELLOW | TB_BOLD -#define LIST_MSGID_SPECIAL_BG TB_DEFAULT -#define LIST_MSGID_SPECIAL_SEL_FG TB_WHITE | TB_BOLD -#define LIST_MSGID_SPECIAL_SEL_BG TB_BLUE -#define LIST_MSGSTR_FG TB_GREEN -#define LIST_MSGSTR_BG TB_DEFAULT -#define LIST_MSGSTR_SEL_FG TB_WHITE | TB_BOLD -#define LIST_MSGSTR_SEL_BG TB_BLUE -#define LIST_SEL_FG TB_WHITE -#define LIST_SEL_BG TB_BLUE -#define MSGID_FG TB_GREEN -#define MSGID_BG TB_DEFAULT -#define MSGSTR_FG TB_WHITE -#define MSGSTR_BG TB_DEFAULT -#define PROMPT_FG TB_YELLOW | TB_BOLD -#define PROMPT_BG TB_MAGENTA -#define SEARCH_FG TB_BLACK -#define SEARCH_BG TB_WHITE -#define SEARCH_EDIT_FG TB_WHITE | TB_BOLD -#define SEARCH_EDIT_BG TB_BLACK -#define SEARCH_HL_FG TB_WHITE | TB_BOLD -#define SEARCH_HL_BG TB_GREEN -#define SHADOW_FG TB_BLACK -#define SHADOW_BG TB_BLACK | TB_BOLD -#define TITLE_FG TB_BLACK -#define TITLE_BG TB_WHITE +#define DLG_FG TB_BLACK +#define DLG_BG TB_WHITE +#define EDIT_MSGID_FG TB_BLACK | TB_BOLD +#define EDIT_MSGID_BG TB_WHITE +#define EDIT_MSGID_FOCUS_FG TB_BLACK +#define EDIT_MSGID_FOCUS_BG TB_WHITE +#define EDIT_MSGID_HEADING_FG TB_WHITE | TB_BOLD +#define EDIT_MSGID_HEADING_BG TB_WHITE +#define EDIT_MSGSTR_FG TB_BLACK | TB_BOLD +#define EDIT_MSGSTR_BG TB_BLACK +#define EDIT_MSGSTR_FOCUS_FG TB_YELLOW | TB_BOLD +#define EDIT_MSGSTR_FOCUS_BG TB_BLACK +#define ERROR_FG TB_WHITE | TB_BOLD +#define ERROR_BG TB_RED +#define FLAG_FG TB_YELLOW | TB_BOLD +#define FLAG_BG TB_DEFAULT +#define LIST_FLAGS_FG TB_YELLOW | TB_BOLD +#define LIST_FLAGS_BG TB_DEFAULT +#define LIST_FLAGS_SEL_FG TB_WHITE | TB_BOLD +#define LIST_FLAGS_SEL_BG TB_BLUE +#define LIST_MSGID_FG TB_WHITE +#define LIST_MSGID_BG TB_DEFAULT +#define LIST_MSGID_SEL_FG TB_WHITE +#define LIST_MSGID_SEL_BG TB_BLUE +#define LIST_MSGID_SPECIAL_FG TB_YELLOW | TB_BOLD +#define LIST_MSGID_SPECIAL_BG TB_DEFAULT +#define LIST_MSGID_SPECIAL_SEL_FG TB_WHITE | TB_BOLD +#define LIST_MSGID_SPECIAL_SEL_BG TB_BLUE +#define LIST_MSGSTR_FG TB_GREEN +#define LIST_MSGSTR_BG TB_DEFAULT +#define LIST_MSGSTR_SEL_FG TB_WHITE | TB_BOLD +#define LIST_MSGSTR_SEL_BG TB_BLUE +#define LIST_MSGSTR_WARN_FG TB_RED | TB_BOLD +#define LIST_MSGSTR_WARN_BG TB_DEFAULT +#define LIST_MSGSTR_SEL_WARN_FG TB_RED | TB_BOLD +#define LIST_MSGSTR_SEL_WARN_BG TB_BLUE +#define LIST_SEL_FG TB_WHITE +#define LIST_SEL_BG TB_BLUE +#define MSGID_FG TB_GREEN +#define MSGID_BG TB_DEFAULT +#define MSGSTR_FG TB_WHITE +#define MSGSTR_BG TB_DEFAULT +#define PROMPT_FG TB_YELLOW | TB_BOLD +#define PROMPT_BG TB_MAGENTA +#define SEARCH_FG TB_BLACK +#define SEARCH_BG TB_WHITE +#define SEARCH_EDIT_FG TB_WHITE | TB_BOLD +#define SEARCH_EDIT_BG TB_BLACK +#define SEARCH_HL_FG TB_WHITE | TB_BOLD +#define SEARCH_HL_BG TB_GREEN +#define SHADOW_FG TB_BLACK +#define SHADOW_BG TB_BLACK | TB_BOLD +#define TITLE_FG TB_BLACK +#define TITLE_BG TB_WHITE void format_filename(char* result, size_t result_size, const char* format, - const struct DrawState* state); + const struct DrawState* state); void format_msgs(char* result, size_t result_size, const char* format, - const struct DrawState* state); + const struct DrawState* state); static const struct StatusSegment status_segments[] = { - { .format = "%s%s", .callback = format_filename, - .fg = TB_BLACK, .bg = TB_WHITE, .alignment = LEFT }, - { .format = "%ld/%ld (%ldu/%ldf/%ldo)", - .callback = format_msgs, - .fg = TB_WHITE | TB_BOLD, .bg = TB_BLUE, .alignment = CENTER }, - { .format = "H=HELP, q=QUIT", .callback = NULL, - .fg = TB_BLACK, .bg = TB_WHITE, .alignment = RIGHT }, + {.format = "%s%s", + .callback = format_filename, + .fg = TB_BLACK, + .bg = TB_WHITE, + .alignment = LEFT}, + {.format = "%ld/%ld (%ldu/%ldf/%ldo)", + .callback = format_msgs, + .fg = TB_WHITE | TB_BOLD, + .bg = TB_BLUE, + .alignment = CENTER}, + {.format = "H=HELP, q=QUIT", + .callback = NULL, + .fg = TB_BLACK, + .bg = TB_WHITE, + .alignment = RIGHT}, }; diff --git a/draw.c b/draw.c @@ -826,18 +826,21 @@ draw_list_visible(const struct DrawState* state) /* don't use msgstr_index as then the entire list will shift * when changing currently displayed plural form msgstr in an * edit box */ - if (current->msgstr && current->msgstr_count > 0 - && current->msgstr + state->msgstr_index) - u32_draw_string(state->maxx/2, cy, - sel ? LIST_MSGSTR_SEL_FG - : LIST_MSGSTR_FG, - sel ? LIST_MSGSTR_SEL_BG - : LIST_MSGSTR_BG, - *(current->msgstr /*+ - state->msgstr_index*/), - state->maxx/2, state->maxx/2, 2, 2, - LEFT, 0, 1, state->search, SEARCH_HL_FG, - SEARCH_HL_BG); + if (current->msgstr && current->msgstr_count > 0) + u32_draw_string(state->maxx / 2, cy, + current->warning + ? (sel ? LIST_MSGSTR_SEL_WARN_FG + : LIST_MSGSTR_WARN_FG) + : (sel ? LIST_MSGSTR_SEL_FG + : LIST_MSGSTR_FG), + current->warning + ? (sel ? LIST_MSGSTR_SEL_WARN_BG + : LIST_MSGSTR_WARN_BG) + : (sel ? LIST_MSGSTR_SEL_BG + : LIST_MSGSTR_BG), + *(current->msgstr), state->maxx / 2, + state->maxx / 2, 2, 2, LEFT, 0, 1, + state->search, SEARCH_HL_FG, SEARCH_HL_BG); i++; cy++; } diff --git a/po.c b/po.c @@ -29,6 +29,7 @@ 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; @@ -399,6 +400,7 @@ load_file(struct DrawState* dstate, long* lineno, long* col) { struct PoEntry* newchunk = calloc(ALLOC_DELTA, sizeof(struct PoEntry)); struct PoEntry* entry = NULL; + struct PoEntry* current = NULL; ParseState pstate = PS_NONE; int has_plural_msgid = 0; @@ -479,6 +481,15 @@ load_file(struct DrawState* dstate, long* lineno, long* col) update_statistics(dstate->entries, dstate, entry); dstate->real_msgid_count = dstate->msgid_count + dstate->obsolete_count; + current = dstate->entries; + while (current != dstate->entries + dstate->real_msgid_count) + { + if (!current->obsolete) + update_warning(current->msgid, *current->msgstr, + &current->warning); + current++; + } + return LOAD_ERR_NONE; } @@ -1194,3 +1205,18 @@ update_statistics(const struct PoEntry* entries, struct DrawState* state, current++; } } + +void +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") + || (WARN_COUNT_DOTS + && u32_count_chars(msgid, '.') + != u32_count_chars(msgstr, '.'))) + *warning = 1; + else + *warning = 0; +} diff --git a/po.h b/po.h @@ -69,6 +69,7 @@ typedef enum { struct PoEntry { Flags flags; int obsolete; + int warning; uint32_t* msgid; size_t msgid_size; size_t msgid_len; @@ -119,3 +120,4 @@ int parse_po_line(const char* line, struct DrawState* dstate, int* msgstr_index); 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 @@ -167,6 +167,23 @@ _ . .sp 1 . +.SH "ERROR CHECKING" +. +.LP +.B poe +has some rudimentary checks of the translated messages built in. First, when +saving changes in the edit box, if the msgid has a newline character +(\fC\\n\fP), a dot (\fC.\fP) or a space (\fC\~\fP) at the end, and the +corresponding msgstr doesn't, msgstr being saved will have its ending character +made to match the one from the msgid. +. +.PP +Second, if the numbers of newline characters (\fC\\n\fP) in msgid and msgstr +don't match, msgstr will be shown in a different color on the main screen. If +the number of newlines was intended, you can simply ignore this warning. +Otherwise, it can be useful to detect unwanted discrepancies in formatting +between the original message and the translation. +. .SH AUTHOR . Strahinya Radich, diff --git a/poe.c b/poe.c @@ -468,8 +468,9 @@ load_msgstr(struct DrawState* state) void save_msgstr(struct DrawState* state) { - uint32_t* to_save = NULL; - size_t to_save_size = 0; + uint32_t* to_save = NULL; + size_t to_save_size = 0; + struct PoEntry* current = NULL; state->show_edit = 0; state->edit_info_focused = 0; @@ -510,10 +511,11 @@ save_msgstr(struct DrawState* state) state->info_rows_count = 0; state->info_buffer = NULL; u32_encode_tabs(to_save, to_save_size * sizeof(uint32_t)); - u32_match_msgid_ending(state->entries[state->msgid_number - 1].msgid, - to_save, to_save_size * sizeof(uint32_t)); - u32_set_msgstr(&state->entries[state->msgid_number - 1], to_save, - state->msgstr_index, 0, state); + current = &state->entries[state->msgid_number - 1]; + u32_match_msgid_ending(current->msgid, to_save, + to_save_size * sizeof(uint32_t)); + u32_set_msgstr(current, to_save, state->msgstr_index, 0, state); + update_warning(current->msgid, to_save, &current->warning); state->input_changed_fuzzy = UNCHANGED; tb_set_cursor(TB_HIDE_CURSOR, TB_HIDE_CURSOR); diff --git a/util.c b/util.c @@ -94,14 +94,12 @@ u32_match_msgid_ending(const uint32_t* msgid, uint32_t* msgstr, size_t max) } } else if (buf_len > 1 - && u32_starts_with(buf + buf_len - 2, - (uint32_t*)L"\\n")) + && u32_starts_with(buf + buf_len - 2, (uint32_t*)L"\\n")) { buf_len -= 2; buf[buf_len] = 0; } - else if (buf_len > 0 - && u32_strstr((uint32_t*)L" .", buf + buf_len - 1)) + else if (buf_len > 0 && u32_strstr((uint32_t*)L" .", buf + buf_len - 1)) { buf_len--; buf[buf_len] = 0; @@ -113,10 +111,10 @@ u32_match_msgid_ending(const uint32_t* msgid, uint32_t* msgstr, size_t max) } size_t -u32_num_chars(uint32_t* s, uint32_t ch) +u32_count_chars(const uint32_t* s, const uint32_t ch) { - size_t result = 0; - uint32_t* ps = s; + size_t result = 0; + const uint32_t* ps = s; while (*ps) { if (*ps == ch) @@ -127,6 +125,29 @@ u32_num_chars(uint32_t* s, 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; + + if (!haystack || !needle) + return 0; + + needle_len = u32_strlen(needle); + if (!needle_len) + return 0; + + while (*phaystack) + { + if (u32_starts_with(phaystack, needle)) + result++; + phaystack++; + } + return result; +} + +size_t u32_encode_tabs(uint32_t* s, size_t max) { uint32_t* buf = calloc(max + 1, sizeof(uint32_t)); diff --git a/util.h b/util.h @@ -31,7 +31,8 @@ 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); -size_t u32_num_chars(uint32_t* s, uint32_t ch); +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); size_t u32_strlen(const uint32_t* s);