Blame locale/po2strings.c

Packit 2fc92b
/*
Packit 2fc92b
 * Convert a GNU gettext .po file to an Apple .strings file.
Packit 2fc92b
 *
Packit 2fc92b
 * Copyright 2007-2017 by Apple Inc.
Packit 2fc92b
 *
Packit 2fc92b
 * These coded instructions, statements, and computer programs are the
Packit 2fc92b
 * property of Apple Inc. and are protected by Federal copyright
Packit 2fc92b
 * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
Packit 2fc92b
 * which should have been included with this file.  If this file is
Packit 2fc92b
 * missing or damaged, see the license at "http://www.cups.org/".
Packit 2fc92b
 *
Packit 2fc92b
 * Usage:
Packit 2fc92b
 *
Packit 2fc92b
 *   po2strings filename.strings filename.po
Packit 2fc92b
 *
Packit 2fc92b
 * Compile with:
Packit 2fc92b
 *
Packit 2fc92b
 *   gcc -o po2strings po2strings.c `cups-config --libs`
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
#include <cups/cups-private.h>
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * The .strings file format is simple:
Packit 2fc92b
 *
Packit 2fc92b
 * // comment
Packit 2fc92b
 * "msgid" = "msgstr";
Packit 2fc92b
 *
Packit 2fc92b
 * The GNU gettext .po format is also fairly simple:
Packit 2fc92b
 *
Packit 2fc92b
 *     #. comment
Packit 2fc92b
 *     msgid "some text"
Packit 2fc92b
 *     msgstr "localized text"
Packit 2fc92b
 *
Packit 2fc92b
 * The comment, msgid, and msgstr text can span multiple lines using the form:
Packit 2fc92b
 *
Packit 2fc92b
 *     #. comment
Packit 2fc92b
 *     #. more comments
Packit 2fc92b
 *     msgid ""
Packit 2fc92b
 *     "some long text"
Packit 2fc92b
 *     msgstr ""
Packit 2fc92b
 *     "localized text spanning "
Packit 2fc92b
 *     "multiple lines"
Packit 2fc92b
 *
Packit 2fc92b
 * Both the msgid and msgstr strings use standard C quoting for special
Packit 2fc92b
 * characters like newline and the double quote character.
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static char	*normalize_string(const char *idstr, char *buffer, size_t bufsize);
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 *   main() - Convert .po file to .strings.
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
int					/* O - Exit code */
Packit 2fc92b
main(int  argc,				/* I - Number of command-line args */
Packit 2fc92b
     char *argv[])			/* I - Command-line arguments */
Packit 2fc92b
{
Packit 2fc92b
  int			i;		/* Looping var */
Packit 2fc92b
  const char		*pofile,	/* .po filename */
Packit 2fc92b
			*stringsfile;	/* .strings filename */
Packit 2fc92b
  cups_file_t		*po,		/* .po file */
Packit 2fc92b
			*strings;	/* .strings file */
Packit 2fc92b
  char			s[4096],	/* String buffer */
Packit 2fc92b
			*ptr,		/* Pointer into buffer */
Packit 2fc92b
			*temp,		/* New string */
Packit 2fc92b
			*msgid,		/* msgid string */
Packit 2fc92b
			*msgstr,	/* msgstr string */
Packit 2fc92b
			normalized[8192];/* Normalized msgid string */
Packit 2fc92b
  size_t		length;		/* Length of combined strings */
Packit 2fc92b
  int			use_msgid;	/* Use msgid strings for msgstr? */
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Process command-line arguments...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  pofile      = NULL;
Packit 2fc92b
  stringsfile = NULL;
Packit 2fc92b
  use_msgid   = 0;
Packit 2fc92b
Packit 2fc92b
  for (i = 1; i < argc; i ++)
Packit 2fc92b
  {
Packit 2fc92b
    if (!strcmp(argv[i], "-m"))
Packit 2fc92b
      use_msgid = 1;
Packit 2fc92b
    else if (argv[i][0] == '-')
Packit 2fc92b
    {
Packit 2fc92b
      puts("Usage: po2strings [-m] filename.po filename.strings");
Packit 2fc92b
      return (1);
Packit 2fc92b
    }
Packit 2fc92b
    else if (!pofile)
Packit 2fc92b
      pofile = argv[i];
Packit 2fc92b
    else if (!stringsfile)
Packit 2fc92b
      stringsfile = argv[i];
Packit 2fc92b
    else
Packit 2fc92b
    {
Packit 2fc92b
      puts("Usage: po2strings [-m] filename.po filename.strings");
Packit 2fc92b
      return (1);
Packit 2fc92b
    }
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
  if (!pofile || !stringsfile)
Packit 2fc92b
  {
Packit 2fc92b
    puts("Usage: po2strings [-m] filename.po filename.strings");
Packit 2fc92b
    return (1);
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Read strings from the .po file and write to the .strings file...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  if ((po = cupsFileOpen(pofile, "r")) == NULL)
Packit 2fc92b
  {
Packit 2fc92b
    perror(pofile);
Packit 2fc92b
    return (1);
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
  if ((strings = cupsFileOpen(stringsfile, "w")) == NULL)
Packit 2fc92b
  {
Packit 2fc92b
    perror(stringsfile);
Packit 2fc92b
    cupsFileClose(po);
Packit 2fc92b
    return (1);
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
  msgid = msgstr = NULL;
Packit 2fc92b
Packit 2fc92b
  while (cupsFileGets(po, s, sizeof(s)) != NULL)
Packit 2fc92b
  {
Packit 2fc92b
    if (s[0] == '#' && s[1] == '.')
Packit 2fc92b
    {
Packit 2fc92b
     /*
Packit 2fc92b
      * Copy comment string...
Packit 2fc92b
      */
Packit 2fc92b
Packit 2fc92b
      if (msgid && msgstr)
Packit 2fc92b
      {
Packit 2fc92b
       /*
Packit 2fc92b
        * First output the last localization string...
Packit 2fc92b
	*/
Packit 2fc92b
Packit 2fc92b
	if (*msgid)
Packit 2fc92b
	  cupsFilePrintf(strings, "\"%s\" = \"%s\";\n", msgid,
Packit 2fc92b
			 (use_msgid || !*msgstr) ? msgid : msgstr);
Packit 2fc92b
Packit 2fc92b
	free(msgid);
Packit 2fc92b
	free(msgstr);
Packit 2fc92b
	msgid = msgstr = NULL;
Packit 2fc92b
      }
Packit 2fc92b
Packit 2fc92b
      cupsFilePrintf(strings, "//%s\n", s + 2);
Packit 2fc92b
    }
Packit 2fc92b
    else if (s[0] == '#' || !s[0])
Packit 2fc92b
    {
Packit 2fc92b
     /*
Packit 2fc92b
      * Skip blank and file comment lines...
Packit 2fc92b
      */
Packit 2fc92b
Packit 2fc92b
      continue;
Packit 2fc92b
    }
Packit 2fc92b
    else
Packit 2fc92b
    {
Packit 2fc92b
     /*
Packit 2fc92b
      * Strip the trailing quote...
Packit 2fc92b
      */
Packit 2fc92b
Packit 2fc92b
      if ((ptr = strrchr(s, '\"')) == NULL)
Packit 2fc92b
	continue;
Packit 2fc92b
Packit 2fc92b
      *ptr = '\0';
Packit 2fc92b
Packit 2fc92b
     /*
Packit 2fc92b
      * Find start of value...
Packit 2fc92b
      */
Packit 2fc92b
Packit 2fc92b
      if ((ptr = strchr(s, '\"')) == NULL)
Packit 2fc92b
	continue;
Packit 2fc92b
Packit 2fc92b
      ptr ++;
Packit 2fc92b
Packit 2fc92b
     /*
Packit 2fc92b
      * Create or add to a message...
Packit 2fc92b
      */
Packit 2fc92b
Packit 2fc92b
      if (!strncmp(s, "msgid", 5))
Packit 2fc92b
      {
Packit 2fc92b
       /*
Packit 2fc92b
	* Output previous message as needed...
Packit 2fc92b
	*/
Packit 2fc92b
Packit 2fc92b
        if (msgid && msgstr)
Packit 2fc92b
	{
Packit 2fc92b
	  if (*msgid)
Packit 2fc92b
            cupsFilePrintf(strings, "\"%s\" = \"%s\";\n", msgid, normalize_string((use_msgid || !*msgstr) ? msgid : msgstr, normalized, sizeof(normalized)));
Packit 2fc92b
	}
Packit 2fc92b
Packit 2fc92b
	if (msgid)
Packit 2fc92b
	  free(msgid);
Packit 2fc92b
Packit 2fc92b
	if (msgstr)
Packit 2fc92b
	  free(msgstr);
Packit 2fc92b
Packit 2fc92b
        msgid  = strdup(ptr);
Packit 2fc92b
	msgstr = NULL;
Packit 2fc92b
      }
Packit 2fc92b
      else if (s[0] == '\"' && (msgid || msgstr))
Packit 2fc92b
      {
Packit 2fc92b
       /*
Packit 2fc92b
	* Append to current string...
Packit 2fc92b
	*/
Packit 2fc92b
Packit 2fc92b
        size_t ptrlen = strlen(ptr);	/* Length of string */
Packit 2fc92b
Packit 2fc92b
	length = strlen(msgstr ? msgstr : msgid);
Packit 2fc92b
Packit 2fc92b
	if ((temp = realloc(msgstr ? msgstr : msgid,
Packit 2fc92b
			    length + ptrlen + 1)) == NULL)
Packit 2fc92b
	{
Packit 2fc92b
	  free(msgid);
Packit 2fc92b
	  if (msgstr)
Packit 2fc92b
	    free(msgstr);
Packit 2fc92b
	  perror("Unable to allocate string");
Packit 2fc92b
	  return (1);
Packit 2fc92b
	}
Packit 2fc92b
Packit 2fc92b
	if (msgstr)
Packit 2fc92b
	{
Packit 2fc92b
	 /*
Packit 2fc92b
	  * Copy the new portion to the end of the msgstr string - safe
Packit 2fc92b
	  * to use strcpy because the buffer is allocated to the correct
Packit 2fc92b
	  * size...
Packit 2fc92b
	  */
Packit 2fc92b
Packit 2fc92b
	  msgstr = temp;
Packit 2fc92b
Packit 2fc92b
	  memcpy(msgstr + length, ptr, ptrlen + 1);
Packit 2fc92b
	}
Packit 2fc92b
	else
Packit 2fc92b
	{
Packit 2fc92b
	 /*
Packit 2fc92b
	  * Copy the new portion to the end of the msgid string - safe
Packit 2fc92b
	  * to use strcpy because the buffer is allocated to the correct
Packit 2fc92b
	  * size...
Packit 2fc92b
	  */
Packit 2fc92b
Packit 2fc92b
	  msgid = temp;
Packit 2fc92b
Packit 2fc92b
	  memcpy(msgid + length, ptr, ptrlen + 1);
Packit 2fc92b
	}
Packit 2fc92b
      }
Packit 2fc92b
      else if (!strncmp(s, "msgstr", 6) && msgid)
Packit 2fc92b
      {
Packit 2fc92b
       /*
Packit 2fc92b
	* Set the string...
Packit 2fc92b
	*/
Packit 2fc92b
Packit 2fc92b
        if (msgstr)
Packit 2fc92b
          free(msgstr);
Packit 2fc92b
Packit 2fc92b
	if ((msgstr = strdup(ptr)) == NULL)
Packit 2fc92b
	{
Packit 2fc92b
	  free(msgid);
Packit 2fc92b
	  perror("Unable to allocate msgstr");
Packit 2fc92b
	  return (1);
Packit 2fc92b
	}
Packit 2fc92b
      }
Packit 2fc92b
    }
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
  if (msgid && msgstr)
Packit 2fc92b
  {
Packit 2fc92b
    if (*msgid)
Packit 2fc92b
      cupsFilePrintf(strings, "\"%s\" = \"%s\";\n", msgid, normalize_string((use_msgid || !*msgstr) ? msgid : msgstr, normalized, sizeof(normalized)));
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
  if (msgid)
Packit 2fc92b
    free(msgid);
Packit 2fc92b
Packit 2fc92b
  if (msgstr)
Packit 2fc92b
    free(msgstr);
Packit 2fc92b
Packit 2fc92b
  cupsFileClose(po);
Packit 2fc92b
  cupsFileClose(strings);
Packit 2fc92b
Packit 2fc92b
  return (0);
Packit 2fc92b
}
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * 'normalize_string()' - Normalize a msgid string.
Packit 2fc92b
 *
Packit 2fc92b
 * This function converts ASCII ellipsis and double quotes to their Unicode
Packit 2fc92b
 * counterparts.
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static char *				/* O - Normalized string */
Packit 2fc92b
normalize_string(const char *idstr,	/* I - msgid string */
Packit 2fc92b
                 char       *buffer,	/* I - Normalized string buffer */
Packit 2fc92b
                 size_t     bufsize)	/* I - Size of string buffer */
Packit 2fc92b
{
Packit 2fc92b
  char	*bufptr = buffer,		/* Pointer into buffer */
Packit 2fc92b
	*bufend = buffer + bufsize - 3;	/* End of buffer */
Packit 2fc92b
  int	quote = 0,			/* Quote direction */
Packit 2fc92b
	html = 0;			/* HTML text */
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
  while (*idstr && bufptr < bufend)
Packit 2fc92b
  {
Packit 2fc92b
    if (!strncmp(idstr, "
Packit 2fc92b
      html = 1;
Packit 2fc92b
    else if (html && *idstr == '>')
Packit 2fc92b
      html = 0;
Packit 2fc92b
Packit 2fc92b
    if (*idstr == '.' && idstr[1] == '.' && idstr[2] == '.')
Packit 2fc92b
    {
Packit 2fc92b
     /*
Packit 2fc92b
      * Convert ... to Unicode ellipsis...
Packit 2fc92b
      */
Packit 2fc92b
Packit 2fc92b
      *bufptr++ = (char)0xE2;
Packit 2fc92b
      *bufptr++ = (char)0x80;
Packit 2fc92b
      *bufptr++ = (char)0xA6;
Packit 2fc92b
      idstr += 2;
Packit 2fc92b
    }
Packit 2fc92b
    else if (!html && *idstr == '\\' && idstr[1] == '\"')
Packit 2fc92b
    {
Packit 2fc92b
      if (quote)
Packit 2fc92b
      {
Packit 2fc92b
       /*
Packit 2fc92b
        * Convert second \" to Unicode right (curley) double quote.
Packit 2fc92b
        */
Packit 2fc92b
Packit 2fc92b
	*bufptr++ = (char)0xE2;
Packit 2fc92b
	*bufptr++ = (char)0x80;
Packit 2fc92b
	*bufptr++ = (char)0x9D;
Packit 2fc92b
	quote     = 0;
Packit 2fc92b
      }
Packit 2fc92b
      else if (strchr(idstr + 2, '\"') != NULL)
Packit 2fc92b
      {
Packit 2fc92b
       /*
Packit 2fc92b
        * Convert first \" to Unicode left (curley) double quote.
Packit 2fc92b
        */
Packit 2fc92b
Packit 2fc92b
	*bufptr++ = (char)0xE2;
Packit 2fc92b
	*bufptr++ = (char)0x80;
Packit 2fc92b
	*bufptr++ = (char)0x9C;
Packit 2fc92b
	quote     = 1;
Packit 2fc92b
      }
Packit 2fc92b
      else
Packit 2fc92b
      {
Packit 2fc92b
       /*
Packit 2fc92b
        * Convert lone \" to Unicode double prime.
Packit 2fc92b
        */
Packit 2fc92b
Packit 2fc92b
        *bufptr++ = (char)0xE2;
Packit 2fc92b
        *bufptr++ = (char)0x80;
Packit 2fc92b
        *bufptr++ = (char)0xB3;
Packit 2fc92b
      }
Packit 2fc92b
Packit 2fc92b
      idstr ++;
Packit 2fc92b
    }
Packit 2fc92b
    else if (*idstr == '\'')
Packit 2fc92b
    {
Packit 2fc92b
      if (strchr(idstr + 1, '\'') == NULL || quote)
Packit 2fc92b
      {
Packit 2fc92b
       /*
Packit 2fc92b
        * Convert second ' (or ' used for a contraction) to Unicode right
Packit 2fc92b
        * (curley) single quote.
Packit 2fc92b
        */
Packit 2fc92b
Packit 2fc92b
	*bufptr++ = (char)0xE2;
Packit 2fc92b
	*bufptr++ = (char)0x80;
Packit 2fc92b
	*bufptr++ = (char)0x99;
Packit 2fc92b
	quote     = 0;
Packit 2fc92b
      }
Packit 2fc92b
      else
Packit 2fc92b
      {
Packit 2fc92b
       /*
Packit 2fc92b
        * Convert first ' to Unicode left (curley) single quote.
Packit 2fc92b
        */
Packit 2fc92b
Packit 2fc92b
	*bufptr++ = (char)0xE2;
Packit 2fc92b
	*bufptr++ = (char)0x80;
Packit 2fc92b
	*bufptr++ = (char)0x98;
Packit 2fc92b
	quote     = 1;
Packit 2fc92b
      }
Packit 2fc92b
    }
Packit 2fc92b
    else
Packit 2fc92b
      *bufptr++ = *idstr;
Packit 2fc92b
Packit 2fc92b
    idstr ++;
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
  *bufptr = '\0';
Packit 2fc92b
Packit 2fc92b
  return (buffer);
Packit 2fc92b
}