libacfutils
A general purpose library of utility functions designed to make it easier to develop addons for the X-Plane flight simulator.
Loading...
Searching...
No Matches
intl.c
1/*
2 * CDDL HEADER START
3 *
4 * This file and its contents are supplied under the terms of the
5 * Common Development and Distribution License ("CDDL"), version 1.0.
6 * You may only use this file in accordance with the terms of version
7 * 1.0 of the CDDL.
8 *
9 * A full copy of the text of the CDDL should have accompanied this
10 * source. A copy of the CDDL is also available via the Internet at
11 * http://www.illumos.org/license/CDDL.
12 *
13 * CDDL HEADER END
14*/
15/*
16 * Copyright 2023 Saso Kiselkov. All rights reserved.
17 */
18
19#include <stdlib.h>
20#include <stdio.h>
21#include <string.h>
22#include <stddef.h>
23#include <ctype.h>
24
25#include <XPLMUtilities.h>
26
27#include <acfutils/assert.h>
28#include <acfutils/avl.h>
29#include <acfutils/intl.h>
30#include <acfutils/helpers.h>
31
32static avl_tree_t tbl;
33static bool_t acfutils_xlate_inited = B_FALSE;
34
35typedef struct {
36 char *msgid;
37 char *msgstr;
38 avl_node_t node;
40
41static int
42xlate_compar(const void *a, const void *b)
43{
44 const xlate_ent_t *xa = a, *xb = b;
45 int res = strcmp(xa->msgid, xb->msgid);
46
47 if (res < 0)
48 return (-1);
49 else if (res == 0)
50 return (0);
51 else
52 return (1);
53}
54
78bool_t
79acfutils_xlate_init(const char *po_file)
80{
81 FILE *fp = NULL;
82 char cmd[32];
83 xlate_ent_t *e = NULL;
84
85 ASSERT(!acfutils_xlate_inited);
86 avl_create(&tbl, xlate_compar, sizeof (xlate_ent_t),
87 offsetof(xlate_ent_t, node));
88
89 fp = fopen(po_file, "r");
90 if (fp == NULL)
91 goto errout;
92
93 while (!feof(fp)) {
94 char c;
95
96 do {
97 c = fgetc(fp);
98 } while (isspace(c));
99
100 if (c == EOF)
101 break;
102
103 ungetc(c, fp);
104
105 switch (c) {
106 case '#':
107 do {
108 c = fgetc(fp);
109 } while (c != '\n' && c != '\r' && c != EOF);
110 break;
111 case '"': {
112 char *str = parser_get_next_quoted_str(fp);
113 if (e == NULL) {
114 logMsg("malformed po file %s: out of place "
115 "quoted string found", po_file);
116 free(str);
117 goto errout;
118 }
119 if (e->msgid == NULL) {
120 e->msgid = str;
121 } else if (e->msgstr == NULL) {
122 e->msgstr = str;
123 } else {
124 logMsg("malformed po file %s: too many strings "
125 "following msgid or msgstr", po_file);
126 free(str);
127 free(e->msgid);
128 free(e->msgstr);
129 free(e);
130 goto errout;
131 }
132 break;
133 }
134 default:
135 VERIFY3S(fscanf(fp, "%31s", cmd), ==, 1);
136 if (strcmp(cmd, "msgid") == 0) {
137 if (e != NULL) {
138 if (e->msgid == NULL ||
139 e->msgstr == NULL) {
140 logMsg("malformed po file %s: "
141 "incomplete msgid entry",
142 po_file);
143 free(e->msgid);
144 free(e);
145 goto errout;
146 }
147 if (*e->msgid == 0) {
148 free(e->msgid);
149 free(e->msgstr);
150 free(e);
151 } else {
152 avl_add(&tbl, e);
153 }
154 }
155 e = calloc(1, sizeof (*e));
156 } else if (strcmp(cmd, "msgstr") == 0) {
157 if (e == NULL || e->msgid == NULL) {
158 logMsg("malformed po file %s: "
159 "misplaced \"msgstr\" diretive",
160 po_file);
161 free(e);
162 goto errout;
163 }
164 } else {
165 logMsg("maformed po file %s: unknown "
166 "directive \"%s\".", po_file, cmd);
167 free(e->msgid);
168 free(e->msgstr);
169 free(e);
170 goto errout;
171 }
172 break;
173 }
174 }
175
176 if (e != NULL) {
177 if (e->msgid == NULL || e->msgstr == NULL) {
178 logMsg("malformed po file %s: incomplete msgid entry",
179 po_file);
180 free(e->msgid);
181 free(e);
182 goto errout;
183 }
184 if (*e->msgid == 0) {
185 free(e->msgid);
186 free(e->msgstr);
187 free(e);
188 } else {
189 avl_add(&tbl, e);
190 }
191 }
192
193 fclose(fp);
194
195 acfutils_xlate_inited = B_TRUE;
196 return (B_TRUE);
197errout:
198 if (fp != NULL)
199 fclose(fp);
200
201 /* fake init success and immediately tear down */
202 acfutils_xlate_inited = B_TRUE;
204 return (B_FALSE);
205}
206
212void
214{
215 xlate_ent_t *ent;
216 void *cookie = NULL;
217
218 if (!acfutils_xlate_inited)
219 return;
220
221 while ((ent = avl_destroy_nodes(&tbl, &cookie)) != NULL) {
222 free(ent->msgid);
223 free(ent->msgstr);
224 free(ent);
225 }
226 avl_destroy(&tbl);
227
228 acfutils_xlate_inited = B_FALSE;
229}
230
248const char *
249acfutils_xlate(const char *msgid)
250{
251 const xlate_ent_t srch = { .msgid = (char *)msgid };
252 const xlate_ent_t *ent;
253
254 if (!acfutils_xlate_inited)
255 return (msgid);
256
257 ent = avl_find(&tbl, &srch, NULL);
258 if (ent == NULL)
259 return (msgid);
260 else
261 return (ent->msgstr);
262}
263
270const char *
272{
273 switch (lang) {
274 case xplm_Language_English:
275 return ("en");
276 case xplm_Language_French:
277 return ("fr");
278 case xplm_Language_German:
279 return ("de");
280 case xplm_Language_Italian:
281 return ("it");
282 case xplm_Language_Spanish:
283 return ("es");
284 case xplm_Language_Korean:
285 return ("ko");
286 case xplm_Language_Russian:
287 return ("ru");
288 case xplm_Language_Greek:
289 return ("el");
290 case xplm_Language_Japanese:
291 return ("ja");
292 case xplm_Language_Chinese:
293 return ("ch");
294 default:
295 return ("xx");
296 }
297}
#define ASSERT(x)
Definition assert.h:208
#define VERIFY3S(x, op, y)
Definition assert.h:125
void * avl_destroy_nodes(avl_tree_t *tree, void **cookie)
Definition avl.c:938
void avl_add(avl_tree_t *tree, void *node)
Definition avl.c:621
void avl_create(avl_tree_t *tree, int(*compar)(const void *, const void *), size_t size, size_t offset)
Definition avl.c:867
void avl_destroy(avl_tree_t *tree)
Definition avl.c:890
void * avl_find(const avl_tree_t *tree, const void *node, avl_index_t *where)
Definition avl.c:244
static char * parser_get_next_quoted_str(FILE *fp)
Definition helpers.h:416
void acfutils_xlate_fini(void)
Definition intl.c:213
const char * acfutils_xlate(const char *msgid)
Definition intl.c:249
const char * acfutils_xplang2code(int lang)
Definition intl.c:271
bool_t acfutils_xlate_init(const char *po_file)
Definition intl.c:79
#define logMsg(...)
Definition log.h:112