Rearrange gettext sources to taste.
authorjbj <devnull@localhost>
Sun, 20 Sep 1998 18:27:03 +0000 (18:27 +0000)
committerjbj <devnull@localhost>
Sun, 20 Sep 1998 18:27:03 +0000 (18:27 +0000)
CVS patchset: 2332
CVS date: 1998/09/20 18:27:03

tools/fstrcmp.h [deleted file]
tools/message.c [new file with mode: 0644]
tools/rpmgettext.c
tools/rpmpo.h [deleted file]
tools/str-list.h [deleted file]

diff --git a/tools/fstrcmp.h b/tools/fstrcmp.h
deleted file mode 100644 (file)
index 0404717..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/* GNU gettext - internationalization aids
-   Copyright (C) 1995 Free Software Foundation, Inc.
-
-   This file was written by Peter Miller <pmiller@agso.gov.au>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
-
-#ifndef _FSTRCMP_H
-#define _FSTRCMP_H
-
-double fstrcmp PARAMS ((const char *__s1, const char *__s2));
-
-#endif
diff --git a/tools/message.c b/tools/message.c
new file mode 100644 (file)
index 0000000..e381a29
--- /dev/null
@@ -0,0 +1,1404 @@
+/* GNU gettext - internationalization aids
+   Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
+
+   This file was written by Peter Miller <millerp@canb.auug.org.au>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#endif
+
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#endif
+
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+#endif
+
+#include "fstrcmp.h"
+#include "message.h"
+#include "system.h"
+#include "error.h"
+#include "libgettext.h"
+
+
+/* Our regular abbreviation.  */
+#define _(str) gettext (str)
+
+
+/* These two variables control the output style of the message_print
+   function.  Interface functions for them are to be used.  */
+static int indent;
+static int uniforum;
+static int escape;
+
+/* This variable controls the page width when printing messages.
+   Defaults to PAGE_WIDTH if not set.  Zero (0) given to message_page_-
+   width_set will result in no wrapping being performed.  */
+static size_t page_width = PAGE_WIDTH;
+
+
+/* Prototypes for local functions.  */
+static void wrap PARAMS ((FILE *__fp, const char *__line_prefix,
+                         const char *__name, const char *__value,
+                         int do_wrap));
+static void print_blank_line PARAMS ((FILE *__fp));
+static void message_print PARAMS ((const message_ty *__mp, FILE *__fp,
+                                  const char *__domain, int blank_line,
+                                  int __debug));
+static void message_print_obsolete PARAMS ((const message_ty *__mp, FILE *__fp,
+                                           const char *__domain,
+                                           int blank_line));
+static int msgid_cmp PARAMS ((const void *__va, const void *__vb));
+static int filepos_cmp PARAMS ((const void *__va, const void *__vb));
+static const char *make_c_format_description_string PARAMS ((enum is_c_format,
+                                                            int debug));
+static const char *make_c_width_description_string PARAMS ((enum is_c_format));
+static int significant_c_format_p PARAMS ((enum is_c_format __is_c_format));
+
+
+
+message_ty *
+message_alloc (msgid)
+     char *msgid;
+{
+  message_ty *mp;
+
+  mp = xmalloc (sizeof (message_ty));
+  mp->msgid = msgid;
+  mp->comment = NULL;
+  mp->comment_dot = NULL;
+  mp->filepos_count = 0;
+  mp->filepos = NULL;
+  mp->variant_count = 0;
+  mp->variant = NULL;
+  mp->used = 0;
+  mp->obsolete = 0;
+  mp->is_fuzzy = 0;
+  mp->is_c_format = undecided;
+  mp->do_wrap = undecided;
+  return mp;
+}
+
+
+void
+message_free (mp)
+     message_ty *mp;
+{
+  size_t j;
+
+  if (mp->comment != NULL)
+    string_list_free (mp->comment);
+  if (mp->comment_dot != NULL)
+    string_list_free (mp->comment_dot);
+  free ((char *) mp->msgid);
+  for (j = 0; j < mp->variant_count; ++j)
+    free ((char *) mp->variant[j].msgstr);
+  if (mp->variant != NULL)
+    free (mp->variant);
+  for (j = 0; j < mp->filepos_count; ++j)
+    free ((char *) mp->filepos[j].file_name);
+  if (mp->filepos != NULL)
+    free (mp->filepos);
+  free (mp);
+}
+
+
+message_variant_ty *
+message_variant_search (mp, domain)
+     message_ty *mp;
+     const char *domain;
+{
+  size_t j;
+  message_variant_ty *mvp;
+
+  for (j = 0; j < mp->variant_count; ++j)
+    {
+      mvp = &mp->variant[j];
+      if (0 == strcmp (domain, mvp->domain))
+       return mvp;
+    }
+  return 0;
+}
+
+
+void
+message_variant_append (mp, domain, msgstr, pp)
+     message_ty *mp;
+     const char *domain;
+     const char *msgstr;
+     const lex_pos_ty *pp;
+{
+  size_t nbytes;
+  message_variant_ty *mvp;
+
+  nbytes = (mp->variant_count + 1) * sizeof (mp->variant[0]);
+  mp->variant = xrealloc (mp->variant, nbytes);
+  mvp = &mp->variant[mp->variant_count++];
+  mvp->domain = domain;
+  mvp->msgstr = msgstr;
+  mvp->pos = *pp;
+}
+
+
+void
+message_comment_append (mp, s)
+     message_ty *mp;
+     const char *s;
+{
+  if (mp->comment == NULL)
+    mp->comment = string_list_alloc ();
+  string_list_append (mp->comment, s);
+}
+
+
+void
+message_comment_dot_append (mp, s)
+     message_ty *mp;
+     const char *s;
+{
+  if (mp->comment_dot == NULL)
+    mp->comment_dot = string_list_alloc ();
+  string_list_append (mp->comment_dot, s);
+}
+
+
+message_ty *
+message_copy (mp)
+     message_ty *mp;
+{
+  message_ty *result;
+  size_t j;
+
+  result = message_alloc (xstrdup (mp->msgid));
+
+  for (j = 0; j < mp->variant_count; ++j)
+    {
+      message_variant_ty *mvp = &mp->variant[j];
+      message_variant_append (result, mvp->domain, mvp->msgstr, &mvp->pos);
+    }
+  if (mp->comment)
+    {
+      for (j = 0; j < mp->comment->nitems; ++j)
+       message_comment_append (result, mp->comment->item[j]);
+    }
+  if (mp->comment_dot)
+    {
+      for (j = 0; j < mp->comment_dot->nitems; ++j)
+       message_comment_dot_append (result, mp->comment_dot->item[j]);
+    }
+  result->is_fuzzy = mp->is_fuzzy;
+  result->is_c_format = mp->is_c_format;
+  result->do_wrap = mp->do_wrap;
+  for (j = 0; j < mp->filepos_count; ++j)
+    {
+      lex_pos_ty *pp = &mp->filepos[j];
+      message_comment_filepos (result, pp->file_name, pp->line_number);
+    }
+  return result;
+}
+
+
+message_ty *
+message_merge (def, ref)
+     message_ty *def;
+     message_ty *ref;
+{
+  message_ty *result;
+  const char *pot_date_ptr = NULL;
+  size_t pot_date_len = 0;
+  size_t j;
+
+  /* Take the msgid from the reference.  When fuzzy matches are made,
+     the definition will not be unique, but the reference will be -
+     usually because it has a typo.  */
+  result = message_alloc (xstrdup (ref->msgid));
+
+  /* If msgid is the header entry (i.e., "") we find the
+     POT-Creation-Date line in the reference.  */
+  if (ref->msgid[0] == '\0')
+    {
+      pot_date_ptr = strstr (ref->variant[0].msgstr, "POT-Creation-Date:");
+      if (pot_date_ptr != NULL)
+       {
+         const char *endp;
+
+         pot_date_ptr += sizeof ("POT-Creation-Date:") - 1;
+
+         endp = strchr (pot_date_ptr, '\n');
+         if (endp == NULL)
+           {
+             char *extended;
+             endp = strchr (pot_date_ptr, '\0');
+             pot_date_len = (endp - pot_date_ptr) + 1;
+             extended = (char *) alloca (pot_date_len + 1);
+             stpcpy (stpcpy (extended, pot_date_ptr), "\n");
+             pot_date_ptr = extended;
+           }
+         else
+           pot_date_len = (endp - pot_date_ptr) + 1;
+
+         if (pot_date_len == 0)
+           pot_date_ptr = NULL;
+       }
+    }
+
+  /* Take the variant list from the definition.  The msgstr of the
+     refences will be empty, as they were generated by xgettext.  If
+     we currently process the header entry we have to merge the msgstr
+     by using the POT-Creation-Date field from the .pot file.  */
+  for (j = 0; j < def->variant_count; ++j)
+    {
+      message_variant_ty *mvp = &def->variant[j];
+
+      if (ref->msgid[0] == '\0')
+       {
+         /* Oh, oh.  The header entry and we have something to fill in.  */
+         static const struct
+         {
+           const char *name;
+           size_t len;
+         } known_fields[] =
+         {
+           { "Project-Id-Version:", sizeof ("Project-Id-Version:") - 1 },
+#define PROJECT_ID     0
+           { "POT-Creation-Date:", sizeof ("POT-Creation-Date:") - 1 },
+#define POT_CREATION   1
+           { "PO-Revision-Date:", sizeof ("PO-Revision-Date:") - 1 },
+#define PO_REVISION    2
+           { "Last-Translator:", sizeof ("Last-Translator:") - 1 },
+#define LAST_TRANSLATOR        3
+           { "Language-Team:", sizeof ("Language-Team:") - 1 },
+#define LANGUAGE_TEAM  4
+           { "MIME-Version:", sizeof ("MIME-Version:") - 1 },
+#define MIME_VERSION   5
+           { "Content-Type:", sizeof ("Content-Type:") - 1 },
+#define CONTENT_TYPE   6
+           { "Content-Transfer-Encoding:",
+             sizeof ("Content-Transfer-Encoding:") - 1 }
+#define CONTENT_TRANSFER 7
+         };
+#define UNKNOWN        8
+         struct
+         {
+           const char *string;
+           size_t len;
+         } header_fields[UNKNOWN + 1];
+         const char *cp;
+         char *newp;
+         size_t len, cnt;
+
+         /* Clear all fields.  */
+         memset (header_fields, '\0', sizeof (header_fields));
+
+         cp = mvp->msgstr;
+         while (*cp != '\0')
+           {
+             const char *endp = strchr (cp, '\n');
+             int terminated = endp != NULL;
+
+             if (!terminated)
+               {
+                 char *copy;
+                 endp = strchr (cp, '\0');
+
+                 len = endp - cp + 1;
+
+                 copy = (char *) alloca (len + 1);
+                 stpcpy (stpcpy (copy, cp), "\n");
+                 cp = copy;
+               }
+             else
+               {
+                 len = (endp - cp) + 1;
+                 ++endp;
+               }
+
+             /* Compare with any of the known fields.  */
+             for (cnt = 0;
+                  cnt < sizeof (known_fields) / sizeof (known_fields[0]);
+                  ++cnt)
+               if (strncasecmp (cp, known_fields[cnt].name,
+                                known_fields[cnt].len) == 0)
+                 break;
+
+             if (cnt < sizeof (known_fields) / sizeof (known_fields[0]))
+               {
+                 header_fields[cnt].string = &cp[known_fields[cnt].len];
+                 header_fields[cnt].len = len - known_fields[cnt].len;
+               }
+             else
+               {
+                 /* It's an unknown field.  Append content to what is
+                    already known.  */
+                 char *extended = (char *) alloca (header_fields[UNKNOWN].len
+                                                   + len + 1);
+                 memcpy (extended, header_fields[UNKNOWN].string,
+                         header_fields[UNKNOWN].len);
+                 memcpy (&extended[header_fields[UNKNOWN].len], cp, len);
+                 extended[header_fields[UNKNOWN].len + len] = '\0';
+                 header_fields[UNKNOWN].string = extended;
+                 header_fields[UNKNOWN].len += len;
+               }
+
+             cp = endp;
+           }
+
+         if (pot_date_ptr != NULL)
+           {
+             header_fields[POT_CREATION].string = pot_date_ptr;
+             header_fields[POT_CREATION].len = pot_date_len;
+           }
+
+         /* Concatenate all the various fields.  */
+         len = 0;
+         for (cnt = 0; cnt < UNKNOWN; ++cnt)
+           if (header_fields[cnt].string != NULL)
+             len += known_fields[cnt].len + header_fields[cnt].len;
+         len += header_fields[UNKNOWN].len;
+
+         cp = newp = (char *) xmalloc (len + 1);
+         newp[len] = '\0';
+
+#define IF_FILLED(idx)                                                       \
+         if (header_fields[idx].string)                                      \
+           newp = stpncpy (stpcpy (newp, known_fields[idx].name),            \
+                           header_fields[idx].string, header_fields[idx].len)
+
+         IF_FILLED (PROJECT_ID);
+         IF_FILLED (POT_CREATION);
+         IF_FILLED (PO_REVISION);
+         IF_FILLED (LAST_TRANSLATOR);
+         IF_FILLED (LANGUAGE_TEAM);
+         IF_FILLED (MIME_VERSION);
+         IF_FILLED (CONTENT_TYPE);
+         IF_FILLED (CONTENT_TRANSFER);
+         if (header_fields[UNKNOWN].string != NULL)
+           stpcpy (newp, header_fields[UNKNOWN].string);
+
+         message_variant_append (result, mvp->domain, cp, &mvp->pos);
+       }
+      else
+       message_variant_append (result, mvp->domain, mvp->msgstr, &mvp->pos);
+    }
+
+  /* Take the comments from the definition file.  There will be none at
+     all in the reference file, as it was generated by xgettext.  */
+  if (def->comment)
+    for (j = 0; j < def->comment->nitems; ++j)
+      message_comment_append (result, def->comment->item[j]);
+
+  /* Take the dot comments from the reference file, as they are
+     generated by xgettext.  Any in the definition file are old ones
+     collected by previous runs of xgettext and msgmerge.  */
+  if (ref->comment_dot)
+    for (j = 0; j < ref->comment_dot->nitems; ++j)
+      message_comment_dot_append (result, ref->comment_dot->item[j]);
+
+  /* The flags are mixed in a special way.  Some informations come
+     from the reference message (such as format/no-format), others
+     come from the definition file (fuzzy or not).  */
+  result->is_fuzzy = def->is_fuzzy;
+  result->is_c_format = ref->is_c_format;
+  result->do_wrap = ref->do_wrap;
+
+  /* Take the file position comments from the reference file, as they
+     are generated by xgettext.  Any in the definition file are old ones
+     collected by previous runs of xgettext and msgmerge.  */
+  for (j = 0; j < ref->filepos_count; ++j)
+    {
+      lex_pos_ty *pp = &ref->filepos[j];
+      message_comment_filepos (result, pp->file_name, pp->line_number);
+    }
+
+  /* All done, return the merged message to the caller.  */
+  return result;
+}
+
+
+void
+message_comment_filepos (mp, name, line)
+     message_ty *mp;
+     const char *name;
+     size_t line;
+{
+  size_t nbytes;
+  lex_pos_ty *pp;
+  int min, max;
+  int j;
+
+  /* See if we have this position already.  They are kept in sorted
+     order, so use a binary chop.  */
+  /* FIXME: use bsearch */
+  min = 0;
+  max = (int) mp->filepos_count - 1;
+  while (min <= max)
+    {
+      int mid;
+      int cmp;
+
+      mid = (min + max) / 2;
+      pp = &mp->filepos[mid];
+      cmp = strcmp (pp->file_name, name);
+      if (cmp == 0)
+       cmp = (int) pp->line_number - line;
+      if (cmp == 0)
+       return;
+      if (cmp < 0)
+       min = mid + 1;
+      else
+       max = mid - 1;
+    }
+
+  /* Extend the list so that we can add an position to it.  */
+  nbytes = (mp->filepos_count + 1) * sizeof (mp->filepos[0]);
+  mp->filepos = xrealloc (mp->filepos, nbytes);
+
+  /* Shuffle the rest of the list up one, so that we can insert the
+     position at ``min''.  */
+  /* FIXME: use memmove */
+  for (j = mp->filepos_count; j > min; --j)
+    mp->filepos[j] = mp->filepos[j - 1];
+  mp->filepos_count++;
+
+  /* Insert the postion into the empty slot.  */
+  pp = &mp->filepos[min];
+  pp->file_name = xstrdup (name);
+  pp->line_number = line;
+}
+
+
+void
+message_print_style_indent ()
+{
+  indent = 1;
+}
+
+
+void
+message_print_style_uniforum ()
+{
+  uniforum = 1;
+}
+
+
+void
+message_print_style_escape (flag)
+     int flag;
+{
+  escape = (flag != 0);
+}
+
+
+message_list_ty *
+message_list_alloc ()
+{
+  message_list_ty *mlp;
+
+  mlp = xmalloc (sizeof (message_list_ty));
+  mlp->nitems = 0;
+  mlp->nitems_max = 0;
+  mlp->item = 0;
+  return mlp;
+}
+
+
+void
+message_list_append (mlp, mp)
+     message_list_ty *mlp;
+     message_ty *mp;
+{
+  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);
+    }
+  mlp->item[mlp->nitems++] = mp;
+}
+
+
+void
+message_list_delete_nth (mlp, n)
+     message_list_ty *mlp;
+     size_t n;
+{
+  size_t j;
+
+  if (n >= mlp->nitems)
+    return;
+  message_free (mlp->item[n]);
+  for (j = n + 1; j < mlp->nitems; ++j)
+    mlp->item[j - 1] = mlp->item[j];
+  mlp->nitems--;
+}
+
+
+message_ty *
+message_list_search (mlp, msgid)
+     message_list_ty *mlp;
+     const char *msgid;
+{
+  size_t j;
+
+  for (j = 0; j < mlp->nitems; ++j)
+    {
+      message_ty *mp;
+
+      mp = mlp->item[j];
+      if (0 == strcmp (msgid, mp->msgid))
+       return mp;
+    }
+  return 0;
+}
+
+
+message_ty *
+message_list_search_fuzzy (mlp, msgid)
+     message_list_ty *mlp;
+     const char *msgid;
+{
+  size_t j;
+  double best_weight;
+  message_ty *best_mp;
+
+  best_weight = 0.6;
+  best_mp = NULL;
+  for (j = 0; j < mlp->nitems; ++j)
+    {
+      size_t k;
+      double weight;
+      message_ty *mp;
+
+      mp = mlp->item[j];
+
+      for (k = 0; k < mp->variant_count; ++k)
+       if (mp->variant[k].msgstr != NULL && mp->variant[k].msgstr[0] != '\0')
+         break;
+      if (k >= mp->variant_count)
+       continue;
+
+      weight = fstrcmp (msgid, mp->msgid);
+      if (weight > best_weight)
+       {
+         best_weight = weight;
+         best_mp = mp;
+       }
+    }
+  return best_mp;
+}
+
+
+void
+message_list_free (mlp)
+     message_list_ty *mlp;
+{
+  size_t j;
+
+  for (j = 0; j < mlp->nitems; ++j)
+    message_free (mlp->item[j]);
+  if (mlp->item)
+    free (mlp->item);
+  free (mlp);
+}
+
+
+/* Local functions.  */
+
+static void
+wrap (fp, line_prefix, name, value, do_wrap)
+     FILE *fp;
+     const char *line_prefix;
+     const char *name;
+     const char *value;
+     int do_wrap;
+{
+  const char *s;
+  int first_line;
+  /* The \a and \v escapes were added by the ANSI C Standard.  Prior
+     to the Standard, most compilers did not have them.  Because we
+     need the same program on all platforms we don't provide support
+     for them here.  */
+  static const char escapes[] = "\b\f\n\r\t";
+  static const char escape_names[] = "bfnrt";
+
+  /* The empty string is a special case.  */
+  if (*value == '\0')
+    {
+      if (line_prefix != NULL)
+       fputs (line_prefix, fp);
+      fputs (name, fp);
+      putc (indent ? '\t' : ' ', fp);
+      fputs ("\"\"\n", fp);
+      return;
+    }
+
+  s = value;
+  first_line = 1;
+  while (*s)
+    {
+      const char *ep;
+      int ocol;
+
+      /* The line starts with different things depending on whether it
+         is the first line, and if we are using the indented style.  */
+      if (first_line)
+       {
+         ocol = strlen (name) + (line_prefix ? strlen (line_prefix) : 0);
+         if (indent && ocol < 8)
+           ocol = 8;
+         else
+           ++ocol;
+       }
+      else
+       ocol = (indent ? 8 : 0);
+
+      /* Allow room for the opening quote character. */
+      ++ocol;
+
+      /* Work out how many characters from the string will fit on a
+         line.  Natural breaks occur at embedded newline characters.  */
+      for (ep = s; *ep; ++ep)
+       {
+         const char *esc;
+         int cw;
+         int c;
+
+         c = (unsigned char) *ep;
+         /* FIXME This is the wrong locale.  While message_list_print
+            set the "C" locale for LC_CTYPE, the need is to use the
+            correct locale for the file's contents.  */
+         esc = strchr (escapes, c);
+         if (esc == NULL && (!escape || isprint (c)))
+           cw = 1 + (c == '\\' || c == '"');
+         else
+           cw = esc != NULL ? 2 : 4;
+         /* Allow 1 character for the closing quote.  */
+         if (ocol + cw >= (do_wrap == no ? INT_MAX : page_width))
+           break;
+         ocol += cw;
+         if (c == '\n')
+           {
+             ++ep;
+             break;
+           }
+       }
+
+      /* The above loop detects if a line is too long.  If it is too
+        long, see if there is a better place to break the line.  */
+      if (*ep)
+       {
+         const char *bp;
+
+         for (bp = ep; bp > s; --bp)
+           if (bp[-1] == ' ' || bp[-1] == '\n')
+             {
+               ep = bp;
+               break;
+             }
+       }
+
+      /* If this is the first line, and we are not using the indented
+         style, and the line would wrap, then use an empty first line
+         and restart.  */
+      if (first_line && !indent && *ep != '\0')
+       {
+         fprintf (fp, "%s%s \"\"\n", line_prefix ? line_prefix : "", name);
+         s = value;
+         first_line = 0;
+         continue;
+       }
+
+      /* Print the beginning of the line.  This will depend on whether
+        this is the first line, and if the indented style is being
+        used.  */
+      if (first_line)
+       {
+         first_line = 0;
+         if (line_prefix != NULL)
+           fputs (line_prefix, fp);
+         fputs (name, fp);
+         putc (indent ? '\t' : ' ', fp);
+       }
+      else
+       {
+         if (line_prefix != NULL)
+           fputs (line_prefix, fp);
+         if (indent)
+           putc ('\t', fp);
+       }
+
+      /* Print the body of the line.  C escapes are used for
+        unprintable characters.  */
+      putc ('"', fp);
+      while (s < ep)
+       {
+         const char *esc;
+         int c;
+
+         c = (unsigned char) *s++;
+         /* FIXME This is the wrong locale.  While message_list_print
+            set the "C" locale for LC_CTYPE, the need is to use the
+            correct locale for the file's contents.  */
+         esc = strchr (escapes, c);
+         if (esc == NULL && (!escape || isprint (c)))
+           {
+             if (c == '\\' || c == '"')
+               putc ('\\', fp);
+             putc (c, fp);
+           }
+         else if (esc != NULL)
+           {
+             c = escape_names[esc - escapes];
+
+             putc ('\\', fp);
+             putc (c, fp);
+
+             /* We warn about any use of escape sequences beside
+                '\n' and '\t'.  */
+             if (c != 'n' && c != 't')
+               error (0, 0, _("\
+internationalized messages should not contain the `\\%c' escape sequence"),
+                      c);
+           }
+         else
+           fprintf (fp, "\\%3.3o", c);
+       }
+      fputs ("\"\n", fp);
+    }
+}
+
+
+static void
+print_blank_line (fp)
+     FILE *fp;
+{
+  if (uniforum)
+    fputs ("#\n", fp);
+  else
+    putc ('\n', fp);
+}
+
+
+static void
+message_print (mp, fp, domain, blank_line, debug)
+     const message_ty *mp;
+     FILE *fp;
+     const char *domain;
+     int blank_line;
+     int debug;
+{
+  message_variant_ty *mvp;
+  int first;
+  size_t j;
+
+  /* Find the relevant message variant.  If there isn't one, remember
+     this using a NULL pointer.  */
+  mvp = NULL;
+  first = 0;
+
+  for (j = 0; j < mp->variant_count; ++j)
+    {
+      if (strcmp (domain, mp->variant[j].domain) == 0)
+       {
+         mvp = &mp->variant[j];
+         first = (j == 0);
+         break;
+       }
+    }
+
+  /* Separate messages with a blank line.  Uniforum doesn't like blank
+     lines, so use an empty comment (unless there already is one).  */
+  if (blank_line && (!uniforum
+                    || mp->comment == NULL
+                    || mp->comment->nitems == 0
+                    || mp->comment->item[0][0] != '\0'))
+    print_blank_line (fp);
+
+  /* The first variant of a message will have the comments attached to
+     it.  We can't attach them to all variants in case we are read in
+     again, multiplying the number of comment lines.  Usually there is
+     only one variant.  */
+  if (first)
+    {
+      if (mp->comment != NULL)
+       for (j = 0; j < mp->comment->nitems; ++j)
+         {
+           const unsigned char *s = mp->comment->item[j];
+           do
+             {
+               const unsigned char *e;
+               putc ('#', fp);
+              /* FIXME This is the wrong locale.  While
+                 message_list_print set the "C" locale for LC_CTYPE,
+                 the need to use the correct locale for the file's
+                 contents.  */
+               if (*s != '\0' && !isspace (*s))
+                 putc (' ', fp);
+               e = strchr (s, '\n');
+               if (e == NULL)
+                 {
+                   fputs (s, fp);
+                   s = NULL;
+                 }
+               else
+                 {
+                   fwrite (s, 1, e - s, fp);
+                   s = e + 1;
+                 }
+               putc ('\n', fp);
+             }
+           while (s != NULL);
+         }
+
+      if (mp->comment_dot != NULL)
+       for (j = 0; j < mp->comment_dot->nitems; ++j)
+         {
+           const unsigned char *s = mp->comment_dot->item[j];
+           putc ('#', fp);
+           putc ('.', fp);
+           /* FIXME This is the wrong locale.  While
+              message_list_print set the "C" locale for LC_CTYPE, the
+              need to use the correct locale for the file's contents.  */
+           if (*s && !isspace (*s))
+             putc (' ', fp);
+           fputs (s, fp);
+           putc ('\n', fp);
+         }
+    }
+
+  /* Print the file position comments for every domain.  This will
+     help a human who is trying to navigate the sources.  There is no
+     problem of getting repeat positions, because duplicates are
+     checked for.  */
+  if (mp->filepos_count != 0)
+    {
+      if (uniforum)
+       for (j = 0; j < mp->filepos_count; ++j)
+         {
+           lex_pos_ty *pp = &mp->filepos[j];
+           char *cp = pp->file_name;
+           while (cp[0] == '.' && cp[1] == '/')
+             cp += 2;
+           /* There are two Sun formats to choose from: SunOS and
+              Solaris.  Use the Solaris form here.  */
+           fprintf (fp, "# File: %s, line: %ld\n",
+                    cp, (long) pp->line_number);
+         }
+      else
+       {
+         size_t column;
+
+         fputs ("#:", fp);
+         column = 2;
+         for (j = 0; j < mp->filepos_count; ++j)
+           {
+             lex_pos_ty *pp;
+             char buffer[20];
+             char *cp;
+             size_t len;
+
+             pp = &mp->filepos[j];
+             cp = pp->file_name;
+             while (cp[0] == '.' && cp[1] == '/')
+               cp += 2;
+             sprintf (buffer, "%ld", (long) pp->line_number);
+             len = strlen (cp) + strlen (buffer) + 2;
+             if (column > 2 && column + len >= page_width)
+               {
+                 fputs ("\n#:", fp);
+                 column = 2;
+               }
+             fprintf (fp, " %s:%s", cp, buffer);
+             column += len;
+           }
+         putc ('\n', fp);
+       }
+    }
+
+  /* Print flag information in special comment.  */
+  if (first && ((mp->is_fuzzy && mvp != NULL && mvp->msgstr[0] != '\0')
+               || significant_c_format_p (mp->is_c_format)
+               || mp->do_wrap == no))
+    {
+      int first_flag = 1;
+
+      putc ('#', fp);
+      putc (',', fp);
+
+      /* We don't print the fuzzy flag if the msgstr is empty.  This
+        might be introduced by the user but we want to normalize the
+        output.  */
+      if (mp->is_fuzzy && mvp != NULL && mvp->msgstr[0] != '\0')
+       {
+         fputs (" fuzzy", fp);
+         first_flag = 0;
+       }
+
+      if (significant_c_format_p (mp->is_c_format))
+       {
+         if (!first_flag)
+           putc (',', fp);
+
+         fputs (make_c_format_description_string (mp->is_c_format, debug),
+                fp);
+         first_flag = 0;
+       }
+
+      if (mp->do_wrap == no)
+       {
+         if (!first_flag)
+           putc (',', fp);
+
+         fputs (make_c_width_description_string (mp->do_wrap), fp);
+         first_flag = 0;
+       }
+
+      putc ('\n', fp);
+    }
+
+  /* Print each of the message components.  Wrap them nicely so they
+     are as readable as possible.  If there is no recorded msgstr for
+     this domain, emit an empty string.  */
+  wrap (fp, NULL, "msgid", mp->msgid, mp->do_wrap);
+  wrap (fp, NULL, "msgstr", mvp ? mvp->msgstr : "", mp->do_wrap);
+}
+
+
+static void
+message_print_obsolete (mp, fp, domain, blank_line)
+     const message_ty *mp;
+     FILE *fp;
+     const char *domain;
+     int blank_line;
+{
+  message_variant_ty *mvp;
+  size_t j;
+
+  /* Find the relevant message variant.  If there isn't one, remember
+     this using a NULL pointer.  */
+  mvp = NULL;
+
+  for (j = 0; j < mp->variant_count; ++j)
+    {
+      if (strcmp (domain, mp->variant[j].domain) == 0)
+       {
+         mvp = &mp->variant[j];
+         break;
+       }
+    }
+
+  /* If no msgstr is found or it is the empty string we print nothing.  */
+  if (mvp == NULL || mvp->msgstr[0] == '\0')
+    return;
+
+  /* Separate messages with a blank line.  Uniforum doesn't like blank
+     lines, so use an empty comment (unless there already is one).  */
+  if (blank_line)
+    print_blank_line (fp);
+
+  /* Print translator comment if available.  */
+  if (mp->comment)
+    for (j = 0; j < mp->comment->nitems; ++j)
+      {
+       const unsigned char *s = mp->comment->item[j];
+       do
+         {
+           const unsigned char *e;
+           putc ('#', fp);
+           /* FIXME This is the wrong locale.  While
+              message_list_print set the "C" locale for LC_CTYPE, the
+              need to use the correct locale for the file's contents.  */
+           if (*s != '\0' && !isspace (*s))
+             putc (' ', fp);
+           e = strchr (s, '\n');
+           if (e == NULL)
+             {
+               fputs (s, fp);
+               s = NULL;
+             }
+           else
+             {
+               fwrite (s, 1, e - s, fp);
+               s = e + 1;
+             }
+           putc ('\n', fp);
+         }
+       while (s != NULL);
+      }
+
+  /* Print flag information in special comment.  */
+  if (mp->is_fuzzy)
+    {
+      int first = 1;
+
+      putc ('#', fp);
+      putc (',', fp);
+
+      if (mp->is_fuzzy)
+       {
+         fputs (" fuzzy", fp);
+         first = 0;
+       }
+
+      putc ('\n', fp);
+    }
+
+  /* Print each of the message components.  Wrap them nicely so they
+     are as readable as possible.  */
+  wrap (fp, "#~ ", "msgid", mp->msgid, mp->do_wrap);
+  wrap (fp, "#~ ", "msgstr", mvp->msgstr, mp->do_wrap);
+}
+
+
+void
+message_list_print (mlp, filename, force, debug)
+     message_list_ty *mlp;
+     const char *filename;
+     int force;
+     int debug;
+{
+  FILE *fp;
+  size_t j, k;
+  string_list_ty *dl;
+  int blank_line;
+#ifdef HAVE_SETLOCALE
+  char *old_locale;
+#endif
+
+  /* We will not write anything if we have no message or only the
+     header entry.  */
+  if (force == 0
+      && (mlp->nitems == 0
+         || (mlp->nitems == 1 && *mlp->item[0]->msgid == '\0')))
+    return;
+
+  /* Build the list of domains.  */
+  dl = string_list_alloc ();
+  for (j = 0; j < mlp->nitems; ++j)
+    {
+      message_ty *mp = mlp->item[j];
+      for (k = 0; k < mp->variant_count; ++k)
+       string_list_append_unique (dl, mp->variant[k].domain);
+    }
+
+  /* Open the output file.  */
+  if (filename != NULL && strcmp (filename, "-") != 0
+      && strcmp (filename, "/dev/stdout") != 0)
+    {
+      fp = fopen (filename, "w");
+      if (fp == NULL)
+       error (EXIT_FAILURE, errno, _("cannot create output file \"%s\""),
+              filename);
+    }
+  else
+    {
+      fp = stdout;
+      /* xgettext:no-c-format */
+      filename = _("standard output");
+    }
+
+#ifdef HAVE_SETLOCALE
+  /* FIXME This is the wrong locale.  The program is currently set for
+     the user's native language locale, for the error messages.  This
+     code sets it to the "C" locale, but that isn't right either.  The
+     need is to use the correct locale for the file's contents.  */
+  old_locale = setlocale (LC_CTYPE, "C");
+  if (old_locale)
+    old_locale = xstrdup (old_locale);
+#endif
+
+  /* Write out the messages for each domain.  */
+  blank_line = 0;
+  for (k = 0; k < dl->nitems; ++k)
+    {
+      /* If there is only one domain, and that domain is the default,
+        don't bother emitting the domain name, because it is the
+        default.  */
+      if (dl->nitems != 1 || strcmp (dl->item[0], MESSAGE_DOMAIN_DEFAULT) != 0)
+       {
+         if (blank_line)
+           print_blank_line (fp);
+         fprintf (fp, "domain \"%s\"\n", dl->item[k]);
+         blank_line = 1;
+       }
+
+      /* Write out each of the messages for this domain.  */
+      for (j = 0; j < mlp->nitems; ++j)
+       if (mlp->item[j]->obsolete == 0)
+         {
+           message_print (mlp->item[j], fp, dl->item[k], blank_line, debug);
+           blank_line = 1;
+         }
+
+      /* Write out each of the obsolete messages for this domain.  */
+      for (j = 0; j < mlp->nitems; ++j)
+       if (mlp->item[j]->obsolete != 0)
+         {
+           message_print_obsolete (mlp->item[j], fp, dl->item[k], blank_line);
+           blank_line = 1;
+         }
+    }
+  string_list_free (dl);
+
+  /* Restore the old locale.  Do this before emitting error messages,
+     so that the correct locale is used for the error.  (Ideally,
+     error should ensure this before calling gettext for the format
+     string.)  */
+#ifdef HAVE_SETLOCALE
+  if (old_locale)
+    {
+      setlocale (LC_CTYPE, old_locale);
+      free (old_locale);
+    }
+#endif
+
+  /* Make sure nothing went wrong.  */
+  if (fflush (fp))
+    error (EXIT_FAILURE, errno, _("error while writing \"%s\" file"),
+          filename);
+  fclose (fp);
+}
+
+
+static int
+msgid_cmp (va, vb)
+     const void *va;
+     const void *vb;
+{
+  const message_ty *a = *(const message_ty **) va;
+  const message_ty *b = *(const message_ty **) vb;
+#ifdef HAVE_STRCOLL
+  return strcoll (a->msgid, b->msgid);
+#else
+  return strcmp (a->msgid, b->msgid);
+#endif
+}
+
+
+void
+message_list_sort_by_msgid (mlp)
+     message_list_ty *mlp;
+{
+  /* FIXME This is the wrong locale.  The program is currently set for
+     the user's native language locale, for the error messages.  This
+     code sets it to the "C" locale, but that isn't right either.  The
+     need is to use the correct locale for the file's contents.  */
+#ifdef HAVE_SETLOCALE
+  char *tmp = setlocale (LC_COLLATE, "C");
+  if (tmp)
+    tmp = xstrdup (tmp);
+#endif
+  qsort (mlp->item, mlp->nitems, sizeof (mlp->item[0]), msgid_cmp);
+#ifdef HAVE_SETLOCALE
+  if (tmp)
+    {
+      setlocale (LC_COLLATE, tmp);
+      free (tmp);
+    }
+#endif
+}
+
+
+static int
+filepos_cmp (va, vb)
+     const void *va;
+     const void *vb;
+{
+  const message_ty *a = *(const message_ty **) va;
+  const message_ty *b = *(const message_ty **) vb;
+  int cmp;
+
+  /* No filepos is smaller than any other filepos.  */
+  if (a->filepos_count == 0)
+  {
+    if (b->filepos_count != 0)
+      return -1;
+  }
+  if (b->filepos_count == 0)
+    return 1;
+
+  /* Compare on the file names...  */
+  cmp = strcmp (a->filepos[0].file_name, b->filepos[0].file_name);
+  if (cmp != 0)
+       return cmp;
+
+  /* If they are equal, compare on the line numbers...  */
+  cmp = a->filepos[0].line_number - b->filepos[0].line_number;
+  if (cmp != 0)
+       return cmp;
+
+  /* If they are equal, compare on the msgid strings.  */
+#ifdef HAVE_STRCOLL
+  return strcoll (a->msgid, b->msgid);
+#else
+  return strcmp (a->msgid, b->msgid);
+#endif
+}
+
+
+void
+message_list_sort_by_filepos (mlp)
+    message_list_ty *mlp;
+{
+  /* FIXME This is the wrong locale.  The program is currently set for
+     the user's native language locale, for the error messages.  This
+     code sets it to the "C" locale, but that isn't right either.  The
+     need is to use the correct locale for the file's contents.  */
+#ifdef HAVE_SETLOCALE
+  char *tmp = setlocale (LC_COLLATE, "C");
+  if (tmp)
+    tmp = xstrdup (tmp);
+#endif
+  qsort (mlp->item, mlp->nitems, sizeof (mlp->item[0]), filepos_cmp);
+#ifdef HAVE_SETLOCALE
+  if (tmp)
+    {
+      setlocale (LC_COLLATE, tmp);
+      free (tmp);
+    }
+#endif
+}
+
+
+enum is_c_format
+parse_c_format_description_string (s)
+     const char *s;
+{
+  if (strstr (s, "no-c-format") != NULL)
+    return no;
+  else if (strstr (s, "impossible-c-format") != NULL)
+    return impossible;
+  else if (strstr (s, "possible-c-format") != NULL)
+    return possible;
+  else if (strstr (s, "c-format") != NULL)
+    return yes;
+  return undecided;
+}
+
+
+enum is_c_format
+parse_c_width_description_string (s)
+     const char *s;
+{
+  if (strstr (s, "no-wrap") != NULL)
+    return no;
+  else if (strstr (s, "wrap") != NULL)
+    return yes;
+  return undecided;
+}
+
+
+static const char *
+make_c_format_description_string (is_c_format, debug)
+     enum is_c_format is_c_format;
+     int debug;
+{
+  const char *result = NULL;
+
+  switch (is_c_format)
+    {
+    case possible:
+      if (debug)
+       {
+         result = " possible-c-format";
+         break;
+       }
+      /* FALLTHROUGH */
+    case yes:
+      result = " c-format";
+      break;
+    case impossible:
+      result = " impossible-c-format";
+      break;
+    case no:
+      result = " no-c-format";
+      break;
+    case undecided:
+      result = " undecided";
+      break;
+    default:
+      abort ();
+    }
+
+  return result;
+}
+
+
+static const char *
+make_c_width_description_string (do_wrap)
+     enum is_c_format do_wrap;
+{
+  const char *result = NULL;
+
+  switch (do_wrap)
+    {
+    case yes:
+      result = " wrap";
+      break;
+    case no:
+      result = " no-wrap";
+      break;
+    default:
+      abort ();
+    }
+
+  return result;
+}
+
+
+int
+possible_c_format_p (is_c_format)
+     enum is_c_format is_c_format;
+{
+  return is_c_format == possible || is_c_format == yes;
+}
+
+
+static int
+significant_c_format_p (is_c_format)
+     enum is_c_format is_c_format;
+{
+  return is_c_format != undecided && is_c_format != impossible;
+}
+
+
+void
+message_page_width_set (n)
+     size_t n;
+{
+  if (n == 0)
+    {
+      page_width = INT_MAX;
+      return;
+    }
+
+  if (n < 20)
+    n = 20;
+
+  page_width = n;
+}
index 805295e..b23439f 100644 (file)
@@ -1,5 +1,7 @@
 /* rpmheader: spit out the header portion of a package */
 
+#define        _GNU_SOURCE     1
+
 #include "system.h"
 
 #include "../build/rpmbuild.h"
@@ -31,7 +33,6 @@ static void dpf(char *format, ...)
 const char *progname = NULL;
 int debug = MYDEBUG;
 int verbose = 0;
-int escape = 1;                /* use octal escape sequence for !isprint(c)? */
 char *inputdir = "/mnt/redhat/comps/dist/5.2";
 char *outputdir = "/tmp/OUT";
 char *onlylang = NULL;
@@ -122,6 +123,90 @@ headerGetLangs(Header h)
     return table;
 }
 
+/* ================================================================== */
+
+static const char *
+genSrpmFileName(Header h)
+{
+    char *name, *version, *release, *sourcerpm;
+    char sfn[BUFSIZ], bfn[BUFSIZ];
+
+    headerGetEntry(h, RPMTAG_NAME, NULL, (void **)&name, NULL);
+    headerGetEntry(h, RPMTAG_VERSION, NULL, (void **)&version, NULL);
+    headerGetEntry(h, RPMTAG_RELEASE, NULL, (void **)&release, NULL);
+    sprintf(sfn, "%s-%s-%s.src.rpm", name, version, release);
+
+    headerGetEntry(h, RPMTAG_SOURCERPM, NULL, (void **)&sourcerpm, NULL);
+
+#if 0
+    if (strcmp(sourcerpm, sfn))
+       return strdup(sourcerpm);
+
+    return NULL;
+#else
+    return strdup(sourcerpm);
+#endif
+
+}
+
+static const char *
+hasLang(const char *onlylang, char **langs, char **s)
+{
+       const char *e = *s;
+       int i = 0;
+
+       while(langs[i] && strcmp(langs[i], onlylang)) {
+               i++;
+               e += strlen(e) + 1;
+       }
+#if 0
+       if (langs[i] && *e)
+               return e;
+       return NULL;
+#else
+       return onlylang;
+#endif
+}
+
+/* ================================================================== */
+/* XXX stripped down gettext environment */
+
+#define        HAVE_LOCALE_H   1
+#include <libintl.h>
+#include <string.h>
+
+#define        xstrdup         strdup
+#define        xmalloc         malloc
+#define        xrealloc        realloc
+
+#define        PARAMS(_x)      _x
+/* Length from which starting on warnings about too long strings are given.
+   Several systems have limits for strings itself, more have problems with
+   strings in their tools (important here: gencat).  1024 bytes is a
+   conservative limit.  Because many translation let the message size grow
+   (German translations are always bigger) choose a length < 1024.  */
+#if !defined(WARN_ID_LEN)
+#define WARN_ID_LEN 900
+#endif
+
+/* This is the page width for the message_print function.  It should
+   not be set to more than 79 characters (Emacs users will appreciate
+   it).  It is used to wrap the msgid and msgstr strings, and also to
+   wrap the file position (#:) comments.  */
+#if !defined(PAGE_WIDTH)
+#define PAGE_WIDTH 79
+#endif
+
+#include "fstrcmp.c"
+
+#include "str-list.c"
+
+#define        _LIBGETTEXT_H   1       /* XXX WTFO? _LIBINTL_H is undef? */
+#undef _                       /* XXX WTFO? */
+#include "message.c"
+
+/* ================================================================== */
+
 /* XXX cribbed from gettext/src/message.c */
 static const char escapes[] = "\b\f\n\r\t";
 static const char escape_names[] = "bfnrt";
@@ -190,51 +275,6 @@ contractRpmPO(char *t, const char *s)
     *t = '\0';
 }
 
-/* ================================================================== */
-
-static const char *
-genSrpmFileName(Header h)
-{
-    char *name, *version, *release, *sourcerpm;
-    char sfn[BUFSIZ], bfn[BUFSIZ];
-
-    headerGetEntry(h, RPMTAG_NAME, NULL, (void **)&name, NULL);
-    headerGetEntry(h, RPMTAG_VERSION, NULL, (void **)&version, NULL);
-    headerGetEntry(h, RPMTAG_RELEASE, NULL, (void **)&release, NULL);
-    sprintf(sfn, "%s-%s-%s.src.rpm", name, version, release);
-
-    headerGetEntry(h, RPMTAG_SOURCERPM, NULL, (void **)&sourcerpm, NULL);
-
-#if 0
-    if (strcmp(sourcerpm, sfn))
-       return strdup(sourcerpm);
-
-    return NULL;
-#else
-    return strdup(sourcerpm);
-#endif
-
-}
-
-static const char *
-hasLang(const char *onlylang, char **langs, char **s)
-{
-       const char *e = *s;
-       int i = 0;
-
-       while(langs[i] && strcmp(langs[i], onlylang)) {
-               i++;
-               e += strlen(e) + 1;
-       }
-#if 0
-       if (langs[i] && *e)
-               return e;
-       return NULL;
-#else
-       return onlylang;
-#endif
-}
-
 static int poTags[] = {
     RPMTAG_DESCRIPTION,
     RPMTAG_GROUP,
@@ -334,277 +374,6 @@ gettextfile(int fd, const char *file, FILE *fp, int *poTags)
 }
 
 /* ================================================================== */
-#define        xstrdup         strdup
-#define        xmalloc         malloc
-#define        xrealloc        realloc
-#define        PARAMS(_x)      _x
-
-#include "fstrcmp.c"
-
-#include "str-list.c"
-
-#include "rpmpo.h"
-
-message_ty *
-message_alloc (msgid)
-     char *msgid;
-{
-  message_ty *mp;
-
-  mp = xmalloc (sizeof (message_ty));
-  mp->msgid = msgid;
-  mp->comment = NULL;
-  mp->comment_dot = NULL;
-  mp->filepos_count = 0;
-  mp->filepos = NULL;
-  mp->variant_count = 0;
-  mp->variant = NULL;
-  mp->used = 0;
-  mp->obsolete = 0;
-  mp->is_fuzzy = 0;
-  mp->is_c_format = undecided;
-  mp->do_wrap = undecided;
-  return mp;
-}
-
-void
-message_free (mp)
-     message_ty *mp;
-{
-  size_t j;
-
-  if (mp->comment != NULL)
-    string_list_free (mp->comment);
-  if (mp->comment_dot != NULL)
-    string_list_free (mp->comment_dot);
-  free ((char *) mp->msgid);
-  for (j = 0; j < mp->variant_count; ++j)
-    free ((char *) mp->variant[j].msgstr);
-  if (mp->variant != NULL)
-    free (mp->variant);
-  for (j = 0; j < mp->filepos_count; ++j)
-    free ((char *) mp->filepos[j].file_name);
-  if (mp->filepos != NULL)
-    free (mp->filepos);
-  free (mp);
-}
-
-message_variant_ty *
-message_variant_search (mp, domain)
-     message_ty *mp;
-     const char *domain;
-{
-  size_t j;
-  message_variant_ty *mvp;
-
-  for (j = 0; j < mp->variant_count; ++j)
-    {
-      mvp = &mp->variant[j];
-      if (0 == strcmp (domain, mvp->domain))
-        return mvp;
-    }
-  return 0;
-}
-
-void
-message_variant_append (mp, domain, msgstr, pp)
-     message_ty *mp;
-     const char *domain;
-     const char *msgstr;
-     const lex_pos_ty *pp;
-{
-  size_t nbytes;
-  message_variant_ty *mvp;
-
-  nbytes = (mp->variant_count + 1) * sizeof (mp->variant[0]);
-  mp->variant = xrealloc (mp->variant, nbytes);
-  mvp = &mp->variant[mp->variant_count++];
-  mvp->domain = domain;
-  mvp->msgstr = msgstr;
-  mvp->pos = *pp;
-}
-
-void
-message_comment_append (mp, s)
-     message_ty *mp;
-     const char *s;
-{
-  if (mp->comment == NULL)
-    mp->comment = string_list_alloc ();
-  string_list_append (mp->comment, s);
-}
-
-void
-message_comment_dot_append (mp, s)
-     message_ty *mp;
-     const char *s;
-{
-  if (mp->comment_dot == NULL)
-    mp->comment_dot = string_list_alloc ();
-  string_list_append (mp->comment_dot, s);
-}
-
-void
-message_comment_filepos (mp, name, line)
-     message_ty *mp;
-     const char *name;
-     size_t line;
-{
-  size_t nbytes;
-  lex_pos_ty *pp;
-  int min, max;
-  int j;
-
-  /* See if we have this position already.  They are kept in sorted
-     order, so use a binary chop.  */
-  /* FIXME: use bsearch */
-  min = 0;
-  max = (int) mp->filepos_count - 1;
-  while (min <= max)
-    {
-      int mid;
-      int cmp;
-
-      mid = (min + max) / 2;
-      pp = &mp->filepos[mid];
-      cmp = strcmp (pp->file_name, name);
-      if (cmp == 0)
-       cmp = (int) pp->line_number - line;
-      if (cmp == 0)
-       return;
-      if (cmp < 0)
-       min = mid + 1;
-      else
-       max = mid - 1;
-    }
-
-  /* Extend the list so that we can add an position to it.  */
-  nbytes = (mp->filepos_count + 1) * sizeof (mp->filepos[0]);
-  mp->filepos = xrealloc (mp->filepos, nbytes);
-
-  /* Shuffle the rest of the list up one, so that we can insert the
-     position at ``min''.  */
-  /* FIXME: use memmove */
-  for (j = mp->filepos_count; j > min; --j)
-    mp->filepos[j] = mp->filepos[j - 1];
-  mp->filepos_count++;
-
-  /* Insert the postion into the empty slot.  */
-  pp = &mp->filepos[min];
-  pp->file_name = xstrdup (name);
-  pp->line_number = line;
-}
-
-message_list_ty *
-message_list_alloc ()
-{
-  message_list_ty *mlp;
-
-  mlp = xmalloc (sizeof (message_list_ty));
-  mlp->nitems = 0;
-  mlp->nitems_max = 0;
-  mlp->item = 0;
-  return mlp;
-}
-
-void
-message_list_append (mlp, mp)
-     message_list_ty *mlp;
-     message_ty *mp;
-{
-  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);
-    }
-  mlp->item[mlp->nitems++] = mp;
-}
-
-void
-message_list_delete_nth (mlp, n)
-     message_list_ty *mlp;
-     size_t n;
-{
-  size_t j;
-
-  if (n >= mlp->nitems)
-    return;
-  message_free (mlp->item[n]);
-  for (j = n + 1; j < mlp->nitems; ++j)
-    mlp->item[j - 1] = mlp->item[j];
-  mlp->nitems--;
-}
-
-message_ty *
-message_list_search (mlp, msgid)
-     message_list_ty *mlp;
-     const char *msgid;
-{
-  size_t j;
-
-  for (j = 0; j < mlp->nitems; ++j)
-    {
-      message_ty *mp;
-
-      mp = mlp->item[j];
-      if (0 == strcmp (msgid, mp->msgid))
-        return mp;
-    }
-  return 0;
-}
-
-message_ty *
-message_list_search_fuzzy (mlp, msgid)
-     message_list_ty *mlp;
-     const char *msgid;
-{
-  size_t j;
-  double best_weight;
-  message_ty *best_mp;
-
-  best_weight = 0.6;
-  best_mp = NULL;
-  for (j = 0; j < mlp->nitems; ++j)
-    {
-      size_t k;
-      double weight;
-      message_ty *mp;
-
-      mp = mlp->item[j];
-
-      for (k = 0; k < mp->variant_count; ++k)
-        if (mp->variant[k].msgstr != NULL && mp->variant[k].msgstr[0] != '\0')
-          break;
-      if (k >= mp->variant_count)
-        continue;
-
-      weight = fstrcmp (msgid, mp->msgid);
-      if (weight > best_weight)
-        {
-          best_weight = weight;
-          best_mp = mp;
-        }
-    }
-  return best_mp;
-}
-
-void
-message_list_free (mlp)
-     message_list_ty *mlp;
-{
-  size_t j;
-
-  for (j = 0; j < mlp->nitems; ++j)
-    message_free (mlp->item[j]);
-  if (mlp->item)
-    free (mlp->item);
-  free (mlp);
-}
-
-/* ================================================================== */
 
 static int
 slurp(const char *file, char **ibufp, size_t *nbp)
@@ -742,7 +511,9 @@ DPRINTF(100, ("%.*s\n", (int)(se-s), s));
                        if (c)
                                *se = '\0';     /* zap \n */
                        switch (s[1]) {
-                       case ':':
+                       case '~':       /* archival translations */
+                               break;
+                       case ':':       /* file cross reference */
                                f = s+2;
                                while (*f && strchr(" \t", *f)) f++;
                                fe = f;
@@ -755,10 +526,15 @@ DPRINTF(100, ("%.*s\n", (int)(se-s), s));
                                string_list_append_unique(flp, f);
                                message_comment_filepos(mp, f, getTagVal(fe));
                                break;
-                       case '.':
+                       case '.':       /* automatic comments */
                                message_comment_dot_append(mp, xstrdup(s));
                                break;
+                       case ',':       /* flag... */
+                               mp->is_c_format = parse_c_format_description_string(f);
+                               break;
                        default:
+                               /* XXX might want to fix and/or warn here */
+                       case ' ':       /* flag... */
                                message_comment_append(mp, xstrdup(s));
                                break;
                        }
@@ -1194,13 +970,16 @@ main(int argc, char **argv)
 
     progname = basename(argv[0]);
 
-    while((c = getopt(argc, argv, "del:I:O:Tv")) != EOF)
+    while((c = getopt(argc, argv, "deEl:I:O:Tv")) != EOF)
     switch (c) {
     case 'd':
        debug++;
        break;
     case 'e':
-       escape = 0;
+       message_print_style_escape(0);
+       break;
+    case 'E':
+       message_print_style_escape(1);
        break;
     case 'l':
        onlylang = optarg;
diff --git a/tools/rpmpo.h b/tools/rpmpo.h
deleted file mode 100644 (file)
index 0411d71..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-#ifndef _H_RPMPO_
-#define _H_RPMPO_
-
-/* XXX lifted from gettext/src/po-lex.h */
-typedef struct lex_pos_ty lex_pos_ty;
-struct lex_pos_ty
-{
-  char *file_name;
-  size_t line_number;
-};
-
-/* XXX lifted from gettext/src/message.h */
-
-/* Is current msgid a format string?  */
-enum is_c_format
-{
-  undecided,
-  yes,
-  no,
-  possible,
-  impossible
-};
-
-typedef struct message_variant_ty message_variant_ty;
-struct message_variant_ty
-{
-  const char *domain;
-  lex_pos_ty pos;
-  const char *msgstr;
-};
-
-typedef struct message_ty message_ty;
-struct message_ty
-{
-  /* Plain comments (#) appearing before the message.  */
-  string_list_ty *comment;
-
-  /* Extracted comments (#.) appearing before the message.  */
-  string_list_ty *comment_dot;
-
-  /* File position comments (#:) appearing before the message, one for
-     each unique file position instance, sorted by file name and then
-     by line.  */
-  size_t filepos_count;
-  lex_pos_ty *filepos;
-
-  /* Informations from special comments (e.g. generated by msgmerge).  */
-  int is_fuzzy;
-  enum is_c_format is_c_format;
-
-  /* Do we want the string to be wrapped in the emitted PO file?  */
-  enum is_c_format do_wrap;
-
-  /* The msgid string.  */
-  const char *msgid;
-
-  /* The msgstr strings, one for each observed domain in the file.  */
-  size_t variant_count;
-  message_variant_ty *variant;
-
-  /* Used for checking that messages have been used, in the msgcmp and
-     msgmerge programs.  */
-  int used;
-
-  /* If set the message is obsolete and while writing out it should be
-     commented out.  */
-  int obsolete;
-};
-
-typedef struct message_list_ty message_list_ty;
-struct message_list_ty
-{
-  message_ty **item;
-  size_t nitems;
-  size_t nitems_max;
-};
-
-#endif /* _H_RPMPO_ */
diff --git a/tools/str-list.h b/tools/str-list.h
deleted file mode 100644 (file)
index 9e2b998..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/* GNU gettext - internationalization aids
-   Copyright (C) 1995, 1996, 1998 Free Software Foundation, Inc.
-
-   This file was written by Peter Miller <millerp@canb.auug.org.au>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
-
-#ifndef SRC_STR_LIST_H
-#define SRC_STR_LIST_H 1
-
-#ifdef STC_HEADERS
-# define __need_size_t
-# define __need_NULL
-# include <stddef.h>
-#else
-# include <sys/types.h>
-# include <stdio.h>
-#endif
-
-/* Type describing list of strings implemented using a dynamic array.  */
-typedef struct string_list_ty string_list_ty;
-struct string_list_ty
-{
-  const char **item;
-  size_t nitems;
-  size_t nitems_max;
-};
-
-
-string_list_ty *string_list_alloc PARAMS ((void));
-void string_list_append PARAMS ((string_list_ty *__slp, const char *__s));
-void string_list_append_unique PARAMS ((string_list_ty *__slp,
-                                       const char *__s));
-void string_list_free PARAMS ((string_list_ty *__slp));
-char *string_list_join PARAMS ((const string_list_ty *__slp));
-int string_list_member PARAMS ((const string_list_ty *__slp, const char *__s));
-
-#endif