diff options
Diffstat (limited to 'src/catalog.c')
-rw-r--r-- | src/catalog.c | 507 |
1 files changed, 507 insertions, 0 deletions
diff --git a/src/catalog.c b/src/catalog.c new file mode 100644 index 0000000..31aef9a --- /dev/null +++ b/src/catalog.c @@ -0,0 +1,507 @@ +#include "config.h" + +#include <assert.h> +#include <errno.h> +#include <nl_types.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "logerr.h" +#include "catalog.h" + +#ifdef TEST +#include "testing.h" +#include "../tests/slurp.h" +#endif + + +#ifdef TEST +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 +}; +#endif + + + +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; +} + +#ifdef TEST +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; +} +#endif + + +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; +} + +#ifdef TEST +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; +} +#endif + + +/** + * 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; +} + +#ifdef TEST +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; +} +#endif + + +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; +} + +#ifdef TEST +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; +} +#endif + + +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); +} + +#ifdef TEST +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; +} +#endif + + +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 +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 |