Coverage Report

Created: 2026-03-12 07:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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