summaryrefslogtreecommitdiff
path: root/src/catalog.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/catalog.c')
-rw-r--r--src/catalog.c507
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