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