Anope IRC Services  Version 1.8
language.c
Go to the documentation of this file.
1 /* Multi-language support.
2  *
3  * (C) 2003-2014 Anope Team
4  * Contact us at team@anope.org
5  *
6  * Please read COPYING and README for further details.
7  *
8  * Based on the original code of Epona by Lara.
9  * Based on the original code of Services by Andy Church.
10  *
11  *
12  */
13 
14 #include "services.h"
15 #include "language.h"
16 
17 /*************************************************************************/
18 
19 /* The list of lists of messages. */
21 
22 /* The list of names of languages. */
24 
25 /* Indexes of available languages: */
27 
28 /* Order in which languages should be displayed: (alphabetical) */
29 static int langorder[NUM_LANGS] = {
30  LANG_EN_US, /* English (US) */
31  LANG_FR, /* French */
32  LANG_DE, /* German */
33  LANG_IT, /* Italian */
34  LANG_JA_JIS, /* Japanese (JIS encoding) */
35  LANG_JA_EUC, /* Japanese (EUC encoding) */
36  LANG_JA_SJIS, /* Japanese (SJIS encoding) */
37  LANG_PT, /* Portugese */
38  LANG_ES, /* Spanish */
39  LANG_TR, /* Turkish */
40  LANG_CAT, /* Catalan */
41  LANG_GR, /* Greek */
42  LANG_NL, /* Dutch */
43  LANG_RU, /* Russian */
44  LANG_HUN, /* Hungarian */
45  LANG_PL, /* Polish */
46  LANG_JA_UTF8, /* Japanese (UTF-8 encoding) */
47 };
48 
49 /*************************************************************************/
50 
51 /* Load a language file. */
52 
53 static int read_int32(int32 * ptr, FILE * f)
54 {
55  int a = fgetc(f);
56  int b = fgetc(f);
57  int c = fgetc(f);
58  int d = fgetc(f);
59  if (a == EOF || b == EOF || c == EOF || d == EOF)
60  return -1;
61  *ptr = a << 24 | b << 16 | c << 8 | d;
62  return 0;
63 }
64 
65 static void load_lang(int index, const char *filename)
66 {
67  char buf[256];
68  FILE *f;
69  int32 num, i;
70 
71  if (debug) {
72  alog("debug: Loading language %d from file `languages/%s'",
73  index, filename);
74  }
75  snprintf(buf, sizeof(buf), "languages/%s", filename);
76 #ifndef _WIN32
77  if (!(f = fopen(buf, "r"))) {
78 #else
79  if (!(f = fopen(buf, "rb"))) {
80 #endif
81  log_perror("Failed to load language %d (%s)", index, filename);
82  return;
83  } else if (read_int32(&num, f) < 0) {
84  alog("Failed to read number of strings for language %d (%s)",
85  index, filename);
86  return;
87  } else if (num != NUM_STRINGS) {
88  alog("Warning: Bad number of strings (%d, wanted %d) "
89  "for language %d (%s)", num, NUM_STRINGS, index, filename);
90  }
91  langtexts[index] = scalloc(sizeof(char *), NUM_STRINGS);
92  if (num > NUM_STRINGS)
93  num = NUM_STRINGS;
94  for (i = 0; i < num; i++) {
95  int32 pos, len;
96  fseek(f, i * 8 + 4, SEEK_SET);
97  if (read_int32(&pos, f) < 0 || read_int32(&len, f) < 0) {
98  alog("Failed to read entry %d in language %d (%s) TOC",
99  i, index, filename);
100  while (--i >= 0) {
101  if (langtexts[index][i])
102  free(langtexts[index][i]);
103  }
104  free(langtexts[index]);
105  langtexts[index] = NULL;
106  return;
107  }
108  if (len == 0) {
109  langtexts[index][i] = NULL;
110  } else if (len >= 65536) {
111  alog("Entry %d in language %d (%s) is too long (over 64k)--"
112  "corrupt TOC?", i, index, filename);
113  while (--i >= 0) {
114  if (langtexts[index][i])
115  free(langtexts[index][i]);
116  }
117  free(langtexts[index]);
118  langtexts[index] = NULL;
119  return;
120  } else if (len < 0) {
121  alog("Entry %d in language %d (%s) has negative length--"
122  "corrupt TOC?", i, index, filename);
123  while (--i >= 0) {
124  if (langtexts[index][i])
125  free(langtexts[index][i]);
126  }
127  free(langtexts[index]);
128  langtexts[index] = NULL;
129  return;
130  } else {
131  langtexts[index][i] = scalloc(len + 1, 1);
132  fseek(f, pos, SEEK_SET);
133  if (fread(langtexts[index][i], 1, len, f) != len) {
134  alog("Failed to read string %d in language %d (%s)",
135  i, index, filename);
136  while (--i >= 0) {
137  if (langtexts[index][i])
138  free(langtexts[index][i]);
139  }
140  free(langtexts[index]);
141  langtexts[index] = NULL;
142  return;
143  }
144  langtexts[index][i][len] = 0;
145  }
146  }
147  fclose(f);
148 }
149 
150 /*************************************************************************/
151 
152 /* Replace all %M's with "/msg " or "/" */
154 {
155  int i = 0, j = 0;
156  char tmp[2000];
157  char *newstr = NULL;
158  for (i = 0; i < NUM_LANGS; i++) {
159  for (j = 0; j < NUM_STRINGS; j++) {
160  if (strstr(langtexts[i][j], "%R")) {
161  strscpy(tmp, langtexts[i][j], sizeof(tmp));
162  if (UseStrictPrivMsg) {
163  strnrepl(tmp, sizeof(tmp), "%R", "/");
164  } else {
165  strnrepl(tmp, sizeof(tmp), "%R", "/msg ");
166  }
167  newstr = sstrdup(tmp);
168  free(langtexts[i][j]);
169  langtexts[i][j] = newstr;
170  }
171  }
172  }
173 }
174 
175 
176 /* Initialize list of lists. */
177 
178 void lang_init()
179 {
180  int i, j, n = 0;
181 
182  load_lang(LANG_CAT, "cat");
183  load_lang(LANG_DE, "de");
184  load_lang(LANG_EN_US, "en_us");
185  load_lang(LANG_ES, "es");
186  load_lang(LANG_FR, "fr");
187  load_lang(LANG_GR, "gr");
188  load_lang(LANG_PT, "pt");
189  load_lang(LANG_TR, "tr");
190  load_lang(LANG_IT, "it");
191  load_lang(LANG_NL, "nl");
192  load_lang(LANG_RU, "ru");
193  load_lang(LANG_HUN, "hun");
194  load_lang(LANG_PL, "pl");
195  load_lang(LANG_JA_UTF8, "ja_utf8");
196 
197  for (i = 0; i < NUM_LANGS; i++) {
198  if (langtexts[langorder[i]] != NULL) {
199  langnames[langorder[i]] = langtexts[langorder[i]][LANG_NAME];
200  langlist[n++] = langorder[i];
201  for (j = 0; j < NUM_STRINGS; j++) {
202  if (!langtexts[langorder[i]][j]) {
203  langtexts[langorder[i]][j] =
205  }
206  if (!langtexts[langorder[i]][j]) {
207  langtexts[langorder[i]][j] = langtexts[LANG_EN_US][j];
208  }
209  }
210  }
211  }
212  while (n < NUM_LANGS)
213  langlist[n++] = -1;
214 
215  /* Not what I intended to do, but these services are so archa´c
216  * that it's difficult to do more. */
217  if ((NSDefLanguage = langlist[NSDefLanguage]) < 0)
218  NSDefLanguage = DEF_LANGUAGE;
219 
220  if (!langtexts[DEF_LANGUAGE])
221  fatal("Unable to load default language");
222  for (i = 0; i < NUM_LANGS; i++) {
223  if (!langtexts[i])
225  }
226  lang_sanitize();
227 }
228 
229 /*************************************************************************/
230 /*************************************************************************/
231 
232 /* Format a string in a strftime()-like way, but heed the user's language
233  * setting for month and day names. The string stored in the buffer will
234  * always be null-terminated, even if the actual string was longer than the
235  * buffer size.
236  * Assumption: No month or day name has a length (including trailing null)
237  * greater than BUFSIZE.
238  */
239 
240 int strftime_lang(char *buf, int size, User * u, int format, struct tm *tm)
241 {
242  int language = u && u->na ? u->na->nc->language : NSDefLanguage;
243  char tmpbuf[BUFSIZE], buf2[BUFSIZE];
244  char *s;
245  int i, ret;
246 
247  if (!tm) {
248  return 0;
249  }
250 
251  strscpy(tmpbuf, langtexts[language][format], sizeof(tmpbuf));
252  if ((s = langtexts[language][STRFTIME_DAYS_SHORT]) != NULL) {
253  for (i = 0; i < tm->tm_wday; i++)
254  s += strcspn(s, "\n") + 1;
255  i = strcspn(s, "\n");
256  strncpy(buf2, s, i);
257  buf2[i] = 0;
258  strnrepl(tmpbuf, sizeof(tmpbuf), "%a", buf2);
259  }
260  if ((s = langtexts[language][STRFTIME_DAYS_LONG]) != NULL) {
261  for (i = 0; i < tm->tm_wday; i++)
262  s += strcspn(s, "\n") + 1;
263  i = strcspn(s, "\n");
264  strncpy(buf2, s, i);
265  buf2[i] = 0;
266  strnrepl(tmpbuf, sizeof(tmpbuf), "%A", buf2);
267  }
268  if ((s = langtexts[language][STRFTIME_MONTHS_SHORT]) != NULL) {
269  for (i = 0; i < tm->tm_mon; i++)
270  s += strcspn(s, "\n") + 1;
271  i = strcspn(s, "\n");
272  strncpy(buf2, s, i);
273  buf2[i] = 0;
274  strnrepl(tmpbuf, sizeof(tmpbuf), "%b", buf2);
275  }
276  if ((s = langtexts[language][STRFTIME_MONTHS_LONG]) != NULL) {
277  for (i = 0; i < tm->tm_mon; i++)
278  s += strcspn(s, "\n") + 1;
279  i = strcspn(s, "\n");
280  strncpy(buf2, s, i);
281  buf2[i] = 0;
282  strnrepl(tmpbuf, sizeof(tmpbuf), "%B", buf2);
283  }
284  ret = strftime(buf, size, tmpbuf, tm);
285  if (ret == size)
286  buf[size - 1] = 0;
287  return ret;
288 }
289 
290 /*************************************************************************/
291 /*************************************************************************/
292 
293 /* Send a syntax-error message to the user. */
294 
295 void syntax_error(char *service, User * u, const char *command, int msgnum)
296 {
297  const char *str;
298 
299  if (!u) {
300  return;
301  }
302 
303  str = getstring(u->na, msgnum);
304  notice_lang(service, u, SYNTAX_ERROR, str);
305  notice_lang(service, u, MORE_INFO, service, command);
306 }
307 
308 /*************************************************************************/
#define LANG_TR
Definition: services.h:1322
#define LANG_JA_EUC
Definition: services.h:1317
#define LANG_DE
Definition: services.h:1324
E int snprintf(char *buf, size_t size, const char *fmt,...)
Definition: compat.c:37
#define LANG_JA_JIS
Definition: services.h:1316
#define LANG_ES
Definition: services.h:1319
#define LANG_RU
Definition: services.h:1328
#define LANG_NL
Definition: services.h:1327
E int NSDefLanguage
Definition: extern.h:378
uint16 language
Definition: services.h:549
#define getstring(na, index)
Definition: extern.h:731
char ** langtexts[NUM_LANGS]
Definition: language.c:20
E char * strscpy(char *d, const char *s, size_t len)
Definition: db-merger.c:1886
#define NUM_LANGS
Definition: services.h:1333
void lang_init()
Definition: language.c:178
E void notice_lang(char *source, User *dest, int message,...)
Definition: send.c:169
E char * sstrdup(const char *s)
Definition: memory.c:105
int strftime_lang(char *buf, int size, User *u, int format, struct tm *tm)
Definition: language.c:240
NickCore * nc
Definition: services.h:533
#define LANG_PL
Definition: services.h:1330
E void * scalloc(long elsize, long els)
Definition: memory.c:55
void lang_sanitize()
Definition: language.c:153
E void E void E void fatal(const char *fmt,...) FORMAT(printf
#define LANG_FR
Definition: services.h:1321
int32_t int32
Definition: db-merger.c:122
static void load_lang(int index, const char *filename)
Definition: language.c:65
Command * c
Definition: ns_recover.c:17
E void alog(const char *fmt,...) FORMAT(printf
#define LANG_IT
Definition: services.h:1323
#define LANG_JA_UTF8
Definition: services.h:1331
E int debug
Definition: extern.h:775
#define LANG_CAT
Definition: services.h:1325
#define DEF_LANGUAGE
Definition: services.h:1337
#define LANG_GR
Definition: services.h:1326
void syntax_error(char *service, User *u, const char *command, int msgnum)
Definition: language.c:295
#define LANG_EN_US
Definition: services.h:1315
static int read_int32(int32 *ptr, FILE *f)
Definition: language.c:53
#define LANG_HUN
Definition: services.h:1329
E void E void log_perror(const char *fmt,...) FORMAT(printf
#define LANG_JA_SJIS
Definition: services.h:1318
E int UseStrictPrivMsg
Definition: extern.h:353
#define LANG_PT
Definition: services.h:1320
static int langorder[NUM_LANGS]
Definition: language.c:29
#define BUFSIZE
Definition: config.h:47
E char * strnrepl(char *s, int32 size, const char *old, const char *new)
Definition: misc.c:144
char * langnames[NUM_LANGS]
Definition: language.c:23
NickAlias * na
Definition: services.h:892
int langlist[NUM_LANGS]
Definition: language.c:26