language.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. /*
  2. * language.c -- handles:
  3. * language support code
  4. *
  5. */
  6. /*
  7. * DOES:
  8. * Nothing <- typical BB code :)
  9. *
  10. * ENVIRONMENT VARIABLES:
  11. * EGG_LANG - language to use (default: "english")
  12. * EGG_LANGDIR - directory with all lang files
  13. * (default: "./language")
  14. * WILL DO:
  15. * Upon loading:
  16. * o default loads section core, if possible.
  17. * Commands:
  18. * DCC .+lang <language>
  19. * DCC .-lang <language>
  20. * DCC .+lsec <section>
  21. * DCC .-lsec <section>
  22. * DCC .relang
  23. * DCC .ldump
  24. * DCC .lstat
  25. *
  26. * FILE FORMAT: language.lang
  27. * <textidx>,<text>
  28. * TEXT MESSAGE USAGE:
  29. * get_language(<textidx> [,<PARMS>])
  30. *
  31. * ADDING LANGUAGES:
  32. * o Copy an existing <section>.<oldlanguage>.lang to a
  33. * new .lang file and modify as needed.
  34. * Use %s or %d where necessary, for plug-in
  35. * insertions of parameters (see core.english.lang).
  36. * o Ensure <section>.<newlanguage>.lang is in the lang
  37. * directory.
  38. * o .+lang <newlanguage>
  39. * ADDING SECTIONS:
  40. * o Create a <newsection>.english.lang file.
  41. * o Add add_lang_section("<newsection>"); to your module
  42. * startup function.
  43. *
  44. */
  45. #include "main.h"
  46. extern struct dcc_t *dcc;
  47. typedef struct lang_st {
  48. struct lang_st *next;
  49. char *lang;
  50. char *section;
  51. } lang_sec;
  52. typedef struct lang_pr {
  53. struct lang_pr *next;
  54. char *lang;
  55. } lang_pri;
  56. typedef struct lang_t {
  57. int idx;
  58. char *text;
  59. struct lang_t *next;
  60. } lang_tab;
  61. static lang_tab *langtab[64];
  62. static lang_sec *langsection = NULL;
  63. static lang_pri *langpriority = NULL;
  64. static int add_message(int, char *);
  65. static void read_lang(char *);
  66. void add_lang_section(char *);
  67. int del_lang_section(char *);
  68. int exist_lang_section(char *);
  69. static char *get_specific_langfile(char *, lang_sec *);
  70. static char *get_langfile(lang_sec *);
  71. /* Add a new preferred language to the list of languages. Newly added
  72. * languages get the highest priority.
  73. */
  74. void add_lang(char *lang)
  75. {
  76. lang_pri *lp = langpriority, *lpo = NULL;
  77. while (lp) {
  78. /* The language already exists, moving to the beginning */
  79. if (!strcmp(lang, lp->lang)) {
  80. /* Already at the front? */
  81. if (!lpo)
  82. return;
  83. lpo->next = lp->next;
  84. lp->next = lpo;
  85. langpriority = lp;
  86. return;
  87. }
  88. lpo = lp;
  89. lp = lp->next;
  90. }
  91. /* No existing entry, create a new one */
  92. lp = nmalloc(sizeof(lang_pri));
  93. lp->lang = nmalloc(strlen(lang) + 1);
  94. strcpy(lp->lang, lang);
  95. lp->next = NULL;
  96. /* If we have other entries, point to the beginning of the old list */
  97. if (langpriority)
  98. lp->next = langpriority;
  99. langpriority = lp;
  100. debug1("LANG: Language loaded: %s", lang);
  101. }
  102. static int add_message(int lidx, char *ltext)
  103. {
  104. lang_tab *l = langtab[lidx & 63];
  105. while (l) {
  106. if (l->idx && (l->idx == lidx)) {
  107. nfree(l->text);
  108. l->text = nmalloc(strlen(ltext) + 1);
  109. strcpy(l->text, ltext);
  110. return 1;
  111. }
  112. if (!l->next)
  113. break;
  114. l = l->next;
  115. }
  116. if (l) {
  117. l->next = nmalloc(sizeof(lang_tab));
  118. l = l->next;
  119. } else
  120. l = langtab[lidx & 63] = nmalloc(sizeof(lang_tab));
  121. l->idx = lidx;
  122. l->text = nmalloc(strlen(ltext) + 1);
  123. strcpy(l->text, ltext);
  124. l->next = 0;
  125. return 0;
  126. }
  127. /* Parse a language file
  128. */
  129. static void read_lang(char *langfile)
  130. {
  131. FILE *FLANG;
  132. char lbuf[512];
  133. char *ltext = NULL;
  134. char *ctmp, *ctmp1;
  135. int lidx;
  136. int lline = 0;
  137. int lskip;
  138. int ltexts = 0;
  139. int ladd = 0, lupdate = 0;
  140. FLANG = fopen(langfile, "r");
  141. if (FLANG == NULL) {
  142. putlog(LOG_MISC, "*", "LANG: unexpected: reading from file %s failed.",
  143. langfile);
  144. return;
  145. }
  146. lskip = 0;
  147. while (fgets(lbuf, 511, FLANG)) {
  148. lline++;
  149. if (lbuf[0] != '#' || lskip) {
  150. ltext = nrealloc(ltext, 512);
  151. if (sscanf(lbuf, "%s", ltext) != EOF) {
  152. #ifdef LIBSAFE_HACKS
  153. if (sscanf(lbuf, "0x%x,%500c", &lidx, ltext) != 1) {
  154. #else
  155. if (sscanf(lbuf, "0x%x,%500c", &lidx, ltext) != 2) {
  156. #endif
  157. putlog(LOG_MISC, "*", "Malformed text line in %s at %d.",
  158. langfile, lline);
  159. } else {
  160. ltexts++;
  161. ctmp = strchr(ltext, '\n');
  162. *ctmp = 0;
  163. while (ltext[strlen(ltext) - 1] == '\\') {
  164. ltext[strlen(ltext) - 1] = 0;
  165. if (fgets(lbuf, 511, FLANG)) {
  166. lline++;
  167. ctmp = strchr(lbuf, '\n');
  168. *ctmp = 0;
  169. ltext = nrealloc(ltext, strlen(lbuf) + strlen(ltext) + 1);
  170. strcpy(strchr(ltext, 0), lbuf);
  171. }
  172. }
  173. }
  174. /* We gotta fix \n's here as, being arguments to sprintf(),
  175. * they won't get translated.
  176. */
  177. ctmp = ltext;
  178. ctmp1 = ltext;
  179. while (*ctmp1) {
  180. if ((*ctmp1 == '\\') && (*(ctmp1 + 1) == 'n')) {
  181. *ctmp = '\n';
  182. ctmp1++;
  183. } else if ((*ctmp1 == '\\') && (*(ctmp1 + 1) == 't')) {
  184. *ctmp = '\t';
  185. ctmp1++;
  186. } else
  187. *ctmp = *ctmp1;
  188. ctmp++;
  189. ctmp1++;
  190. }
  191. *ctmp = '\0';
  192. if (add_message(lidx, ltext)) {
  193. lupdate++;
  194. } else
  195. ladd++;
  196. }
  197. } else {
  198. ctmp = strchr(lbuf, '\n');
  199. if (lskip && (strlen(lbuf) == 1 || *(ctmp - 1) != '\\'))
  200. lskip = 0;
  201. }
  202. }
  203. nfree(ltext);
  204. fclose(FLANG);
  205. debug3("LANG: %d messages of %d lines loaded from %s", ltexts, lline,
  206. langfile);
  207. debug2("LANG: %d adds, %d updates to message table", ladd, lupdate);
  208. }
  209. /* Returns 1 if the section exists, otherwise 0.
  210. */
  211. int exist_lang_section(char *section)
  212. {
  213. lang_sec *ls;
  214. for (ls = langsection; ls; ls = ls->next)
  215. if (!strcmp(section, ls->section))
  216. return 1;
  217. return 0;
  218. }
  219. /* Add a new language section. e.g. section "core"
  220. * Load an apropriate language file for the specified section.
  221. */
  222. void add_lang_section(char *section)
  223. {
  224. char *langfile = NULL;
  225. lang_sec *ls, *ols = NULL;
  226. int ok = 0;
  227. for (ls = langsection; ls; ols = ls, ls = ls->next)
  228. /* Already know of that section? */
  229. if (!strcmp(section, ls->section))
  230. return;
  231. /* Create new section entry */
  232. ls = nmalloc(sizeof(lang_sec));
  233. ls->section = nmalloc(strlen(section) + 1);
  234. strcpy(ls->section, section);
  235. ls->lang = NULL;
  236. ls->next = NULL;
  237. /* Connect to existing list of sections */
  238. if (ols)
  239. ols->next = ls;
  240. else
  241. langsection = ls;
  242. debug1("LANG: Section loaded: %s", section);
  243. /* Always load base language */
  244. langfile = get_specific_langfile(BASELANG, ls);
  245. if (langfile) {
  246. read_lang(langfile);
  247. nfree(langfile);
  248. ok = 1;
  249. }
  250. /* Now overwrite base language with a more preferred one */
  251. langfile = get_langfile(ls);
  252. if (!langfile) {
  253. // if (!ok)
  254. // putlog(LOG_MISC, "*", "LANG: No lang files found for section %s.",section);
  255. return;
  256. }
  257. read_lang(langfile);
  258. nfree(langfile);
  259. }
  260. int del_lang_section(char *section)
  261. {
  262. lang_sec *ls, *ols;
  263. for (ls = langsection, ols = NULL; ls; ols = ls, ls = ls->next)
  264. if (ls->section && !strcmp(ls->section, section)) {
  265. if (ols)
  266. ols->next = ls->next;
  267. else
  268. langsection = ls->next;
  269. nfree(ls->section);
  270. if (ls->lang)
  271. nfree(ls->lang);
  272. nfree(ls);
  273. debug1("LANG: Section unloaded: %s", section);
  274. return 1;
  275. }
  276. return 0;
  277. }
  278. static char *get_specific_langfile(char *language, lang_sec *sec)
  279. {
  280. char *ldir = getenv("EGG_LANGDIR");
  281. char *langfile;
  282. FILE *sfile = NULL;
  283. if (!ldir)
  284. ldir = LANGDIR;
  285. langfile = nmalloc(strlen(ldir) + strlen(sec->section) + strlen(language)+8);
  286. sprintf(langfile, "%s/%s.%s.lang", ldir, sec->section, language);
  287. sfile = fopen(langfile, "r");
  288. if (sfile) {
  289. fclose(sfile);
  290. /* Save language used for this section */
  291. sec->lang = nrealloc(sec->lang, strlen(language) + 1);
  292. strcpy(sec->lang, language);
  293. return langfile;
  294. }
  295. nfree(langfile);
  296. return NULL;
  297. }
  298. /* Searches for available language files and returns the file with the
  299. * most preferred language.
  300. */
  301. static char *get_langfile(lang_sec *sec)
  302. {
  303. char *langfile;
  304. lang_pri *lp;
  305. for (lp = langpriority; lp; lp = lp->next) {
  306. /* There is no need to reload the same language */
  307. if (sec->lang && !strcmp(sec->lang, lp->lang))
  308. return NULL;
  309. langfile = get_specific_langfile(lp->lang, sec);
  310. if (langfile)
  311. return langfile;
  312. }
  313. /* We did not find any files, clear the language field */
  314. if (sec->lang)
  315. nfree(sec->lang);
  316. sec->lang = NULL;
  317. return NULL;
  318. }
  319. static char text[512];
  320. char *get_language(int idx)
  321. {
  322. lang_tab *l;
  323. if (!idx)
  324. return "MSG-0-";
  325. for (l = langtab[idx & 63]; l; l = l->next)
  326. if (idx == l->idx)
  327. return l->text;
  328. egg_snprintf(text, sizeof text, "MSG%03X", idx);
  329. return text;
  330. }
  331. int expmem_language()
  332. {
  333. lang_tab *l;
  334. lang_sec *ls;
  335. lang_pri *lp;
  336. int i, size = 0;
  337. for (i = 0; i < 64; i++)
  338. for (l = langtab[i]; l; l = l->next) {
  339. size += sizeof(lang_tab);
  340. size += (strlen(l->text) + 1);
  341. }
  342. for (ls = langsection; ls; ls = ls->next) {
  343. size += sizeof(lang_sec);
  344. if (ls->section)
  345. size += strlen(ls->section)+1;
  346. if (ls->lang)
  347. size += strlen(ls->lang)+1;
  348. }
  349. for (lp = langpriority; lp; lp = lp->next) {
  350. size += sizeof(lang_pri);
  351. if (lp->lang)
  352. size += strlen(lp->lang)+1;
  353. }
  354. return size;
  355. }
  356. void init_language(int flag)
  357. {
  358. int i;
  359. char *deflang;
  360. if (flag) {
  361. for (i = 0; i < 32; i++)
  362. langtab[i] = 0;
  363. /* The default language is always BASELANG as language files are
  364. * gauranteed to exist in that language.
  365. */
  366. add_lang(BASELANG);
  367. /* Let the user choose a different, preferred language */
  368. deflang = getenv("EGG_LANG");
  369. if (deflang)
  370. add_lang(deflang);
  371. add_lang_section("core");
  372. }
  373. }