/src/gettext/gettext-tools/src/message.c
Line | Count | Source |
1 | | /* GNU gettext - internationalization aids |
2 | | Copyright (C) 1995-2026 Free Software Foundation, Inc. |
3 | | |
4 | | This program is free software: you can redistribute it and/or modify |
5 | | it under the terms of the GNU General Public License as published by |
6 | | the Free Software Foundation; either version 3 of the License, or |
7 | | (at your option) any later version. |
8 | | |
9 | | This program is distributed in the hope that it will be useful, |
10 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | GNU General Public License for more details. |
13 | | |
14 | | You should have received a copy of the GNU General Public License |
15 | | along with this program. If not, see <https://www.gnu.org/licenses/>. */ |
16 | | |
17 | | /* Written by Peter Miller, Ulrich Drepper, and Bruno Haible. */ |
18 | | |
19 | | #include <config.h> |
20 | | |
21 | | /* Specification. */ |
22 | | #include "message.h" |
23 | | |
24 | | #include <stdlib.h> |
25 | | #include <string.h> |
26 | | |
27 | | #include "fstrcmp.h" |
28 | | #include "mem-hash-map.h" |
29 | | #include "xalloc.h" |
30 | | #include "xmalloca.h" |
31 | | |
32 | | |
33 | | const char *const format_language[NFORMATS] = |
34 | | { |
35 | | /* format_c */ "c", |
36 | | /* format_objc */ "objc", |
37 | | /* format_cplusplus_brace */ "c++", |
38 | | /* format_python */ "python", |
39 | | /* format_python_brace */ "python-brace", |
40 | | /* format_java */ "java", |
41 | | /* format_java_printf */ "java-printf", |
42 | | /* format_csharp */ "csharp", |
43 | | /* format_javascript */ "javascript", |
44 | | /* format_scheme */ "scheme", |
45 | | /* format_lisp */ "lisp", |
46 | | /* format_elisp */ "elisp", |
47 | | /* format_librep */ "librep", |
48 | | /* format_rust */ "rust", |
49 | | /* format_go */ "go", |
50 | | /* format_ruby */ "ruby", |
51 | | /* format_sh */ "sh", |
52 | | /* format_sh_printf */ "sh-printf", |
53 | | /* format_awk */ "awk", |
54 | | /* format_lua */ "lua", |
55 | | /* format_pascal */ "object-pascal", |
56 | | /* format_modula2 */ "modula2", |
57 | | /* format_d */ "d", |
58 | | /* format_ocaml */ "ocaml", |
59 | | /* format_smalltalk */ "smalltalk", |
60 | | /* format_qt */ "qt", |
61 | | /* format_qt_plursl */ "qt-plural", |
62 | | /* format_kde */ "kde", |
63 | | /* format_kde_kuit */ "kde-kuit", |
64 | | /* format_boost */ "boost", |
65 | | /* format_tcl */ "tcl", |
66 | | /* format_perl */ "perl", |
67 | | /* format_perl_brace */ "perl-brace", |
68 | | /* format_php */ "php", |
69 | | /* format_gcc_internal */ "gcc-internal", |
70 | | /* format_gfc_internal */ "gfc-internal", |
71 | | /* format_ycp */ "ycp" |
72 | | }; |
73 | | |
74 | | const char *const format_language_pretty[NFORMATS] = |
75 | | { |
76 | | /* format_c */ "C", |
77 | | /* format_objc */ "Objective C", |
78 | | /* format_cplusplus_brace */ "C++", |
79 | | /* format_python */ "Python", |
80 | | /* format_python_brace */ "Python brace", |
81 | | /* format_java */ "Java MessageFormat", |
82 | | /* format_java_printf */ "Java printf", |
83 | | /* format_csharp */ "C#", |
84 | | /* format_javascript */ "JavaScript", |
85 | | /* format_scheme */ "Scheme", |
86 | | /* format_lisp */ "Lisp", |
87 | | /* format_elisp */ "Emacs Lisp", |
88 | | /* format_librep */ "librep", |
89 | | /* format_rust */ "Rust", |
90 | | /* format_go */ "Go", |
91 | | /* format_ruby */ "Ruby", |
92 | | /* format_sh */ "Shell", |
93 | | /* format_sh_printf */ "Shell printf", |
94 | | /* format_awk */ "awk", |
95 | | /* format_lua */ "Lua", |
96 | | /* format_pascal */ "Object Pascal", |
97 | | /* format_modula2 */ "Modula-2", |
98 | | /* format_d */ "D", |
99 | | /* format_ocaml */ "OCaml", |
100 | | /* format_smalltalk */ "Smalltalk", |
101 | | /* format_qt */ "Qt", |
102 | | /* format_qt_plural */ "Qt plural", |
103 | | /* format_kde */ "KDE", |
104 | | /* format_kde_kuit */ "KDE KUIT", |
105 | | /* format_boost */ "Boost", |
106 | | /* format_tcl */ "Tcl", |
107 | | /* format_perl */ "Perl", |
108 | | /* format_perl_brace */ "Perl brace", |
109 | | /* format_php */ "PHP", |
110 | | /* format_gcc_internal */ "GCC internal", |
111 | | /* format_gfc_internal */ "GFC internal", |
112 | | /* format_ycp */ "YCP" |
113 | | }; |
114 | | |
115 | | const char *const format_flag[NFORMATS] = |
116 | | { |
117 | | /* format_c */ "no-" "c" "-format", |
118 | | /* format_objc */ "no-" "objc" "-format", |
119 | | /* format_cplusplus_brace */ "no-" "c++" "-format", |
120 | | /* format_python */ "no-" "python" "-format", |
121 | | /* format_python_brace */ "no-" "python-brace" "-format", |
122 | | /* format_java */ "no-" "java" "-format", |
123 | | /* format_java_printf */ "no-" "java-printf" "-format", |
124 | | /* format_csharp */ "no-" "csharp" "-format", |
125 | | /* format_javascript */ "no-" "javascript" "-format", |
126 | | /* format_scheme */ "no-" "scheme" "-format", |
127 | | /* format_lisp */ "no-" "lisp" "-format", |
128 | | /* format_elisp */ "no-" "elisp" "-format", |
129 | | /* format_librep */ "no-" "librep" "-format", |
130 | | /* format_rust */ "no-" "rust" "-format", |
131 | | /* format_go */ "no-" "go" "-format", |
132 | | /* format_ruby */ "no-" "ruby" "-format", |
133 | | /* format_sh */ "no-" "sh" "-format", |
134 | | /* format_sh_printf */ "no-" "sh-printf" "-format", |
135 | | /* format_awk */ "no-" "awk" "-format", |
136 | | /* format_lua */ "no-" "lua" "-format", |
137 | | /* format_pascal */ "no-" "object-pascal" "-format", |
138 | | /* format_modula2 */ "no-" "modula2" "-format", |
139 | | /* format_d */ "no-" "d" "-format", |
140 | | /* format_ocaml */ "no-" "ocaml" "-format", |
141 | | /* format_smalltalk */ "no-" "smalltalk" "-format", |
142 | | /* format_qt */ "no-" "qt" "-format", |
143 | | /* format_qt_plursl */ "no-" "qt-plural" "-format", |
144 | | /* format_kde */ "no-" "kde" "-format", |
145 | | /* format_kde_kuit */ "no-" "kde-kuit" "-format", |
146 | | /* format_boost */ "no-" "boost" "-format", |
147 | | /* format_tcl */ "no-" "tcl" "-format", |
148 | | /* format_perl */ "no-" "perl" "-format", |
149 | | /* format_perl_brace */ "no-" "perl-brace" "-format", |
150 | | /* format_php */ "no-" "php" "-format", |
151 | | /* format_gcc_internal */ "no-" "gcc-internal" "-format", |
152 | | /* format_gfc_internal */ "no-" "gfc-internal" "-format", |
153 | | /* format_ycp */ "no-" "ycp" "-format" |
154 | | }; |
155 | | |
156 | | |
157 | | bool |
158 | | possible_format_p (enum is_format is_format) |
159 | 0 | { |
160 | 0 | return is_format == possible |
161 | 0 | || is_format == yes_according_to_context |
162 | 0 | || is_format == yes; |
163 | 0 | } |
164 | | |
165 | | |
166 | | bool |
167 | | not_format_p (enum is_format is_format) |
168 | 0 | { |
169 | 0 | return is_format == no; |
170 | 0 | } |
171 | | |
172 | | |
173 | | const char *const syntax_check_name[NSYNTAXCHECKS] = |
174 | | { |
175 | | /* sc_ellipsis_unicode */ "ellipsis-unicode", |
176 | | /* sc_space_ellipsis */ "space-ellipsis", |
177 | | /* sc_quote_unicode */ "quote-unicode", |
178 | | /* sc_bullet_unicode */ "bullet-unicode", |
179 | | /* sc_url */ "url", |
180 | | /* sc_email */ "email" |
181 | | }; |
182 | | |
183 | | |
184 | | message_ty * |
185 | | message_alloc (const char *msgctxt, |
186 | | const char *msgid, const char *msgid_plural, |
187 | | const char *msgstr, size_t msgstr_len, |
188 | | const lex_pos_ty *pp) |
189 | 28.3k | { |
190 | 28.3k | message_ty *mp = XMALLOC (message_ty); |
191 | 28.3k | mp->msgctxt = msgctxt; |
192 | 28.3k | mp->msgid = msgid; |
193 | 28.3k | mp->msgid_plural = (msgid_plural != NULL ? xstrdup (msgid_plural) : NULL); |
194 | 28.3k | mp->msgstr = msgstr; |
195 | 28.3k | mp->msgstr_len = msgstr_len; |
196 | 28.3k | mp->pos = *pp; |
197 | 28.3k | mp->comment = NULL; |
198 | 28.3k | mp->comment_dot = NULL; |
199 | 28.3k | mp->filepos_count = 0; |
200 | 28.3k | mp->filepos = NULL; |
201 | 28.3k | mp->is_fuzzy = false; |
202 | 1.07M | for (size_t i = 0; i < NFORMATS; i++) |
203 | 1.05M | mp->is_format[i] = undecided; |
204 | 28.3k | mp->range.min = -1; |
205 | 28.3k | mp->range.max = -1; |
206 | 28.3k | mp->do_wrap = undecided; |
207 | 198k | for (size_t i = 0; i < NSYNTAXCHECKS; i++) |
208 | 170k | mp->do_syntax_check[i] = undecided; |
209 | 28.3k | mp->prev_msgctxt = NULL; |
210 | 28.3k | mp->prev_msgid = NULL; |
211 | 28.3k | mp->prev_msgid_plural = NULL; |
212 | 28.3k | mp->used = 0; |
213 | 28.3k | mp->obsolete = false; |
214 | | |
215 | 28.3k | return mp; |
216 | 28.3k | } |
217 | | |
218 | | |
219 | | void |
220 | | message_free (message_ty *mp) |
221 | 28.3k | { |
222 | 28.3k | free ((char *) mp->msgid); |
223 | 28.3k | if (mp->msgid_plural != NULL) |
224 | 28.3k | free ((char *) mp->msgid_plural); |
225 | 28.3k | free ((char *) mp->msgstr); |
226 | 28.3k | if (mp->comment != NULL) |
227 | 5.20k | string_list_free (mp->comment); |
228 | 28.3k | if (mp->comment_dot != NULL) |
229 | 1.12k | string_list_free (mp->comment_dot); |
230 | 58.5k | for (size_t j = 0; j < mp->filepos_count; ++j) |
231 | 30.1k | free ((char *) mp->filepos[j].file_name); |
232 | 28.3k | if (mp->filepos != NULL) |
233 | 28.3k | free (mp->filepos); |
234 | 28.3k | if (mp->prev_msgctxt != NULL) |
235 | 28.3k | free ((char *) mp->prev_msgctxt); |
236 | 28.3k | if (mp->prev_msgid != NULL) |
237 | 28.3k | free ((char *) mp->prev_msgid); |
238 | 28.3k | if (mp->prev_msgid_plural != NULL) |
239 | 28.3k | free ((char *) mp->prev_msgid_plural); |
240 | 28.3k | free (mp); |
241 | 28.3k | } |
242 | | |
243 | | |
244 | | void |
245 | | message_comment_append (message_ty *mp, const char *s) |
246 | 514k | { |
247 | 514k | if (mp->comment == NULL) |
248 | 5.20k | mp->comment = string_list_alloc (); |
249 | 514k | string_list_append (mp->comment, s); |
250 | 514k | } |
251 | | |
252 | | |
253 | | void |
254 | | message_comment_dot_append (message_ty *mp, const char *s) |
255 | 11.3k | { |
256 | 11.3k | if (mp->comment_dot == NULL) |
257 | 1.12k | mp->comment_dot = string_list_alloc (); |
258 | 11.3k | string_list_append (mp->comment_dot, s); |
259 | 11.3k | } |
260 | | |
261 | | |
262 | | void |
263 | | message_comment_filepos (message_ty *mp, |
264 | | const char *file_name, size_t line_number) |
265 | 106k | { |
266 | | /* See if we have this position already. */ |
267 | 900k | for (size_t j = 0; j < mp->filepos_count; j++) |
268 | 870k | { |
269 | 870k | lex_pos_ty *pp = &mp->filepos[j]; |
270 | 870k | if (strcmp (pp->file_name, file_name) == 0 |
271 | 79.6k | && pp->line_number == line_number) |
272 | 76.3k | return; |
273 | 870k | } |
274 | | |
275 | | /* Extend the list so that we can add a position to it. */ |
276 | 30.1k | size_t nbytes = (mp->filepos_count + 1) * sizeof (mp->filepos[0]); |
277 | 30.1k | mp->filepos = xrealloc (mp->filepos, nbytes); |
278 | | |
279 | | /* Insert the position at the end. Don't sort the file positions here. */ |
280 | 30.1k | lex_pos_ty *pp = &mp->filepos[mp->filepos_count++]; |
281 | 30.1k | pp->file_name = xstrdup (file_name); |
282 | 30.1k | pp->line_number = line_number; |
283 | 30.1k | } |
284 | | |
285 | | |
286 | | message_ty * |
287 | | message_copy (message_ty *mp) |
288 | 0 | { |
289 | 0 | message_ty *result = |
290 | 0 | message_alloc (mp->msgctxt != NULL ? xstrdup (mp->msgctxt) : NULL, |
291 | 0 | xstrdup (mp->msgid), mp->msgid_plural, |
292 | 0 | mp->msgstr, mp->msgstr_len, &mp->pos); |
293 | |
|
294 | 0 | if (mp->comment) |
295 | 0 | { |
296 | 0 | for (size_t j = 0; j < mp->comment->nitems; ++j) |
297 | 0 | message_comment_append (result, mp->comment->item[j]); |
298 | 0 | } |
299 | 0 | if (mp->comment_dot) |
300 | 0 | { |
301 | 0 | for (size_t j = 0; j < mp->comment_dot->nitems; ++j) |
302 | 0 | message_comment_dot_append (result, mp->comment_dot->item[j]); |
303 | 0 | } |
304 | 0 | result->is_fuzzy = mp->is_fuzzy; |
305 | 0 | for (size_t i = 0; i < NFORMATS; i++) |
306 | 0 | result->is_format[i] = mp->is_format[i]; |
307 | 0 | result->range = mp->range; |
308 | 0 | result->do_wrap = mp->do_wrap; |
309 | 0 | for (size_t i = 0; i < NSYNTAXCHECKS; i++) |
310 | 0 | result->do_syntax_check[i] = mp->do_syntax_check[i]; |
311 | 0 | for (size_t j = 0; j < mp->filepos_count; ++j) |
312 | 0 | { |
313 | 0 | lex_pos_ty *pp = &mp->filepos[j]; |
314 | 0 | message_comment_filepos (result, pp->file_name, pp->line_number); |
315 | 0 | } |
316 | 0 | result->prev_msgctxt = |
317 | 0 | (mp->prev_msgctxt != NULL ? xstrdup (mp->prev_msgctxt) : NULL); |
318 | 0 | result->prev_msgid = |
319 | 0 | (mp->prev_msgid != NULL ? xstrdup (mp->prev_msgid) : NULL); |
320 | 0 | result->prev_msgid_plural = |
321 | 0 | (mp->prev_msgid_plural != NULL ? xstrdup (mp->prev_msgid_plural) : NULL); |
322 | 0 | return result; |
323 | 0 | } |
324 | | |
325 | | |
326 | | message_list_ty * |
327 | | message_list_alloc (bool use_hashtable) |
328 | 13.5k | { |
329 | 13.5k | message_list_ty *mlp = XMALLOC (message_list_ty); |
330 | 13.5k | mlp->nitems = 0; |
331 | 13.5k | mlp->nitems_max = 0; |
332 | 13.5k | mlp->item = NULL; |
333 | 13.5k | if ((mlp->use_hashtable = use_hashtable)) |
334 | 13.5k | hash_init (&mlp->htable, 10); |
335 | | |
336 | 13.5k | return mlp; |
337 | 13.5k | } |
338 | | |
339 | | |
340 | | void |
341 | | message_list_free (message_list_ty *mlp, int keep_messages) |
342 | 13.5k | { |
343 | 13.5k | if (keep_messages == 0) |
344 | 41.9k | for (size_t j = 0; j < mlp->nitems; ++j) |
345 | 28.3k | message_free (mlp->item[j]); |
346 | 13.5k | if (mlp->item) |
347 | 13.5k | free (mlp->item); |
348 | 13.5k | if (mlp->use_hashtable) |
349 | 13.5k | hash_destroy (&mlp->htable); |
350 | 13.5k | free (mlp); |
351 | 13.5k | } |
352 | | |
353 | | |
354 | | static int |
355 | | message_list_hash_insert_entry (hash_table *htable, message_ty *mp) |
356 | 28.3k | { |
357 | 28.3k | char *alloced_key; |
358 | 28.3k | const char *key; |
359 | 28.3k | size_t keylen; |
360 | 28.3k | if (mp->msgctxt != NULL) |
361 | 1.54k | { |
362 | | /* Concatenate mp->msgctxt and mp->msgid, to form the hash table key. */ |
363 | 1.54k | size_t msgctxt_len = strlen (mp->msgctxt); |
364 | 1.54k | size_t msgid_len = strlen (mp->msgid); |
365 | 1.54k | keylen = msgctxt_len + 1 + msgid_len + 1; |
366 | 1.54k | alloced_key = (char *) xmalloca (keylen); |
367 | 1.54k | memcpy (alloced_key, mp->msgctxt, msgctxt_len); |
368 | 1.54k | alloced_key[msgctxt_len] = MSGCTXT_SEPARATOR; |
369 | 1.54k | memcpy (alloced_key + msgctxt_len + 1, mp->msgid, msgid_len + 1); |
370 | 1.54k | key = alloced_key; |
371 | 1.54k | } |
372 | 26.8k | else |
373 | 26.8k | { |
374 | 26.8k | alloced_key = NULL; |
375 | 26.8k | key = mp->msgid; |
376 | 26.8k | keylen = strlen (mp->msgid) + 1; |
377 | 26.8k | } |
378 | | |
379 | 28.3k | int found = (hash_insert_entry (htable, key, keylen, mp) == NULL); |
380 | | |
381 | 28.3k | if (mp->msgctxt != NULL) |
382 | 1.54k | freea (alloced_key); |
383 | | |
384 | 28.3k | return found; |
385 | 28.3k | } |
386 | | |
387 | | |
388 | | void |
389 | | message_list_append (message_list_ty *mlp, message_ty *mp) |
390 | 28.3k | { |
391 | 28.3k | if (mlp->nitems >= mlp->nitems_max) |
392 | 8.45k | { |
393 | 8.45k | mlp->nitems_max = mlp->nitems_max * 2 + 4; |
394 | 8.45k | size_t nbytes = mlp->nitems_max * sizeof (message_ty *); |
395 | 8.45k | mlp->item = xrealloc (mlp->item, nbytes); |
396 | 8.45k | } |
397 | 28.3k | mlp->item[mlp->nitems++] = mp; |
398 | | |
399 | 28.3k | if (mlp->use_hashtable) |
400 | 28.3k | if (message_list_hash_insert_entry (&mlp->htable, mp)) |
401 | | /* A message list has duplicates, although it was allocated with the |
402 | | assertion that it wouldn't have duplicates. It is a bug. */ |
403 | 0 | abort (); |
404 | 28.3k | } |
405 | | |
406 | | |
407 | | void |
408 | | message_list_prepend (message_list_ty *mlp, message_ty *mp) |
409 | 0 | { |
410 | 0 | if (mlp->nitems >= mlp->nitems_max) |
411 | 0 | { |
412 | 0 | mlp->nitems_max = mlp->nitems_max * 2 + 4; |
413 | 0 | size_t nbytes = mlp->nitems_max * sizeof (message_ty *); |
414 | 0 | mlp->item = xrealloc (mlp->item, nbytes); |
415 | 0 | } |
416 | 0 | for (size_t j = mlp->nitems; j > 0; j--) |
417 | 0 | mlp->item[j] = mlp->item[j - 1]; |
418 | 0 | mlp->item[0] = mp; |
419 | 0 | mlp->nitems++; |
420 | |
|
421 | 0 | if (mlp->use_hashtable) |
422 | 0 | if (message_list_hash_insert_entry (&mlp->htable, mp)) |
423 | | /* A message list has duplicates, although it was allocated with the |
424 | | assertion that it wouldn't have duplicates. It is a bug. */ |
425 | 0 | abort (); |
426 | 0 | } |
427 | | |
428 | | |
429 | | void |
430 | | message_list_insert_at (message_list_ty *mlp, size_t n, message_ty *mp) |
431 | 0 | { |
432 | 0 | if (mlp->nitems >= mlp->nitems_max) |
433 | 0 | { |
434 | 0 | mlp->nitems_max = mlp->nitems_max * 2 + 4; |
435 | 0 | size_t nbytes = mlp->nitems_max * sizeof (message_ty *); |
436 | 0 | mlp->item = xrealloc (mlp->item, nbytes); |
437 | 0 | } |
438 | 0 | { |
439 | 0 | size_t j; |
440 | 0 | for (j = mlp->nitems; j > n; j--) |
441 | 0 | mlp->item[j] = mlp->item[j - 1]; |
442 | 0 | mlp->item[j] = mp; |
443 | 0 | } |
444 | 0 | mlp->nitems++; |
445 | |
|
446 | 0 | if (mlp->use_hashtable) |
447 | 0 | if (message_list_hash_insert_entry (&mlp->htable, mp)) |
448 | | /* A message list has duplicates, although it was allocated with the |
449 | | assertion that it wouldn't have duplicates. It is a bug. */ |
450 | 0 | abort (); |
451 | 0 | } |
452 | | |
453 | | |
454 | | #if 0 /* unused */ |
455 | | void |
456 | | message_list_delete_nth (message_list_ty *mlp, size_t n) |
457 | | { |
458 | | if (n >= mlp->nitems) |
459 | | return; |
460 | | message_free (mlp->item[n]); |
461 | | for (size_t j = n + 1; j < mlp->nitems; ++j) |
462 | | mlp->item[j - 1] = mlp->item[j]; |
463 | | mlp->nitems--; |
464 | | |
465 | | if (mlp->use_hashtable) |
466 | | { |
467 | | /* Our simple-minded hash tables don't support removal. */ |
468 | | hash_destroy (&mlp->htable); |
469 | | mlp->use_hashtable = false; |
470 | | } |
471 | | } |
472 | | #endif |
473 | | |
474 | | |
475 | | void |
476 | | message_list_remove_if_not (message_list_ty *mlp, |
477 | | message_predicate_ty *predicate) |
478 | 0 | { |
479 | 0 | size_t i = 0; |
480 | 0 | for (size_t j = 0; j < mlp->nitems; j++) |
481 | 0 | if (predicate (mlp->item[j])) |
482 | 0 | mlp->item[i++] = mlp->item[j]; |
483 | 0 | if (mlp->use_hashtable && i < mlp->nitems) |
484 | 0 | { |
485 | | /* Our simple-minded hash tables don't support removal. */ |
486 | 0 | hash_destroy (&mlp->htable); |
487 | 0 | mlp->use_hashtable = false; |
488 | 0 | } |
489 | 0 | mlp->nitems = i; |
490 | 0 | } |
491 | | |
492 | | |
493 | | bool |
494 | | message_list_msgids_changed (message_list_ty *mlp) |
495 | 0 | { |
496 | 0 | if (mlp->use_hashtable) |
497 | 0 | { |
498 | 0 | unsigned long int size = mlp->htable.size; |
499 | |
|
500 | 0 | hash_destroy (&mlp->htable); |
501 | 0 | hash_init (&mlp->htable, size); |
502 | |
|
503 | 0 | for (size_t j = 0; j < mlp->nitems; j++) |
504 | 0 | { |
505 | 0 | message_ty *mp = mlp->item[j]; |
506 | |
|
507 | 0 | if (message_list_hash_insert_entry (&mlp->htable, mp)) |
508 | | /* A message list has duplicates, although it was allocated with |
509 | | the assertion that it wouldn't have duplicates, and before the |
510 | | msgids changed it indeed didn't have duplicates. */ |
511 | 0 | { |
512 | 0 | hash_destroy (&mlp->htable); |
513 | 0 | mlp->use_hashtable = false; |
514 | 0 | return true; |
515 | 0 | } |
516 | 0 | } |
517 | 0 | } |
518 | 0 | return false; |
519 | 0 | } |
520 | | |
521 | | |
522 | | message_list_ty * |
523 | | message_list_copy (message_list_ty *mlp, int copy_level) |
524 | 0 | { |
525 | 0 | message_list_ty *result = message_list_alloc (mlp->use_hashtable); |
526 | |
|
527 | 0 | for (size_t j = 0; j < mlp->nitems; j++) |
528 | 0 | { |
529 | 0 | message_ty *mp = mlp->item[j]; |
530 | |
|
531 | 0 | message_list_append (result, copy_level ? mp : message_copy (mp)); |
532 | 0 | } |
533 | |
|
534 | 0 | return result; |
535 | 0 | } |
536 | | |
537 | | |
538 | | message_ty * |
539 | | message_list_search (const message_list_ty *mlp, |
540 | | const char *msgctxt, const char *msgid) |
541 | 76.7k | { |
542 | 76.7k | if (mlp->use_hashtable) |
543 | 76.7k | { |
544 | 76.7k | char *alloced_key; |
545 | 76.7k | const char *key; |
546 | 76.7k | size_t keylen; |
547 | 76.7k | if (msgctxt != NULL) |
548 | 3.69k | { |
549 | | /* Concatenate the msgctxt and msgid, to form the hash table key. */ |
550 | 3.69k | size_t msgctxt_len = strlen (msgctxt); |
551 | 3.69k | size_t msgid_len = strlen (msgid); |
552 | 3.69k | keylen = msgctxt_len + 1 + msgid_len + 1; |
553 | 3.69k | alloced_key = (char *) xmalloca (keylen); |
554 | 3.69k | memcpy (alloced_key, msgctxt, msgctxt_len); |
555 | 3.69k | alloced_key[msgctxt_len] = MSGCTXT_SEPARATOR; |
556 | 3.69k | memcpy (alloced_key + msgctxt_len + 1, msgid, msgid_len + 1); |
557 | 3.69k | key = alloced_key; |
558 | 3.69k | } |
559 | 73.0k | else |
560 | 73.0k | { |
561 | 73.0k | alloced_key = NULL; |
562 | 73.0k | key = msgid; |
563 | 73.0k | keylen = strlen (msgid) + 1; |
564 | 73.0k | } |
565 | | |
566 | 76.7k | void *htable_value; |
567 | 76.7k | int found = !hash_find_entry (&mlp->htable, key, keylen, &htable_value); |
568 | | |
569 | 76.7k | if (msgctxt != NULL) |
570 | 3.69k | freea (alloced_key); |
571 | | |
572 | 76.7k | if (found) |
573 | 48.3k | return (message_ty *) htable_value; |
574 | 28.3k | else |
575 | 28.3k | return NULL; |
576 | 76.7k | } |
577 | 0 | else |
578 | 0 | { |
579 | 0 | for (size_t j = 0; j < mlp->nitems; ++j) |
580 | 0 | { |
581 | 0 | message_ty *mp = mlp->item[j]; |
582 | |
|
583 | 0 | if ((msgctxt != NULL |
584 | 0 | ? mp->msgctxt != NULL && strcmp (msgctxt, mp->msgctxt) == 0 |
585 | 0 | : mp->msgctxt == NULL) |
586 | 0 | && strcmp (msgid, mp->msgid) == 0) |
587 | 0 | return mp; |
588 | 0 | } |
589 | 0 | return NULL; |
590 | 0 | } |
591 | 76.7k | } |
592 | | |
593 | | |
594 | | double |
595 | | fuzzy_search_goal_function (const message_ty *mp, |
596 | | const char *msgctxt, const char *msgid, |
597 | | double lower_bound) |
598 | 0 | { |
599 | 0 | double bonus = 0.0; |
600 | | /* A translation for a context is a good proposal also for another. But |
601 | | give mp a small advantage if mp is valid regardless of any context or |
602 | | has the same context as the one being looked up. */ |
603 | 0 | if (mp->msgctxt == NULL |
604 | 0 | || (msgctxt != NULL && strcmp (msgctxt, mp->msgctxt) == 0)) |
605 | 0 | { |
606 | 0 | bonus = 0.00001; |
607 | | /* Since we will consider (weight + bonus) at the end, we are only |
608 | | interested in weights that are >= lower_bound - bonus. Subtract |
609 | | a little more than the bonus, in order to avoid trouble due to |
610 | | rounding errors. */ |
611 | 0 | lower_bound -= bonus * 1.01; |
612 | 0 | } |
613 | | |
614 | | /* The use of 'volatile' guarantees that excess precision bits are dropped |
615 | | before the addition and before the following comparison at the caller's |
616 | | site. It is necessary on x86 systems where double-floats are not IEEE |
617 | | compliant by default, to avoid that msgmerge results become platform and |
618 | | compiler option dependent. 'volatile' is a portable alternative to |
619 | | gcc's -ffloat-store option. */ |
620 | 0 | volatile double weight = fstrcmp_bounded (msgid, mp->msgid, lower_bound); |
621 | |
|
622 | 0 | weight += bonus; |
623 | |
|
624 | 0 | return weight; |
625 | 0 | } |
626 | | |
627 | | |
628 | | static message_ty * |
629 | | message_list_search_fuzzy_inner (message_list_ty *mlp, |
630 | | const char *msgctxt, const char *msgid, |
631 | | double *best_weight_p) |
632 | 0 | { |
633 | 0 | message_ty *best_mp = NULL; |
634 | |
|
635 | 0 | for (size_t j = 0; j < mlp->nitems; ++j) |
636 | 0 | { |
637 | 0 | message_ty *mp = mlp->item[j]; |
638 | |
|
639 | 0 | if (mp->msgstr != NULL && mp->msgstr[0] != '\0') |
640 | 0 | { |
641 | 0 | double weight = |
642 | 0 | fuzzy_search_goal_function (mp, msgctxt, msgid, *best_weight_p); |
643 | 0 | if (weight > *best_weight_p) |
644 | 0 | { |
645 | 0 | *best_weight_p = weight; |
646 | 0 | best_mp = mp; |
647 | 0 | } |
648 | 0 | } |
649 | 0 | } |
650 | |
|
651 | 0 | return best_mp; |
652 | 0 | } |
653 | | |
654 | | |
655 | | message_ty * |
656 | | message_list_search_fuzzy (message_list_ty *mlp, |
657 | | const char *msgctxt, const char *msgid) |
658 | 0 | { |
659 | 0 | double best_weight = FUZZY_THRESHOLD; |
660 | 0 | return message_list_search_fuzzy_inner (mlp, msgctxt, msgid, &best_weight); |
661 | 0 | } |
662 | | |
663 | | |
664 | | message_list_list_ty * |
665 | | message_list_list_alloc () |
666 | 0 | { |
667 | 0 | message_list_list_ty *mllp = XMALLOC (message_list_list_ty); |
668 | 0 | mllp->nitems = 0; |
669 | 0 | mllp->nitems_max = 0; |
670 | 0 | mllp->item = NULL; |
671 | |
|
672 | 0 | return mllp; |
673 | 0 | } |
674 | | |
675 | | |
676 | | void |
677 | | message_list_list_free (message_list_list_ty *mllp, int keep_level) |
678 | 0 | { |
679 | 0 | if (keep_level < 2) |
680 | 0 | for (size_t j = 0; j < mllp->nitems; ++j) |
681 | 0 | message_list_free (mllp->item[j], keep_level); |
682 | 0 | if (mllp->item) |
683 | 0 | free (mllp->item); |
684 | 0 | free (mllp); |
685 | 0 | } |
686 | | |
687 | | |
688 | | void |
689 | | message_list_list_append (message_list_list_ty *mllp, message_list_ty *mlp) |
690 | 0 | { |
691 | 0 | if (mllp->nitems >= mllp->nitems_max) |
692 | 0 | { |
693 | 0 | mllp->nitems_max = mllp->nitems_max * 2 + 4; |
694 | 0 | size_t nbytes = mllp->nitems_max * sizeof (message_list_ty *); |
695 | 0 | mllp->item = xrealloc (mllp->item, nbytes); |
696 | 0 | } |
697 | 0 | mllp->item[mllp->nitems++] = mlp; |
698 | 0 | } |
699 | | |
700 | | |
701 | | void |
702 | | message_list_list_append_list (message_list_list_ty *mllp, |
703 | | message_list_list_ty *mllp2) |
704 | 0 | { |
705 | 0 | for (size_t j = 0; j < mllp2->nitems; ++j) |
706 | 0 | message_list_list_append (mllp, mllp2->item[j]); |
707 | 0 | } |
708 | | |
709 | | |
710 | | message_ty * |
711 | | message_list_list_search (message_list_list_ty *mllp, |
712 | | const char *msgctxt, const char *msgid) |
713 | 0 | { |
714 | 0 | message_ty *best_mp = NULL; |
715 | 0 | int best_weight = 0; /* 0: not found, 1: found without msgstr, 2: translated */ |
716 | 0 | for (size_t j = 0; j < mllp->nitems; ++j) |
717 | 0 | { |
718 | 0 | message_list_ty *mlp = mllp->item[j]; |
719 | 0 | message_ty *mp = message_list_search (mlp, msgctxt, msgid); |
720 | 0 | if (mp) |
721 | 0 | { |
722 | 0 | int weight = (mp->msgstr_len == 1 && mp->msgstr[0] == '\0' ? 1 : 2); |
723 | 0 | if (weight > best_weight) |
724 | 0 | { |
725 | 0 | best_mp = mp; |
726 | 0 | best_weight = weight; |
727 | 0 | } |
728 | 0 | } |
729 | 0 | } |
730 | 0 | return best_mp; |
731 | 0 | } |
732 | | |
733 | | |
734 | | #if 0 /* unused */ |
735 | | message_ty * |
736 | | message_list_list_search_fuzzy (message_list_list_ty *mllp, |
737 | | const char *msgctxt, const char *msgid) |
738 | | { |
739 | | double best_weight = FUZZY_THRESHOLD; |
740 | | message_ty *best_mp = NULL; |
741 | | for (size_t j = 0; j < mllp->nitems; ++j) |
742 | | { |
743 | | message_list_ty *mlp = mllp->item[j]; |
744 | | message_ty *mp = |
745 | | message_list_search_fuzzy_inner (mlp, msgctxt, msgid, &best_weight); |
746 | | if (mp) |
747 | | best_mp = mp; |
748 | | } |
749 | | return best_mp; |
750 | | } |
751 | | #endif |
752 | | |
753 | | |
754 | | msgdomain_ty* |
755 | | msgdomain_alloc (const char *domain, bool use_hashtable) |
756 | 13.5k | { |
757 | 13.5k | msgdomain_ty *mdp = XMALLOC (msgdomain_ty); |
758 | 13.5k | mdp->domain = domain; |
759 | 13.5k | mdp->messages = message_list_alloc (use_hashtable); |
760 | | |
761 | 13.5k | return mdp; |
762 | 13.5k | } |
763 | | |
764 | | |
765 | | void |
766 | | msgdomain_free (msgdomain_ty *mdp) |
767 | 13.5k | { |
768 | 13.5k | message_list_free (mdp->messages, 0); |
769 | 13.5k | free (mdp); |
770 | 13.5k | } |
771 | | |
772 | | |
773 | | msgdomain_list_ty * |
774 | | msgdomain_list_alloc (bool use_hashtable) |
775 | 11.9k | { |
776 | 11.9k | msgdomain_list_ty *mdlp = XMALLOC (msgdomain_list_ty); |
777 | | /* Put the default domain first, so that when we output it, |
778 | | we can omit the 'domain' directive. */ |
779 | 11.9k | mdlp->nitems = 1; |
780 | 11.9k | mdlp->nitems_max = 1; |
781 | 11.9k | mdlp->item = XNMALLOC (mdlp->nitems_max, msgdomain_ty *); |
782 | 11.9k | mdlp->item[0] = msgdomain_alloc (MESSAGE_DOMAIN_DEFAULT, use_hashtable); |
783 | 11.9k | mdlp->use_hashtable = use_hashtable; |
784 | 11.9k | mdlp->encoding = NULL; |
785 | | |
786 | 11.9k | return mdlp; |
787 | 11.9k | } |
788 | | |
789 | | |
790 | | void |
791 | | msgdomain_list_free (msgdomain_list_ty *mdlp) |
792 | 11.9k | { |
793 | 25.5k | for (size_t j = 0; j < mdlp->nitems; ++j) |
794 | 13.5k | msgdomain_free (mdlp->item[j]); |
795 | 11.9k | if (mdlp->item) |
796 | 11.9k | free (mdlp->item); |
797 | 11.9k | free (mdlp); |
798 | 11.9k | } |
799 | | |
800 | | |
801 | | void |
802 | | msgdomain_list_append (msgdomain_list_ty *mdlp, msgdomain_ty *mdp) |
803 | 1.53k | { |
804 | 1.53k | if (mdlp->nitems >= mdlp->nitems_max) |
805 | 685 | { |
806 | 685 | mdlp->nitems_max = mdlp->nitems_max * 2 + 4; |
807 | 685 | size_t nbytes = mdlp->nitems_max * sizeof (msgdomain_ty *); |
808 | 685 | mdlp->item = xrealloc (mdlp->item, nbytes); |
809 | 685 | } |
810 | 1.53k | mdlp->item[mdlp->nitems++] = mdp; |
811 | 1.53k | } |
812 | | |
813 | | |
814 | | #if 0 /* unused */ |
815 | | void |
816 | | msgdomain_list_append_list (msgdomain_list_ty *mdlp, msgdomain_list_ty *mdlp2) |
817 | | { |
818 | | for (size_t j = 0; j < mdlp2->nitems; ++j) |
819 | | msgdomain_list_append (mdlp, mdlp2->item[j]); |
820 | | } |
821 | | #endif |
822 | | |
823 | | |
824 | | message_list_ty * |
825 | | msgdomain_list_sublist (msgdomain_list_ty *mdlp, const char *domain, |
826 | | bool create) |
827 | 88.7k | { |
828 | 155k | for (size_t j = 0; j < mdlp->nitems; j++) |
829 | 154k | if (strcmp (mdlp->item[j]->domain, domain) == 0) |
830 | 87.1k | return mdlp->item[j]->messages; |
831 | | |
832 | 1.53k | if (create) |
833 | 1.53k | { |
834 | 1.53k | msgdomain_ty *mdp = msgdomain_alloc (domain, mdlp->use_hashtable); |
835 | 1.53k | msgdomain_list_append (mdlp, mdp); |
836 | 1.53k | return mdp->messages; |
837 | 1.53k | } |
838 | 0 | else |
839 | 0 | return NULL; |
840 | 1.53k | } |
841 | | |
842 | | |
843 | | /* Copy a message domain list. |
844 | | If copy_level = 0, also copy the messages. If copy_level = 1, share the |
845 | | messages but copy the domains. If copy_level = 2, share the domains. */ |
846 | | msgdomain_list_ty * |
847 | | msgdomain_list_copy (msgdomain_list_ty *mdlp, int copy_level) |
848 | 0 | { |
849 | 0 | msgdomain_list_ty *result = XMALLOC (msgdomain_list_ty); |
850 | 0 | result->nitems = 0; |
851 | 0 | result->nitems_max = 0; |
852 | 0 | result->item = NULL; |
853 | 0 | result->use_hashtable = mdlp->use_hashtable; |
854 | 0 | result->encoding = mdlp->encoding; |
855 | |
|
856 | 0 | for (size_t j = 0; j < mdlp->nitems; j++) |
857 | 0 | { |
858 | 0 | msgdomain_ty *mdp = mdlp->item[j]; |
859 | |
|
860 | 0 | if (copy_level < 2) |
861 | 0 | { |
862 | 0 | msgdomain_ty *result_mdp = XMALLOC (msgdomain_ty); |
863 | 0 | result_mdp->domain = mdp->domain; |
864 | 0 | result_mdp->messages = message_list_copy (mdp->messages, copy_level); |
865 | |
|
866 | 0 | msgdomain_list_append (result, result_mdp); |
867 | 0 | } |
868 | 0 | else |
869 | 0 | msgdomain_list_append (result, mdp); |
870 | 0 | } |
871 | |
|
872 | 0 | return result; |
873 | 0 | } |
874 | | |
875 | | |
876 | | #if 0 /* unused */ |
877 | | message_ty * |
878 | | msgdomain_list_search (msgdomain_list_ty *mdlp, |
879 | | const char *msgctxt, const char *msgid) |
880 | | { |
881 | | for (size_t j = 0; j < mdlp->nitems; ++j) |
882 | | { |
883 | | msgdomain_ty *mdp = mdlp->item[j]; |
884 | | message_ty *mp = message_list_search (mdp->messages, msgctxt, msgid); |
885 | | if (mp) |
886 | | return mp; |
887 | | } |
888 | | return NULL; |
889 | | } |
890 | | #endif |
891 | | |
892 | | |
893 | | #if 0 /* unused */ |
894 | | message_ty * |
895 | | msgdomain_list_search_fuzzy (msgdomain_list_ty *mdlp, |
896 | | const char *msgctxt, const char *msgid) |
897 | | { |
898 | | double best_weight = FUZZY_THRESHOLD; |
899 | | message_ty *best_mp = NULL; |
900 | | for (size_t j = 0; j < mdlp->nitems; ++j) |
901 | | { |
902 | | msgdomain_ty *mdp = mdlp->item[j]; |
903 | | message_ty *mp = |
904 | | message_list_search_fuzzy_inner (mdp->messages, msgctxt, msgid, |
905 | | &best_weight); |
906 | | if (mp) |
907 | | best_mp = mp; |
908 | | } |
909 | | return best_mp; |
910 | | } |
911 | | #endif |