#include "config.h" #include #include #include #include #include #include #include "logerr.h" #include "catalog.h" static const char *const CATALOG_NAME = NAME; static nl_catd catalog_descriptor = NULL; static const char *const NLSPATH = LOCALEDIR "/%l_%t/LC_MESSAGES/%N.cat" ":" LOCALEDIR "/%l/LC_MESSAGES/%N.cat"; static const char *const NLSPATH_KEY = "NLSPATH"; int i18n_init(void) { int rc = 0; static const int should_overwrite = 0; if (setenv(NLSPATH_KEY, NLSPATH, should_overwrite)) { logerr("setenv(\"%s\", \"%s\", 0): %s\n", NLSPATH_KEY, NLSPATH, strerror(errno)); rc = -1; goto out; } catalog_descriptor = catopen(CATALOG_NAME, 0); if (catalog_descriptor && catalog_descriptor == (nl_catd)-1) { logerr("catopen(\"%s\", 0): %s\n", CATALOG_NAME, strerror(errno)); catalog_descriptor = NULL; rc = -1; goto out; } out: return rc; } int i18n_destroy(void) { int rc = 0; if (catalog_descriptor) { if (catclose(catalog_descriptor)) { logerr("catclose(...): %s\n", strerror(errno)); rc = -1; goto out; } } out: if (catalog_descriptor) { catalog_descriptor = NULL; } return rc; } /** * Infallible: always returns a valid string, no matter what. */ const char * s(const char* const MSGS[], const int msg_id) { assert(msg_id > 0); // FIXME: assert within bounds! // printf("sizeof(MSGS): %ld\n", sizeof(MSGS)); if (!catalog_descriptor) { return MSGS[msg_id]; } errno = 0; const char *const ret = catgets(catalog_descriptor, NL_SETD, msg_id, MSGS[msg_id]); if (errno) { logerr("catgets(%d): %s\n", msg_id, strerror(errno)); } return ret; } int s_print_msgs( const char *const MSGS[], FILE *restrict stream, const int msg_begin, const int msg_end ) { int rc = 0; for (int i = msg_begin; i <= msg_end; i++) { if (fprintf(stream, "%s", s(MSGS, i)) < 0) { logerr("fprintf(stream, \"%%s\", _(%d)): %s\n", i, strerror(errno)); rc = -1; goto out; } } out: return rc; } int s_print_msg(const char *const MSGS[], FILE *const fd, const int msg_id) { return s_print_msgs(MSGS, fd, msg_id, msg_id); } int dump_translatable_strings(const char *const MSGS[]) { int rc = 0; for (size_t i = 1; MSGS[i]; i++) { if (printf("%ld ", i) < 0) { logerr("printf(\"%%ld\", %d): %s\n", i); rc = -1; goto out; } for (size_t j = 0; MSGS[i][j]; j++) { if (MSGS[i][j] == '\n') { if (printf("\\n") < 0) { logerr("printf(\"\\\\n\"): %s\n", strerror(errno)); rc = -1; goto out; } } else { if (printf("%c", MSGS[i][j]) < 0) { logerr("printf(\"%%c\", " "MSGS[%ld][%ld]): %s\n", i, j, strerror(errno)); rc = -1; goto out; } } } if (printf("\n\n") < 0) { logerr("printf(\"\\n\\n\"): %s\n", strerror(errno)); rc = -1; goto out; } } out: return rc; } #ifdef TEST #include "testing.h" #include "../tests/slurp.h" static const char *const FNAME = __FILE__ ".txt"; enum TEST_MSGCATALOG_ID { MSG_X_FIRST = 1, MSG_X_1, MSG_X_2, MSG_X_LAST, MSG_STANDALONE, }; static const char *const TEST_MSGS[] = { "", [MSG_X_FIRST]="First line\n", [MSG_X_1]="a second\n", [MSG_X_2]="a third\n", [MSG_X_LAST]="and the last one\n", [MSG_STANDALONE]="single line message\n", NULL }; static int test_i18n_init(void) { int rc = 0; test_start("i18n_init()"); { testing("simple call without touching the environment"); const int should_overwrite = 1; if (setenv(NLSPATH_KEY, "src/%N.en.cat", should_overwrite)) { logerr("setenv(\"%s\", \"src/%%N.en.cat\", 1): %s\n", NLSPATH_KEY, strerror(errno)); rc = -1; goto out; } if (i18n_init()) { logerr("i18n_init()\n"); rc = -1; goto out; } test_ok(); } out: if (i18n_destroy()) { logerr("i18n_destroy()\n"); rc = -1; } return rc; } static int test_s_print_msgs(void) { int rc = 0; test_start("s_print_msgs()"); FILE *file = NULL; char *str = NULL; { testing("message in range"); file = fopen(FNAME, "w"); if (!file) { perror("fopen(FNAME, \"w\")"); rc = -1; goto out; } if (s_print_msgs(TEST_MSGS, file, MSG_X_FIRST, MSG_X_LAST)) { logerr("print_msgs(TEST_MSGS, file, MSG_X_FIRST, MSG_X_LAST)\n"); rc = -1; goto out; } const int ret = fclose(file); file = NULL; if (ret) { logerr("fclose(file): %s\n", strerror(errno)); rc = -1; goto out; } if (slurp_for_tests(FNAME, &str)) { logerr("slurp_for_tests(FNAME, &str)\n"); rc = -1; goto out; } const char *const expected = "First line\n" "a second\n" "a third\n" "and the last one\n" ; assert(strcmp(expected, str) == 0); free(str); str = NULL; test_ok(); } { testing("range begin and end is the same"); file = fopen(FNAME, "w"); if (!file) { logerr("fopen(FNAME, \"w\"): %s\n", strerror(errno)); rc = -1; goto out; } if (s_print_msgs(TEST_MSGS, file, MSG_X_FIRST, MSG_X_FIRST)) { logerr("s_print_msgs(TEST_MSGS, file, MSG_X_FIRST, MSG_X_FIRST)\n"); rc = -1; goto out; } const int ret = fclose(file); file = NULL; if (ret) { logerr("fclose(file): %s\n", strerror(errno)); rc = -1; goto out; } if (slurp_for_tests(FNAME, &str)) { logerr("slurp_for_tests(FNAME, &str)\n"); rc = -1; goto out; } const char *const expected = "First line\n"; assert(strcmp(expected, str) == 0); free(str); str = NULL; test_ok(); } out: if (str) { free(str); } if (file) { if (fclose(file)) { logerr("fclose(file): %s\n", strerror(errno)); rc = -1; } } return rc; } static int test_s(void) { int rc = 0; test_start("_()"); FILE *file = NULL; char *str = NULL; { testing("empty string"); file = fopen(FNAME, "w"); if (!file) { perror("fopen(FNAME, \"w\")"); rc = -1; goto out; } // FIXME: implement correct test test_ok(); } out: if (str) { free(str); } if (file) { if (fclose(file)) { logerr("fclose(file): %s\n", strerror(errno)); rc = -1; } } return rc; } static int test_i18n_destroy(void) { int rc = 0; test_start("i18n_destroy()"); { testing("simple call without init first"); if (i18n_destroy()) { logerr("i18n_destroy()\n"); rc = -1; goto out; } test_ok(); } out: return rc; } static int test_s_print_msg(void) { int rc = 0; test_start("s_print_msg()"); FILE *file = NULL; char *str = NULL; { testing("simple individual message"); file = fopen(FNAME, "w"); if (!file) { logerr("fopen(FNAME, \"w\"): %s\n"); rc = -1; goto out; } if (s_print_msg(TEST_MSGS, file, MSG_STANDALONE)) { logerr("s_print_msg(TEST_MSGS, file, MSG_STANDALONE)\n"); rc = -1; goto out; } const int ret = fclose(file); file = NULL; if (ret) { logerr("fopen(file): %s\n", strerror(errno)); rc = -1; goto out; } if (slurp_for_tests(FNAME, &str)) { logerr("slurp_for_tests(FNAME, &str)\n"); rc = -1; goto out; } const char *const expected = "single line message\n"; assert(strcmp(expected, str) == 0); free(str); str = NULL; test_ok(); } out: if (str) { free(str); } if (file) { if (fclose(file)) { logerr("fclose(file): %s\n", strerror(errno)); rc = -1; } } return rc; } int main(void) { int rc = 0; if (test_i18n_init()) { logerr("test_i18n_init()\n"); rc = -1; goto out; } if (test_i18n_destroy()) { logerr("test_i18n_destroy()\n"); rc = -1; goto out; } if (test_s()) { logerr("test_s()\n"); rc = -1; goto out; } if (test_s_print_msgs()) { logerr("test_s_print_msgs()\n"); rc = -1; goto out; } if (test_s_print_msg()) { logerr("test_s_print_msg()\n"); rc = -1; goto out; } out: return !!rc; } #endif