1 /*
2 * language.c -- handles:
3 * language support code
4 *
5 * $Id: language.c,v 1.23 (1.0.0) 2004/04/29 12:31:04 [Xp-AvR] Exp $
6 */
7
8 #include "main.h"
9
10 extern struct dcc_t *dcc;
11
12 typedef struct lang_st {
13 struct lang_st *next;
14 char *lang;
15 char *section;
16 } lang_sec;
17
18 typedef struct lang_pr {
19 struct lang_pr *next;
20 char *lang;
21 } lang_pri;
22
23 typedef struct lang_t {
24 int idx;
25 char *text;
26 struct lang_t *next;
27 } lang_tab;
28
29 static lang_tab *langtab[64];
30 static lang_sec *langsection = NULL;
31 static lang_pri *langpriority = NULL;
32
33 static int del_lang(char *);
34 static int add_message(int, char *);
35 static void recheck_lang_sections(void);
36 static void read_lang(char *);
37 void add_lang_section(char *);
38 int del_lang_section(char *);
39 int exist_lang_section(char *);
40 static char *get_specific_langfile(char *, lang_sec *);
41 static char *get_langfile(lang_sec *);
42 static int split_lang(char *, char **, char **);
43 int cmd_loadlanguage(struct userrec *, int, char *);
44
45
46 /* Add a new preferred language to the list of languages. Newly added
47 * languages get the highest priority. */
add_lang(char * lang)48 void add_lang(char *lang)
49 {
50 lang_pri *lp = langpriority, *lpo = NULL;
51
52 while (lp) {
53 /* The language already exists, moving to the beginning */
54 if (!strcmp(lang, lp->lang)) {
55 /* Already at the front? */
56 if (!lpo)
57 return;
58 lpo->next = lp->next;
59 lp->next = lpo;
60 langpriority = lp;
61 return;
62 }
63 lpo = lp;
64 lp = lp->next;
65 }
66
67 /* No existing entry, create a new one */
68 lp = nmalloc(sizeof(lang_pri));
69 lp->lang = nmalloc(strlen(lang) + 1);
70 strcpy(lp->lang, lang);
71 lp->next = NULL;
72
73 /* If we have other entries, point to the beginning of the old list */
74 if (langpriority)
75 lp->next = langpriority;
76 langpriority = lp;
77 debug1("(language.c->add_lang) LANG: Language loaded: %s", lang);
78 }
79
80 /* Remove a language from the list of preferred languages.
81 */
del_lang(char * lang)82 static int del_lang(char *lang)
83 {
84 lang_pri *lp = langpriority, *lpo = NULL;
85
86 while (lp) {
87 /* Found the language? */
88 if (!strcmp(lang, lp->lang)) {
89 if (lpo)
90 lpo->next = lp->next;
91 else
92 langpriority = lp->next;
93 if (lp->lang)
94 nfree(lp->lang);
95 nfree(lp);
96 debug1("(language.c->del_lang) LANG: Language unloaded: %s", lang);
97 return 1;
98 }
99 lpo = lp;
100 lp = lp->next;
101 }
102 /* Language not found */
103 return 0;
104 }
105
add_message(int lidx,char * ltext)106 static int add_message(int lidx, char *ltext)
107 {
108 lang_tab *l = langtab[lidx & 63];
109
110 while (l) {
111 if (l->idx && (l->idx == lidx)) {
112 nfree(l->text);
113 l->text = nmalloc(strlen(ltext) + 1);
114 strcpy(l->text, ltext);
115 return 1;
116 }
117 if (!l->next)
118 break;
119 l = l->next;
120 }
121 if (l) {
122 l->next = nmalloc(sizeof(lang_tab));
123 l = l->next;
124 } else
125 l = langtab[lidx & 63] = nmalloc(sizeof(lang_tab));
126 l->idx = lidx;
127 l->text = nmalloc(strlen(ltext) + 1);
128 strcpy(l->text, ltext);
129 l->next = 0;
130 return 0;
131 }
132
133 /* Recheck all sections and check if any language files are available
134 * which match the preferred language(s) more closely
135 */
recheck_lang_sections(void)136 static void recheck_lang_sections(void)
137 {
138 lang_sec *ls;
139 char *langfile;
140
141 for (ls = langsection; ls && ls->section; ls = ls->next) {
142 langfile = get_langfile(ls);
143 /* Found a language with a more preferred language? */
144 if (langfile) {
145 read_lang(langfile);
146 nfree(langfile);
147 }
148 }
149 }
150
151 /* Parse a language file
152 */
read_lang(char * langfile)153 static void read_lang(char *langfile)
154 {
155 FILE *FLANG;
156 char lbuf[512];
157 char *ltext = NULL;
158 char *ctmp, *ctmp1;
159 int lidx;
160 int lline = 0;
161 int lskip;
162 int ltexts = 0;
163 int ladd = 0, lupdate = 0;
164
165 FLANG = fopen(langfile, "r");
166 if (FLANG == NULL) {
167 putlog(LOG_MISC, "*", "LANG: unexpected: reading from file %s failed.",
168 langfile);
169 return;
170 }
171
172 lskip = 0;
173 while (fgets(lbuf, 511, FLANG)) {
174 lline++;
175 if (lbuf[0] != '#' || lskip) {
176 ltext = nrealloc(ltext, 512);
177 if (sscanf(lbuf, "%s", ltext) != EOF) {
178 #ifdef LIBSAFE_HACKS
179 if (sscanf(lbuf, "0x%x,%500c", &lidx, ltext) != 1) {
180 #else
181 if (sscanf(lbuf, "0x%x,%500c", &lidx, ltext) != 2) {
182 #endif
183 putlog(LOG_MISC, "*", "Malformed text line in %s at %d.",
184 langfile, lline);
185 } else {
186 ltexts++;
187 ctmp = strchr(ltext, '\n');
188 *ctmp = 0;
189 while (ltext[strlen(ltext) - 1] == '\\') {
190 ltext[strlen(ltext) - 1] = 0;
191 if (fgets(lbuf, 511, FLANG)) {
192 lline++;
193 ctmp = strchr(lbuf, '\n');
194 *ctmp = 0;
195 ltext = nrealloc(ltext, strlen(lbuf) + strlen(ltext) + 1);
196 strcpy(strchr(ltext, 0), lbuf);
197 }
198 }
199 }
200 /* We gotta fix \n's here as, being arguments to sprintf(),
201 * they won't get translated.
202 */
203 ctmp = ltext;
204 ctmp1 = ltext;
205 while (*ctmp1) {
206 if ((*ctmp1 == '\\') && (*(ctmp1 + 1) == 'n')) {
207 *ctmp = '\n';
208 ctmp1++;
209 } else if ((*ctmp1 == '\\') && (*(ctmp1 + 1) == 't')) {
210 *ctmp = '\t';
211 ctmp1++;
212 } else
213 *ctmp = *ctmp1;
214 ctmp++;
215 ctmp1++;
216 }
217 *ctmp = '\0';
218 if (add_message(lidx, ltext)) {
219 lupdate++;
220 } else
221 ladd++;
222 }
223 } else {
224 ctmp = strchr(lbuf, '\n');
225 if (lskip && (strlen(lbuf) == 1 || *(ctmp - 1) != '\\'))
226 lskip = 0;
227 }
228 }
229 nfree(ltext);
230 fclose(FLANG);
231
232 debug3("(language.c->read_lang) LANG: %d messages of %d lines loaded from %s", ltexts, lline,
233 langfile);
234 debug2("(language.c->read_lang) LANG: %d adds, %d updates to message table", ladd, lupdate);
235 }
236
237 /* Returns 1 if the section exists, otherwise 0.
238 */
239 int exist_lang_section(char *section)
240 {
241 lang_sec *ls;
242
243 for (ls = langsection; ls; ls = ls->next)
244 if (!strcmp(section, ls->section))
245 return 1;
246 return 0;
247 }
248
249 /* Add a new language section. e.g. section "core"
250 * Load an apropriate language file for the specified section.
251 */
252 void add_lang_section(char *section)
253 {
254 char *langfile = NULL;
255 lang_sec *ls, *ols = NULL;
256 int ok = 0;
257
258 for (ls = langsection; ls; ols = ls, ls = ls->next)
259 /* Already know of that section? */
260 if (!strcmp(section, ls->section))
261 return;
262
263 /* Create new section entry */
264 ls = nmalloc(sizeof(lang_sec));
265 ls->section = nmalloc(strlen(section) + 1);
266 strcpy(ls->section, section);
267 ls->lang = NULL;
268 ls->next = NULL;
269
270 /* Connect to existing list of sections */
271 if (ols)
272 ols->next = ls;
273 else
274 langsection = ls;
275 debug1("(language.c->add_lang_section) LANG: Section loaded: %s", section);
276
277 /* Always load base language */
278 langfile = get_specific_langfile(BASELANG, ls);
279 if (langfile) {
280 read_lang(langfile);
281 nfree(langfile);
282 ok = 1;
283 }
284 /* Now overwrite base language with a more preferred one */
285 langfile = get_langfile(ls);
286 if (!langfile) {
287 if (!ok)
288 debug1("(language.c->add_lang_section) LANG: No lang files found for section %s.",
289 section);
290 return;
291 }
292 read_lang(langfile);
293 nfree(langfile);
294 }
295
296 int del_lang_section(char *section)
297 {
298 lang_sec *ls, *ols;
299
300 for (ls = langsection, ols = NULL; ls; ols = ls, ls = ls->next)
301 if (ls->section && !strcmp(ls->section, section)) {
302 if (ols)
303 ols->next = ls->next;
304 else
305 langsection = ls->next;
306 nfree(ls->section);
307 if (ls->lang)
308 nfree(ls->lang);
309 nfree(ls);
310 debug1("(language.c->del_lang_section) LANG: Section unloaded: %s", section);
311 return 1;
312 }
313 return 0;
314 }
315
316 static char *get_specific_langfile(char *language, lang_sec *sec)
317 {
318 char *ldir = getenv("EVANGELINE_LANGDIR");
319 char *langfile;
320
321 if (!ldir)
322 ldir = LANGDIR;
323 langfile = nmalloc(strlen(ldir) + strlen(sec->section) + strlen(language) +
324 8);
325 sprintf(langfile, "%s/%s.%s.lang", ldir, sec->section, language);
326
327 if (file_readable(langfile)) {
328 /* Save language used for this section */
329 sec->lang = nrealloc(sec->lang, strlen(language) + 1);
330 strcpy(sec->lang, language);
331 return langfile;
332 }
333
334 nfree(langfile);
335 return NULL;
336 }
337
338 /* Searches for available language files and returns the file with the
339 * most preferred language.
340 */
341 static char *get_langfile(lang_sec *sec)
342 {
343 char *langfile;
344 lang_pri *lp;
345
346 for (lp = langpriority; lp; lp = lp->next) {
347 /* There is no need to reload the same language */
348 if (sec->lang && !strcmp(sec->lang, lp->lang))
349 return NULL;
350 langfile = get_specific_langfile(lp->lang, sec);
351 if (langfile)
352 return langfile;
353 }
354 /* We did not find any files, clear the language field */
355 if (sec->lang)
356 nfree(sec->lang);
357 sec->lang = NULL;
358 return NULL;
359 }
360
361 /* Split up a string /path/<section>.<language>.lang into the
362 * needed information for the new language system.
363 * Only needed for compability functions.
364 */
365 static int split_lang(char *par, char **lang, char **section)
366 {
367 char *p;
368
369 p = strrchr(par, '/');
370 /* path attached? */
371 if (p)
372 *section = p + 1;
373 else
374 *section = par;
375 p = strchr(*section, '.');
376 if (p)
377 p[0] = 0;
378 else
379 return 0;
380 *lang = p + 1;
381 p = strstr(*lang, ".lang");
382 if (p)
383 p[0] = 0;
384 return 1;
385 }
386
387 /* Compability function to allow users/modules to use the old command.
388 */
389 int cmd_loadlanguage(struct userrec *u, int idx, char *par)
390 {
391 char *section, *lang, *buf;
392
393 dprintf(idx, "Note: This command is obsoleted by +lang.\n");
394 if (!par || !par[0]) {
395 dprintf(idx, "Usage: language <section>.<language>\n");
396 return 0;
397 }
398 if (idx != DP_LOG)
399 putlog(LOG_CMDS, "*", "#%s# language %s", dcc[idx].nick, par);
400 buf = nmalloc(strlen(par) + 1);
401 strcpy(buf, par);
402 if (!split_lang(buf, &lang, §ion)) {
403 nfree(buf);
404 dprintf(idx, "Invalid parameter %s.\n", par);
405 return 0;
406 }
407 add_lang(lang);
408 add_lang_section(section);
409 nfree(buf);
410 recheck_lang_sections();
411 return 0;
412 }
413
414 static int cmd_plslang(struct userrec *u, int idx, char *par)
415 {
416 if (!par || !par[0]) {
417 dprintf(idx, "Usage: +lang <language>\n");
418 return 0;
419 }
420 putlog(LOG_CMDS, "*", "#%s# +lang %s", dcc[idx].nick, par);
421 add_lang(par);
422 recheck_lang_sections();
423 return 0;
424 }
425
426 static int cmd_mnslang(struct userrec *u, int idx, char *par)
427 {
428 if (!par || !par[0]) {
429 dprintf(idx, "Usage: -lang <language>\n");
430 return 0;
431 }
432 putlog(LOG_CMDS, "*", "#%s# -lang %s", dcc[idx].nick, par);
433 if (!del_lang(par))
434 dprintf(idx, "Language %s not found.\n", par);
435 else
436 recheck_lang_sections();
437 return 0;
438 }
439
440 static int cmd_plslsec(struct userrec *u, int idx, char *par)
441 {
442 if (!par || !par[0]) {
443 dprintf(idx, "Usage: +lsec <section>\n");
444 return 0;
445 }
446 putlog(LOG_CMDS, "*", "#%s# +lsec %s", dcc[idx].nick, par);
447 add_lang_section(par);
448 return 0;
449 }
450
451 static int cmd_mnslsec(struct userrec *u, int idx, char *par)
452 {
453 if (!par || !par[0]) {
454 dprintf(idx, "Usage: -lsec <section>\n");
455 return 0;
456 }
457 putlog(LOG_CMDS, "*", "#%s# -lsec %s", dcc[idx].nick, par);
458 if (!del_lang_section(par))
459 dprintf(idx, "Section %s not found.\n", par);
460 return 0;
461 }
462
463 static int cmd_relang(struct userrec *u, int idx, char *par)
464 {
465 dprintf(idx, "Rechecking language sections...\n");
466 recheck_lang_sections();
467 return 0;
468 }
469
470 static int cmd_languagedump(struct userrec *u, int idx, char *par)
471 {
472 lang_tab *l;
473 char ltext2[512];
474 int idx2, i;
475
476 putlog(LOG_CMDS, "*", "#%s# ldump %s", dcc[idx].nick, par);
477 if (par[0]) {
478 /* atoi (hence strtol) don't work right here for hex */
479 if (strlen(par) > 2 && par[0] == '0' && par[1] == 'x')
480 sscanf(par, "%x", &idx2);
481 else
482 idx2 = (int) strtol(par, (char **) NULL, 10);
483 strcpy(ltext2, get_language(idx2));
484 dprintf(idx, "0x%x: %s\n", idx2, ltext2);
485 return 0;
486 }
487 dprintf(idx, " LANGIDX TEXT\n");
488 for (i = 0; i < 64; i++)
489 for (l = langtab[i]; l; l = l->next)
490 dprintf(idx, "0x%x %s\n", l->idx, l->text);
491 return 0;
492 }
493
494 static char text[512];
495 char *get_language(int idx)
496 {
497 lang_tab *l;
498
499 if (!idx)
500 return "MSG-0-";
501 for (l = langtab[idx & 63]; l; l = l->next)
502 if (idx == l->idx)
503 return l->text;
504 EvangelineSnprintf(text, sizeof text, "MSG%03X", idx);
505 return text;
506 }
507
508 int expmem_language()
509 {
510 lang_tab *l;
511 lang_sec *ls;
512 lang_pri *lp;
513 int i, size = 0;
514
515 for (i = 0; i < 64; i++)
516 for (l = langtab[i]; l; l = l->next) {
517 size += sizeof(lang_tab);
518 size += (strlen(l->text) + 1);
519 }
520 for (ls = langsection; ls; ls = ls->next) {
521 size += sizeof(lang_sec);
522 if (ls->section)
523 size += strlen(ls->section) + 1;
524 if (ls->lang)
525 size += strlen(ls->lang) + 1;
526 }
527 for (lp = langpriority; lp; lp = lp->next) {
528 size += sizeof(lang_pri);
529 if (lp->lang)
530 size += strlen(lp->lang) + 1;
531 }
532 return size;
533 }
534
535 /* A report on the module status - only for debugging purposes
536 */
537 static int cmd_languagestatus(struct userrec *u, int idx, char *par)
538 {
539 int ltexts = 0;
540 register int i, c, maxdepth = 0, used = 0, empty = 0;
541 lang_tab *l;
542 lang_sec *ls = langsection;
543 lang_pri *lp = langpriority;
544
545 putlog(LOG_CMDS, "*", "#%s# lstat %s", dcc[idx].nick, par);
546 for (i = 0; i < 64; i++) {
547 c = 0;
548 for (l = langtab[i]; l; l = l->next)
549 c++;
550 if (c > maxdepth)
551 maxdepth = c;
552 if (c)
553 used++;
554 else
555 empty++;
556 ltexts += c;
557 }
558 dprintf(idx, "Language code report:\n");
559 dprintf(idx, " Table size : %d bytes\n", expmem_language());
560 dprintf(idx, " Text messages: %d\n", ltexts);
561 dprintf(idx, " %d used, %d unused, maxdepth %d, avg %f\n",
562 used, empty, maxdepth, (float) ltexts / 64.0);
563 if (lp) {
564 int c = 0;
565
566 dprintf(idx, " Supported languages:");
567 for (; lp; lp = lp->next) {
568 dprintf(idx, "%s %s", c ? "," : "", lp->lang);
569 c = 1;
570 }
571 dprintf(idx, "\n");
572 }
573 if (ls) {
574 dprintf(idx, "\n SECTION LANG\n");
575 dprintf(idx, " ==============================\n");
576 for (; ls; ls = ls->next)
577 dprintf(idx, " %-20s %s\n", ls->section,
578 ls->lang ? ls->lang : "<none>");
579 }
580 return 0;
581 }
582
583 /* Compability function to allow scripts to use the old command.
584 */
585 static int tcl_language STDVAR
586 {
587 char *lang, *section, *buf;
588
589 putlog(LOG_MISC, "*", "The Tcl command 'language' is obsolete. Use "
590 "'addlang' instead.");
591 BADARGS(2, 2, " language");
592
593 buf = nmalloc(strlen(argv[1]) + 1);
594 strcpy(buf, argv[1]);
595
596 if (!split_lang(buf, &lang, §ion)) {
597 Tcl_AppendResult(irp, "Invalid parameter", NULL);
598 nfree(buf);
599 return TCL_ERROR;
600 }
601 add_lang(lang);
602
603 add_lang_section(section);
604 nfree(buf);
605 recheck_lang_sections();
606 return TCL_OK;
607 }
608
609 static int tcl_plslang STDVAR
610 {
611 BADARGS(2, 2, " language");
612
613 add_lang(argv[1]);
614 recheck_lang_sections();
615
616 return TCL_OK;
617 }
618
619 static int tcl_mnslang STDVAR
620 {
621 BADARGS(2, 2, " language");
622
623 if (!del_lang(argv[1])) {
624 Tcl_AppendResult(irp, "Language not found.", NULL);
625 return TCL_ERROR;
626 }
627 recheck_lang_sections();
628
629 return TCL_OK;
630 }
631
632 static int tcl_addlangsection STDVAR
633 {
634 BADARGS(2, 2, " section");
635
636 add_lang_section(argv[1]);
637 return TCL_OK;
638 }
639
640 static int tcl_dellangsection STDVAR
641 {
642 BADARGS(2, 2, " section");
643
644 if (!del_lang_section(argv[1])) {
645 Tcl_AppendResult(irp, "Section not found", NULL);
646 return TCL_ERROR;
647 }
648 return TCL_OK;
649 }
650
651 static int tcl_relang STDVAR
652 {
653 recheck_lang_sections();
654 return TCL_OK;
655 }
656
657 static cmd_t langdcc[] = {
658 {"language", "n", cmd_loadlanguage, NULL},
659 {"+lang", "n", cmd_plslang, NULL},
660 {"-lang", "n", cmd_mnslang, NULL},
661 {"+lsec", "n", cmd_plslsec, NULL},
662 {"-lsec", "n", cmd_mnslsec, NULL},
663 {"ldump", "n", cmd_languagedump, NULL},
664 {"lstat", "n", cmd_languagestatus, NULL},
665 {"relang", "n", cmd_relang, NULL},
666 {NULL, NULL, NULL, NULL}
667 };
668
669 static tcl_cmds langtcls[] = {
670 {"language", tcl_language},
671 {"addlang", tcl_plslang},
672 {"dellang", tcl_mnslang},
673 {"addlangsection", tcl_addlangsection},
674 {"dellangsection", tcl_dellangsection},
675 {"relang", tcl_relang},
676 {NULL, NULL}
677 };
678
679 void init_language(int flag)
680 {
681 int i;
682 char *deflang;
683
684 if (flag) {
685 for (i = 0; i < 32; i++)
686 langtab[i] = 0;
687 /* The default language is always BASELANG as language files are
688 * gauranteed to exist in that language.
689 */
690 add_lang(BASELANG);
691 /* Let the user choose a different, preferred language */
692 deflang = getenv("EVANGELINE_LANG");
693 if (deflang)
694 add_lang(deflang);
695 } else {
696 add_tcl_commands(langtcls);
697 add_builtins(H_dcc, langdcc);
698 }
699 }
700