No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / gettext / gettext-tools / src / gettext-po.c
bloba8a67319d42ec5f8efe8c3889114525c5f3dcf5c
1 /* Public API for GNU gettext PO files.
2 Copyright (C) 2003-2005 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 2, or (at your option)
8 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, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
23 /* Specification. */
24 #include "gettext-po.h"
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdarg.h>
30 #include <string.h>
32 #include "message.h"
33 #include "xalloc.h"
34 #include "read-po.h"
35 #include "write-po.h"
36 #include "error.h"
37 #include "xerror.h"
38 #include "po-error.h"
39 #include "vasprintf.h"
40 #include "format.h"
41 #include "gettext.h"
43 #define _(str) gettext(str)
46 struct po_file
48 msgdomain_list_ty *mdlp;
49 const char *real_filename;
50 const char *logical_filename;
51 const char **domains;
54 struct po_message_iterator
56 po_file_t file;
57 char *domain;
58 message_list_ty *mlp;
59 size_t index;
62 /* A po_message_t is actually a 'struct message_ty *'. */
64 /* A po_filepos_t is actually a 'lex_pos_ty *'. */
67 /* Version number: (major<<16) + (minor<<8) + subminor */
68 int libgettextpo_version = LIBGETTEXTPO_VERSION;
71 /* Create an empty PO file representation in memory. */
73 po_file_t
74 po_file_create (void)
76 po_file_t file;
78 file = (struct po_file *) xmalloc (sizeof (struct po_file));
79 file->mdlp = msgdomain_list_alloc (false);
80 file->real_filename = _("<unnamed>");
81 file->logical_filename = file->real_filename;
82 file->domains = NULL;
83 return file;
87 /* Read a PO file into memory.
88 Return its contents. Upon failure, return NULL and set errno. */
90 po_file_t
91 po_file_read (const char *filename, po_error_handler_t handler)
93 FILE *fp;
94 po_file_t file;
96 if (strcmp (filename, "-") == 0 || strcmp (filename, "/dev/stdin") == 0)
98 filename = _("<stdin>");
99 fp = stdin;
101 else
103 fp = fopen (filename, "r");
104 if (fp == NULL)
105 return NULL;
108 /* Establish error handler around read_po(). */
109 po_error = handler->error;
110 po_error_at_line = handler->error_at_line;
111 po_multiline_warning = handler->multiline_warning;
112 po_multiline_error = handler->multiline_error;
114 file = (struct po_file *) xmalloc (sizeof (struct po_file));
115 file->real_filename = filename;
116 file->logical_filename = filename;
117 file->mdlp = read_po (fp, file->real_filename, file->logical_filename);
118 file->domains = NULL;
120 /* Restore error handler. */
121 po_error = error;
122 po_error_at_line = error_at_line;
123 po_multiline_warning = multiline_warning;
124 po_multiline_error = multiline_error;
126 if (fp != stdin)
127 fclose (fp);
128 return file;
130 #undef po_file_read
132 /* Older version for binary backward compatibility. */
133 po_file_t
134 po_file_read (const char *filename)
136 FILE *fp;
137 po_file_t file;
139 if (strcmp (filename, "-") == 0 || strcmp (filename, "/dev/stdin") == 0)
141 filename = _("<stdin>");
142 fp = stdin;
144 else
146 fp = fopen (filename, "r");
147 if (fp == NULL)
148 return NULL;
151 file = (struct po_file *) xmalloc (sizeof (struct po_file));
152 file->real_filename = filename;
153 file->logical_filename = filename;
154 file->mdlp = read_po (fp, file->real_filename, file->logical_filename);
155 file->domains = NULL;
157 if (fp != stdin)
158 fclose (fp);
159 return file;
163 /* Write an in-memory PO file to a file.
164 Upon failure, return NULL and set errno. */
166 po_file_t
167 po_file_write (po_file_t file, const char *filename, po_error_handler_t handler)
169 /* Establish error handler around msgdomain_list_print(). */
170 po_error = handler->error;
171 po_error_at_line = handler->error_at_line;
172 po_multiline_warning = handler->multiline_warning;
173 po_multiline_error = handler->multiline_error;
175 msgdomain_list_print (file->mdlp, filename, true, false);
177 /* Restore error handler. */
178 po_error = error;
179 po_error_at_line = error_at_line;
180 po_multiline_warning = multiline_warning;
181 po_multiline_error = multiline_error;
183 return file;
187 /* Free a PO file from memory. */
189 void
190 po_file_free (po_file_t file)
192 msgdomain_list_free (file->mdlp);
193 if (file->domains != NULL)
194 free (file->domains);
195 free (file);
199 /* Return the names of the domains covered by a PO file in memory. */
201 const char * const *
202 po_file_domains (po_file_t file)
204 if (file->domains == NULL)
206 size_t n = file->mdlp->nitems;
207 const char **domains =
208 (const char **) xmalloc ((n + 1) * sizeof (const char *));
209 size_t j;
211 for (j = 0; j < n; j++)
212 domains[j] = file->mdlp->item[j]->domain;
213 domains[n] = NULL;
215 file->domains = domains;
218 return file->domains;
222 /* Return the header entry of a domain of a PO file in memory.
223 The domain NULL denotes the default domain.
224 Return NULL if there is no header entry. */
226 const char *
227 po_file_domain_header (po_file_t file, const char *domain)
229 message_list_ty *mlp;
230 size_t j;
232 if (domain == NULL)
233 domain = MESSAGE_DOMAIN_DEFAULT;
234 mlp = msgdomain_list_sublist (file->mdlp, domain, false);
235 if (mlp != NULL)
236 for (j = 0; j < mlp->nitems; j++)
237 if (mlp->item[j]->msgid[0] == '\0' && !mlp->item[j]->obsolete)
239 const char *header = mlp->item[j]->msgstr;
241 if (header != NULL)
242 return xstrdup (header);
243 else
244 return NULL;
246 return NULL;
250 /* Return the value of a field in a header entry.
251 The return value is either a freshly allocated string, to be freed by the
252 caller, or NULL. */
254 char *
255 po_header_field (const char *header, const char *field)
257 size_t field_len = strlen (field);
258 const char *line;
260 for (line = header;;)
262 if (strncmp (line, field, field_len) == 0
263 && line[field_len] == ':' && line[field_len + 1] == ' ')
265 const char *value_start;
266 const char *value_end;
267 char *value;
269 value_start = line + field_len + 2;
270 value_end = strchr (value_start, '\n');
271 if (value_end == NULL)
272 value_end = value_start + strlen (value_start);
274 value = (char *) xmalloc (value_end - value_start + 1);
275 memcpy (value, value_start, value_end - value_start);
276 value[value_end - value_start] = '\0';
278 return value;
281 line = strchr (line, '\n');
282 if (line != NULL)
283 line++;
284 else
285 break;
288 return NULL;
292 /* Return the header entry with a given field set to a given value. The field
293 is added if necessary.
294 The return value is a freshly allocated string. */
296 char *
297 po_header_set_field (const char *header, const char *field, const char *value)
299 size_t header_len = strlen (header);
300 size_t field_len = strlen (field);
301 size_t value_len = strlen (value);
304 const char *line;
306 for (line = header;;)
308 if (strncmp (line, field, field_len) == 0
309 && line[field_len] == ':' && line[field_len + 1] == ' ')
311 const char *oldvalue_start;
312 const char *oldvalue_end;
313 size_t oldvalue_len;
314 size_t header_part1_len;
315 size_t header_part3_len;
316 size_t result_len;
317 char *result;
319 oldvalue_start = line + field_len + 2;
320 oldvalue_end = strchr (oldvalue_start, '\n');
321 if (oldvalue_end == NULL)
322 oldvalue_end = oldvalue_start + strlen (oldvalue_start);
323 oldvalue_len = oldvalue_end - oldvalue_start;
325 header_part1_len = oldvalue_start - header;
326 header_part3_len = header + header_len - oldvalue_end;
327 result_len = header_part1_len + value_len + header_part3_len;
328 /* = header_len - oldvalue_len + value_len */
329 result = (char *) xmalloc (result_len + 1);
330 memcpy (result, header, header_part1_len);
331 memcpy (result + header_part1_len, value, value_len);
332 memcpy (result + header_part1_len + value_len, oldvalue_end,
333 header_part3_len);
334 *(result + result_len) = '\0';
336 return result;
339 line = strchr (line, '\n');
340 if (line != NULL)
341 line++;
342 else
343 break;
347 size_t newline;
348 size_t result_len;
349 char *result;
351 newline = (header_len > 0 && header[header_len - 1] != '\n' ? 1 : 0);
352 result_len = header_len + newline + field_len + 2 + value_len + 1;
353 result = (char *) xmalloc (result_len + 1);
354 memcpy (result, header, header_len);
355 if (newline)
356 *(result + header_len) = '\n';
357 memcpy (result + header_len + newline, field, field_len);
358 *(result + header_len + newline + field_len) = ':';
359 *(result + header_len + newline + field_len + 1) = ' ';
360 memcpy (result + header_len + newline + field_len + 2, value, value_len);
361 *(result + header_len + newline + field_len + 2 + value_len) = '\n';
362 *(result + result_len) = '\0';
364 return result;
369 /* Create an iterator for traversing a domain of a PO file in memory.
370 The domain NULL denotes the default domain. */
372 po_message_iterator_t
373 po_message_iterator (po_file_t file, const char *domain)
375 po_message_iterator_t iterator;
377 if (domain == NULL)
378 domain = MESSAGE_DOMAIN_DEFAULT;
380 iterator =
381 (struct po_message_iterator *)
382 xmalloc (sizeof (struct po_message_iterator));
383 iterator->file = file;
384 iterator->domain = xstrdup (domain);
385 iterator->mlp = msgdomain_list_sublist (file->mdlp, domain, false);
386 iterator->index = 0;
388 return iterator;
392 /* Free an iterator. */
394 void
395 po_message_iterator_free (po_message_iterator_t iterator)
397 free (iterator->domain);
398 free (iterator);
402 /* Return the next message, and advance the iterator.
403 Return NULL at the end of the message list. */
405 po_message_t
406 po_next_message (po_message_iterator_t iterator)
408 if (iterator->mlp != NULL && iterator->index < iterator->mlp->nitems)
409 return (po_message_t) iterator->mlp->item[iterator->index++];
410 else
411 return NULL;
415 /* Insert a message in a PO file in memory, in the domain and at the position
416 indicated by the iterator. The iterator thereby advances past the freshly
417 inserted message. */
419 void
420 po_message_insert (po_message_iterator_t iterator, po_message_t message)
422 message_ty *mp = (message_ty *) message;
424 if (iterator->mlp == NULL)
425 /* Now we need to allocate a sublist corresponding to the iterator. */
426 iterator->mlp =
427 msgdomain_list_sublist (iterator->file->mdlp, iterator->domain, true);
428 /* Insert the message. */
429 message_list_insert_at (iterator->mlp, iterator->index, mp);
430 /* Advance the iterator. */
431 iterator->index++;
435 /* Return a freshly constructed message.
436 To finish initializing the message, you must set the msgid and msgstr. */
438 po_message_t
439 po_message_create (void)
441 lex_pos_ty pos = { NULL, 0 };
443 return (po_message_t) message_alloc (NULL, NULL, NULL, 0, &pos);
447 /* Return the msgid (untranslated English string) of a message. */
449 const char *
450 po_message_msgid (po_message_t message)
452 message_ty *mp = (message_ty *) message;
454 return mp->msgid;
458 /* Change the msgid (untranslated English string) of a message. */
460 void
461 po_message_set_msgid (po_message_t message, const char *msgid)
463 message_ty *mp = (message_ty *) message;
465 if (msgid != mp->msgid)
467 char *old_msgid = (char *) mp->msgid;
469 mp->msgid = xstrdup (msgid);
470 if (old_msgid != NULL)
471 free (old_msgid);
476 /* Return the msgid_plural (untranslated English plural string) of a message,
477 or NULL for a message without plural. */
479 const char *
480 po_message_msgid_plural (po_message_t message)
482 message_ty *mp = (message_ty *) message;
484 return mp->msgid_plural;
488 /* Change the msgid_plural (untranslated English plural string) of a message.
489 NULL means a message without plural. */
491 void
492 po_message_set_msgid_plural (po_message_t message, const char *msgid_plural)
494 message_ty *mp = (message_ty *) message;
496 if (msgid_plural != mp->msgid_plural)
498 char *old_msgid_plural = (char *) mp->msgid_plural;
500 mp->msgid_plural = (msgid_plural != NULL ? xstrdup (msgid_plural) : NULL);
501 if (old_msgid_plural != NULL)
502 free (old_msgid_plural);
507 /* Return the msgstr (translation) of a message.
508 Return the empty string for an untranslated message. */
510 const char *
511 po_message_msgstr (po_message_t message)
513 message_ty *mp = (message_ty *) message;
515 return mp->msgstr;
519 /* Change the msgstr (translation) of a message.
520 Use an empty string to denote an untranslated message. */
522 void
523 po_message_set_msgstr (po_message_t message, const char *msgstr)
525 message_ty *mp = (message_ty *) message;
527 if (msgstr != mp->msgstr)
529 char *old_msgstr = (char *) mp->msgstr;
531 mp->msgstr = xstrdup (msgstr);
532 mp->msgstr_len = strlen (mp->msgstr) + 1;
533 if (old_msgstr != NULL)
534 free (old_msgstr);
539 /* Return the msgstr[index] for a message with plural handling, or
540 NULL when the index is out of range or for a message without plural. */
542 const char *
543 po_message_msgstr_plural (po_message_t message, int index)
545 message_ty *mp = (message_ty *) message;
547 if (mp->msgid_plural != NULL && index >= 0)
549 const char *p;
550 const char *p_end = mp->msgstr + mp->msgstr_len;
552 for (p = mp->msgstr; ; p += strlen (p) + 1, index--)
554 if (p >= p_end)
555 return NULL;
556 if (index == 0)
557 break;
559 return p;
561 else
562 return NULL;
566 /* Change the msgstr[index] for a message with plural handling.
567 Use a NULL value at the end to reduce the number of plural forms. */
569 void
570 po_message_set_msgstr_plural (po_message_t message, int index, const char *msgstr)
572 message_ty *mp = (message_ty *) message;
574 if (mp->msgid_plural != NULL && index >= 0)
576 char *p = (char *) mp->msgstr;
577 char *p_end = (char *) mp->msgstr + mp->msgstr_len;
578 char *copied_msgstr;
580 /* Special care must be taken of the case that msgstr points into the
581 mp->msgstr string list, because mp->msgstr may be relocated before we
582 are done with msgstr. */
583 if (msgstr >= p && msgstr < p_end)
584 msgstr = copied_msgstr = xstrdup (msgstr);
585 else
586 copied_msgstr = NULL;
588 for (; ; p += strlen (p) + 1, index--)
590 if (p >= p_end)
592 /* Append at the end. */
593 if (msgstr != NULL)
595 size_t new_msgstr_len = mp->msgstr_len + index + strlen (msgstr) + 1;
597 mp->msgstr =
598 (char *) xrealloc ((char *) mp->msgstr, new_msgstr_len);
599 p = (char *) mp->msgstr + mp->msgstr_len;
600 for (; index > 0; index--)
601 *p++ = '\0';
602 memcpy (p, msgstr, strlen (msgstr) + 1);
603 mp->msgstr_len = new_msgstr_len;
605 if (copied_msgstr != NULL)
606 free (copied_msgstr);
607 return;
609 if (index == 0)
610 break;
612 if (msgstr == NULL)
614 if (p + strlen (p) + 1 >= p_end)
616 /* Remove the string that starts at p. */
617 mp->msgstr_len = p - mp->msgstr;
618 return;
620 /* It is not possible to remove an element of the string list
621 except the last one. So just replace it with the empty string.
622 That's the best we can do here. */
623 msgstr = "";
626 /* Replace the string that starts at p. */
627 size_t i1 = p - mp->msgstr;
628 size_t i2before = i1 + strlen (p);
629 size_t i2after = i1 + strlen (msgstr);
630 size_t new_msgstr_len = mp->msgstr_len - i2before + i2after;
632 if (i2after > i2before)
633 mp->msgstr = (char *) xrealloc ((char *) mp->msgstr, new_msgstr_len);
634 memmove ((char *) mp->msgstr + i2after, mp->msgstr + i2before,
635 mp->msgstr_len - i2before);
636 memcpy ((char *) mp->msgstr + i1, msgstr, i2after - i1);
637 mp->msgstr_len = new_msgstr_len;
639 if (copied_msgstr != NULL)
640 free (copied_msgstr);
645 /* Return the comments for a message. */
647 const char *
648 po_message_comments (po_message_t message)
650 /* FIXME: memory leak. */
651 message_ty *mp = (message_ty *) message;
653 if (mp->comment == NULL || mp->comment->nitems == 0)
654 return "";
655 else
656 return string_list_join (mp->comment, '\n', '\n', true);
660 /* Change the comments for a message.
661 comments should be a multiline string, ending in a newline, or empty. */
663 void
664 po_message_set_comments (po_message_t message, const char *comments)
666 message_ty *mp = (message_ty *) message;
667 string_list_ty *slp = string_list_alloc ();
670 char *copy = xstrdup (comments);
671 char *rest;
673 rest = copy;
674 while (*rest != '\0')
676 char *newline = strchr (rest, '\n');
678 if (newline != NULL)
680 *newline = '\0';
681 string_list_append (slp, rest);
682 rest = newline + 1;
684 else
686 string_list_append (slp, rest);
687 break;
690 free (copy);
693 if (mp->comment != NULL)
694 string_list_free (mp->comment);
696 mp->comment = slp;
700 /* Return the extracted comments for a message. */
702 const char *
703 po_message_extracted_comments (po_message_t message)
705 /* FIXME: memory leak. */
706 message_ty *mp = (message_ty *) message;
708 if (mp->comment_dot == NULL || mp->comment_dot->nitems == 0)
709 return "";
710 else
711 return string_list_join (mp->comment_dot, '\n', '\n', true);
715 /* Change the extracted comments for a message.
716 comments should be a multiline string, ending in a newline, or empty. */
718 void
719 po_message_set_extracted_comments (po_message_t message, const char *comments)
721 message_ty *mp = (message_ty *) message;
722 string_list_ty *slp = string_list_alloc ();
725 char *copy = xstrdup (comments);
726 char *rest;
728 rest = copy;
729 while (*rest != '\0')
731 char *newline = strchr (rest, '\n');
733 if (newline != NULL)
735 *newline = '\0';
736 string_list_append (slp, rest);
737 rest = newline + 1;
739 else
741 string_list_append (slp, rest);
742 break;
745 free (copy);
748 if (mp->comment_dot != NULL)
749 string_list_free (mp->comment_dot);
751 mp->comment_dot = slp;
755 /* Return the i-th file position for a message, or NULL if i is out of
756 range. */
758 po_filepos_t
759 po_message_filepos (po_message_t message, int i)
761 message_ty *mp = (message_ty *) message;
763 if (i >= 0 && (size_t)i < mp->filepos_count)
764 return (po_filepos_t) &mp->filepos[i];
765 else
766 return NULL;
770 /* Remove the i-th file position from a message.
771 The indices of all following file positions for the message are decremented
772 by one. */
774 void
775 po_message_remove_filepos (po_message_t message, int i)
777 message_ty *mp = (message_ty *) message;
779 if (i >= 0)
781 size_t j = (size_t)i;
782 size_t n = mp->filepos_count;
784 if (j < n)
786 mp->filepos_count = n = n - 1;
787 free ((char *) mp->filepos[j].file_name);
788 for (; j < n; j++)
789 mp->filepos[j] = mp->filepos[j + 1];
795 /* Add a file position to a message, if it is not already present for the
796 message.
797 file is the file name.
798 start_line is the line number where the string starts, or (size_t)(-1) if no
799 line number is available. */
801 void
802 po_message_add_filepos (po_message_t message, const char *file, size_t start_line)
804 message_ty *mp = (message_ty *) message;
806 message_comment_filepos (mp, file, start_line);
810 /* Return true if the message is marked obsolete. */
813 po_message_is_obsolete (po_message_t message)
815 message_ty *mp = (message_ty *) message;
817 return (mp->obsolete ? 1 : 0);
821 /* Change the obsolete mark of a message. */
823 void
824 po_message_set_obsolete (po_message_t message, int obsolete)
826 message_ty *mp = (message_ty *) message;
828 mp->obsolete = obsolete;
832 /* Return true if the message is marked fuzzy. */
835 po_message_is_fuzzy (po_message_t message)
837 message_ty *mp = (message_ty *) message;
839 return (mp->is_fuzzy ? 1 : 0);
843 /* Change the fuzzy mark of a message. */
845 void
846 po_message_set_fuzzy (po_message_t message, int fuzzy)
848 message_ty *mp = (message_ty *) message;
850 mp->is_fuzzy = fuzzy;
854 /* Return true if the message is marked as being a format string of the given
855 type (e.g. "c-format"). */
858 po_message_is_format (po_message_t message, const char *format_type)
860 message_ty *mp = (message_ty *) message;
861 size_t len = strlen (format_type);
862 size_t i;
864 if (len >= 7 && memcmp (format_type + len - 7, "-format", 7) == 0)
865 for (i = 0; i < NFORMATS; i++)
866 if (strlen (format_language[i]) == len - 7
867 && memcmp (format_language[i], format_type, len - 7) == 0)
868 /* The given format_type corresponds to (enum format_type) i. */
869 return (possible_format_p (mp->is_format[i]) ? 1 : 0);
870 return 0;
874 /* Change the format string mark for a given type of a message. */
876 void
877 po_message_set_format (po_message_t message, const char *format_type, /*bool*/int value)
879 message_ty *mp = (message_ty *) message;
880 size_t len = strlen (format_type);
881 size_t i;
883 if (len >= 7 && memcmp (format_type + len - 7, "-format", 7) == 0)
884 for (i = 0; i < NFORMATS; i++)
885 if (strlen (format_language[i]) == len - 7
886 && memcmp (format_language[i], format_type, len - 7) == 0)
887 /* The given format_type corresponds to (enum format_type) i. */
888 mp->is_format[i] = (value ? yes : no);
892 /* An error logger based on the po_error function pointer. */
893 static void
894 po_error_logger (const char *format, ...)
896 va_list args;
897 char *error_message;
899 va_start (args, format);
900 if (vasprintf (&error_message, format, args) < 0)
901 error (EXIT_FAILURE, 0, _("memory exhausted"));
902 va_end (args);
903 po_error (0, 0, "%s", error_message);
904 free (error_message);
907 /* Test whether the message translation is a valid format string if the message
908 is marked as being a format string. If it is invalid, pass the reasons to
909 the handler. */
910 void
911 po_message_check_format (po_message_t message, po_error_handler_t handler)
913 message_ty *mp = (message_ty *) message;
915 /* Establish error handler for po_error_logger(). */
916 po_error = handler->error;
918 check_msgid_msgstr_format (mp->msgid, mp->msgid_plural,
919 mp->msgstr, mp->msgstr_len,
920 mp->is_format, po_error_logger);
922 /* Restore error handler. */
923 po_error = error;
927 /* Return the file name. */
929 const char *
930 po_filepos_file (po_filepos_t filepos)
932 lex_pos_ty *pp = (lex_pos_ty *) filepos;
934 return pp->file_name;
938 /* Return the line number where the string starts, or (size_t)(-1) if no line
939 number is available. */
941 size_t
942 po_filepos_start_line (po_filepos_t filepos)
944 lex_pos_ty *pp = (lex_pos_ty *) filepos;
946 return pp->line_number;