aboutsummaryrefslogtreecommitdiff
path: root/src/gistatic.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gistatic.c')
-rw-r--r--src/gistatic.c168
1 files changed, 139 insertions, 29 deletions
diff --git a/src/gistatic.c b/src/gistatic.c
index 8ab9aa0..b356748 100644
--- a/src/gistatic.c
+++ b/src/gistatic.c
@@ -7,6 +7,8 @@
#include <assert.h>
#include <sys/stat.h>
#include <errno.h>
+#include <nl_types.h>
+#include <locale.h>
#include <git2.h>
@@ -15,16 +17,60 @@
#include "unit-test.h"
#endif
-// FIXME: translate
-static const char *const DEFAULT_TITLE = "Repositories";
-static const char *const LOGO_ALT = "Logo image of the repository list";
-static const char *const FOOTER_TEMPLATE = "Generated with %s";
-static const char *const LANG = "en";
-static const char *const NAME = "Name";
-static const char *const DESCRIPTION = "Description";
-static const char *const LAST_COMMIT = "Last commit";
-static const char *const USAGE = "Usage: -h -o -i -x...\n";
+static const int EXIT_ERROR = 1;
+static const int EXIT_USAGE = 2;
+
+
+#define PROGNAME "gistatic"
+static const char *const CATALOG_NAME = PROGNAME;
+static nl_catd catalog_descriptor = NULL;
+
+#define MSG_DEFAULT_TITLE 1
+#define MSG_LOGO_ALT 2
+#define MSG_FOOTER_TEMPLATE 3
+#define MSG_LANG 4
+#define MSG_NAME 5
+#define MSG_DESCRIPTION 6
+#define MSG_LAST_COMMIT 7
+#define MSG_USAGE 8
+#define MSG_HELP 9
+
+static const char *const MSGS[] = {
+ "",
+ [MSG_DEFAULT_TITLE]="Repositories",
+ [MSG_LOGO_ALT]="Logo image of the repository list",
+ [MSG_FOOTER_TEMPLATE]="Generated with %s",
+ [MSG_LANG]="en",
+ [MSG_NAME]="Name",
+ [MSG_DESCRIPTION]="Description",
+ [MSG_LAST_COMMIT]="Last commit",
+ [MSG_USAGE]="Usage: -h -o -i\n-z -x...\n",
+ [MSG_HELP]="help: ...\n",
+ NULL
+};
+
+#ifdef TEST
+static void dump_translatable_strings() {
+ const size_t size =
+ strlen(__FILE__) - strlen(".c") + strlen(".msg") + sizeof('\0');
+ char *const catalog_path = malloc(size);
+ assert(catalog_path);
+
+ strcpy(catalog_path, __FILE__);
+ catalog_path[strlen(__FILE__) - strlen(".c")] = '\0';
+ strcat(catalog_path, ".msg");
+
+ FILE *const f = fopen(catalog_path, "w");
+ assert(f);
+ for (size_t i = 1; MSGS[i] != NULL; i++) {
+ assert(fprintf(f, "%ld %s\n\n", i, MSGS[i]) > 0);
+ }
+
+ assert(fclose(f) == 0);
+ free(catalog_path);
+}
+#endif
static const char *const LOGO_STR = ""
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
@@ -119,8 +165,7 @@ static const char *const STYLE_STR = ""
"}\n"
"\n"
"table {\n"
- " margin-top: 2em;\n"
- " margin-bottom: 2em;\n"
+ " margin: 2em auto;\n"
"}\n"
"\n"
"thead td {\n"
@@ -143,17 +188,49 @@ static const char *const STYLE_STR = ""
"";
-#define PROGNAME "gistatic"
+static bool verbose = false;
static const char *const GIT_SUFFIX = ".git";
static const char *const PROJECT_HOMEPAGE_LINK =
"<a href=\"https://euandreh.xyz/" PROGNAME "\">" PROGNAME "</a>.";
-static const int EXIT_ERROR = 1;
-static const int EXIT_USAGE = 2;
+static const char *_(int msg_id) {
+ if (!catalog_descriptor || catalog_descriptor == (nl_catd)-1) {
+ return MSGS[msg_id];
+ }
+ errno = 0;
+ const char *const ret =
+ catgets(catalog_descriptor, NL_SETD, msg_id, MSGS[msg_id]);
+ if (errno && verbose) {
+ fprintf(
+ stderr,
+ "%s:%s:%d: catgets(%d): %s\n",
+ PROGNAME,
+ __FILE__,
+ __LINE__,
+ msg_id,
+ strerror(errno)
+ );
+ }
+ return ret;
+}
+
+#ifdef TEST
+static void test_underscore() {
+ test_start("test_underscore");
+ const char *const original_locale = setlocale(LC_ALL, NULL);
+ setlocale(LC_ALL, "");
+ setenv("NLSPATH", "", 1);
+ {
+ testing("a NULL value for catalog_descriptor");
+ test_ok();
+ }
+ setlocale(LC_ALL, original_locale);
+}
+#endif
-static int usage(FILE *const fd) {
- if (fprintf(fd, "%s", USAGE) < 0) {
+static int print_msg(FILE *const fd, const char *const msg) {
+ if (fprintf(fd, "%s", msg) < 0) {
fprintf(
stderr,
"%s:%s:%d: fprintf(): %s\n",
@@ -167,6 +244,19 @@ static int usage(FILE *const fd) {
return 0;
}
+static int print_help(FILE *const fd) {
+ return print_msg(fd, _(MSG_HELP));
+}
+
+static int print_version(FILE *const fd) {
+ return print_msg(fd, PROGNAME "-" VERSION " " DATE "\n");
+}
+
+static int print_usage(FILE *const fd) {
+ return print_msg(fd, _(MSG_USAGE));
+}
+
+
static char *remove_suffix(char *const s, const char *const suffix) {
if (!s || !suffix) {
return NULL;
@@ -390,8 +480,8 @@ static char *formatted_date(const time_t time_sec) {
return NULL;
}
- // Expected size, plus breathing room
- const size_t size = strlen("XXX-XX-XX XX:XX") * 2;
+ /* Expected size, plus breathing room */
+ const size_t size = (strlen("XXX-XX-XX XX:XX") + sizeof('\0')) * 2;
char *const formatted = malloc(size);
if (!formatted) {
fprintf(
@@ -688,13 +778,13 @@ static int write_header(FILE *const fd, const char *const idx_title) {
" </tr>\n"
" </thead>\n"
" <tbody>\n",
- LANG,
+ _(MSG_LANG),
idx_title,
- LOGO_ALT,
+ _(MSG_LOGO_ALT),
idx_title,
- NAME,
- DESCRIPTION,
- LAST_COMMIT
+ _(MSG_NAME),
+ _(MSG_DESCRIPTION),
+ _(MSG_LAST_COMMIT)
);
if (e < 0) {
fprintf(
@@ -711,7 +801,7 @@ static int write_header(FILE *const fd, const char *const idx_title) {
}
static int write_footer(FILE *const fd) {
- const size_t footer_size = strlen(FOOTER_TEMPLATE) - strlen("%s")
+ const size_t footer_size = strlen(_(MSG_FOOTER_TEMPLATE)) - strlen("%s")
+ strlen(PROJECT_HOMEPAGE_LINK) + sizeof('\0');
char *const footer_text = malloc(footer_size);
if (!footer_text) {
@@ -726,7 +816,7 @@ static int write_footer(FILE *const fd) {
);
return -1;
}
- sprintf(footer_text, FOOTER_TEMPLATE, PROJECT_HOMEPAGE_LINK);
+ sprintf(footer_text, _(MSG_FOOTER_TEMPLATE), PROJECT_HOMEPAGE_LINK);
const int e = fprintf(
fd,
@@ -1098,7 +1188,10 @@ cleanup:
#ifdef TEST
static void unit_tests(){
+ dump_translatable_strings();
+
git_libgit2_init();
+ test_underscore();
test_remove_suffix();
test_strjoin();
test_formatted_date();
@@ -1114,17 +1207,21 @@ int main(int argc, char *argv[]) {
return EXIT_SUCCESS;
#endif
+ catalog_descriptor = catopen(CATALOG_NAME, NL_CAT_LOCALE);
+
if (argc < 2) {
- usage(stderr);
+ if (print_usage(stderr)) {
+ return EXIT_ERROR;
+ }
return EXIT_USAGE;
}
int ret = 0;
int flag;
bool index = false;
- const char *idx_title = DEFAULT_TITLE;
+ const char *idx_title = _(MSG_DEFAULT_TITLE);
const char *outdir = ".";
- while ((flag = getopt(argc, argv, "o:t:ihV")) != -1) {
+ while ((flag = getopt(argc, argv, "o:t:ivhV")) != -1) {
switch (flag) {
case 'i':
index = true;
@@ -1135,8 +1232,21 @@ int main(int argc, char *argv[]) {
case 'o':
outdir = optarg;
break;
+ case 'v':
+ verbose = true;
+ break;
+ case 'h':
+ if (print_help(stdout)) {
+ return EXIT_ERROR;
+ }
+ return EXIT_SUCCESS;
+ case 'V':
+ if (print_version(stdout)) {
+ return EXIT_ERROR;
+ }
+ return EXIT_SUCCESS;
default:
- if (usage(stderr)) {
+ if (print_usage(stderr)) {
return EXIT_ERROR;
}
return EXIT_USAGE;