Coverage Report

Created: 2026-10-28 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gettext/gettext-tools/libgettextpo/gettext-po.c
Line
Count
Source
1
/* Public API for GNU gettext PO files.
2
   Copyright (C) 2003-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 Bruno Haible.  */
18
19
#include <config.h>
20
21
/* Specification.  */
22
#include "gettext-po.h"
23
24
#include <limits.h>
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <string.h>
28
29
#include "message.h"
30
#include "str-list.h"
31
#include "xalloc.h"
32
#include "read-catalog.h"
33
#include "read-po.h"
34
#include "write-catalog.h"
35
#include "write-po.h"
36
#include "xvasprintf.h"
37
#include "msgl-check.h"
38
#include "glthread/once.h"
39
#include "gettext.h"
40
41
0
#define _(str) gettext(str)
42
43
44
struct po_file
45
{
46
  msgdomain_list_ty *mdlp;
47
  const char *real_filename;
48
  const char *logical_filename;
49
  const char **domains;
50
  /* Heap allocations that are guaranteed to persist until the po_file_free
51
     call.  */
52
  string_list_ty arena;
53
};
54
55
struct po_message_iterator
56
{
57
  po_file_t file;
58
  char *domain;
59
  message_list_ty *mlp;
60
  size_t index;
61
};
62
63
/* A po_message_t is actually a 'struct message_ty *'.  */
64
65
/* A po_filepos_t is actually a 'lex_pos_ty *'.  */
66
67
struct po_flag_iterator
68
{
69
  po_message_t message;
70
  int kind; /* 0: workflow flags, 1: sticky flags */
71
  size_t index;
72
};
73
74
75
/* Version number: (major<<16) + (minor<<8) + subminor */
76
int libgettextpo_version = LIBGETTEXTPO_VERSION;
77
78
79
/* Create an empty PO file representation in memory.  */
80
81
po_file_t
82
po_file_create (void)
83
0
{
84
0
  po_file_t file;
85
86
0
  file = XMALLOC (struct po_file);
87
0
  file->mdlp = msgdomain_list_alloc (false);
88
0
  file->real_filename = _("<unnamed>");
89
0
  file->logical_filename = file->real_filename;
90
0
  file->domains = NULL;
91
0
  string_list_init (&file->arena);
92
0
  return file;
93
0
}
94
95
96
/* Read a PO file into memory.
97
   Return its contents.  Upon failure, return NULL and set errno.  */
98
99
po_file_t
100
po_file_read (const char *filename, po_xerror_handler_t handler)
101
13.0k
{
102
13.0k
  FILE *fp;
103
13.0k
  po_file_t file;
104
105
13.0k
  if (strcmp (filename, "-") == 0 || strcmp (filename, "/dev/stdin") == 0)
106
0
    {
107
0
      filename = _("<stdin>");
108
0
      fp = stdin;
109
0
    }
110
13.0k
  else
111
13.0k
    {
112
13.0k
      fp = fopen (filename, "r");
113
13.0k
      if (fp == NULL)
114
0
        return NULL;
115
13.0k
    }
116
117
  /* Establish error handler for read_catalog_stream().  */
118
13.0k
  unsigned int error_count = 0;
119
13.0k
  struct xerror_handler local_xerror_handler =
120
13.0k
    {
121
13.0k
      (void (*) (int, const message_ty *, const char *, size_t, size_t, int, const char *))
122
13.0k
      handler->xerror,
123
13.0k
      (void (*) (int, const message_ty *, const char *, size_t, size_t, int, const char *, const message_ty *, const char *, size_t, size_t, int, const char *))
124
13.0k
      handler->xerror2,
125
13.0k
      &error_count
126
13.0k
    };
127
128
13.0k
  file = XMALLOC (struct po_file);
129
13.0k
  file->real_filename = filename;
130
13.0k
  file->logical_filename = filename;
131
13.0k
  string_list_init (&file->arena);
132
13.0k
  file->mdlp = read_catalog_stream (fp, file->real_filename,
133
13.0k
                                    file->logical_filename, &input_format_po,
134
13.0k
                                    &local_xerror_handler, &file->arena);
135
13.0k
  file->domains = NULL;
136
137
13.0k
  if (fp != stdin)
138
13.0k
    fclose (fp);
139
13.0k
  return file;
140
13.0k
}
141
142
143
/* Write an in-memory PO file to a file.
144
   Upon failure, return NULL and set errno.  */
145
146
po_file_t
147
po_file_write (po_file_t file, const char *filename, po_xerror_handler_t handler)
148
0
{
149
  /* Establish error handler for msgdomain_list_print().  */
150
0
  unsigned int error_count = 0;
151
0
  struct xerror_handler local_xerror_handler =
152
0
    {
153
0
      (void (*) (int, const message_ty *, const char *, size_t, size_t, int, const char *))
154
0
      handler->xerror,
155
0
      (void (*) (int, const message_ty *, const char *, size_t, size_t, int, const char *, const message_ty *, const char *, size_t, size_t, int, const char *))
156
0
      handler->xerror2,
157
0
      &error_count
158
0
    };
159
160
0
  msgdomain_list_print (file->mdlp, filename, &output_format_po,
161
0
                        &local_xerror_handler, true, false);
162
163
0
  return file;
164
0
}
165
166
167
/* Free a PO file from memory.  */
168
169
void
170
po_file_free (po_file_t file)
171
13.0k
{
172
13.0k
  msgdomain_list_free (file->mdlp);
173
13.0k
  if (file->domains != NULL)
174
13.0k
    free (file->domains);
175
13.0k
  string_list_destroy (&file->arena);
176
13.0k
  free (file);
177
13.0k
}
178
179
180
/* Return the names of the domains covered by a PO file in memory.  */
181
182
const char * const *
183
po_file_domains (po_file_t file)
184
0
{
185
0
  if (file->domains == NULL)
186
0
    {
187
0
      size_t n = file->mdlp->nitems;
188
0
      const char **domains = XNMALLOC (n + 1, const char *);
189
0
      size_t j;
190
191
0
      for (j = 0; j < n; j++)
192
0
        domains[j] = file->mdlp->item[j]->domain;
193
0
      domains[n] = NULL;
194
195
0
      file->domains = domains;
196
0
    }
197
198
0
  return file->domains;
199
0
}
200
201
202
/* Return the header entry of a domain of a PO file in memory.
203
   The domain NULL denotes the default domain.
204
   Return NULL if there is no header entry.  */
205
206
const char *
207
po_file_domain_header (po_file_t file, const char *domain)
208
0
{
209
0
  message_list_ty *mlp;
210
0
  size_t j;
211
212
0
  if (domain == NULL)
213
0
    domain = MESSAGE_DOMAIN_DEFAULT;
214
0
  mlp = msgdomain_list_sublist (file->mdlp, domain, false);
215
0
  if (mlp != NULL)
216
0
    for (j = 0; j < mlp->nitems; j++)
217
0
      if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
218
0
        {
219
0
          const char *header = mlp->item[j]->msgstr;
220
221
0
          if (header != NULL)
222
0
            return xstrdup (header);
223
0
          else
224
0
            return NULL;
225
0
        }
226
0
  return NULL;
227
0
}
228
229
230
/* Return the value of a field in a header entry.
231
   The return value is either a freshly allocated string, to be freed by the
232
   caller, or NULL.  */
233
234
char *
235
po_header_field (const char *header, const char *field)
236
0
{
237
0
  size_t field_len = strlen (field);
238
0
  const char *line;
239
240
0
  for (line = header;;)
241
0
    {
242
0
      if (strncmp (line, field, field_len) == 0 && line[field_len] == ':')
243
0
        {
244
0
          const char *value_start;
245
0
          const char *value_end;
246
0
          char *value;
247
248
0
          value_start = line + field_len + 1;
249
0
          if (*value_start == ' ')
250
0
            value_start++;
251
0
          value_end = strchr (value_start, '\n');
252
0
          if (value_end == NULL)
253
0
            value_end = value_start + strlen (value_start);
254
255
0
          value = XNMALLOC (value_end - value_start + 1, char);
256
0
          memcpy (value, value_start, value_end - value_start);
257
0
          value[value_end - value_start] = '\0';
258
259
0
          return value;
260
0
        }
261
262
0
      line = strchr (line, '\n');
263
0
      if (line != NULL)
264
0
        line++;
265
0
      else
266
0
        break;
267
0
    }
268
269
0
  return NULL;
270
0
}
271
272
273
/* Return the header entry with a given field set to a given value.  The field
274
   is added if necessary.
275
   The return value is a freshly allocated string.  */
276
277
char *
278
po_header_set_field (const char *header, const char *field, const char *value)
279
0
{
280
0
  size_t header_len = strlen (header);
281
0
  size_t field_len = strlen (field);
282
0
  size_t value_len = strlen (value);
283
284
0
  {
285
0
    const char *line;
286
287
0
    for (line = header;;)
288
0
      {
289
0
        if (strncmp (line, field, field_len) == 0 && line[field_len] == ':')
290
0
          {
291
0
            const char *oldvalue_start;
292
0
            const char *oldvalue_end;
293
0
            size_t header_part1_len;
294
0
            size_t header_part3_len;
295
0
            size_t result_len;
296
0
            char *result;
297
298
0
            oldvalue_start = line + field_len + 1;
299
0
            if (*oldvalue_start == ' ')
300
0
              oldvalue_start++;
301
0
            oldvalue_end = strchr (oldvalue_start, '\n');
302
0
            if (oldvalue_end == NULL)
303
0
              oldvalue_end = oldvalue_start + strlen (oldvalue_start);
304
305
0
            header_part1_len = oldvalue_start - header;
306
0
            header_part3_len = header + header_len - oldvalue_end;
307
0
            result_len = header_part1_len + value_len + header_part3_len;
308
                    /* = header_len - oldvalue_len + value_len */
309
0
            result = XNMALLOC (result_len + 1, char);
310
0
            memcpy (result, header, header_part1_len);
311
0
            memcpy (result + header_part1_len, value, value_len);
312
0
            memcpy (result + header_part1_len + value_len, oldvalue_end,
313
0
                    header_part3_len);
314
0
            *(result + result_len) = '\0';
315
316
0
            return result;
317
0
          }
318
319
0
        line = strchr (line, '\n');
320
0
        if (line != NULL)
321
0
          line++;
322
0
        else
323
0
          break;
324
0
      }
325
0
  }
326
0
  {
327
0
    size_t newline;
328
0
    size_t result_len;
329
0
    char *result;
330
331
0
    newline = (header_len > 0 && header[header_len - 1] != '\n' ? 1 : 0);
332
0
    result_len = header_len + newline + field_len + 2 + value_len + 1;
333
0
    result = XNMALLOC (result_len + 1, char);
334
0
    memcpy (result, header, header_len);
335
0
    if (newline)
336
0
      *(result + header_len) = '\n';
337
0
    memcpy (result + header_len + newline, field, field_len);
338
0
    *(result + header_len + newline + field_len) = ':';
339
0
    *(result + header_len + newline + field_len + 1) = ' ';
340
0
    memcpy (result + header_len + newline + field_len + 2, value, value_len);
341
0
    *(result + header_len + newline + field_len + 2 + value_len) = '\n';
342
0
    *(result + result_len) = '\0';
343
344
0
    return result;
345
0
  }
346
0
}
347
348
349
/* Create an iterator for traversing a domain of a PO file in memory.
350
   The domain NULL denotes the default domain.  */
351
352
po_message_iterator_t
353
po_message_iterator (po_file_t file, const char *domain)
354
0
{
355
0
  po_message_iterator_t iterator;
356
357
0
  if (domain == NULL)
358
0
    domain = MESSAGE_DOMAIN_DEFAULT;
359
360
0
  iterator = XMALLOC (struct po_message_iterator);
361
0
  iterator->file = file;
362
0
  iterator->domain = xstrdup (domain);
363
0
  iterator->mlp = msgdomain_list_sublist (file->mdlp, domain, false);
364
0
  iterator->index = 0;
365
366
0
  return iterator;
367
0
}
368
369
370
/* Free an iterator.  */
371
372
void
373
po_message_iterator_free (po_message_iterator_t iterator)
374
0
{
375
0
  free (iterator->domain);
376
0
  free (iterator);
377
0
}
378
379
380
/* Return the next message, and advance the iterator.
381
   Return NULL at the end of the message list.  */
382
383
po_message_t
384
po_next_message (po_message_iterator_t iterator)
385
0
{
386
0
  if (iterator->mlp != NULL && iterator->index < iterator->mlp->nitems)
387
0
    return (po_message_t) iterator->mlp->item[iterator->index++];
388
0
  else
389
0
    return NULL;
390
0
}
391
392
393
/* Insert a message in a PO file in memory, in the domain and at the position
394
   indicated by the iterator.  The iterator thereby advances past the freshly
395
   inserted message.  */
396
397
void
398
po_message_insert (po_message_iterator_t iterator, po_message_t message)
399
0
{
400
0
  message_ty *mp = (message_ty *) message;
401
402
0
  if (iterator->mlp == NULL)
403
    /* Now we need to allocate a sublist corresponding to the iterator.  */
404
0
    iterator->mlp =
405
0
      msgdomain_list_sublist (iterator->file->mdlp, iterator->domain, true);
406
  /* Insert the message.  */
407
0
  message_list_insert_at (iterator->mlp, iterator->index, mp);
408
  /* Advance the iterator.  */
409
0
  iterator->index++;
410
0
}
411
412
413
/* Return a freshly constructed message.
414
   To finish initializing the message, you must set the msgid and msgstr.  */
415
416
po_message_t
417
po_message_create (void)
418
0
{
419
0
  lex_pos_ty pos = { NULL, 0 };
420
421
0
  return (po_message_t) message_alloc (NULL, NULL, NULL, xstrdup (""), 1, &pos);
422
0
}
423
424
425
/* Return the context of a message, or NULL for a message not restricted to a
426
   context.  */
427
const char *
428
po_message_msgctxt (po_message_t message)
429
0
{
430
0
  message_ty *mp = (message_ty *) message;
431
432
0
  return mp->msgctxt;
433
0
}
434
435
436
/* Change the context of a message. NULL means a message not restricted to a
437
   context.  */
438
void
439
po_message_set_msgctxt (po_message_t message, const char *msgctxt)
440
0
{
441
0
  message_ty *mp = (message_ty *) message;
442
443
0
  if (msgctxt != mp->msgctxt)
444
0
    {
445
0
      char *old_msgctxt = (char *) mp->msgctxt;
446
447
0
      mp->msgctxt = (msgctxt != NULL ? xstrdup (msgctxt) : NULL);
448
0
      if (old_msgctxt != NULL)
449
0
        free (old_msgctxt);
450
0
    }
451
0
}
452
453
454
/* Return the msgid (untranslated English string) of a message.  */
455
456
const char *
457
po_message_msgid (po_message_t message)
458
0
{
459
0
  message_ty *mp = (message_ty *) message;
460
461
0
  return mp->msgid;
462
0
}
463
464
465
/* Change the msgid (untranslated English string) of a message.  */
466
467
void
468
po_message_set_msgid (po_message_t message, const char *msgid)
469
0
{
470
0
  message_ty *mp = (message_ty *) message;
471
472
0
  if (msgid != mp->msgid)
473
0
    {
474
0
      char *old_msgid = (char *) mp->msgid;
475
476
0
      mp->msgid = xstrdup (msgid);
477
0
      if (old_msgid != NULL)
478
0
        free (old_msgid);
479
0
    }
480
0
}
481
482
483
/* Return the msgid_plural (untranslated English plural string) of a message,
484
   or NULL for a message without plural.  */
485
486
const char *
487
po_message_msgid_plural (po_message_t message)
488
0
{
489
0
  message_ty *mp = (message_ty *) message;
490
491
0
  return mp->msgid_plural;
492
0
}
493
494
495
/* Change the msgid_plural (untranslated English plural string) of a message.
496
   NULL means a message without plural.  */
497
498
void
499
po_message_set_msgid_plural (po_message_t message, const char *msgid_plural)
500
0
{
501
0
  message_ty *mp = (message_ty *) message;
502
503
0
  if (msgid_plural != mp->msgid_plural)
504
0
    {
505
0
      char *old_msgid_plural = (char *) mp->msgid_plural;
506
507
0
      mp->msgid_plural = (msgid_plural != NULL ? xstrdup (msgid_plural) : NULL);
508
0
      if (old_msgid_plural != NULL)
509
0
        free (old_msgid_plural);
510
0
    }
511
0
}
512
513
514
/* Return the msgstr (translation) of a message.
515
   Return the empty string for an untranslated message.  */
516
517
const char *
518
po_message_msgstr (po_message_t message)
519
0
{
520
0
  message_ty *mp = (message_ty *) message;
521
522
0
  return mp->msgstr;
523
0
}
524
525
526
/* Change the msgstr (translation) of a message.
527
   Use an empty string to denote an untranslated message.  */
528
529
void
530
po_message_set_msgstr (po_message_t message, const char *msgstr)
531
0
{
532
0
  message_ty *mp = (message_ty *) message;
533
534
0
  if (msgstr != mp->msgstr)
535
0
    {
536
0
      char *old_msgstr = (char *) mp->msgstr;
537
538
0
      mp->msgstr = xstrdup (msgstr);
539
0
      mp->msgstr_len = strlen (mp->msgstr) + 1;
540
0
      if (old_msgstr != NULL)
541
0
        free (old_msgstr);
542
0
    }
543
0
}
544
545
546
/* Return the msgstr[index] for a message with plural handling, or
547
   NULL when the index is out of range or for a message without plural.  */
548
549
const char *
550
po_message_msgstr_plural (po_message_t message, int index)
551
0
{
552
0
  message_ty *mp = (message_ty *) message;
553
554
0
  if (mp->msgid_plural != NULL && index >= 0)
555
0
    {
556
0
      const char *p;
557
0
      const char *p_end = mp->msgstr + mp->msgstr_len;
558
559
0
      for (p = mp->msgstr; ; p += strlen (p) + 1, index--)
560
0
        {
561
0
          if (p >= p_end)
562
0
            return NULL;
563
0
          if (index == 0)
564
0
            break;
565
0
        }
566
0
      return p;
567
0
    }
568
0
  else
569
0
    return NULL;
570
0
}
571
572
573
/* Change the msgstr[index] for a message with plural handling.
574
   Use a NULL value at the end to reduce the number of plural forms.  */
575
576
void
577
po_message_set_msgstr_plural (po_message_t message, int index, const char *msgstr)
578
0
{
579
0
  message_ty *mp = (message_ty *) message;
580
581
0
  if (mp->msgid_plural != NULL && index >= 0)
582
0
    {
583
0
      char *p = (char *) mp->msgstr;
584
0
      char *p_end = (char *) mp->msgstr + mp->msgstr_len;
585
0
      char *copied_msgstr;
586
587
      /* Special care must be taken of the case that msgstr points into the
588
         mp->msgstr string list, because mp->msgstr may be relocated before we
589
         are done with msgstr.  */
590
0
      if (msgstr >= p && msgstr < p_end)
591
0
        msgstr = copied_msgstr = xstrdup (msgstr);
592
0
      else
593
0
        copied_msgstr = NULL;
594
595
0
      for (; ; p += strlen (p) + 1, index--)
596
0
        {
597
0
          if (p >= p_end)
598
0
            {
599
              /* Append at the end.  */
600
0
              if (msgstr != NULL)
601
0
                {
602
0
                  size_t new_msgstr_len = mp->msgstr_len + index + strlen (msgstr) + 1;
603
604
0
                  mp->msgstr =
605
0
                    (char *) xrealloc ((char *) mp->msgstr, new_msgstr_len);
606
0
                  p = (char *) mp->msgstr + mp->msgstr_len;
607
0
                  for (; index > 0; index--)
608
0
                    *p++ = '\0';
609
0
                  memcpy (p, msgstr, strlen (msgstr) + 1);
610
0
                  mp->msgstr_len = new_msgstr_len;
611
0
                }
612
0
              if (copied_msgstr != NULL)
613
0
                free (copied_msgstr);
614
0
              return;
615
0
            }
616
0
          if (index == 0)
617
0
            break;
618
0
        }
619
0
      if (msgstr == NULL)
620
0
        {
621
0
          if (p + strlen (p) + 1 >= p_end)
622
0
            {
623
              /* Remove the string that starts at p.  */
624
0
              mp->msgstr_len = p - mp->msgstr;
625
0
              return;
626
0
            }
627
          /* It is not possible to remove an element of the string list
628
             except the last one.  So just replace it with the empty string.
629
             That's the best we can do here.  */
630
0
          msgstr = "";
631
0
        }
632
0
      {
633
        /* Replace the string that starts at p.  */
634
0
        size_t i1 = p - mp->msgstr;
635
0
        size_t i2before = i1 + strlen (p);
636
0
        size_t i2after = i1 + strlen (msgstr);
637
0
        size_t new_msgstr_len = mp->msgstr_len - i2before + i2after;
638
639
0
        if (i2after > i2before)
640
0
          mp->msgstr = (char *) xrealloc ((char *) mp->msgstr, new_msgstr_len);
641
0
        memmove ((char *) mp->msgstr + i2after, mp->msgstr + i2before,
642
0
                 mp->msgstr_len - i2before);
643
0
        memcpy ((char *) mp->msgstr + i1, msgstr, i2after - i1);
644
0
        mp->msgstr_len = new_msgstr_len;
645
0
      }
646
0
      if (copied_msgstr != NULL)
647
0
        free (copied_msgstr);
648
0
    }
649
0
}
650
651
652
/* Return the comments for a message.  */
653
654
const char *
655
po_message_comments (po_message_t message)
656
0
{
657
  /* FIXME: memory leak.  */
658
0
  message_ty *mp = (message_ty *) message;
659
660
0
  if (mp->comment == NULL || mp->comment->nitems == 0)
661
0
    return "";
662
0
  else
663
0
    return string_list_join (mp->comment, "\n", '\n', true);
664
0
}
665
666
667
/* Change the comments for a message.
668
   comments should be a multiline string, ending in a newline, or empty.  */
669
670
void
671
po_message_set_comments (po_message_t message, const char *comments)
672
0
{
673
0
  message_ty *mp = (message_ty *) message;
674
0
  string_list_ty *slp = string_list_alloc ();
675
676
0
  {
677
0
    char *copy = xstrdup (comments);
678
0
    char *rest;
679
680
0
    rest = copy;
681
0
    while (*rest != '\0')
682
0
      {
683
0
        char *newline = strchr (rest, '\n');
684
685
0
        if (newline != NULL)
686
0
          {
687
0
            *newline = '\0';
688
0
            string_list_append (slp, rest);
689
0
            rest = newline + 1;
690
0
          }
691
0
        else
692
0
          {
693
0
            string_list_append (slp, rest);
694
0
            break;
695
0
          }
696
0
      }
697
0
    free (copy);
698
0
  }
699
700
0
  if (mp->comment != NULL)
701
0
    string_list_free (mp->comment);
702
703
0
  mp->comment = slp;
704
0
}
705
706
707
/* Return the extracted comments for a message.  */
708
709
const char *
710
po_message_extracted_comments (po_message_t message)
711
0
{
712
  /* FIXME: memory leak.  */
713
0
  message_ty *mp = (message_ty *) message;
714
715
0
  if (mp->comment_dot == NULL || mp->comment_dot->nitems == 0)
716
0
    return "";
717
0
  else
718
0
    return string_list_join (mp->comment_dot, "\n", '\n', true);
719
0
}
720
721
722
/* Change the extracted comments for a message.
723
   comments should be a multiline string, ending in a newline, or empty.  */
724
725
void
726
po_message_set_extracted_comments (po_message_t message, const char *comments)
727
0
{
728
0
  message_ty *mp = (message_ty *) message;
729
0
  string_list_ty *slp = string_list_alloc ();
730
731
0
  {
732
0
    char *copy = xstrdup (comments);
733
0
    char *rest;
734
735
0
    rest = copy;
736
0
    while (*rest != '\0')
737
0
      {
738
0
        char *newline = strchr (rest, '\n');
739
740
0
        if (newline != NULL)
741
0
          {
742
0
            *newline = '\0';
743
0
            string_list_append (slp, rest);
744
0
            rest = newline + 1;
745
0
          }
746
0
        else
747
0
          {
748
0
            string_list_append (slp, rest);
749
0
            break;
750
0
          }
751
0
      }
752
0
    free (copy);
753
0
  }
754
755
0
  if (mp->comment_dot != NULL)
756
0
    string_list_free (mp->comment_dot);
757
758
0
  mp->comment_dot = slp;
759
0
}
760
761
762
/* Return the i-th file position for a message, or NULL if i is out of
763
   range.  */
764
765
po_filepos_t
766
po_message_filepos (po_message_t message, int i)
767
0
{
768
0
  message_ty *mp = (message_ty *) message;
769
770
0
  if (i >= 0 && (size_t)i < mp->filepos_count)
771
0
    return (po_filepos_t) &mp->filepos[i];
772
0
  else
773
0
    return NULL;
774
0
}
775
776
777
/* Remove the i-th file position from a message.
778
   The indices of all following file positions for the message are decremented
779
   by one.  */
780
781
void
782
po_message_remove_filepos (po_message_t message, int i)
783
0
{
784
0
  message_ty *mp = (message_ty *) message;
785
786
0
  if (i >= 0)
787
0
    {
788
0
      size_t j = (size_t)i;
789
0
      size_t n = mp->filepos_count;
790
791
0
      if (j < n)
792
0
        {
793
0
          n = n - 1;
794
0
          free ((char *) mp->filepos[j].file_name);
795
0
          for (; j < n; j++)
796
0
            mp->filepos[j] = mp->filepos[j + 1];
797
0
          mp->filepos_count = n;
798
0
        }
799
0
    }
800
0
}
801
802
803
/* Add a file position to a message, if it is not already present for the
804
   message.
805
   file is the file name.
806
   start_line is the line number where the string starts, or (size_t)(-1) if no
807
   line number is available.  */
808
809
void
810
po_message_add_filepos (po_message_t message, const char *file, size_t start_line)
811
0
{
812
0
  message_ty *mp = (message_ty *) message;
813
814
0
  message_comment_filepos (mp, file, start_line);
815
0
}
816
817
818
/* Return the previous context of a message, or NULL for none.  */
819
820
const char *
821
po_message_prev_msgctxt (po_message_t message)
822
0
{
823
0
  message_ty *mp = (message_ty *) message;
824
825
0
  return mp->prev_msgctxt;
826
0
}
827
828
829
/* Change the previous context of a message.  NULL is allowed.  */
830
831
void
832
po_message_set_prev_msgctxt (po_message_t message, const char *prev_msgctxt)
833
0
{
834
0
  message_ty *mp = (message_ty *) message;
835
836
0
  if (prev_msgctxt != mp->prev_msgctxt)
837
0
    {
838
0
      char *old_prev_msgctxt = (char *) mp->prev_msgctxt;
839
840
0
      mp->prev_msgctxt = (prev_msgctxt != NULL ? xstrdup (prev_msgctxt) : NULL);
841
0
      if (old_prev_msgctxt != NULL)
842
0
        free (old_prev_msgctxt);
843
0
    }
844
0
}
845
846
847
/* Return the previous msgid (untranslated English string) of a message, or
848
   NULL for none.  */
849
850
const char *
851
po_message_prev_msgid (po_message_t message)
852
0
{
853
0
  message_ty *mp = (message_ty *) message;
854
855
0
  return mp->prev_msgid;
856
0
}
857
858
859
/* Change the previous msgid (untranslated English string) of a message.
860
   NULL is allowed.  */
861
862
void
863
po_message_set_prev_msgid (po_message_t message, const char *prev_msgid)
864
0
{
865
0
  message_ty *mp = (message_ty *) message;
866
867
0
  if (prev_msgid != mp->prev_msgid)
868
0
    {
869
0
      char *old_prev_msgid = (char *) mp->prev_msgid;
870
871
0
      mp->prev_msgid = (prev_msgid != NULL ? xstrdup (prev_msgid) : NULL);
872
0
      if (old_prev_msgid != NULL)
873
0
        free (old_prev_msgid);
874
0
    }
875
0
}
876
877
878
/* Return the previous msgid_plural (untranslated English plural string) of a
879
   message, or NULL for none.  */
880
881
const char *
882
po_message_prev_msgid_plural (po_message_t message)
883
0
{
884
0
  message_ty *mp = (message_ty *) message;
885
886
0
  return mp->prev_msgid_plural;
887
0
}
888
889
890
/* Change the previous msgid_plural (untranslated English plural string) of a
891
   message.  NULL is allowed.  */
892
893
void
894
po_message_set_prev_msgid_plural (po_message_t message, const char *prev_msgid_plural)
895
0
{
896
0
  message_ty *mp = (message_ty *) message;
897
898
0
  if (prev_msgid_plural != mp->prev_msgid_plural)
899
0
    {
900
0
      char *old_prev_msgid_plural = (char *) mp->prev_msgid_plural;
901
902
0
      mp->prev_msgid_plural =
903
0
        (prev_msgid_plural != NULL ? xstrdup (prev_msgid_plural) : NULL);
904
0
      if (old_prev_msgid_plural != NULL)
905
0
        free (old_prev_msgid_plural);
906
0
    }
907
0
}
908
909
910
/* Return true if the message is marked obsolete.  */
911
912
int
913
po_message_is_obsolete (po_message_t message)
914
0
{
915
0
  message_ty *mp = (message_ty *) message;
916
917
0
  return (mp->obsolete ? 1 : 0);
918
0
}
919
920
921
/* Change the obsolete mark of a message.  */
922
923
void
924
po_message_set_obsolete (po_message_t message, int obsolete)
925
0
{
926
0
  message_ty *mp = (message_ty *) message;
927
928
0
  mp->obsolete = obsolete;
929
0
}
930
931
932
/* Return true if the message is marked fuzzy.  */
933
934
int
935
po_message_is_fuzzy (po_message_t message)
936
0
{
937
0
  message_ty *mp = (message_ty *) message;
938
939
0
  return (mp->is_fuzzy ? 1 : 0);
940
0
}
941
942
943
/* Change the fuzzy mark of a message.  */
944
945
void
946
po_message_set_fuzzy (po_message_t message, int fuzzy)
947
0
{
948
0
  message_ty *mp = (message_ty *) message;
949
950
0
  mp->is_fuzzy = fuzzy;
951
0
}
952
953
954
/* Return true if the message has a given workflow flag.
955
   This function is a generalization of po_message_is_fuzzy.  */
956
957
int
958
po_message_has_workflow_flag (po_message_t message, const char *workflow_flag)
959
0
{
960
0
  if (strcmp (workflow_flag, "fuzzy") == 0)
961
0
    return po_message_is_fuzzy (message);
962
0
  return 0;
963
0
}
964
965
966
/* Set or unset a given workflow flag on a message.
967
   This function is a generalization of po_message_set_fuzzy.    */
968
969
void
970
po_message_set_workflow_flag (po_message_t message, const char *workflow_flag, int value)
971
0
{
972
0
  if (strcmp (workflow_flag, "fuzzy") == 0)
973
0
    po_message_set_fuzzy (message, value);
974
0
}
975
976
977
/* Create an iterator for traversing the list of workflow flags of a message.
978
   This includes the "fuzzy" flag.  */
979
980
po_flag_iterator_t
981
po_message_workflow_flags_iterator (po_message_t message)
982
0
{
983
0
  po_flag_iterator_t iterator = XMALLOC (struct po_flag_iterator);
984
0
  iterator->message = message;
985
0
  iterator->kind = 0;
986
0
  iterator->index = 0;
987
988
0
  return iterator;
989
0
}
990
991
992
/* Return true if the message is marked as being a format string of the given
993
   type (e.g. "c-format").  */
994
995
int
996
po_message_is_format (po_message_t message, const char *format_type)
997
0
{
998
0
  message_ty *mp = (message_ty *) message;
999
0
  size_t len = strlen (format_type);
1000
0
  size_t i;
1001
1002
0
  if (len >= 7 && memcmp (format_type + len - 7, "-format", 7) == 0)
1003
0
    for (i = 0; i < NFORMATS; i++)
1004
0
      if (strlen (format_language[i]) == len - 7
1005
0
          && memcmp (format_language[i], format_type, len - 7) == 0)
1006
        /* The given format_type corresponds to (enum format_type) i.  */
1007
        /* See make_format_description_string in write-po.c.  */
1008
0
        return (possible_format_p (mp->is_format[i]) ? 1 : 0);
1009
0
  return 0;
1010
0
}
1011
1012
1013
/* Return the format string mark for a given type (e.g. "c-format") of a
1014
   message.  Returns 1 if the the mark is set, 0 if the opposite mark ("no-*")
1015
   is set, -1 if neither the mark nor the opposite mark is set.  */
1016
1017
int
1018
po_message_get_format (po_message_t message, const char *format_type)
1019
0
{
1020
0
  message_ty *mp = (message_ty *) message;
1021
0
  size_t len = strlen (format_type);
1022
0
  size_t i;
1023
1024
0
  if (len >= 7 && memcmp (format_type + len - 7, "-format", 7) == 0)
1025
0
    for (i = 0; i < NFORMATS; i++)
1026
0
      if (strlen (format_language[i]) == len - 7
1027
0
          && memcmp (format_language[i], format_type, len - 7) == 0)
1028
        /* The given format_type corresponds to (enum format_type) i.  */
1029
0
        {
1030
0
          enum is_format is_format = mp->is_format[i];
1031
          /* See significant_format_p and make_format_description_string
1032
             in write-po.c.  */
1033
0
          return (possible_format_p (is_format) ? 1 :
1034
0
                  not_format_p (is_format) ? 0 :
1035
0
                  -1);
1036
0
        }
1037
0
  return -1;
1038
0
}
1039
1040
1041
/* Change the format string mark for a given type of a message.  */
1042
1043
void
1044
po_message_set_format (po_message_t message, const char *format_type, int value)
1045
0
{
1046
0
  message_ty *mp = (message_ty *) message;
1047
0
  size_t len = strlen (format_type);
1048
0
  size_t i;
1049
1050
0
  if (len >= 7 && memcmp (format_type + len - 7, "-format", 7) == 0)
1051
0
    for (i = 0; i < NFORMATS; i++)
1052
0
      if (strlen (format_language[i]) == len - 7
1053
0
          && memcmp (format_language[i], format_type, len - 7) == 0)
1054
        /* The given format_type corresponds to (enum format_type) i.  */
1055
0
        mp->is_format[i] = (value >= 0 ? (value ? yes : no) : undecided);
1056
0
}
1057
1058
1059
/* Return true if the message has a given sticky flag.
1060
   This function is a generalization of po_message_is_format and
1061
   po_message_get_format.  */
1062
1063
int
1064
po_message_has_sticky_flag (po_message_t message, const char *sticky_flag)
1065
0
{
1066
0
  message_ty *mp = (message_ty *) message;
1067
0
  size_t len = strlen (sticky_flag);
1068
1069
0
  if (len >= 7 && memcmp (sticky_flag + len - 7, "-format", 7) == 0)
1070
0
    {
1071
0
      if (len >= 3 + 7 && memcmp (sticky_flag, "no-", 3) == 0)
1072
0
        {
1073
0
          size_t i;
1074
0
          for (i = 0; i < NFORMATS; i++)
1075
0
            if (strlen (format_language[i]) == len - 3 - 7
1076
0
                && memcmp (format_language[i], sticky_flag + 3, len - 3 - 7) == 0)
1077
              /* The given sticky_flag corresponds to the opposite of
1078
                 (enum format_type) i.  */
1079
0
              return not_format_p (mp->is_format[i]);
1080
0
        }
1081
0
      else
1082
0
        {
1083
0
          size_t i;
1084
0
          for (i = 0; i < NFORMATS; i++)
1085
0
            if (strlen (format_language[i]) == len - 7
1086
0
                && memcmp (format_language[i], sticky_flag, len - 7) == 0)
1087
              /* The given sticky_flag corresponds to (enum format_type) i.  */
1088
0
              return possible_format_p (mp->is_format[i]);
1089
0
        }
1090
0
    }
1091
0
  else if (strcmp (sticky_flag, "no-wrap") == 0)
1092
0
    return mp->do_wrap == no;
1093
0
  return 0;
1094
0
}
1095
1096
1097
/* Set or unset a given sticky flag on a message.
1098
   This function is a generalization of po_message_set_format.  */
1099
1100
void
1101
po_message_set_sticky_flag (po_message_t message, const char *sticky_flag, int value)
1102
0
{
1103
0
  message_ty *mp = (message_ty *) message;
1104
0
  size_t len = strlen (sticky_flag);
1105
1106
0
  if (len >= 7 && memcmp (sticky_flag + len - 7, "-format", 7) == 0)
1107
0
    {
1108
0
      if (len >= 3 + 7 && memcmp (sticky_flag, "no-", 3) == 0)
1109
0
        {
1110
0
          size_t i;
1111
0
          for (i = 0; i < NFORMATS; i++)
1112
0
            if (strlen (format_language[i]) == len - 3 - 7
1113
0
                && memcmp (format_language[i], sticky_flag + 3, len - 3 - 7) == 0)
1114
              /* The given sticky_flag corresponds to the opposite of
1115
                 (enum format_type) i.  */
1116
0
              {
1117
0
                if (value)
1118
0
                  mp->is_format[i] = no;
1119
0
                else
1120
0
                  if (!possible_format_p (mp->is_format[i]))
1121
0
                    mp->is_format[i] = undecided;
1122
0
              }
1123
0
        }
1124
0
      else
1125
0
        {
1126
0
          size_t i;
1127
0
          for (i = 0; i < NFORMATS; i++)
1128
0
            if (strlen (format_language[i]) == len - 7
1129
0
                && memcmp (format_language[i], sticky_flag, len - 7) == 0)
1130
              /* The given sticky_flag corresponds to (enum format_type) i.  */
1131
0
              {
1132
0
                if (value)
1133
0
                  mp->is_format[i] = yes;
1134
0
                else
1135
0
                  if (!not_format_p (mp->is_format[i]))
1136
0
                    mp->is_format[i] = undecided;
1137
0
              }
1138
0
        }
1139
0
    }
1140
0
  else if (strcmp (sticky_flag, "no-wrap") == 0)
1141
0
    mp->do_wrap = (value ? no : yes);
1142
0
}
1143
1144
1145
/* Create an iterator for traversing the list of sticky flags of a message.
1146
   This includes the "*-format" and "no-*-format" flags, as well as the
1147
   "no-wrap" flag.
1148
   It does *not* include the "range", because that is not a flag.  */
1149
1150
po_flag_iterator_t
1151
po_message_sticky_flags_iterator (po_message_t message)
1152
0
{
1153
0
  po_flag_iterator_t iterator = XMALLOC (struct po_flag_iterator);
1154
0
  iterator->message = message;
1155
0
  iterator->kind = 1;
1156
0
  iterator->index = 0;
1157
1158
0
  return iterator;
1159
0
}
1160
1161
1162
/* If a numeric range of a message is set, return true and store the minimum
1163
   and maximum value in *MINP and *MAXP.  */
1164
1165
int
1166
po_message_is_range (po_message_t message, int *minp, int *maxp)
1167
0
{
1168
0
  message_ty *mp = (message_ty *) message;
1169
1170
0
  if (has_range_p (mp->range))
1171
0
    {
1172
0
      *minp = mp->range.min;
1173
0
      *maxp = mp->range.max;
1174
0
      return 1;
1175
0
    }
1176
0
  else
1177
0
    return 0;
1178
0
}
1179
1180
1181
/* Change the numeric range of a message.  MIN and MAX must be non-negative,
1182
   with MIN < MAX.  Use MIN = MAX = -1 to remove the numeric range of a
1183
   message.  */
1184
1185
void
1186
po_message_set_range (po_message_t message, int min, int max)
1187
0
{
1188
0
  message_ty *mp = (message_ty *) message;
1189
1190
0
  if (min >= 0 && max >= min)
1191
0
    {
1192
0
      mp->range.min = min;
1193
0
      mp->range.max = max;
1194
0
    }
1195
0
  else if (min < 0 && max < 0)
1196
0
    {
1197
0
      mp->range.min = -1;
1198
0
      mp->range.max = -1;
1199
0
    }
1200
  /* Other values of min and max are invalid.  */
1201
0
}
1202
1203
1204
/* Return the file name.  */
1205
1206
const char *
1207
po_filepos_file (po_filepos_t filepos)
1208
0
{
1209
0
  lex_pos_ty *pp = (lex_pos_ty *) filepos;
1210
1211
0
  return pp->file_name;
1212
0
}
1213
1214
1215
/* Return the line number where the string starts, or (size_t)(-1) if no line
1216
   number is available.  */
1217
1218
size_t
1219
po_filepos_start_line (po_filepos_t filepos)
1220
0
{
1221
0
  lex_pos_ty *pp = (lex_pos_ty *) filepos;
1222
1223
0
  return pp->line_number;
1224
0
}
1225
1226
1227
/* A NULL terminated array of the supported format types.  */
1228
static const char * const * all_formats;
1229
1230
static void
1231
all_formats_init (void)
1232
0
{
1233
0
  const char **list = XNMALLOC (NFORMATS + 1, const char *);
1234
0
  size_t i;
1235
0
  for (i = 0; i < NFORMATS; i++)
1236
0
    list[i] = xasprintf ("%s-format", format_language[i]);
1237
0
  list[i] = NULL;
1238
0
  all_formats = list;
1239
0
}
1240
1241
/* Ensure that all_formats_init is called once only.  */
1242
gl_once_define(static, all_formats_init_once)
1243
1244
/* Return a NULL terminated array of the supported format types.  */
1245
1246
const char * const *
1247
po_format_list (void)
1248
0
{
1249
0
  gl_once (all_formats_init_once, all_formats_init);
1250
0
  return all_formats;
1251
0
}
1252
1253
1254
/* Return the pretty name associated with a format type.
1255
   For example, for "csharp-format", return "C#".
1256
   Return NULL if the argument is not a supported format type.  */
1257
1258
const char *
1259
po_format_pretty_name (const char *format_type)
1260
0
{
1261
0
  size_t len = strlen (format_type);
1262
0
  size_t i;
1263
1264
0
  if (len >= 7 && memcmp (format_type + len - 7, "-format", 7) == 0)
1265
0
    for (i = 0; i < NFORMATS; i++)
1266
0
      if (strlen (format_language[i]) == len - 7
1267
0
          && memcmp (format_language[i], format_type, len - 7) == 0)
1268
        /* The given format_type corresponds to (enum format_type) i.  */
1269
0
        return format_language_pretty[i];
1270
0
  return NULL;
1271
0
}
1272
1273
1274
/* Free an iterator.  */
1275
1276
void
1277
po_flag_iterator_free (po_flag_iterator_t iterator)
1278
0
{
1279
0
  free (iterator);
1280
0
}
1281
1282
1283
/* Return the next flag, and advance the iterator.
1284
   Return NULL at the end of the list of flags.  */
1285
const char *
1286
po_flag_next (po_flag_iterator_t iterator)
1287
0
{
1288
0
  message_ty *mp = (message_ty *) iterator->message;
1289
1290
0
  if (mp != NULL)
1291
0
    switch (iterator->kind)
1292
0
      {
1293
0
      case 0:
1294
        /* Return the next workflow flag.  */
1295
0
        if (iterator->index == 0)
1296
0
          {
1297
0
            iterator->index++;
1298
0
            if (mp->is_fuzzy)
1299
0
              return "fuzzy";
1300
0
          }
1301
        /* Here iterator->index == 1.  */
1302
0
        return NULL;
1303
1304
0
      case 1:
1305
        /* Return the next sticky flag.  */
1306
0
        while (iterator->index < 2 * NFORMATS + 1)
1307
0
          {
1308
0
            size_t i = iterator->index;
1309
0
            iterator->index++;
1310
0
            if (i < 2 * NFORMATS)
1311
0
              {
1312
0
                if ((i % 2) == 0)
1313
0
                  {
1314
0
                    i = i / 2;
1315
0
                    if (possible_format_p (mp->is_format[i]))
1316
0
                      return format_flag[i] + 3; /* without "no-" prefix */
1317
0
                  }
1318
0
                else
1319
0
                  {
1320
0
                    i = i / 2;
1321
0
                    if (not_format_p (mp->is_format[i]))
1322
0
                      return format_flag[i] + 0; /* with "no-" prefix */
1323
0
                  }
1324
0
              }
1325
0
            else if (i == 2 * NFORMATS)
1326
0
              {
1327
0
                if (mp->do_wrap == no)
1328
0
                  return "no-wrap";
1329
0
              }
1330
0
          }
1331
        /* Here iterator->index == 2 * NFORMATS + 1.  */
1332
0
        return NULL;
1333
1334
0
      default:
1335
0
        abort ();
1336
0
      }
1337
0
  return NULL;
1338
0
}
1339
1340
1341
/* Test whether an entire file PO file is valid, like msgfmt does it.
1342
   If it is invalid, pass the reasons to the handler.  */
1343
1344
void
1345
po_file_check_all (po_file_t file, po_xerror_handler_t handler)
1346
0
{
1347
0
  msgdomain_list_ty *mdlp;
1348
0
  size_t k;
1349
1350
  /* Establish error handler for check_message_list().  */
1351
0
  unsigned int error_count = 0;
1352
0
  struct xerror_handler local_xerror_handler =
1353
0
    {
1354
0
      (void (*) (int, const message_ty *, const char *, size_t, size_t, int, const char *))
1355
0
      handler->xerror,
1356
0
      (void (*) (int, const message_ty *, const char *, size_t, size_t, int, const char *, const message_ty *, const char *, size_t, size_t, int, const char *))
1357
0
      handler->xerror2,
1358
0
      &error_count
1359
0
    };
1360
1361
0
  mdlp = file->mdlp;
1362
0
  for (k = 0; k < mdlp->nitems; k++)
1363
0
    check_message_list (mdlp->item[k]->messages, 1, 1, 1, 1, 1, 0, 0, 0,
1364
0
                        &local_xerror_handler);
1365
0
}
1366
1367
1368
/* Test a single message, to be inserted in a PO file in memory, like msgfmt
1369
   does it.  If it is invalid, pass the reasons to the handler.  The iterator
1370
   is not modified by this call; it only specifies the file and the domain.  */
1371
1372
void
1373
po_message_check_all (po_message_t message, po_message_iterator_t iterator,
1374
                      po_xerror_handler_t handler)
1375
0
{
1376
0
  message_ty *mp = (message_ty *) message;
1377
1378
  /* Establish error handler for check_message_list().  */
1379
0
  unsigned int error_count = 0;
1380
0
  struct xerror_handler local_xerror_handler =
1381
0
    {
1382
0
      (void (*) (int, const message_ty *, const char *, size_t, size_t, int, const char *))
1383
0
      handler->xerror,
1384
0
      (void (*) (int, const message_ty *, const char *, size_t, size_t, int, const char *, const message_ty *, const char *, size_t, size_t, int, const char *))
1385
0
      handler->xerror2,
1386
0
      &error_count
1387
0
    };
1388
1389
  /* For plural checking, combine the message and its header into a small,
1390
     two-element message list.  */
1391
0
  {
1392
0
    message_ty *header;
1393
1394
    /* Find the header.  */
1395
0
    {
1396
0
      message_list_ty *mlp;
1397
0
      size_t j;
1398
1399
0
      header = NULL;
1400
0
      mlp =
1401
0
        msgdomain_list_sublist (iterator->file->mdlp, iterator->domain, false);
1402
0
      if (mlp != NULL)
1403
0
        for (j = 0; j < mlp->nitems; j++)
1404
0
          if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
1405
0
            {
1406
0
              header = mlp->item[j];
1407
0
              break;
1408
0
            }
1409
0
    }
1410
1411
0
    {
1412
0
      message_ty *items[2];
1413
0
      struct message_list_ty ml;
1414
0
      ml.item = items;
1415
0
      ml.nitems = 0;
1416
0
      ml.nitems_max = 2;
1417
0
      ml.use_hashtable = false;
1418
1419
0
      if (header != NULL)
1420
0
        message_list_append (&ml, header);
1421
0
      if (mp != header)
1422
0
        message_list_append (&ml, mp);
1423
1424
0
      check_message_list (&ml, 1, 1, 1, 1, 1, 0, 0, 0, &local_xerror_handler);
1425
0
    }
1426
0
  }
1427
0
}
1428
1429
1430
/* Test whether the message translation is a valid format string if the message
1431
   is marked as being a format string.  If it is invalid, pass the reasons to
1432
   the handler.  */
1433
void
1434
po_message_check_format (po_message_t message, po_xerror_handler_t handler)
1435
0
{
1436
0
  message_ty *mp = (message_ty *) message;
1437
1438
  /* Establish error handler for check_message().  */
1439
0
  unsigned int error_count = 0;
1440
0
  struct xerror_handler local_xerror_handler =
1441
0
    {
1442
0
      (void (*) (int, const message_ty *, const char *, size_t, size_t, int, const char *))
1443
0
      handler->xerror,
1444
0
      (void (*) (int, const message_ty *, const char *, size_t, size_t, int, const char *, const message_ty *, const char *, size_t, size_t, int, const char *))
1445
0
      handler->xerror2,
1446
0
      &error_count
1447
0
    };
1448
1449
0
  if (!mp->obsolete)
1450
0
    check_message (mp, &mp->pos, 0, 1, NULL, 0, 0, 0, 0, &local_xerror_handler);
1451
0
}