]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - locale/po2strings.c
Remove all of the Subversion keywords from various source files.
[thirdparty/cups.git] / locale / po2strings.c
index 0431668d8f2ed206842feeef8ec1593e14fccd06..51e64f4225ce63b01f83c55d65161598e03a4fe5 100644 (file)
@@ -1,7 +1,13 @@
 /*
- * "$Id$"
+ * Convert a GNU gettext .po file to an Apple .strings file.
  *
- * Convert GNU gettext .po files to Apple .strings file (UTF-16 BE text file).
+ * Copyright 2007-2015 by Apple Inc.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file.  If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
  *
  * Usage:
  *
  * Compile with:
  *
  *   gcc -o po2strings po2strings.c `cups-config --libs`
- *
- * Contents:
- *
- *   main()         - Convert .po file to .strings.
- *   write_string() - Write a string to the .strings file.
  */
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <cups/i18n.h>
+#include <cups/cups-private.h>
 
 
 /*
  * The .strings file format is simple:
  *
  * // comment
- * "id" = "str";
+ * "msgid" = "msgstr";
  *
- * Both the id and str strings use standard C quoting for special characters
- * like newline and the double quote character.
- */
-
-/*
- * Local functions...
+ * The GNU gettext .po format is also fairly simple:
+ *
+ *     #. comment
+ *     msgid "some text"
+ *     msgstr "localized text"
+ *
+ * The comment, msgid, and msgstr text can span multiple lines using the form:
+ *
+ *     #. comment
+ *     #. more comments
+ *     msgid ""
+ *     "some long text"
+ *     msgstr ""
+ *     "localized text spanning "
+ *     "multiple lines"
+ *
+ * Both the msgid and msgstr strings use standard C quoting for special
+ * characters like newline and the double quote character.
  */
 
-static void    write_string(FILE *strings, const char *s);
+static char    *normalize_string(const char *idstr, char *buffer, size_t bufsize);
 
 
 /*
@@ -47,98 +58,304 @@ int                                        /* O - Exit code */
 main(int  argc,                                /* I - Number of command-line args */
      char *argv[])                     /* I - Command-line arguments */
 {
-  FILE                 *strings;       /* .strings file */
-  cups_array_t         *po;            /* .po file */
-  char                 iconv[1024];    /* iconv command */
-  _cups_message_t      *msg;           /* Current message */
+  int                  i;              /* Looping var */
+  const char           *pofile,        /* .po filename */
+                       *stringsfile;   /* .strings filename */
+  cups_file_t          *po,            /* .po file */
+                       *strings;       /* .strings file */
+  char                 s[4096],        /* String buffer */
+                       *ptr,           /* Pointer into buffer */
+                       *temp,          /* New string */
+                       *msgid,         /* msgid string */
+                       *msgstr,        /* msgstr string */
+                       normalized[8192];/* Normalized msgid string */
+  size_t               length;         /* Length of combined strings */
+  int                  use_msgid;      /* Use msgid strings for msgstr? */
 
 
-  if (argc != 3)
+ /*
+  * Process command-line arguments...
+  */
+
+  pofile      = NULL;
+  stringsfile = NULL;
+  use_msgid   = 0;
+
+  for (i = 1; i < argc; i ++)
+  {
+    if (!strcmp(argv[i], "-m"))
+      use_msgid = 1;
+    else if (argv[i][0] == '-')
+    {
+      puts("Usage: po2strings [-m] filename.po filename.strings");
+      return (1);
+    }
+    else if (!pofile)
+      pofile = argv[i];
+    else if (!stringsfile)
+      stringsfile = argv[i];
+    else
+    {
+      puts("Usage: po2strings [-m] filename.po filename.strings");
+      return (1);
+    }
+  }
+
+  if (!pofile || !stringsfile)
   {
-    puts("Usage: po2strings filename.po filename.strings");
+    puts("Usage: po2strings [-m] filename.po filename.strings");
     return (1);
   }
 
  /*
-  * Use the CUPS .po loader to get the message strings...
+  * Read strings from the .po file and write to the .strings file...
   */
 
-  if ((po = _cupsMessageLoad(argv[1])) == NULL)
+  if ((po = cupsFileOpen(pofile, "r")) == NULL)
   {
-    perror(argv[1]);
+    perror(pofile);
     return (1);
   }
 
- /*
-  * Cheat by using iconv to write the .strings file with a UTF-16 encoding.
-  * The .po file uses UTF-8...
-  */
-
-  snprintf(iconv, sizeof(iconv), "iconv -f utf-8 -t utf-16 >'%s'", argv[2]);
-  if ((strings = popen(iconv, "w")) == NULL)
+  if ((strings = cupsFileOpen(stringsfile, "w")) == NULL)
   {
-    perror(argv[2]);
-    _cupsMessageFree(po);
+    perror(stringsfile);
+    cupsFileClose(po);
     return (1);
   }
 
-  for (msg = (_cups_message_t *)cupsArrayFirst(po);
-       msg;
-       msg = (_cups_message_t *)cupsArrayNext(po))
+  msgid = msgstr = NULL;
+
+  while (cupsFileGets(po, s, sizeof(s)) != NULL)
   {
-    write_string(strings, msg->id);
-    fputs(" = ", strings);
-    write_string(strings, msg->str);
-    fputs(";\n", strings);
+    if (s[0] == '#' && s[1] == '.')
+    {
+     /*
+      * Copy comment string...
+      */
+
+      if (msgid && msgstr)
+      {
+       /*
+        * First output the last localization string...
+       */
+
+       if (*msgid)
+         cupsFilePrintf(strings, "\"%s\" = \"%s\";\n", msgid,
+                        (use_msgid || !*msgstr) ? msgid : msgstr);
+
+       free(msgid);
+       free(msgstr);
+       msgid = msgstr = NULL;
+      }
+
+      cupsFilePrintf(strings, "//%s\n", s + 2);
+    }
+    else if (s[0] == '#' || !s[0])
+    {
+     /*
+      * Skip blank and file comment lines...
+      */
+
+      continue;
+    }
+    else
+    {
+     /*
+      * Strip the trailing quote...
+      */
+
+      if ((ptr = strrchr(s, '\"')) == NULL)
+       continue;
+
+      *ptr = '\0';
+
+     /*
+      * Find start of value...
+      */
+
+      if ((ptr = strchr(s, '\"')) == NULL)
+       continue;
+
+      ptr ++;
+
+     /*
+      * Create or add to a message...
+      */
+
+      if (!strncmp(s, "msgid", 5))
+      {
+       /*
+       * Output previous message as needed...
+       */
+
+        if (msgid && msgstr)
+       {
+         if (*msgid)
+            cupsFilePrintf(strings, "\"%s\" = \"%s\";\n", msgid, normalize_string((use_msgid || !*msgstr) ? msgid : msgstr, normalized, sizeof(normalized)));
+       }
+
+       if (msgid)
+         free(msgid);
+
+       if (msgstr)
+         free(msgstr);
+
+        msgid  = strdup(ptr);
+       msgstr = NULL;
+      }
+      else if (s[0] == '\"' && (msgid || msgstr))
+      {
+       /*
+       * Append to current string...
+       */
+
+        size_t ptrlen = strlen(ptr);   /* Length of string */
+
+       length = strlen(msgstr ? msgstr : msgid);
+
+       if ((temp = realloc(msgstr ? msgstr : msgid,
+                           length + ptrlen + 1)) == NULL)
+       {
+         free(msgid);
+         if (msgstr)
+           free(msgstr);
+         perror("Unable to allocate string");
+         return (1);
+       }
+
+       if (msgstr)
+       {
+        /*
+         * Copy the new portion to the end of the msgstr string - safe
+         * to use strcpy because the buffer is allocated to the correct
+         * size...
+         */
+
+         msgstr = temp;
+
+         memcpy(msgstr + length, ptr, ptrlen + 1);
+       }
+       else
+       {
+        /*
+         * Copy the new portion to the end of the msgid string - safe
+         * to use strcpy because the buffer is allocated to the correct
+         * size...
+         */
+
+         msgid = temp;
+
+         memcpy(msgid + length, ptr, ptrlen + 1);
+       }
+      }
+      else if (!strncmp(s, "msgstr", 6) && msgid)
+      {
+       /*
+       * Set the string...
+       */
+
+        if (msgstr)
+          free(msgstr);
+
+       if ((msgstr = strdup(ptr)) == NULL)
+       {
+         free(msgid);
+         perror("Unable to allocate msgstr");
+         return (1);
+       }
+      }
+    }
   }
 
-  printf("%s: %d messages.\n", argv[2], cupsArrayCount(po));
+  if (msgid && msgstr)
+  {
+    if (*msgid)
+      cupsFilePrintf(strings, "\"%s\" = \"%s\";\n", msgid, normalize_string((use_msgid || !*msgstr) ? msgid : msgstr, normalized, sizeof(normalized)));
+  }
+
+  if (msgid)
+    free(msgid);
 
-  pclose(strings);
-  _cupsMessageFree(po);
+  if (msgstr)
+    free(msgstr);
+
+  cupsFileClose(po);
+  cupsFileClose(strings);
 
   return (0);
 }
 
 
 /*
- * 'write_string()' - Write a string to the .strings file.
+ * 'normalize_string()' - Normalize a msgid string.
+ *
+ * This function converts ASCII ellipsis and double quotes to their Unicode
+ * counterparts.
  */
 
-static void
-write_string(FILE       *strings,      /* I - .strings file */
-             const char *s)            /* I - String to write */
+static char *                          /* O - Normalized string */
+normalize_string(const char *idstr,    /* I - msgid string */
+                 char       *buffer,   /* I - Normalized string buffer */
+                 size_t     bufsize)   /* I - Size of string buffer */
 {
-  putc('\"', strings);
+  char *bufptr = buffer,               /* Pointer into buffer */
+       *bufend = buffer + bufsize - 3; /* End of buffer */
+  int  quote = 0,                      /* Quote direction */
+       html = 0;                       /* HTML text */
 
-  while (*s)
+
+  while (*idstr && bufptr < bufend)
   {
-    switch (*s)
+    if (!strncmp(idstr, "<A ", 3))
+      html = 1;
+    else if (html && *idstr == '>')
+      html = 0;
+
+    if (*idstr == '.' && idstr[1] == '.' && idstr[2] == '.')
+    {
+     /*
+      * Convert ... to Unicode ellipsis...
+      */
+
+      *bufptr++ = (char)0xE2;
+      *bufptr++ = (char)0x80;
+      *bufptr++ = (char)0xA6;
+      idstr += 2;
+    }
+    else if (!html && *idstr == '\\' && idstr[1] == '\"' && (quote || strchr(idstr + 2, '\"') != NULL))
     {
-      case '\n' :
-          fputs("\\n", strings);
-         break;
-      case '\t' :
-          fputs("\\t", strings);
-         break;
-      case '\\' :
-          fputs("\\\\", strings);
-         break;
-      case '\"' :
-          fputs("\\\"", strings);
-         break;
-      default :
-          putc(*s, strings);
-         break;
+      if (quote)
+      {
+       /*
+        * Convert \" to Unicode right (curley) double quote.
+        */
+
+       *bufptr++ = (char)0xE2;
+       *bufptr++ = (char)0x80;
+       *bufptr++ = (char)0x9D;
+      }
+      else
+      {
+       /*
+        * Convert \" to Unicode left (curley) double quote.
+        */
+
+       *bufptr++ = (char)0xE2;
+       *bufptr++ = (char)0x80;
+       *bufptr++ = (char)0x9C;
+      }
+
+      quote = !quote;
+      idstr ++;
     }
+    else
+      *bufptr++ = *idstr;
 
-    s ++;
+    idstr ++;
   }
 
-  putc('\"', strings);
-}
+  *bufptr = '\0';
 
-
-/*
- * End of "$Id$".
- */
+  return (buffer);
+}