/[cvs]/eggdrop1.9/src/language.c
ViewVC logotype

Contents of /eggdrop1.9/src/language.c

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.13 - (show annotations) (download) (as text)
Fri Jun 22 05:49:31 2001 UTC (18 years ago) by guppy
Branch: MAIN
Changes since 1.12: +5 -1 lines
File MIME type: text/x-chdr
I added my libsafe_hacks patch because I want to get a few people to test it tonight also, I have a patch I'm trying to hack into eggdrop

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

webmaster@eggheads.org
ViewVC Help
Powered by ViewVC 1.1.23