Use custom error handlers during reading and writing of PO files.
+2004-09-05 Bruno Haible <bruno@clisp.org>
+
+ * windows/gettextsrc.def: Add the variables names from po-error.h.
+ * windows/gettextpo.def: Add many new function names.
+
2004-04-19 Bruno Haible <bruno@clisp.org>
* gettext.m4: Change jm_ to gl_ in all uses of AC_DEFINE'd names.
+2004-09-05 Bruno Haible <bruno@clisp.org>
+
+ * Makevars (XGETTEXT_OPTIONS): Recognize po_error and po_error_at_line.
+
2004-03-21 Bruno Haible <bruno@clisp.org>
* ca.po: Update from Ivan Vilata i Balaguer <ivan@selidor.net>.
--flag=error:3:c-format --flag=error_at_line:5:c-format \
--flag=asprintf:2:c-format --flag=vasprintf:2:c-format \
--flag=xasprintf:1:c-format \
+ --flag=po_error:3:c-format --flag=po_error_at_line:5:c-format \
--flag=po_gram_error:1:c-format --flag=po_gram_error_at_line:2:c-format
# This is the copyright holder that gets inserted into the header of the
+2004-09-05 Bruno Haible <bruno@clisp.org>
+
+ * po-error.h: New file.
+ * po-error.c: New file.
+ * po-charset.c: Include po-error.h.
+ (po_lex_charset_set): Use po_multiline_warning instead of
+ multiline_warning.
+ * po-lex.h: Include po-error.h.
+ (po_gram_error): Use po_error instead of error.
+ (po_gram_error_at_line): Use po_error_at_line instead of error_at_line.
+ * po-lex.c (po_gram_error): Use po_error instead of error.
+ (po_gram_error_at_line): Use po_error_at_line instead of error_at_line.
+ (mbfile_getc, lex_getc): Use po_error instead of error.
+ * read-po-abstract.c (po_scan): Likewise.
+ * write-po.c: Include po-error.h instead of error.h.
+ (wrap): Use po_error instead of error.
+ (message_print, message_print_obsolete): Use po_multiline_warning
+ instead of multiline_warning.
+ (msgdomain_list_print): Use po_error instead of error, po_error_at_line
+ instead of error_at_line.
+ * str-list.h (string_list_join): New declaration.
+ * str-list.c (string_list_join): New function.
+ * message.h (message_list_insert_at): New declaration.
+ * message.c (message_list_insert_at): New function.
+ * gettext-po.h (po_filepos_t): New type.
+ (struct po_error_handler, po_error_handler_t): New types.
+ (po_file_create): New declaration.
+ (po_file_read): Add handler argument.
+ (po_file_write, po_message_insert, po_message_create,
+ po_message_set_msgid, po_message_set_msgid_plural,
+ po_message_set_msgstr, po_message_set_msgstr_plural,
+ po_message_comments, po_message_set_comments,
+ po_message_extracted_comments, po_message_filepos,
+ po_message_set_obsolete, po_message_set_fuzzy, po_message_set_format,
+ po_filepos_file, po_filepos_start_line): New declarations.
+ * gettext-po.c (po_file_create): New function.
+ (po_file_read): Add handler argument. Keep an old version for backward
+ compatibility.
+ (po_file_write): New function.
+ (po_message_iterator): Store more information, to make
+ po_message_insert possible.
+ (po_message_iterator_free): Update.
+ (po_next_message): Don't crash if iterator->mlp is NULL.
+ (po_message_insert): New function.
+ (po_message_create): New function.
+ (po_message_set_msgid, po_message_set_msgid_plural,
+ po_message_set_msgstr, po_message_set_msgstr_plural,
+ po_message_comments, po_message_set_comments,
+ po_message_extracted_comments, po_message_filepos,
+ po_message_set_obsolete, po_message_set_fuzzy, po_message_set_format,
+ po_filepos_file, po_filepos_start_line): New functions.
+ * Makefile.am (noinst_HEADERS): Add po-error.h.
+ (COMMON_SOURCE): Add po-error.c.
+ * Makefile.msvc (OBJECTS): Add po-error.obj.
+ (po-error.obj): New rule.
+ * Makefile.vms (OBJECTS): Add po-error.obj.
+ (po-error.obj): New rule.
+ * FILES: Update.
+
2004-09-03 Bruno Haible <bruno@clisp.org>
* Makefile.am (msginit_LDADD): Use the INTL_MACOSX_LDFLAGS.
msgl-ascii.c
Message list test for ASCII character set.
+po-error.h
+po-error.c
+ Error handling during writing and reading of PO files.
+
write-po.h
write-po.c
Output of a list-of-messages to a PO file.
include_HEADERS = gettext-po.h
-noinst_HEADERS = pos.h message.h po-gram.h po-hash.h po-charset.h po-lex.h \
-open-po.h read-po-abstract.h read-po.h read-properties.h read-stringtable.h \
-str-list.h write-po.h write-properties.h write-stringtable.h dir-list.h \
-file-list.h po-gram-gen.h po-gram-gen2.h po-hash-gen.h msgl-charset.h \
-msgl-equal.h msgl-iconv.h msgl-ascii.h msgl-cat.h msgl-english.h msgfmt.h \
-msgunfmt.h plural-count.h read-mo.h write-mo.h read-java.h write-java.h \
-read-csharp.h write-csharp.h read-resources.h write-resources.h read-tcl.h \
-write-tcl.h write-qt.h po-time.h plural-table.h format.h \
+noinst_HEADERS = pos.h message.h po-error.h po-gram.h po-hash.h po-charset.h \
+po-lex.h open-po.h \
+read-po-abstract.h read-po.h read-properties.h read-stringtable.h \
+str-list.h \
+write-po.h write-properties.h write-stringtable.h \
+dir-list.h file-list.h po-gram-gen.h po-gram-gen2.h po-hash-gen.h \
+msgl-charset.h msgl-equal.h msgl-iconv.h msgl-ascii.h msgl-cat.h \
+msgl-english.h msgfmt.h msgunfmt.h plural-count.h \
+read-mo.h write-mo.h \
+read-java.h write-java.h \
+read-csharp.h write-csharp.h \
+read-resources.h write-resources.h \
+read-tcl.h write-tcl.h \
+write-qt.h \
+po-time.h plural-table.h format.h \
xgettext.h x-c.h x-po.h x-sh.h x-python.h x-lisp.h x-elisp.h x-librep.h \
x-smalltalk.h x-java.h x-properties.h x-csharp.h x-awk.h x-ycp.h x-tcl.h \
x-perl.h x-php.h x-stringtable.h x-rst.h x-glade.h
# (read-po-abstract.c <--> po-hash-gen.y <--> po-gram-gen.y <--> po-lex.c) -> open-po.c -> dir-list.c -> str-list.c.
# (read-po-abstract.c <--> po-hash-gen.y <--> po-gram-gen.y <--> po-lex.c) -> po-charset.c.
# (read-po-abstract.c <--> po-hash-gen.y <--> po-gram-gen.y <--> po-lex.c) -> message.c -> str-list.c.
-COMMON_SOURCE = message.c \
+COMMON_SOURCE = message.c po-error.c \
read-po-abstract.c po-lex.c po-gram-gen.y po-hash-gen.y po-charset.c \
read-properties.c read-stringtable.c open-po.c dir-list.c str-list.c
OBJECTS = \
message.obj \
+ po-error.obj \
read-po-abstract.obj \
po-lex.obj \
po-gram-gen.obj \
message.obj : message.c
$(CC) $(INCLUDES) $(CFLAGS) $(PICFLAGS) -c message.c
+po-error.obj : po-error.c
+ $(CC) $(INCLUDES) $(CFLAGS) $(PICFLAGS) -c -Tp po-error.c
+
read-po-abstract.obj : read-po-abstract.c
$(CC) $(INCLUDES) $(CFLAGS) $(PICFLAGS) -c read-po-abstract.c
OBJECTS = \
message.obj, \
+ po-error.obj, \
read-po-abstract.obj, \
po-lex.obj, \
po-gram-gen.obj, \
message.obj : message.c
$(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) message.c
+po-error.obj : po-error.c
+ $(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) po-error.c
+
read-po-abstract.obj : read-po-abstract.c
$(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) read-po-abstract.c
/* Public API for GNU gettext PO files.
- Copyright (C) 2003 Free Software Foundation, Inc.
+ Copyright (C) 2003-2004 Free Software Foundation, Inc.
Written by Bruno Haible <bruno@clisp.org>, 2003.
This program is free software; you can redistribute it and/or modify
/* Specification. */
#include "gettext-po.h"
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "message.h"
#include "xalloc.h"
#include "read-po.h"
+#include "write-po.h"
+#include "error.h"
+#include "xerror.h"
+#include "po-error.h"
+#include "gettext.h"
+
+#define _(str) gettext(str)
struct po_file
struct po_message_iterator
{
+ po_file_t file;
+ char *domain;
message_list_ty *mlp;
size_t index;
};
/* A po_message_t is actually a 'struct message_ty *'. */
+/* A po_filepos_t is actually a 'lex_pos_ty *'. */
+
+
+/* Create an empty PO file representation in memory. */
+
+po_file_t
+po_file_create (void)
+{
+ po_file_t file;
+
+ file = (struct po_file *) xmalloc (sizeof (struct po_file));
+ file->mdlp = msgdomain_list_alloc (false);
+ file->real_filename = _("<unnamed>");
+ file->logical_filename = file->real_filename;
+ file->domains = NULL;
+ return file;
+}
+
/* Read a PO file into memory.
Return its contents. Upon failure, return NULL and set errno. */
+po_file_t
+po_file_read (const char *filename, po_error_handler_t handler)
+{
+ FILE *fp;
+ po_file_t file;
+
+ fp = fopen (filename, "r");
+ if (fp == NULL)
+ return NULL;
+
+ /* Establish error handler around read_po(). */
+ po_error = handler->error;
+ po_error_at_line = handler->error_at_line;
+ po_multiline_warning = handler->multiline_warning;
+ po_multiline_error = handler->multiline_error;
+
+ file = (struct po_file *) xmalloc (sizeof (struct po_file));
+ file->real_filename = filename;
+ file->logical_filename = filename;
+ file->mdlp = read_po (fp, file->real_filename, file->logical_filename);
+ file->domains = NULL;
+
+ /* Restore error handler. */
+ po_error = error;
+ po_error_at_line = error_at_line;
+ po_multiline_warning = multiline_warning;
+ po_multiline_error = multiline_error;
+
+ fclose (fp);
+ return file;
+}
+#undef po_file_read
+
+/* Older version for binary backward compatibility. */
po_file_t
po_file_read (const char *filename)
{
fp = fopen (filename, "r");
if (fp == NULL)
return NULL;
+
file = (struct po_file *) xmalloc (sizeof (struct po_file));
file->real_filename = filename;
file->logical_filename = filename;
file->mdlp = read_po (fp, file->real_filename, file->logical_filename);
file->domains = NULL;
+
fclose (fp);
return file;
}
+/* Write an in-memory PO file to a file.
+ Upon failure, return NULL and set errno. */
+
+po_file_t
+po_file_write (po_file_t file, const char *filename, po_error_handler_t handler)
+{
+ /* Establish error handler around msgdomain_list_print(). */
+ po_error = handler->error;
+ po_error_at_line = handler->error_at_line;
+ po_multiline_warning = handler->multiline_warning;
+ po_multiline_error = handler->multiline_error;
+
+ msgdomain_list_print (file->mdlp, filename, true, false);
+
+ /* Restore error handler. */
+ po_error = error;
+ po_error_at_line = error_at_line;
+ po_multiline_warning = multiline_warning;
+ po_multiline_error = multiline_error;
+
+ return file;
+}
+
+
/* Free a PO file from memory. */
void
iterator =
(struct po_message_iterator *)
xmalloc (sizeof (struct po_message_iterator));
+ iterator->file = file;
+ iterator->domain = xstrdup (domain);
iterator->mlp = msgdomain_list_sublist (file->mdlp, domain, false);
iterator->index = 0;
void
po_message_iterator_free (po_message_iterator_t iterator)
{
+ free (iterator->domain);
free (iterator);
}
po_message_t
po_next_message (po_message_iterator_t iterator)
{
- if (iterator->index < iterator->mlp->nitems)
+ if (iterator->mlp != NULL && iterator->index < iterator->mlp->nitems)
return (po_message_t) iterator->mlp->item[iterator->index++];
else
return NULL;
}
+/* Insert a message in a PO file in memory, in the domain and at the position
+ indicated by the iterator. The iterator thereby advances past the freshly
+ inserted message. */
+
+void
+po_message_insert (po_message_iterator_t iterator, po_message_t message)
+{
+ message_ty *mp = (message_ty *) message;
+
+ if (iterator->mlp == NULL)
+ /* Now we need to allocate a sublist corresponding to the iterator. */
+ iterator->mlp =
+ msgdomain_list_sublist (iterator->file->mdlp, iterator->domain, true);
+ /* Insert the message. */
+ message_list_insert_at (iterator->mlp, iterator->index, mp);
+ /* Advance the iterator. */
+ iterator->index++;
+}
+
+
+/* Return a freshly constructed message.
+ To finish initializing the message, you must set the msgid and msgstr. */
+
+po_message_t
+po_message_create (void)
+{
+ lex_pos_ty pos = { NULL, 0 };
+
+ return (po_message_t) message_alloc (NULL, NULL, NULL, 0, &pos);
+}
+
+
/* Return the msgid (untranslated English string) of a message. */
const char *
}
+/* Change the msgid (untranslated English string) of a message. */
+
+void
+po_message_set_msgid (po_message_t message, const char *msgid)
+{
+ message_ty *mp = (message_ty *) message;
+
+ if (msgid != mp->msgid)
+ {
+ char *old_msgid = (char *) mp->msgid;
+
+ mp->msgid = xstrdup (msgid);
+ if (old_msgid != NULL)
+ free (old_msgid);
+ }
+}
+
+
/* Return the msgid_plural (untranslated English plural string) of a message,
or NULL for a message without plural. */
}
+/* Change the msgid_plural (untranslated English plural string) of a message.
+ NULL means a message without plural. */
+
+void
+po_message_set_msgid_plural (po_message_t message, const char *msgid_plural)
+{
+ message_ty *mp = (message_ty *) message;
+
+ if (msgid_plural != mp->msgid_plural)
+ {
+ char *old_msgid_plural = (char *) mp->msgid_plural;
+
+ mp->msgid_plural = (msgid_plural != NULL ? xstrdup (msgid_plural) : NULL);
+ if (old_msgid_plural != NULL)
+ free (old_msgid_plural);
+ }
+}
+
+
/* Return the msgstr (translation) of a message.
Return the empty string for an untranslated message. */
}
+/* Change the msgstr (translation) of a message.
+ Use an empty string to denote an untranslated message. */
+
+void
+po_message_set_msgstr (po_message_t message, const char *msgstr)
+{
+ message_ty *mp = (message_ty *) message;
+
+ if (msgstr != mp->msgstr)
+ {
+ char *old_msgstr = (char *) mp->msgstr;
+
+ mp->msgstr = xstrdup (msgstr);
+ mp->msgstr_len = strlen (mp->msgstr) + 1;
+ if (old_msgstr != NULL)
+ free (old_msgstr);
+ }
+}
+
+
/* Return the msgstr[index] for a message with plural handling, or
NULL when the index is out of range or for a message without plural. */
}
+/* Change the msgstr[index] for a message with plural handling.
+ Use a NULL value at the end to reduce the number of plural forms. */
+
+void
+po_message_set_msgstr_plural (po_message_t message, int index, const char *msgstr)
+{
+ message_ty *mp = (message_ty *) message;
+
+ if (mp->msgid_plural != NULL && index >= 0)
+ {
+ char *p = (char *) mp->msgstr;
+ char *p_end = (char *) mp->msgstr + mp->msgstr_len;
+ char *copied_msgstr;
+
+ /* Special care must be taken of the case that msgstr points into the
+ mp->msgstr string list, because mp->msgstr may be relocated before we
+ are done with msgstr. */
+ if (msgstr >= p && msgstr < p_end)
+ msgstr = copied_msgstr = xstrdup (msgstr);
+ else
+ copied_msgstr = NULL;
+
+ for (; ; p += strlen (p) + 1, index--)
+ {
+ if (p >= p_end)
+ {
+ /* Append at the end. */
+ if (msgstr != NULL)
+ {
+ size_t new_msgstr_len = mp->msgstr_len + index + strlen (msgstr) + 1;
+
+ mp->msgstr =
+ (char *) xrealloc ((char *) mp->msgstr, new_msgstr_len);
+ p = (char *) mp->msgstr + mp->msgstr_len;
+ for (; index > 0; index--)
+ *p++ = '\0';
+ memcpy (p, msgstr, strlen (msgstr) + 1);
+ mp->msgstr_len = new_msgstr_len;
+ }
+ if (copied_msgstr != NULL)
+ free (copied_msgstr);
+ return;
+ }
+ if (index == 0)
+ break;
+ }
+ if (msgstr == NULL)
+ {
+ if (p + strlen (p) + 1 >= p_end)
+ {
+ /* Remove the string that starts at p. */
+ mp->msgstr_len = p - mp->msgstr;
+ return;
+ }
+ /* It is not possible to remove an element of the string list
+ except the last one. So just replace it with the empty string.
+ That's the best we can do here. */
+ msgstr = "";
+ }
+ {
+ /* Replace the string that starts at p. */
+ size_t i1 = p - mp->msgstr;
+ size_t i2before = i1 + strlen (p);
+ size_t i2after = i1 + strlen (msgstr);
+ size_t new_msgstr_len = mp->msgstr_len - i2before + i2after;
+
+ if (i2after > i2before)
+ mp->msgstr = (char *) xrealloc ((char *) mp->msgstr, new_msgstr_len);
+ memmove ((char *) mp->msgstr + i2after, mp->msgstr + i2before,
+ mp->msgstr_len - i2before);
+ memcpy ((char *) mp->msgstr + i1, msgstr, i2after - i1);
+ mp->msgstr_len = new_msgstr_len;
+ }
+ if (copied_msgstr != NULL)
+ free (copied_msgstr);
+ }
+}
+
+
+/* Return the comments for a message. */
+
+const char *
+po_message_comments (po_message_t message)
+{
+ /* FIXME: memory leak. */
+ message_ty *mp = (message_ty *) message;
+
+ if (mp->comment == NULL || mp->comment->nitems == 0)
+ return "";
+ else
+ return string_list_join (mp->comment, '\n', '\n', true);
+}
+
+
+/* Change the comments for a message.
+ comments should be a multiline string, ending in a newline, or empty. */
+
+void
+po_message_set_comments (po_message_t message, const char *comments)
+{
+ message_ty *mp = (message_ty *) message;
+ string_list_ty *slp = string_list_alloc ();
+
+ {
+ char *copy = xstrdup (comments);
+ char *rest;
+
+ rest = copy;
+ while (*rest != '\0')
+ {
+ char *newline = strchr (rest, '\n');
+
+ if (newline != NULL)
+ {
+ *newline = '\0';
+ string_list_append (slp, rest);
+ rest = newline + 1;
+ }
+ else
+ {
+ string_list_append (slp, rest);
+ break;
+ }
+ }
+ free (copy);
+ }
+
+ if (mp->comment != NULL)
+ string_list_free (mp->comment);
+
+ mp->comment = slp;
+}
+
+
+/* Return the extracted comments for a message. */
+
+const char *
+po_message_extracted_comments (po_message_t message)
+{
+ /* FIXME: memory leak. */
+ message_ty *mp = (message_ty *) message;
+
+ if (mp->comment_dot == NULL || mp->comment_dot->nitems == 0)
+ return "";
+ else
+ return string_list_join (mp->comment_dot, '\n', '\n', true);
+}
+
+
+/* Return the i-th file position for a message, or NULL if i is out of
+ range. */
+
+po_filepos_t
+po_message_filepos (po_message_t message, int i)
+{
+ message_ty *mp = (message_ty *) message;
+
+ if (i >= 0 && (size_t)i < mp->filepos_count)
+ return (po_filepos_t) &mp->filepos[i];
+ else
+ return NULL;
+}
+
+
/* Return true if the message is marked obsolete. */
int
}
+/* Change the obsolete mark of a message. */
+
+void
+po_message_set_obsolete (po_message_t message, int obsolete)
+{
+ message_ty *mp = (message_ty *) message;
+
+ mp->obsolete = obsolete;
+}
+
+
/* Return true if the message is marked fuzzy. */
int
}
+/* Change the fuzzy mark of a message. */
+
+void
+po_message_set_fuzzy (po_message_t message, int fuzzy)
+{
+ message_ty *mp = (message_ty *) message;
+
+ mp->is_fuzzy = fuzzy;
+}
+
+
/* Return true if the message is marked as being a format string of the given
type (e.g. "c-format"). */
return (possible_format_p (mp->is_format[i]) ? 1 : 0);
return 0;
}
+
+
+/* Change the format string mark for a given type of a message. */
+
+void
+po_message_set_format (po_message_t message, const char *format_type, /*bool*/int value)
+{
+ message_ty *mp = (message_ty *) message;
+ size_t len = strlen (format_type);
+ size_t i;
+
+ if (len >= 7 && memcmp (format_type + len - 7, "-format", 7) == 0)
+ for (i = 0; i < NFORMATS; i++)
+ if (strlen (format_language[i]) == len - 7
+ && memcmp (format_language[i], format_type, len - 7) == 0)
+ /* The given format_type corresponds to (enum format_type) i. */
+ mp->is_format[i] = (value ? yes : no);
+}
+
+
+#if 0
+/* Test whether the message translation is a valid format string if the message
+ is marked as being a format string. Return NULL if valid or not marked as
+ such, or an explanation string if invalid. */
+
+char *
+po_message_check_format (po_message_t message, const char *format_type)
+{
+ ??
+}
+#endif
+
+
+/* Return the file name. */
+
+const char *
+po_filepos_file (po_filepos_t filepos)
+{
+ lex_pos_ty *pp = (lex_pos_ty *) filepos;
+
+ return pp->file_name;
+}
+
+
+/* Return the line number where the string starts, or (size_t)(-1) if no line
+ number is available. */
+
+size_t
+po_filepos_start_line (po_filepos_t filepos)
+{
+ lex_pos_ty *pp = (lex_pos_ty *) filepos;
+
+ return pp->line_number;
+}
/* Public API for GNU gettext PO files - contained in libgettextpo.
- Copyright (C) 2003 Free Software Foundation, Inc.
+ Copyright (C) 2003-2004 Free Software Foundation, Inc.
Written by Bruno Haible <bruno@clisp.org>, 2003.
This program is free software; you can redistribute it and/or modify
#ifndef _GETTEXT_PO_H
#define _GETTEXT_PO_H 1
+#include <stdlib.h>
+
#ifdef __cplusplus
extern "C" {
#endif
/* A po_message_t represents a message in a PO file. */
typedef struct po_message *po_message_t;
+/* A po_filepos_t represents a string's position within a source file. */
+typedef struct po_filepos *po_filepos_t;
+
+/* A po_error_handler handles error situations. */
+struct po_error_handler
+{
+ /* Signal an error. The error message is built from FORMAT and the following
+ arguments. ERRNUM, if nonzero, is an errno value.
+ Must increment the error_message_count variable declared in error.h.
+ Must not return if STATUS is nonzero. */
+ void (*error) (int status, int errnum,
+ const char *format, ...);
+
+ /* Signal an error. The error message is built from FORMAT and the following
+ arguments. The error location is at FILENAME line LINENO. ERRNUM, if
+ nonzero, is an errno value.
+ Must increment the error_message_count variable declared in error.h.
+ Must not return if STATUS is nonzero. */
+ void (*error_at_line) (int status, int errnum,
+ const char *filename, unsigned int lineno,
+ const char *format, ...);
+
+ /* Signal a multiline warning. The PREFIX applies to all lines of the
+ MESSAGE. Free the PREFIX and MESSAGE when done. */
+ void (*multiline_warning) (char *prefix, char *message);
+
+ /* Signal a multiline error. The PREFIX applies to all lines of the
+ MESSAGE. Free the PREFIX and MESSAGE when done.
+ Must increment the error_message_count variable declared in error.h if
+ PREFIX is non-NULL. */
+ void (*multiline_error) (char *prefix, char *message);
+};
+typedef const struct po_error_handler *po_error_handler_t;
+
/* Memory allocation:
The memory allocations performed by these functions use xmalloc(),
therefore will cause a program exit if memory is exhausted.
/* ============================= po_file_t API ============================= */
+/* Create an empty PO file representation in memory. */
+extern po_file_t po_file_create (void);
+
/* Read a PO file into memory.
Return its contents. Upon failure, return NULL and set errno. */
-extern po_file_t po_file_read (const char *filename);
+#define po_file_read po_file_read_v2
+extern po_file_t po_file_read (const char *filename,
+ po_error_handler_t handler);
+
+/* Write an in-memory PO file to a file.
+ Upon failure, return NULL and set errno. */
+extern po_file_t po_file_write (po_file_t file, const char *filename,
+ po_error_handler_t handler);
/* Free a PO file from memory. */
extern void po_file_free (po_file_t file);
Return NULL at the end of the message list. */
extern po_message_t po_next_message (po_message_iterator_t iterator);
+/* Insert a message in a PO file in memory, in the domain and at the position
+ indicated by the iterator. The iterator thereby advances past the freshly
+ inserted message. */
+extern void po_message_insert (po_message_iterator_t iterator, po_message_t message);
+
/* =========================== po_message_t API ============================ */
+/* Return a freshly constructed message.
+ To finish initializing the message, you must set the msgid and msgstr. */
+extern po_message_t po_message_create (void);
+
/* Return the msgid (untranslated English string) of a message. */
extern const char * po_message_msgid (po_message_t message);
+/* Change the msgid (untranslated English string) of a message. */
+extern void po_message_set_msgid (po_message_t message, const char *msgid);
+
/* Return the msgid_plural (untranslated English plural string) of a message,
or NULL for a message without plural. */
extern const char * po_message_msgid_plural (po_message_t message);
+/* Change the msgid_plural (untranslated English plural string) of a message.
+ NULL means a message without plural. */
+extern void po_message_set_msgid_plural (po_message_t message, const char *msgid_plural);
+
/* Return the msgstr (translation) of a message.
Return the empty string for an untranslated message. */
extern const char * po_message_msgstr (po_message_t message);
+/* Change the msgstr (translation) of a message.
+ Use an empty string to denote an untranslated message. */
+extern void po_message_set_msgstr (po_message_t message, const char *msgstr);
+
/* Return the msgstr[index] for a message with plural handling, or
NULL when the index is out of range or for a message without plural. */
extern const char * po_message_msgstr_plural (po_message_t message, int index);
+/* Change the msgstr[index] for a message with plural handling.
+ Use a NULL value at the end to reduce the number of plural forms. */
+extern void po_message_set_msgstr_plural (po_message_t message, int index, const char *msgstr);
+
+/* Return the comments for a message. */
+extern const char * po_message_comments (po_message_t message);
+
+/* Change the comments for a message.
+ comments should be a multiline string, ending in a newline, or empty. */
+extern void po_message_set_comments (po_message_t message, const char *comments);
+
+/* Return the extracted comments for a message. */
+extern const char * po_message_extracted_comments (po_message_t message);
+
+/* Return the i-th file position for a message, or NULL if i is out of
+ range. */
+extern po_filepos_t po_message_filepos (po_message_t message, int i);
+
/* Return true if the message is marked obsolete. */
extern int po_message_is_obsolete (po_message_t message);
+/* Change the obsolete mark of a message. */
+extern void po_message_set_obsolete (po_message_t message, int obsolete);
+
/* Return true if the message is marked fuzzy. */
extern int po_message_is_fuzzy (po_message_t message);
+/* Change the fuzzy mark of a message. */
+extern void po_message_set_fuzzy (po_message_t message, int fuzzy);
+
/* Return true if the message is marked as being a format string of the given
type (e.g. "c-format"). */
extern int po_message_is_format (po_message_t message, const char *format_type);
+/* Change the format string mark for a given type of a message. */
+extern void po_message_set_format (po_message_t message, const char *format_type, /*bool*/int value);
+
+#if 0
+/* Test whether the message translation is a valid format string if the message
+ is marked as being a format string. Return NULL if valid or not marked as
+ such, or an explanation string if invalid. */
+extern char * po_message_check_format (po_message_t message, const char *format_type);
+#endif
+
+
+/* =========================== po_filepos_t API ============================ */
+
+/* Return the file name. */
+extern const char * po_filepos_file (po_filepos_t filepos);
+
+/* Return the line number where the string starts, or (size_t)(-1) if no line
+ number is available. */
+extern size_t po_filepos_start_line (po_filepos_t filepos);
+
#ifdef __cplusplus
}
/* GNU gettext - internationalization aids
- Copyright (C) 1995-1998, 2000-2003 Free Software Foundation, Inc.
+ Copyright (C) 1995-1998, 2000-2004 Free Software Foundation, Inc.
This file was written by Peter Miller <millerp@canb.auug.org.au>
}
+void
+message_list_insert_at (message_list_ty *mlp, size_t n, message_ty *mp)
+{
+ size_t j;
+
+ if (mlp->nitems >= mlp->nitems_max)
+ {
+ size_t nbytes;
+
+ mlp->nitems_max = mlp->nitems_max * 2 + 4;
+ nbytes = mlp->nitems_max * sizeof (message_ty *);
+ mlp->item = xrealloc (mlp->item, nbytes);
+ }
+ for (j = mlp->nitems; j > n; j--)
+ mlp->item[j] = mlp->item[j - 1];
+ mlp->item[j] = mp;
+ mlp->nitems++;
+
+ if (mlp->use_hashtable)
+ if (insert_entry (&mlp->htable, mp->msgid, strlen (mp->msgid) + 1, mp))
+ /* A message list has duplicates, although it was allocated with the
+ assertion that it wouldn't have duplicates. It is a bug. */
+ abort ();
+}
+
+
#if 0 /* unused */
void
message_list_delete_nth (message_list_ty *mlp, size_t n)
/* GNU gettext - internationalization aids
- Copyright (C) 1995-1998, 2000-2003 Free Software Foundation, Inc.
+ Copyright (C) 1995-1998, 2000-2004 Free Software Foundation, Inc.
This file was written by Peter Miller <millerp@canb.auug.org.au>
message_list_append (message_list_ty *mlp, message_ty *mp);
extern void
message_list_prepend (message_list_ty *mlp, message_ty *mp);
+extern void
+ message_list_insert_at (message_list_ty *mlp, size_t n, message_ty *mp);
extern void
message_list_delete_nth (message_list_ty *mlp, size_t n);
typedef bool message_predicate_ty (const message_ty *mp);
/* Charset handling while reading PO files.
- Copyright (C) 2001-2003 Free Software Foundation, Inc.
+ Copyright (C) 2001-2004 Free Software Foundation, Inc.
Written by Bruno Haible <haible@clisp.cons.org>, 2001.
This program is free software; you can redistribute it and/or modify
#include "xallocsa.h"
#include "xerror.h"
+#include "po-error.h"
#include "basename.h"
#include "progname.h"
#include "strstr.h"
if (!(filenamelen >= 4
&& memcmp (filename + filenamelen - 4, ".pot", 4) == 0
&& strcmp (charset, "CHARSET") == 0))
- multiline_warning (xasprintf (_("%s: warning: "), filename),
- xasprintf (_("\
+ po_multiline_warning (xasprintf (_("%s: warning: "), filename),
+ xasprintf (_("\
Charset \"%s\" is not a portable encoding name.\n\
Message conversion to user's charset might not work.\n"),
- charset));
+ charset));
}
else
{
else
note = _("Continuing anyway.");
- multiline_warning (xasprintf (_("%s: warning: "), filename),
- xasprintf (_("\
+ po_multiline_warning (xasprintf (_("%s: warning: "), filename),
+ xasprintf (_("\
Charset \"%s\" is not supported. %s relies on iconv(),\n\
and iconv() does not support \"%s\".\n"),
- po_lex_charset,
- basename (program_name),
- po_lex_charset));
+ po_lex_charset,
+ basename (program_name),
+ po_lex_charset));
# if !defined _LIBICONV_VERSION
- multiline_warning (NULL,
- xasprintf (_("\
+ po_multiline_warning (NULL,
+ xasprintf (_("\
Installing GNU libiconv and then reinstalling GNU gettext\n\
would fix this problem.\n")));
# endif
- multiline_warning (NULL, xasprintf (_("%s\n"), note));
+ po_multiline_warning (NULL, xasprintf (_("%s\n"), note));
}
#else
/* Test for a charset which has double-byte characters
const char *note =
_("Continuing anyway, expect parse errors.");
- multiline_warning (xasprintf (_("%s: warning: "), filename),
- xasprintf (_("\
+ po_multiline_warning (xasprintf (_("%s: warning: "), filename),
+ xasprintf (_("\
Charset \"%s\" is not supported. %s relies on iconv().\n\
This version was built without iconv().\n"),
- po_lex_charset,
- basename (program_name)));
+ po_lex_charset,
+ basename (program_name)));
- multiline_warning (NULL,
- xasprintf (_("\
+ po_multiline_warning (NULL,
+ xasprintf (_("\
Installing GNU libiconv and then reinstalling GNU gettext\n\
would fix this problem.\n")));
- multiline_warning (NULL, xasprintf (_("%s\n"), note));
+ po_multiline_warning (NULL, xasprintf (_("%s\n"), note));
}
#endif
}
if (!(filenamelen >= 4
&& memcmp (filename + filenamelen - 4, ".pot", 4) == 0))
- multiline_warning (xasprintf (_("%s: warning: "), filename),
- xasprintf (_("\
+ po_multiline_warning (xasprintf (_("%s: warning: "), filename),
+ xasprintf (_("\
Charset missing in header.\n\
Message conversion to user's charset will not work.\n")));
}
/* GNU gettext - internationalization aids
- Copyright (C) 1995-1999, 2000-2003 Free Software Foundation, Inc.
+ Copyright (C) 1995-1999, 2000-2004 Free Software Foundation, Inc.
This file was written by Peter Miller <millerp@canb.auug.org.au>.
Multibyte character handling by Bruno Haible <haible@clisp.cons.org>.
error (EXIT_FAILURE, 0, _("memory exhausted"));
va_end (ap);
error_with_progname = false;
- error (0, 0, "%s:%lu:%d: %s", gram_pos.file_name,
- (unsigned long) gram_pos.line_number, gram_pos_column + 1, buffer);
+ po_error (0, 0, "%s:%lu:%d: %s", gram_pos.file_name,
+ (unsigned long) gram_pos.line_number, gram_pos_column + 1, buffer);
error_with_progname = true;
free (buffer);
if (*fmt == '.')
--error_message_count;
else if (error_message_count >= gram_max_allowed_errors)
- error (EXIT_FAILURE, 0, _("too many errors, aborting"));
+ po_error (EXIT_FAILURE, 0, _("too many errors, aborting"));
}
/* CAUTION: If you change this function, you must also make identical
error (EXIT_FAILURE, 0, _("memory exhausted"));
va_end (ap);
error_with_progname = false;
- error_at_line (0, 0, pp->file_name, pp->line_number, "%s", buffer);
+ po_error_at_line (0, 0, pp->file_name, pp->line_number, "%s", buffer);
error_with_progname = true;
free (buffer);
if (*fmt == '.')
--error_message_count;
else if (error_message_count >= gram_max_allowed_errors)
- error (EXIT_FAILURE, 0, _("too many errors, aborting"));
+ po_error (EXIT_FAILURE, 0, _("too many errors, aborting"));
}
#endif
}
}
else
- error (EXIT_FAILURE, errno, _("iconv failure"));
+ po_error (EXIT_FAILURE, errno, _("iconv failure"));
}
else
{
if (ferror (mbf->fp))
{
bomb:
- error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
- gram_pos.file_name);
+ po_error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
+ gram_pos.file_name);
}
break;
}
/* GNU gettext - internationalization aids
- Copyright (C) 1995-1998, 2000-2003 Free Software Foundation, Inc.
+ Copyright (C) 1995-1998, 2000-2004 Free Software Foundation, Inc.
This file was written by Peter Miller <millerp@canb.auug.org.au>
#include <stdbool.h>
#include "error.h"
#include "error-progname.h"
-#include "pos.h"
#include "xerror.h"
+#include "po-error.h"
+#include "pos.h"
#ifdef __cplusplus
do { \
char *totalfmt = xasprintf ("%s%s", "%s:%lu:%d: ", fmt); \
error_with_progname = false; \
- error (0, 0, totalfmt, gram_pos.file_name, \
- (unsigned long) gram_pos.line_number, gram_pos_column + 1, \
- __VA_ARGS__ + 0); \
+ po_error (0, 0, totalfmt, gram_pos.file_name, \
+ (unsigned long) gram_pos.line_number, gram_pos_column + 1, \
+ __VA_ARGS__ + 0); \
error_with_progname = true; \
free (totalfmt); \
if (*fmt == '.') \
--error_message_count; \
else if (error_message_count >= gram_max_allowed_errors) \
- error (1, 0, _("too many errors, aborting")); \
+ po_error (1, 0, _("too many errors, aborting")); \
} while (0)
/* CAUTION: If you change this macro, you must also make identical
# define po_gram_error_at_line(pos, fmt, ...) \
do { \
error_with_progname = false; \
- error_at_line (0, 0, (pos)->file_name, (pos)->line_number, \
- fmt, __VA_ARGS__ + 0); \
+ po_error_at_line (0, 0, (pos)->file_name, (pos)->line_number, \
+ fmt, __VA_ARGS__ + 0); \
error_with_progname = true; \
if (*fmt == '.') \
--error_message_count; \
else if (error_message_count >= gram_max_allowed_errors) \
- error (1, 0, _("too many errors, aborting")); \
+ po_error (1, 0, _("too many errors, aborting")); \
} while (0)
/* GCC is also smart enough to allow optimizations like this. */
do { \
char *totalfmt = xasprintf ("%s%s", "%s:%d:%d: ", fmt); \
error_with_progname = false; \
- error (0, 0, totalfmt, gram_pos.file_name, gram_pos.line_number, \
- gram_pos_column + 1 , ## args); \
+ po_error (0, 0, totalfmt, gram_pos.file_name, gram_pos.line_number, \
+ gram_pos_column + 1 , ## args); \
error_with_progname = true; \
free (totalfmt); \
if (*fmt == '.') \
--error_message_count; \
else if (error_message_count >= gram_max_allowed_errors) \
- error (1, 0, _("too many errors, aborting")); \
+ po_error (1, 0, _("too many errors, aborting")); \
} while (0)
/* CAUTION: If you change this macro, you must also make identical
# define po_gram_error_at_line(pos, fmt, args...) \
do { \
error_with_progname = false; \
- error_at_line (0, 0, (pos)->file_name, (pos)->line_number, \
- fmt , ## args); \
+ po_error_at_line (0, 0, (pos)->file_name, (pos)->line_number, \
+ fmt , ## args); \
error_with_progname = true; \
if (*fmt == '.') \
--error_message_count; \
else if (error_message_count >= gram_max_allowed_errors) \
- error (1, 0, _("too many errors, aborting")); \
+ po_error (1, 0, _("too many errors, aborting")); \
} while (0)
#else
/* Reading PO files, abstract class.
- Copyright (C) 1995-1996, 1998, 2000-2003 Free Software Foundation, Inc.
+ Copyright (C) 1995-1996, 1998, 2000-2004 Free Software Foundation, Inc.
This file was written by Peter Miller <millerp@canb.auug.org.au>
}
if (error_message_count > 0)
- error (EXIT_FAILURE, 0,
- ngettext ("found %d fatal error", "found %d fatal errors",
- error_message_count),
- error_message_count);
+ po_error (EXIT_FAILURE, 0,
+ ngettext ("found %d fatal error", "found %d fatal errors",
+ error_message_count),
+ error_message_count);
error_message_count = 0;
}
/* GNU gettext - internationalization aids
- Copyright (C) 1995, 1998, 2000-2003 Free Software Foundation, Inc.
+ Copyright (C) 1995, 1998, 2000-2004 Free Software Foundation, Inc.
This file was written by Peter Miller <millerp@canb.auug.org.au>
}
-#if 0 /* unused */
/* Return a freshly allocated string obtained by concatenating all the
- strings in the list, separated by spaces. */
+ strings in the list, separated by the separator character, terminated
+ by the terminator character. The terminator character is not added if
+ drop_redundant_terminator is true and the last string already ends with
+ the terminator. */
char *
-string_list_join (const string_list_ty *slp)
+string_list_join (const string_list_ty *slp, char separator,
+ char terminator, bool drop_redundant_terminator)
{
size_t len;
size_t j;
len = 1;
for (j = 0; j < slp->nitems; ++j)
{
- if (j)
+ if (separator && j > 0)
++len;
len += strlen (slp->item[j]);
}
+ if (terminator)
+ ++len;
result = (char *) xmalloc (len);
pos = 0;
for (j = 0; j < slp->nitems; ++j)
{
- if (j)
- result[pos++] = ' ';
+ if (separator && j > 0)
+ result[pos++] = separator;
len = strlen (slp->item[j]);
memcpy (result + pos, slp->item[j], len);
pos += len;
}
+ if (terminator
+ && !(drop_redundant_terminator
+ && slp->nitems > 0
+ && (len = strlen (slp->item[slp->nitems - 1])) > 0
+ && slp->item[slp->nitems - 1][len - 1] == terminator))
+ result[pos++] = terminator;
result[pos] = '\0';
return result;
}
-#endif
/* Return 1 if s is contained in the list of strings, 0 otherwise. */
/* GNU gettext - internationalization aids
- Copyright (C) 1995-1996, 1998, 2000-2003 Free Software Foundation, Inc.
+ Copyright (C) 1995-1996, 1998, 2000-2004 Free Software Foundation, Inc.
This file was written by Peter Miller <millerp@canb.auug.org.au>
strings in the list, and destroy the list. */
extern char *string_list_concat_destroy (string_list_ty *slp);
-#if 0 /* unused */
/* Return a freshly allocated string obtained by concatenating all the
- strings in the list, separated by spaces. */
-extern char *string_list_join (const string_list_ty *slp);
-#endif
+ strings in the list, separated by the separator character, terminated
+ by the terminator character. The terminator character is not added if
+ drop_redundant_terminator is true and the last string already ends with
+ the terminator. */
+extern char *string_list_join (const string_list_ty *slp, char separator,
+ char terminator, bool drop_redundant_terminator);
/* Return 1 if s is contained in the list of strings, 0 otherwise. */
extern bool string_list_member (const string_list_ty *slp, const char *s);
/* GNU gettext - internationalization aids
- Copyright (C) 1995-1998, 2000-2003 Free Software Foundation, Inc.
+ Copyright (C) 1995-1998, 2000-2004 Free Software Foundation, Inc.
This file was written by Peter Miller <millerp@canb.auug.org.au>
#include "fwriteerror.h"
#include "exit.h"
#include "error-progname.h"
-#include "error.h"
#include "xerror.h"
+#include "po-error.h"
#include "gettext.h"
/* Our regular abbreviation. */
{
if (errno == EILSEQ)
{
- error (0, 0, _("invalid multibyte sequence"));
+ po_error (0, 0, _("invalid multibyte sequence"));
continue;
}
else
/* We warn about any use of escape sequences beside
'\n' and '\t'. */
if (c != 'n' && c != 't')
- error (0, 0, _("\
+ po_error (0, 0, _("\
internationalized messages should not contain the `\\%c' escape sequence"),
- c);
+ c);
}
else if (escape && !c_isprint ((unsigned char) c))
{
{
if (errno == EILSEQ)
{
- error (0, 0, _("invalid multibyte sequence"));
+ po_error (0, 0, _("invalid multibyte sequence"));
continue;
}
else
this domain, emit an empty string. */
if (!is_ascii_string (mp->msgid)
&& po_charset_canonicalize (charset) != po_charset_utf8)
- multiline_warning (xasprintf (_("warning: ")),
- xasprintf (_("\
+ po_multiline_warning (xasprintf (_("warning: ")),
+ xasprintf (_("\
The following msgid contains non-ASCII characters.\n\
This will cause problems to translators who use a character encoding\n\
different from yours. Consider using a pure ASCII msgid instead.\n\
are as readable as possible. */
if (!is_ascii_string (mp->msgid)
&& po_charset_canonicalize (charset) != po_charset_utf8)
- multiline_warning (xasprintf (_("warning: ")),
- xasprintf (_("\
+ po_multiline_warning (xasprintf (_("warning: ")),
+ xasprintf (_("\
The following msgid contains non-ASCII characters.\n\
This will cause problems to translators who use a character encoding\n\
different from yours. Consider using a pure ASCII msgid instead.\n\
if (mdlp->nitems > 1)
{
if (use_syntax_properties)
- error (EXIT_FAILURE, 0, _("Cannot output multiple translation domains into a single file with Java .properties syntax. Try using PO file syntax instead."));
+ po_error (EXIT_FAILURE, 0, _("Cannot output multiple translation domains into a single file with Java .properties syntax. Try using PO file syntax instead."));
if (use_syntax_stringtable)
- error (EXIT_FAILURE, 0, _("Cannot output multiple translation domains into a single file with NeXTstep/GNUstep .strings syntax."));
+ po_error (EXIT_FAILURE, 0, _("Cannot output multiple translation domains into a single file with NeXTstep/GNUstep .strings syntax."));
}
if (mdlp->nitems == 1)
{
{
error_with_progname = false;
if (use_syntax_properties)
- error_at_line (EXIT_FAILURE, 0,
- has_plural->file_name, has_plural->line_number,
- _("message catalog has plural form translations, but the output format does not support them. Try generating a Java class using \"msgfmt --java\", instead of a properties file."));
+ po_error_at_line (EXIT_FAILURE, 0,
+ has_plural->file_name, has_plural->line_number,
+ _("message catalog has plural form translations, but the output format does not support them. Try generating a Java class using \"msgfmt --java\", instead of a properties file."));
if (use_syntax_stringtable)
- error_at_line (EXIT_FAILURE, 0,
- has_plural->file_name, has_plural->line_number,
- _("message catalog has plural form translations, but the output format does not support them."));
+ po_error_at_line (EXIT_FAILURE, 0,
+ has_plural->file_name, has_plural->line_number,
+ _("message catalog has plural form translations, but the output format does not support them."));
error_with_progname = true;
}
}
{
fp = fopen (filename, "w");
if (fp == NULL)
- error (EXIT_FAILURE, errno, _("cannot create output file \"%s\""),
- filename);
+ po_error (EXIT_FAILURE, errno, _("cannot create output file \"%s\""),
+ filename);
}
else
{
/* Make sure nothing went wrong. */
if (fwriteerror (fp))
- error (EXIT_FAILURE, errno, _("error while writing \"%s\" file"),
- filename);
+ po_error (EXIT_FAILURE, errno, _("error while writing \"%s\" file"),
+ filename);
if (fp != stdout)
fclose (fp);
LIBRARY gettextpo
EXPORTS
+po_file_create
po_file_domains
po_file_domain_header
po_file_free
po_file_read
+po_file_read_v2
+po_file_write
+po_filepos_file
+po_filepos_start_line
po_header_field
+po_message_comments
+po_message_create
+po_message_extracted_comments
+po_message_filepos
+po_message_insert
po_message_is_format
po_message_is_fuzzy
po_message_is_obsolete
po_message_msgid_plural
po_message_msgstr
po_message_msgstr_plural
+po_message_set_comments
+po_message_set_format
+po_message_set_fuzzy
+po_message_set_msgid
+po_message_set_msgid_plural
+po_message_set_msgstr
+po_message_set_msgstr_plural
+po_message_set_obsolete
po_next_message
plural_table_size
po_charset_ascii
po_charset_utf8
+po_error
+po_error_at_line
po_gram_lval
po_lex_charset
po_lex_iconv
po_lex_weird_cjk
+po_multiline_error
+po_multiline_warning
use_first
catenate_msgdomain_list
compare_po_locale_charsets