1 /* Public API for GNU gettext PO files.
2 Copyright (C) 2003-2010, 2015 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2003.
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.
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.
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/>. */
23 #include "gettext-po.h"
34 #include "read-catalog.h"
36 #include "write-catalog.h"
41 #include "po-xerror.h"
43 #include "xvasprintf.h"
44 #include "msgl-check.h"
47 #define _(str) gettext(str)
52 msgdomain_list_ty *mdlp;
53 const char *real_filename;
54 const char *logical_filename;
58 struct po_message_iterator
66 /* A po_message_t is actually a 'struct message_ty *'. */
68 /* A po_filepos_t is actually a 'lex_pos_ty *'. */
71 /* Version number: (major<<16) + (minor<<8) + subminor */
72 int libgettextpo_version = LIBGETTEXTPO_VERSION;
75 /* Create an empty PO file representation in memory. */
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;
91 /* Read a PO file into memory.
92 Return its contents. Upon failure, return NULL and set errno. */
95 po_file_read (const char *filename, po_xerror_handler_t handler)
100 if (strcmp (filename, "-") == 0 || strcmp (filename, "/dev/stdin") == 0)
102 filename = _("<stdin>");
107 fp = fopen (filename, "r");
112 /* Establish error handler around read_catalog_stream(). */
114 (void (*) (int, const message_ty *, const char *, size_t, size_t, int, const char *))
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 *))
119 gram_max_allowed_errors = UINT_MAX;
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;
128 /* Restore error handler. */
129 po_xerror = textmode_xerror;
130 po_xerror2 = textmode_xerror2;
131 gram_max_allowed_errors = 20;
140 extern "C" po_file_t po_file_read_v2 (const char *filename, po_error_handler_t handler);
143 po_file_read_v2 (const char *filename, po_error_handler_t handler)
148 if (strcmp (filename, "-") == 0 || strcmp (filename, "/dev/stdin") == 0)
150 filename = _("<stdin>");
155 fp = fopen (filename, "r");
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;
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;
174 /* Restore error handler. */
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;
186 /* Older version for binary backward compatibility. */
188 extern "C" po_file_t po_file_read (const char *filename);
191 po_file_read (const char *filename)
196 if (strcmp (filename, "-") == 0 || strcmp (filename, "/dev/stdin") == 0)
198 filename = _("<stdin>");
203 fp = fopen (filename, "r");
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;
221 /* Write an in-memory PO file to a file.
222 Upon failure, return NULL and set errno. */
225 po_file_write (po_file_t file, const char *filename, po_xerror_handler_t handler)
227 /* Establish error handler around msgdomain_list_print(). */
229 (void (*) (int, const message_ty *, const char *, size_t, size_t, int, const char *))
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 *))
235 msgdomain_list_print (file->mdlp, filename, &output_format_po, true, false);
237 /* Restore error handler. */
238 po_xerror = textmode_xerror;
239 po_xerror2 = textmode_xerror2;
245 /* Older version for binary backward compatibility. */
247 extern "C" po_file_t po_file_write (po_file_t file, const char *filename, po_error_handler_t handler);
250 po_file_write (po_file_t file, const char *filename, po_error_handler_t handler)
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;
258 msgdomain_list_print (file->mdlp, filename, &output_format_po, true, false);
260 /* Restore error handler. */
262 po_error_at_line = error_at_line;
263 po_multiline_warning = multiline_warning;
264 po_multiline_error = multiline_error;
270 /* Free a PO file from memory. */
273 po_file_free (po_file_t file)
275 msgdomain_list_free (file->mdlp);
276 if (file->domains != NULL)
277 free (file->domains);
282 /* Return the names of the domains covered by a PO file in memory. */
285 po_file_domains (po_file_t file)
287 if (file->domains == NULL)
289 size_t n = file->mdlp->nitems;
290 const char **domains = XNMALLOC (n + 1, const char *);
293 for (j = 0; j < n; j++)
294 domains[j] = file->mdlp->item[j]->domain;
297 file->domains = domains;
300 return file->domains;
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. */
309 po_file_domain_header (po_file_t file, const char *domain)
311 message_list_ty *mlp;
315 domain = MESSAGE_DOMAIN_DEFAULT;
316 mlp = msgdomain_list_sublist (file->mdlp, domain, false);
318 for (j = 0; j < mlp->nitems; j++)
319 if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
321 const char *header = mlp->item[j]->msgstr;
324 return xstrdup (header);
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
337 po_header_field (const char *header, const char *field)
339 size_t field_len = strlen (field);
342 for (line = header;;)
344 if (strncmp (line, field, field_len) == 0 && line[field_len] == ':')
346 const char *value_start;
347 const char *value_end;
350 value_start = line + field_len + 1;
351 if (*value_start == ' ')
353 value_end = strchr (value_start, '\n');
354 if (value_end == NULL)
355 value_end = value_start + strlen (value_start);
357 value = XNMALLOC (value_end - value_start + 1, char);
358 memcpy (value, value_start, value_end - value_start);
359 value[value_end - value_start] = '\0';
364 line = strchr (line, '\n');
375 /* Return the header entry with a given field set to a given value. The field
376 is added if necessary.
377 The return value is a freshly allocated string. */
380 po_header_set_field (const char *header, const char *field, const char *value)
382 size_t header_len = strlen (header);
383 size_t field_len = strlen (field);
384 size_t value_len = strlen (value);
389 for (line = header;;)
391 if (strncmp (line, field, field_len) == 0 && line[field_len] == ':')
393 const char *oldvalue_start;
394 const char *oldvalue_end;
395 size_t header_part1_len;
396 size_t header_part3_len;
400 oldvalue_start = line + field_len + 1;
401 if (*oldvalue_start == ' ')
403 oldvalue_end = strchr (oldvalue_start, '\n');
404 if (oldvalue_end == NULL)
405 oldvalue_end = oldvalue_start + strlen (oldvalue_start);
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,
416 *(result + result_len) = '\0';
421 line = strchr (line, '\n');
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);
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';
451 /* Create an iterator for traversing a domain of a PO file in memory.
452 The domain NULL denotes the default domain. */
454 po_message_iterator_t
455 po_message_iterator (po_file_t file, const char *domain)
457 po_message_iterator_t iterator;
460 domain = MESSAGE_DOMAIN_DEFAULT;
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);
472 /* Free an iterator. */
475 po_message_iterator_free (po_message_iterator_t iterator)
477 free (iterator->domain);
482 /* Return the next message, and advance the iterator.
483 Return NULL at the end of the message list. */
486 po_next_message (po_message_iterator_t iterator)
488 if (iterator->mlp != NULL && iterator->index < iterator->mlp->nitems)
489 return (po_message_t) iterator->mlp->item[iterator->index++];
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
500 po_message_insert (po_message_iterator_t iterator, po_message_t message)
502 message_ty *mp = (message_ty *) message;
504 if (iterator->mlp == NULL)
505 /* Now we need to allocate a sublist corresponding to the iterator. */
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. */
515 /* Return a freshly constructed message.
516 To finish initializing the message, you must set the msgid and msgstr. */
519 po_message_create (void)
521 lex_pos_ty pos = { NULL, 0 };
523 return (po_message_t) message_alloc (NULL, NULL, NULL, xstrdup (""), 1, &pos);
527 /* Return the context of a message, or NULL for a message not restricted to a
530 po_message_msgctxt (po_message_t message)
532 message_ty *mp = (message_ty *) message;
538 /* Change the context of a message. NULL means a message not restricted to a
541 po_message_set_msgctxt (po_message_t message, const char *msgctxt)
543 message_ty *mp = (message_ty *) message;
545 if (msgctxt != mp->msgctxt)
547 char *old_msgctxt = (char *) mp->msgctxt;
549 mp->msgctxt = (msgctxt != NULL ? xstrdup (msgctxt) : NULL);
550 if (old_msgctxt != NULL)
556 /* Return the msgid (untranslated English string) of a message. */
559 po_message_msgid (po_message_t message)
561 message_ty *mp = (message_ty *) message;
567 /* Change the msgid (untranslated English string) of a message. */
570 po_message_set_msgid (po_message_t message, const char *msgid)
572 message_ty *mp = (message_ty *) message;
574 if (msgid != mp->msgid)
576 char *old_msgid = (char *) mp->msgid;
578 mp->msgid = xstrdup (msgid);
579 if (old_msgid != NULL)
585 /* Return the msgid_plural (untranslated English plural string) of a message,
586 or NULL for a message without plural. */
589 po_message_msgid_plural (po_message_t message)
591 message_ty *mp = (message_ty *) message;
593 return mp->msgid_plural;
597 /* Change the msgid_plural (untranslated English plural string) of a message.
598 NULL means a message without plural. */
601 po_message_set_msgid_plural (po_message_t message, const char *msgid_plural)
603 message_ty *mp = (message_ty *) message;
605 if (msgid_plural != mp->msgid_plural)
607 char *old_msgid_plural = (char *) mp->msgid_plural;
609 mp->msgid_plural = (msgid_plural != NULL ? xstrdup (msgid_plural) : NULL);
610 if (old_msgid_plural != NULL)
611 free (old_msgid_plural);
616 /* Return the msgstr (translation) of a message.
617 Return the empty string for an untranslated message. */
620 po_message_msgstr (po_message_t message)
622 message_ty *mp = (message_ty *) message;
628 /* Change the msgstr (translation) of a message.
629 Use an empty string to denote an untranslated message. */
632 po_message_set_msgstr (po_message_t message, const char *msgstr)
634 message_ty *mp = (message_ty *) message;
636 if (msgstr != mp->msgstr)
638 char *old_msgstr = (char *) mp->msgstr;
640 mp->msgstr = xstrdup (msgstr);
641 mp->msgstr_len = strlen (mp->msgstr) + 1;
642 if (old_msgstr != NULL)
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. */
652 po_message_msgstr_plural (po_message_t message, int index)
654 message_ty *mp = (message_ty *) message;
656 if (mp->msgid_plural != NULL && index >= 0)
659 const char *p_end = mp->msgstr + mp->msgstr_len;
661 for (p = mp->msgstr; ; p += strlen (p) + 1, index--)
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. */
679 po_message_set_msgstr_plural (po_message_t message, int index, const char *msgstr)
681 message_ty *mp = (message_ty *) message;
683 if (mp->msgid_plural != NULL && index >= 0)
685 char *p = (char *) mp->msgstr;
686 char *p_end = (char *) mp->msgstr + mp->msgstr_len;
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);
695 copied_msgstr = NULL;
697 for (; ; p += strlen (p) + 1, index--)
701 /* Append at the end. */
704 size_t new_msgstr_len = mp->msgstr_len + index + strlen (msgstr) + 1;
707 (char *) xrealloc ((char *) mp->msgstr, new_msgstr_len);
708 p = (char *) mp->msgstr + mp->msgstr_len;
709 for (; index > 0; index--)
711 memcpy (p, msgstr, strlen (msgstr) + 1);
712 mp->msgstr_len = new_msgstr_len;
714 if (copied_msgstr != NULL)
715 free (copied_msgstr);
723 if (p + strlen (p) + 1 >= p_end)
725 /* Remove the string that starts at p. */
726 mp->msgstr_len = p - mp->msgstr;
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. */
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;
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;
748 if (copied_msgstr != NULL)
749 free (copied_msgstr);
754 /* Return the comments for a message. */
757 po_message_comments (po_message_t message)
759 /* FIXME: memory leak. */
760 message_ty *mp = (message_ty *) message;
762 if (mp->comment == NULL || mp->comment->nitems == 0)
765 return string_list_join (mp->comment, "\n", '\n', true);
769 /* Change the comments for a message.
770 comments should be a multiline string, ending in a newline, or empty. */
773 po_message_set_comments (po_message_t message, const char *comments)
775 message_ty *mp = (message_ty *) message;
776 string_list_ty *slp = string_list_alloc ();
779 char *copy = xstrdup (comments);
783 while (*rest != '\0')
785 char *newline = strchr (rest, '\n');
790 string_list_append (slp, rest);
795 string_list_append (slp, rest);
802 if (mp->comment != NULL)
803 string_list_free (mp->comment);
809 /* Return the extracted comments for a message. */
812 po_message_extracted_comments (po_message_t message)
814 /* FIXME: memory leak. */
815 message_ty *mp = (message_ty *) message;
817 if (mp->comment_dot == NULL || mp->comment_dot->nitems == 0)
820 return string_list_join (mp->comment_dot, "\n", '\n', true);
824 /* Change the extracted comments for a message.
825 comments should be a multiline string, ending in a newline, or empty. */
828 po_message_set_extracted_comments (po_message_t message, const char *comments)
830 message_ty *mp = (message_ty *) message;
831 string_list_ty *slp = string_list_alloc ();
834 char *copy = xstrdup (comments);
838 while (*rest != '\0')
840 char *newline = strchr (rest, '\n');
845 string_list_append (slp, rest);
850 string_list_append (slp, rest);
857 if (mp->comment_dot != NULL)
858 string_list_free (mp->comment_dot);
860 mp->comment_dot = slp;
864 /* Return the i-th file position for a message, or NULL if i is out of
868 po_message_filepos (po_message_t message, int i)
870 message_ty *mp = (message_ty *) message;
872 if (i >= 0 && (size_t)i < mp->filepos_count)
873 return (po_filepos_t) &mp->filepos[i];
879 /* Remove the i-th file position from a message.
880 The indices of all following file positions for the message are decremented
884 po_message_remove_filepos (po_message_t message, int i)
886 message_ty *mp = (message_ty *) message;
890 size_t j = (size_t)i;
891 size_t n = mp->filepos_count;
895 mp->filepos_count = n = n - 1;
896 free ((char *) mp->filepos[j].file_name);
898 mp->filepos[j] = mp->filepos[j + 1];
904 /* Add a file position to a message, if it is not already present for the
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. */
911 po_message_add_filepos (po_message_t message, const char *file, size_t start_line)
913 message_ty *mp = (message_ty *) message;
915 message_comment_filepos (mp, file, start_line);
919 /* Return the previous context of a message, or NULL for none. */
922 po_message_prev_msgctxt (po_message_t message)
924 message_ty *mp = (message_ty *) message;
926 return mp->prev_msgctxt;
930 /* Change the previous context of a message. NULL is allowed. */
933 po_message_set_prev_msgctxt (po_message_t message, const char *prev_msgctxt)
935 message_ty *mp = (message_ty *) message;
937 if (prev_msgctxt != mp->prev_msgctxt)
939 char *old_prev_msgctxt = (char *) mp->prev_msgctxt;
941 mp->prev_msgctxt = (prev_msgctxt != NULL ? xstrdup (prev_msgctxt) : NULL);
942 if (old_prev_msgctxt != NULL)
943 free (old_prev_msgctxt);
948 /* Return the previous msgid (untranslated English string) of a message, or
952 po_message_prev_msgid (po_message_t message)
954 message_ty *mp = (message_ty *) message;
956 return mp->prev_msgid;
960 /* Change the previous msgid (untranslated English string) of a message.
964 po_message_set_prev_msgid (po_message_t message, const char *prev_msgid)
966 message_ty *mp = (message_ty *) message;
968 if (prev_msgid != mp->prev_msgid)
970 char *old_prev_msgid = (char *) mp->prev_msgid;
972 mp->prev_msgid = (prev_msgid != NULL ? xstrdup (prev_msgid) : NULL);
973 if (old_prev_msgid != NULL)
974 free (old_prev_msgid);
979 /* Return the previous msgid_plural (untranslated English plural string) of a
980 message, or NULL for none. */
983 po_message_prev_msgid_plural (po_message_t message)
985 message_ty *mp = (message_ty *) message;
987 return mp->prev_msgid_plural;
991 /* Change the previous msgid_plural (untranslated English plural string) of a
992 message. NULL is allowed. */
995 po_message_set_prev_msgid_plural (po_message_t message, const char *prev_msgid_plural)
997 message_ty *mp = (message_ty *) message;
999 if (prev_msgid_plural != mp->prev_msgid_plural)
1001 char *old_prev_msgid_plural = (char *) mp->prev_msgid_plural;
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);
1011 /* Return true if the message is marked obsolete. */
1014 po_message_is_obsolete (po_message_t message)
1016 message_ty *mp = (message_ty *) message;
1018 return (mp->obsolete ? 1 : 0);
1022 /* Change the obsolete mark of a message. */
1025 po_message_set_obsolete (po_message_t message, int obsolete)
1027 message_ty *mp = (message_ty *) message;
1029 mp->obsolete = obsolete;
1033 /* Return true if the message is marked fuzzy. */
1036 po_message_is_fuzzy (po_message_t message)
1038 message_ty *mp = (message_ty *) message;
1040 return (mp->is_fuzzy ? 1 : 0);
1044 /* Change the fuzzy mark of a message. */
1047 po_message_set_fuzzy (po_message_t message, int fuzzy)
1049 message_ty *mp = (message_ty *) message;
1051 mp->is_fuzzy = fuzzy;
1055 /* Return true if the message is marked as being a format string of the given
1056 type (e.g. "c-format"). */
1059 po_message_is_format (po_message_t message, const char *format_type)
1061 message_ty *mp = (message_ty *) message;
1062 size_t len = strlen (format_type);
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);
1075 /* Change the format string mark for a given type of a message. */
1078 po_message_set_format (po_message_t message, const char *format_type, /*bool*/int value)
1080 message_ty *mp = (message_ty *) message;
1081 size_t len = strlen (format_type);
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);
1093 /* If a numeric range of a message is set, return true and store the minimum
1094 and maximum value in *MINP and *MAXP. */
1097 po_message_is_range (po_message_t message, int *minp, int *maxp)
1099 message_ty *mp = (message_ty *) message;
1101 if (has_range_p (mp->range))
1103 *minp = mp->range.min;
1104 *maxp = mp->range.max;
1112 /* Change the numeric range of a message. MIN and MAX must be non-negative,
1113 with MIN < MAX. Use MIN = MAX = -1 to remove the numeric range of a
1117 po_message_set_range (po_message_t message, int min, int max)
1119 message_ty *mp = (message_ty *) message;
1121 if (min >= 0 && max >= min)
1123 mp->range.min = min;
1124 mp->range.max = max;
1126 else if (min < 0 && max < 0)
1131 /* Other values of min and max are invalid. */
1135 /* Return the file name. */
1138 po_filepos_file (po_filepos_t filepos)
1140 lex_pos_ty *pp = (lex_pos_ty *) filepos;
1142 return pp->file_name;
1146 /* Return the line number where the string starts, or (size_t)(-1) if no line
1147 number is available. */
1150 po_filepos_start_line (po_filepos_t filepos)
1152 lex_pos_ty *pp = (lex_pos_ty *) filepos;
1154 return pp->line_number;
1158 /* Return a NULL terminated array of the supported format types. */
1160 const char * const *
1161 po_format_list (void)
1163 static const char * const * whole_list /* = NULL */;
1164 if (whole_list == NULL)
1166 const char **list = XNMALLOC (NFORMATS + 1, const char *);
1168 for (i = 0; i < NFORMATS; i++)
1169 list[i] = xasprintf ("%s-format", format_language[i]);
1177 /* Return the pretty name associated with a format type.
1178 For example, for "csharp-format", return "C#".
1179 Return NULL if the argument is not a supported format type. */
1182 po_format_pretty_name (const char *format_type)
1184 size_t len = strlen (format_type);
1187 if (len >= 7 && memcmp (format_type + len - 7, "-format", 7) == 0)
1188 for (i = 0; i < NFORMATS; i++)
1189 if (strlen (format_language[i]) == len - 7
1190 && memcmp (format_language[i], format_type, len - 7) == 0)
1191 /* The given format_type corresponds to (enum format_type) i. */
1192 return format_language_pretty[i];
1197 /* Test whether an entire file PO file is valid, like msgfmt does it.
1198 If it is invalid, pass the reasons to the handler. */
1201 po_file_check_all (po_file_t file, po_xerror_handler_t handler)
1203 msgdomain_list_ty *mdlp;
1206 /* Establish error handler. */
1208 (void (*) (int, const message_ty *, const char *, size_t, size_t, int, const char *))
1211 (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 *))
1215 for (k = 0; k < mdlp->nitems; k++)
1216 check_message_list (mdlp->item[k]->messages, 1, 1, 1, 1, 1, 0, 0, 0);
1218 /* Restore error handler. */
1219 po_xerror = textmode_xerror;
1220 po_xerror2 = textmode_xerror2;
1224 /* Test a single message, to be inserted in a PO file in memory, like msgfmt
1225 does it. If it is invalid, pass the reasons to the handler. The iterator
1226 is not modified by this call; it only specifies the file and the domain. */
1229 po_message_check_all (po_message_t message, po_message_iterator_t iterator,
1230 po_xerror_handler_t handler)
1232 message_ty *mp = (message_ty *) message;
1234 /* Establish error handler. */
1236 (void (*) (int, const message_ty *, const char *, size_t, size_t, int, const char *))
1239 (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 *))
1242 /* For plural checking, combine the message and its header into a small,
1243 two-element message list. */
1247 /* Find the header. */
1249 message_list_ty *mlp;
1254 msgdomain_list_sublist (iterator->file->mdlp, iterator->domain, false);
1256 for (j = 0; j < mlp->nitems; j++)
1257 if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
1259 header = mlp->item[j];
1265 message_ty *items[2];
1266 struct message_list_ty ml;
1270 ml.use_hashtable = false;
1273 message_list_append (&ml, header);
1275 message_list_append (&ml, mp);
1277 check_message_list (&ml, 1, 1, 1, 1, 1, 0, 0, 0);
1281 /* Restore error handler. */
1282 po_xerror = textmode_xerror;
1283 po_xerror2 = textmode_xerror2;
1287 /* Test whether the message translation is a valid format string if the message
1288 is marked as being a format string. If it is invalid, pass the reasons to
1291 po_message_check_format (po_message_t message, po_xerror_handler_t handler)
1293 message_ty *mp = (message_ty *) message;
1295 /* Establish error handler. */
1297 (void (*) (int, const message_ty *, const char *, size_t, size_t, int, const char *))
1300 (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 *))
1304 check_message (mp, &mp->pos, 0, 1, NULL, 0, 0, 0, 0);
1306 /* Restore error handler. */
1307 po_xerror = textmode_xerror;
1308 po_xerror2 = textmode_xerror2;
1310 #undef po_message_check_format
1312 /* Older version for binary backward compatibility. */
1314 /* An error logger based on the po_error function pointer. */
1316 po_error_logger (const char *format, ...)
1317 __attribute__ ((__format__ (__printf__, 1, 2)));
1319 po_error_logger (const char *format, ...)
1322 char *error_message;
1324 va_start (args, format);
1325 if (vasprintf (&error_message, format, args) < 0)
1326 error (EXIT_FAILURE, 0, _("memory exhausted"));
1328 po_error (0, 0, "%s", error_message);
1329 free (error_message);
1332 /* Test whether the message translation is a valid format string if the message
1333 is marked as being a format string. If it is invalid, pass the reasons to
1336 extern "C" void po_message_check_format (po_message_t message, po_error_handler_t handler);
1339 po_message_check_format (po_message_t message, po_error_handler_t handler)
1341 message_ty *mp = (message_ty *) message;
1343 /* Establish error handler for po_error_logger(). */
1344 po_error = handler->error;
1346 check_msgid_msgstr_format (mp->msgid, mp->msgid_plural,
1347 mp->msgstr, mp->msgstr_len,
1348 mp->is_format, mp->range, NULL, po_error_logger);
1350 /* Restore error handler. */