1 /*
2 Copyright (c) 1991-1999 Thomas T. Wetmore IV
3 "The MIT license"
4 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7 */
8 /*===========================================================
9 * translat.c -- LifeLines character mapping functions
10 * Copyright(c) 1994 by T.T. Wetmore IV; all rights reserved
11 *=========================================================*/
12
13 #include <errno.h>
14 #include "llstdlib.h"
15 #include "btree.h"
16 #include "translat.h"
17 #include "xlat.h"
18 #include "codesets.h"
19 #include "gedcom.h"
20 #include "zstr.h"
21 #include "icvt.h"
22 #include "lloptions.h"
23 #include "gedcomi.h"
24 #include "arch.h"
25
26
27 /*********************************************
28 * global/exported variables
29 *********************************************/
30
31 STRING illegal_char = 0;
32
33 /*********************************************
34 * external/imported variables
35 *********************************************/
36
37 extern BTREE BTR;
38
39 /*********************************************
40 * local types
41 *********************************************/
42
43 /* legacy (embedded) translation table */
44 struct legacytt_s {
45 TRANTABLE tt;
46 BOOLEAN first; /* comes at start of translation ? */
47 };
48 /* a predefined conversion, such as editor-to-internal */
49 struct conversion_s {
50 INT trnum;
51 CNSTRING key;
52 CNSTRING name;
53 INT zon_src;
54 INT zon_dest;
55 STRING * src_codeset;
56 STRING * dest_codeset;
57 XLAT xlat;
58 };
59 /* a predefined codeset, such as editor */
60 struct zone_s {
61 INT znum;
62 CNSTRING name;
63 };
64
65 /*********************************************
66 * local enums & defines
67 *********************************************/
68
69 enum { ZON_X, ZON_INT, ZON_GUI, ZON_EDI, ZON_RPT, ZON_GED, NUM_ZONES };
70
71 /*********************************************
72 * local function prototypes
73 *********************************************/
74
75 /* alphabetical */
76 static void clear_legacy_tt(INT trnum);
77 static void clear_predefined_list(void);
78 static struct conversion_s * getconvert(INT trnum);
79 static BOOLEAN is_legacy_first(INT trnum);
80 static void local_init(void);
81
82
83 /*********************************************
84 * local variables
85 *********************************************/
86
87 struct zone_s zones[] = {
88 { ZON_X, "Invalid zone" }
89 , { ZON_INT, "Internal codeset" }
90 , { ZON_GUI, "Display codeset" }
91 , { ZON_EDI, "Editor codeset" }
92 , { ZON_RPT, "Report codeset" }
93 , { ZON_GED, "GEDCOM codeset" }
94 };
95
96 /* These must be in enumeration order up to NUM_TT_MAPS */
97 static struct conversion_s conversions[] = {
98 /* TRANSLATORS: Character set conversion from external editor to database internal */
99 { MEDIN, "MEDIN", N_("Editor to Internal"), ZON_EDI, ZON_INT, &editor_codeset_in, &int_codeset, 0 }
100 /* TRANSLATORS: Character set conversion from database internal to external editor */
101 , { MINED, "MINED", N_("Internal to Editor"), ZON_INT, ZON_EDI, &int_codeset, &editor_codeset_out, 0 }
102 , { MGDIN, "MGDIN", N_("GEDCOM to Internal"), ZON_GED, ZON_INT, &gedcom_codeset_in, &int_codeset, 0 }
103 , { MINGD, "MINGD", N_("Internal to GEDCOM"), ZON_INT, ZON_GED, &int_codeset, &gedcom_codeset_out, 0 }
104 , { MDSIN, "MDSIN", N_("Display to Internal"), ZON_GUI, ZON_INT, &gui_codeset_in, &int_codeset, 0 }
105 , { MINDS, "MINDS", N_("Internal to Display"), ZON_INT, ZON_GUI, &int_codeset, &gui_codeset_out, 0 }
106 , { MRPIN, "MRPIN", N_("Report to Internal"), ZON_RPT, ZON_INT, &report_codeset_in, &int_codeset, 0 }
107 , { MINRP, "MINRP", N_("Internal to Report"), ZON_INT, ZON_RPT, &int_codeset, &report_codeset_out, 0 }
108 /* These are all special-purpose translation tables, and maybe shouldn't even be here ? */
109 , { MSORT, "MSORT", "Custom Sort", ZON_X, ZON_X, 0, 0, 0 }
110 , { MCHAR, "MCHAR", "Custom Charset", ZON_X, ZON_X, 0, 0, 0 }
111 , { MLCAS, "MLCAS", "Custom Lowercase", ZON_X, ZON_X, 0, 0, 0 }
112 , { MUCAS, "MUCAS", "Custom Uppercase", ZON_X, ZON_X, 0, 0, 0 }
113 , { MPREF, "MPREF", "Custom Prefix", ZON_X, ZON_X, 0, 0, 0 }
114 };
115 static CNSTRING conversions_keys[] = {
116 /* TRANSLATORS: key for "Editor to Internal" on translation table menu
117 Omit everything up to and including final | */
118 N_("menu|trantable|e")
119 /* TRANSLATORS: key for "Internal to Editor" on translation table menu
120 Omit everything up to and including final | */
121 , N_("menu|trantable|m")
122 /* TRANSLATORS: key for "GEDCOM to Internal" on translation table menu
123 Omit everything up to and including final | */
124 , N_("menu|trantable|i")
125 /* TRANSLATORS: key for "Internal to GEDCOM" on translation table menu
126 Omit everything up to and including final | */
127 , N_("menu|trantable|x")
128 /* TRANSLATORS: key for "Display to Internal" on translation table menu
129 Omit everything up to and including final | */
130 , N_("menu|trantable|g")
131 /* TRANSLATORS: key for "Internal to Display" on translation table menu
132 Omit everything up to and including final | */
133 , N_("menu|trantable|d")
134 /* TRANSLATORS: key for "Report to Internal" on translation table menu
135 Omit everything up to and including final | */
136 , N_("menu|trantable|p")
137 /* TRANSLATORS: key for "Internal to Report" on translation table menu
138 Omit everything up to and including final | */
139 , N_("menu|trantable|r")
140 };
141 /* currently loaded legacy (embedded) translation tables */
142 static struct legacytt_s legacytts[NUM_TT_MAPS]; /* initialized once by transl_init() */
143 static BOOLEAN inited=FALSE;
144
145
146 /*********************************************
147 * local & exported function definitions
148 * body of module
149 *********************************************/
150
151 /*===================================================
152 * translate_catn -- Translate & concatenate string
153 *
154 * tt: translation table to use
155 * pdest: address of destination (will be advanced)
156 * src: source string
157 * len: address of space left in destination (will be decremented)
158 *=================================================*/
159 void
translate_catn(XLAT ttm,STRING * pdest,CNSTRING src,INT * len)160 translate_catn (XLAT ttm, STRING * pdest, CNSTRING src, INT * len)
161 {
162 INT added;
163 if (*len > 1)
164 translate_string(ttm, src, *pdest, *len);
165 else
166 (*pdest)[0] = 0; /* to be safe */
167 added = strlen(*pdest);
168 *len -= added;
169 *pdest += added;
170 }
171 /*===================================================
172 * translate_string_to_zstring -- Translate string via TRANTABLE
173 * xlat: [IN] translation to apply
174 * in: [IN] string to translate
175 * Created: 2001/07/19 (Perry Rapp)
176 * Copied from translate_string, except this version
177 * uses dynamic buffer, so it can expand if necessary
178 *=================================================*/
179 ZSTR
translate_string_to_zstring(XLAT xlat,CNSTRING in)180 translate_string_to_zstring (XLAT xlat, CNSTRING in)
181 {
182 ZSTR zstr = zs_news(in);
183 transl_xlat(xlat, zstr);
184 return zstr;
185 }
186 /*===================================================
187 * translate_string -- Translate string via XLAT
188 * ttm: [IN] tranmapping
189 * in: [IN] in string
190 * out: [OUT] string
191 * maxlen: [OUT] max len of out string
192 * Output string is limited to max length via use of
193 * add_char & add_string.
194 *=================================================*/
195 void
translate_string(XLAT ttm,CNSTRING in,STRING out,INT maxlen)196 translate_string (XLAT ttm, CNSTRING in, STRING out, INT maxlen)
197 {
198 ZSTR zstr=0;
199 if (!in || !in[0]) {
200 out[0] = 0;
201 return;
202 }
203 zstr = translate_string_to_zstring(ttm, in);
204 llstrsets(out, maxlen, uu8, zs_str(zstr));
205 zs_free(&zstr);
206 }
207 /*==========================================================
208 * translate_write -- Translate and output lines in a buffer
209 * tt: [in] translation table (may be NULL)
210 * in: [in] input string to write
211 * lenp: [in,out] #characters left in buffer (set to 0 if a full write)
212 * ofp: [in] output file
213 * last: [in] flag to write final line if no trailing \n
214 * Loops thru & prints out lines until end of string
215 * (or until last line if not terminated with \n)
216 * *lenp will be set to zero unless there is a final line
217 * not terminated by \n and caller didn't ask to write it anyway
218 * NB: If no translation table, entire string is always written
219 *========================================================*/
220 BOOLEAN
translate_write(XLAT ttm,STRING in,INT * lenp,FILE * ofp,BOOLEAN last)221 translate_write(XLAT ttm, STRING in, INT *lenp, FILE *ofp, BOOLEAN last)
222 {
223 char intmp[MAXLINELEN+2]="";
224 char out[MAXLINELEN+2]="";
225 char *tp=0;
226 STRING bp = in;
227 int i=0,j=0;
228
229 if(ttm == NULL) {
230 ASSERT(fwrite(in, *lenp, 1, ofp) == 1);
231 *lenp = 0;
232 return TRUE;
233 }
234
235 /* loop through lines one by one */
236 for(i = 0; i < *lenp; ) {
237 /* copy in to intmp, up to first \n or our buffer size-1 */
238 tp = intmp;
239 for(j = 0; (j <= MAXLINELEN) && (i < *lenp) && (*bp != '\n'); j++) {
240 i++;
241 *tp++ = *bp++;
242 }
243 *tp = '\0';
244 if(i < *lenp) {
245 /* partial, either a single line or a single buffer full */
246 if(*bp == '\n') {
247 /* single line, include the \n */
248 /* it is important that we limited size earlier so we
249 have room here to add one more character */
250 *tp++ = *bp++;
251 *tp = '\0';
252 i++;
253 }
254 }
255 else if(!last) {
256 /* the last line is not complete, return it in buffer */
257 strcpy(in, intmp);
258 *lenp = strlen(in);
259 return(TRUE);
260 }
261 /* translate & write out current line */
262 /* TODO (2002-11-28): modify to use dynamic string */
263 translate_string(ttm, intmp, out, MAXLINELEN+2);
264 if (ofp && strlen(out)) {
265 int outbytes = fwrite(out, 1, strlen(out), ofp);
266 if (!outbytes || ferror(ofp)) {
267 crashlog("outbytes=%d, errno=%d, outstr=%s"
268 , outbytes, errno, out);
269 FATAL();
270 }
271 }
272 }
273 *lenp = 0;
274 return(TRUE);
275 }
276 /*==========================================================
277 * get_xlat_to_int -- Get translation to internal codeset
278 * returns NULL if fails
279 * Created: 2002/11/28 (Perry Rapp)
280 *========================================================*/
281 XLAT
transl_get_xlat_to_int(CNSTRING codeset)282 transl_get_xlat_to_int (CNSTRING codeset)
283 {
284 BOOLEAN adhoc = TRUE;
285 return xl_get_xlat(codeset, int_codeset, adhoc);
286 }
287 /*==========================================================
288 * transl_get_xlat -- Get arbitrary translator
289 * returns NULL if fails
290 * Created: 2002/11/30 (Perry Rapp)
291 *========================================================*/
292 XLAT
transl_get_xlat(CNSTRING src,CNSTRING dest)293 transl_get_xlat (CNSTRING src, CNSTRING dest)
294 {
295 BOOLEAN adhoc = TRUE;
296 return xl_get_xlat(src, dest, adhoc);
297 }
298 /*==========================================================
299 * transl_load_all_tts -- Load internal list of available translation
300 * tables (based on *.tt files in TTPATH)
301 * Created: 2002/11/28 (Perry Rapp)
302 *========================================================*/
303 void
transl_load_all_tts(void)304 transl_load_all_tts (void)
305 {
306 CNSTRING ttpath = getlloptstr("TTPATH", ".");
307 if (!inited) local_init();
308 xl_load_all_dyntts(ttpath);
309 }
310 /*==========================================================
311 * transl_xlat -- Perform a translation on a string
312 * Created: 2002/11/28 (Perry Rapp)
313 *========================================================*/
314 void
transl_xlat(XLAT xlat,ZSTR zstr)315 transl_xlat (XLAT xlat, ZSTR zstr)
316 {
317 INT index = xl_get_uparam(xlat)-1;
318 struct legacytt_s * legtt = (index>=0 ? &legacytts[index] : NULL);
319 if (legtt && legtt->tt && legtt->first) {
320 custom_translatez(zstr, legtt->tt);
321 }
322
323 xl_do_xlat(xlat, zstr);
324
325 if (legtt && legtt->tt && !legtt->first) {
326 custom_translatez(zstr, legtt->tt);
327 }
328 }
329 /*==========================================================
330 * transl_init -- One-time initialization of this module
331 * Created: 2002/12/13 (Perry Rapp)
332 *========================================================*/
333 static void
local_init(void)334 local_init (void)
335 {
336 INT i;
337
338 ASSERT(NUM_TT_MAPS == ARRSIZE(conversions));
339 ASSERT(NUM_ZONES == ARRSIZE(zones));
340
341 for (i=0; i<NUM_TT_MAPS; ++i)
342 legacytts[i].tt = 0;
343 inited=TRUE;
344 }
345 /*==========================================================
346 * transl_load_xlats -- Load translations for all regular codesets
347 * (internal, GUI, ...)
348 * returns FALSE if needed conversions not available
349 * Created: 2002/11/28 (Perry Rapp)
350 *========================================================*/
351 void
transl_load_xlats(void)352 transl_load_xlats (void)
353 {
354 INT i;
355
356 if (!inited) local_init();
357
358 clear_predefined_list();
359
360 for (i=0; i<NUM_TT_MAPS; ++i) {
361 struct conversion_s * conv = getconvert(i);
362 STRING src, dest;
363 BOOLEAN adhoc = FALSE;
364 ASSERT(conv->trnum == i);
365 if (conv->src_codeset && int_codeset) {
366 ASSERT(conv->dest_codeset);
367 src = *conv->src_codeset;
368 dest = *conv->dest_codeset;
369 conv->xlat = xl_get_xlat(src, dest, adhoc);
370 } else {
371 /* even if codesets are unspecified, have to have a placeholder
372 in which to store any legacy translation tables */
373 conv->xlat = xl_get_null_xlat();
374 }
375 /* 2003-09-09, Perry
376 I just added this today quickly today to get legacy translations
377 working; this is kind of confusing and ought to be cleaned
378 up */
379 xl_set_uparam(conv->xlat, 0);
380 if (BTR) {
381 TRANTABLE tt=0;
382 if (init_map_from_rec(conv->key, i, &tt) && tt) {
383 transl_set_legacy_tt(i, tt);
384 }
385 xl_set_uparam(conv->xlat, i+1);
386 }
387 }
388 }
389 /*==========================================================
390 * is_legacy_first -- Should this legacy come before rest of translation ?
391 * This is to make legacy tts run in internal codeset
392 * Created: 2002/12/13 (Perry Rapp)
393 *========================================================*/
394 static BOOLEAN
is_legacy_first(INT trnum)395 is_legacy_first (INT trnum)
396 {
397 return (getconvert(trnum)->src_codeset == &int_codeset);
398 }
399 /*==========================================================
400 * clear_predefined_list -- Free cached regular conversions
401 * Created: 2002/11/28 (Perry Rapp)
402 *========================================================*/
403 static void
clear_predefined_list(void)404 clear_predefined_list (void)
405 {
406 INT i;
407 for (i=0; i<NUM_TT_MAPS; ++i) {
408 getconvert(i)->xlat = 0; /* pointer into xlat.c cache, so we don't free it */
409 clear_legacy_tt(i);
410 }
411 }
412 /*==========================================================
413 * transl_get_predefined_xlat -- Fetch a predefined translation
414 * eg, MEDIN (editor-to-internal)
415 * Created: 2002/11/28 (Perry Rapp)
416 *========================================================*/
417 XLAT
transl_get_predefined_xlat(INT trnum)418 transl_get_predefined_xlat (INT trnum)
419 {
420 return getconvert(trnum)->xlat;
421 }
422 /*==========================================================
423 * transl_get_predefined_name -- Fetch name of a predefined translation
424 * eg, transl_get_predefined_name(MEDIN) == "editor-to-internal"
425 * Created: 2002/12/13 (Perry Rapp)
426 *========================================================*/
427 ZSTR
transl_get_predefined_name(INT trnum)428 transl_get_predefined_name (INT trnum)
429 {
430 return zs_news(_(getconvert(trnum)->name));
431 }
432 /*==========================================================
433 * sgettext -- Version of gettext that strips out menu leaders
434 * (menu leaders are everything up to last |)
435 *========================================================*/
436 static const char *
sgettext(const char * msgid)437 sgettext (const char *msgid)
438 {
439 char *msgval = _(msgid);
440 if (msgval == msgid)
441 msgval = strrchr (msgid, '|') + 1;
442 return msgval;
443 }
444 /*==========================================================
445 * transl_get_predefined_menukey -- Menu key for predefined translation
446 * (localized)
447 *========================================================*/
448 ZSTR
transl_get_predefined_menukey(INT trnum)449 transl_get_predefined_menukey (INT trnum)
450 {
451 ASSERT(trnum>=0);
452 ASSERT(trnum<ARRSIZE(conversions_keys));
453 return zs_news(sgettext(conversions_keys[trnum]));
454 }
455 /*==========================================================
456 * transl_get_description -- Fetch description of a translation
457 * eg, "3 steps with iconv(UTF-8, CP1252)"
458 * Created: 2002/12/13 (Perry Rapp)
459 *========================================================*/
460 ZSTR
transl_get_description(XLAT xlat)461 transl_get_description (XLAT xlat)
462 {
463 ZSTR zstr = xlat_get_description(xlat);
464 INT index = xl_get_uparam(xlat)-1;
465 struct legacytt_s * legtt = (index>=0 ? &legacytts[index] : NULL);
466 if (legtt && legtt->tt) {
467 ZSTR zdesc = get_trantable_desc(legtt->tt);
468 /* TRANSLATORS: db internal translation table note for tt menu */
469 zs_appf(zstr, _(" (dbint tt: %s)"), zs_str(zdesc));
470 zs_free(&zdesc);
471 }
472
473 return zstr;
474 }
475 /*==========================================================
476 * transl_parse_codeset -- Parse out subcode suffixes of a codeset
477 * eg, "CP437//TrGreekAscii//TrCyrillicAscii"
478 * will recognize CP437 as the codeset name, and list
479 * "TrGreekAscii" and "TrCyrillicAscii" as subcodes
480 * Created: 2002/11/28 (Perry Rapp)
481 *========================================================*/
482 void
transl_parse_codeset(CNSTRING codeset,ZSTR zcsname,LIST * subcodes)483 transl_parse_codeset (CNSTRING codeset, ZSTR zcsname, LIST * subcodes)
484 {
485 xl_parse_codeset(codeset, zcsname, subcodes);
486 }
487 /*==========================================================
488 * transl_are_all_conversions_ok --
489 * return FALSE if there any conversions we couldn't figure out
490 * Created: 2002/11/28 (Perry Rapp)
491 *========================================================*/
492 BOOLEAN
transl_are_all_conversions_ok(void)493 transl_are_all_conversions_ok (void)
494 {
495 INT i;
496 for (i=0; i<NUM_TT_MAPS; ++i) {
497 if (conversions[i].src_codeset && !conversions[i].xlat)
498 return FALSE;
499 }
500 return TRUE;
501 }
502 /*==========================================================
503 * getconvert -- return conversion for trnum
504 * Simply a wrapper for ASSERT to check validity of trnum
505 * Created: 2002/12/13 (Perry Rapp)
506 *========================================================*/
507 static struct conversion_s *
getconvert(INT trnum)508 getconvert (INT trnum)
509 {
510 ASSERT(trnum>=0);
511 ASSERT(trnum<NUM_TT_MAPS);
512 return &conversions[trnum];
513 }
514 /*==========================================================
515 * transl_has_legacy_tt -- Is there a legacy (in-database)
516 * translation table for this entry ?
517 * Created: 2002/12/13 (Perry Rapp)
518 *========================================================*/
519 TRANTABLE
transl_get_legacy_tt(INT trnum)520 transl_get_legacy_tt (INT trnum)
521 {
522 getconvert(trnum); /* check validity of trnum */
523 return legacytts[trnum].tt;
524 }
525 /*==========================================================
526 * transl_has_legacy_tt -- Is there a legacy (in-database)
527 * translation table for this entry ?
528 * Created: 2002/12/13 (Perry Rapp)
529 *========================================================*/
530 void
transl_set_legacy_tt(INT trnum,TRANTABLE tt)531 transl_set_legacy_tt (INT trnum, TRANTABLE tt)
532 {
533 struct legacytt_s * leg;
534 clear_legacy_tt(trnum); /* ensures trnum validity */
535 getconvert(trnum); /* check validity of trnum */
536 leg = &legacytts[trnum];
537 leg->tt = tt;
538 leg->first = is_legacy_first(trnum);
539 }
540 /*==========================================================
541 * clear_legacy_tt -- Remove this legacy tt if loaded
542 * Created: 2002/12/13 (Perry Rapp)
543 *========================================================*/
544 static void
clear_legacy_tt(INT trnum)545 clear_legacy_tt (INT trnum)
546 {
547 struct legacytt_s * leg;
548 getconvert(trnum); /* check validity of trnum */
549 leg = &legacytts[trnum];
550 if (leg->tt) {
551 remove_trantable(leg->tt);
552 leg->tt = 0;
553 }
554 }
555 /*==========================================================
556 * transl_free_predefined_xlats -- Free all our predefined
557 * translations; this is called when a database is closed
558 * Created: 2002/12/13 (Perry Rapp)
559 *========================================================*/
560 void
transl_free_predefined_xlats(void)561 transl_free_predefined_xlats (void)
562 {
563 clear_predefined_list();
564 }
565 /*==========================================================
566 * transl_is_xlat_valid -- Does it do the job ?
567 * Created: 2002/12/15 (Perry Rapp)
568 *========================================================*/
569 BOOLEAN
transl_is_xlat_valid(XLAT xlat)570 transl_is_xlat_valid (XLAT xlat)
571 {
572 return xl_is_xlat_valid(xlat);
573 }
574 /*==========================================================
575 * transl_get_map_name -- get name of translation
576 * eg, "Editor to Internal"
577 * (localized)
578 *========================================================*/
579 CNSTRING
transl_get_map_name(INT trnum)580 transl_get_map_name (INT trnum)
581 {
582 ASSERT(trnum>=0);
583 ASSERT(trnum<NUM_TT_MAPS);
584 return _(getconvert(trnum)->name);
585 }
586 /*==========================================================
587 * transl_release_xlat -- Client finished with this
588 * Created: 2002/12/15 (Perry Rapp)
589 *========================================================*/
590 void
transl_release_xlat(XLAT xlat)591 transl_release_xlat (XLAT xlat)
592 {
593 xl_release_xlat(xlat);
594 }
595
596