ff4ff35918
Red Bear OS is a full fork. All sources must be available from git clone with zero network access. Removed gitignore rules that excluded fetched source trees under recipes/*/source/, local/recipes/kde/*/source/, local/recipes/qt/*/source/, and vendor source trees. Build artifacts (target/, build/, source.tar, *.o, *.so) remain excluded. 127291 files added — kernel, relibc, base, bootloader, pkgar, all KDE/Qt frameworks, mesa, wayland, DRM drivers, and every other recipe source.
413 lines
10 KiB
C
413 lines
10 KiB
C
/* KUIT (KDE User Interface Text) format strings.
|
|
Copyright (C) 2015, 2018-2019 Free Software Foundation, Inc.
|
|
Written by Daiki Ueno <ueno@gnu.org>, 2015.
|
|
|
|
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 3 of the License, 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, see <https://www.gnu.org/licenses/>. */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "format.h"
|
|
#include "unistr.h"
|
|
#include "xalloc.h"
|
|
#include "xvasprintf.h"
|
|
#include "gettext.h"
|
|
|
|
#if IN_LIBGETTEXTPO
|
|
/* Use included markup parser to avoid extra dependency from
|
|
libgettextpo to libxml2. */
|
|
# ifndef FORMAT_KDE_KUIT_FALLBACK_MARKUP
|
|
# define FORMAT_KDE_KUIT_USE_FALLBACK_MARKUP 1
|
|
# endif
|
|
#else
|
|
# define FORMAT_KDE_KUIT_USE_LIBXML2 1
|
|
#endif
|
|
|
|
#if FORMAT_KDE_KUIT_USE_LIBXML2
|
|
# include <libxml/parser.h>
|
|
#elif FORMAT_KDE_KUIT_USE_FALLBACK_MARKUP
|
|
# include "markup.h"
|
|
#endif
|
|
|
|
|
|
#define _(str) gettext (str)
|
|
|
|
#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
|
|
|
|
|
|
/* KUIT (KDE User Interface Text) is an XML-like markup which augments
|
|
translatable strings with semantic information:
|
|
https://api.kde.org/frameworks/ki18n/html/prg_guide.html#kuit_markup
|
|
KUIT can be seen as a fragment of a well-formed XML document,
|
|
except that it allows '&' as a Qt accelerator marker and '%' as a
|
|
format directive. */
|
|
|
|
struct spec
|
|
{
|
|
/* A format string descriptor returned from formatstring_kde.parse. */
|
|
void *base;
|
|
};
|
|
|
|
#define XML_NS "https://www.gnu.org/s/gettext/kde"
|
|
|
|
struct char_range
|
|
{
|
|
ucs4_t start;
|
|
ucs4_t end;
|
|
};
|
|
|
|
/* Character ranges for NameStartChar defined in:
|
|
https://www.w3.org/TR/REC-xml/#NT-NameStartChar */
|
|
static const struct char_range name_chars1[] =
|
|
{
|
|
{ ':', ':' },
|
|
{ 'A', 'Z' },
|
|
{ '_', '_' },
|
|
{ 'a', 'z' },
|
|
{ 0xC0, 0xD6 },
|
|
{ 0xD8, 0xF6 },
|
|
{ 0xF8, 0x2FF },
|
|
{ 0x370, 0x37D },
|
|
{ 0x37F, 0x1FFF },
|
|
{ 0x200C, 0x200D },
|
|
{ 0x2070, 0x218F },
|
|
{ 0x2C00, 0x2FEF },
|
|
{ 0x3001, 0xD7FF },
|
|
{ 0xF900, 0xFDCF },
|
|
{ 0xFDF0, 0xFFFD },
|
|
{ 0x10000, 0xEFFFF }
|
|
};
|
|
|
|
/* Character ranges for NameChar, excluding NameStartChar:
|
|
https://www.w3.org/TR/REC-xml/#NT-NameChar */
|
|
static const struct char_range name_chars2[] =
|
|
{
|
|
{ '-', '-' },
|
|
{ '.', '.' },
|
|
{ '0', '9' },
|
|
{ 0xB7, 0xB7 },
|
|
{ 0x0300, 0x036F },
|
|
{ 0x203F, 0x2040 }
|
|
};
|
|
|
|
/* Return true if INPUT is an XML reference. */
|
|
static bool
|
|
is_reference (const char *input)
|
|
{
|
|
const char *str = input;
|
|
const char *str_limit = str + strlen (input);
|
|
ucs4_t uc;
|
|
int i;
|
|
|
|
str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
|
|
assert (uc == '&');
|
|
|
|
str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
|
|
|
|
/* CharRef */
|
|
if (uc == '#')
|
|
{
|
|
str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
|
|
if (uc == 'x')
|
|
{
|
|
while (str < str_limit)
|
|
{
|
|
str += u8_mbtouc (&uc, (const unsigned char *) str,
|
|
str_limit - str);
|
|
if (!(('0' <= uc && uc <= '9')
|
|
|| ('A' <= uc && uc <= 'F')
|
|
|| ('a' <= uc && uc <= 'f')))
|
|
break;
|
|
}
|
|
return uc == ';';
|
|
}
|
|
else if ('0' <= uc && uc <= '9')
|
|
{
|
|
while (str < str_limit)
|
|
{
|
|
str += u8_mbtouc (&uc, (const unsigned char *) str,
|
|
str_limit - str);
|
|
if (!('0' <= uc && uc <= '9'))
|
|
break;
|
|
}
|
|
return uc == ';';
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* EntityRef */
|
|
for (i = 0; i < SIZEOF (name_chars1); i++)
|
|
if (name_chars1[i].start <= uc && uc <= name_chars1[i].end)
|
|
break;
|
|
|
|
if (i == SIZEOF (name_chars1))
|
|
return false;
|
|
|
|
while (str < str_limit)
|
|
{
|
|
str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
|
|
for (i = 0; i < SIZEOF (name_chars1); i++)
|
|
if (name_chars1[i].start <= uc && uc <= name_chars1[i].end)
|
|
break;
|
|
if (i == SIZEOF (name_chars1))
|
|
{
|
|
for (i = 0; i < SIZEOF (name_chars2); i++)
|
|
if (name_chars2[i].start <= uc && uc <= name_chars2[i].end)
|
|
break;
|
|
if (i == SIZEOF (name_chars2))
|
|
return false;
|
|
}
|
|
}
|
|
return uc == ';';
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
static void *
|
|
format_parse (const char *format, bool translated, char *fdi,
|
|
char **invalid_reason)
|
|
{
|
|
struct spec spec;
|
|
struct spec *result;
|
|
const char *str;
|
|
const char *str_limit;
|
|
size_t amp_count;
|
|
char *buffer, *bp;
|
|
|
|
spec.base = NULL;
|
|
|
|
/* Preprocess the input, putting the content in a <gt:kuit> element. */
|
|
str = format;
|
|
str_limit = str + strlen (format);
|
|
|
|
for (amp_count = 0; str < str_limit; amp_count++)
|
|
{
|
|
const char *amp = strchrnul (str, '&');
|
|
if (*amp != '&')
|
|
break;
|
|
str = amp + 1;
|
|
}
|
|
|
|
buffer = xmalloc (amp_count * 4
|
|
+ strlen (format)
|
|
+ strlen ("<gt:kuit xmlns:gt=\"" XML_NS "\"></gt:kuit>")
|
|
+ 1);
|
|
*buffer = '\0';
|
|
|
|
bp = buffer;
|
|
bp = stpcpy (bp, "<gt:kuit xmlns:gt=\"" XML_NS "\">");
|
|
str = format;
|
|
while (str < str_limit)
|
|
{
|
|
const char *amp = strchrnul (str, '&');
|
|
|
|
bp = stpncpy (bp, str, amp - str);
|
|
if (*amp != '&')
|
|
break;
|
|
|
|
bp = stpcpy (bp, is_reference (amp) ? "&" : "&");
|
|
str = amp + 1;
|
|
}
|
|
stpcpy (bp, "</gt:kuit>");
|
|
|
|
#if FORMAT_KDE_KUIT_USE_LIBXML2
|
|
{
|
|
xmlDocPtr doc;
|
|
|
|
doc = xmlReadMemory (buffer, strlen (buffer), "", NULL,
|
|
XML_PARSE_NONET
|
|
| XML_PARSE_NOWARNING
|
|
| XML_PARSE_NOERROR
|
|
| XML_PARSE_NOBLANKS);
|
|
if (doc == NULL)
|
|
{
|
|
xmlError *err = xmlGetLastError ();
|
|
*invalid_reason =
|
|
xasprintf (_("error while parsing: %s"),
|
|
err->message);
|
|
free (buffer);
|
|
xmlFreeDoc (doc);
|
|
return NULL;
|
|
}
|
|
|
|
free (buffer);
|
|
xmlFreeDoc (doc);
|
|
}
|
|
#elif FORMAT_KDE_KUIT_USE_FALLBACK_MARKUP
|
|
{
|
|
markup_parser_ty parser;
|
|
markup_parse_context_ty *context;
|
|
|
|
memset (&parser, 0, sizeof (markup_parser_ty));
|
|
context = markup_parse_context_new (&parser, 0, NULL);
|
|
if (!markup_parse_context_parse (context, buffer, strlen (buffer)))
|
|
{
|
|
*invalid_reason =
|
|
xasprintf (_("error while parsing: %s"),
|
|
markup_parse_context_get_error (context));
|
|
free (buffer);
|
|
markup_parse_context_free (context);
|
|
return NULL;
|
|
}
|
|
|
|
if (!markup_parse_context_end_parse (context))
|
|
{
|
|
*invalid_reason =
|
|
xasprintf (_("error while parsing: %s"),
|
|
markup_parse_context_get_error (context));
|
|
free (buffer);
|
|
markup_parse_context_free (context);
|
|
return NULL;
|
|
}
|
|
|
|
free (buffer);
|
|
markup_parse_context_free (context);
|
|
}
|
|
#else
|
|
/* No support for XML. */
|
|
free (buffer);
|
|
#endif
|
|
|
|
spec.base = formatstring_kde.parse (format, translated, fdi, invalid_reason);
|
|
if (spec.base == NULL)
|
|
return NULL;
|
|
|
|
result = XMALLOC (struct spec);
|
|
*result = spec;
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
format_free (void *descr)
|
|
{
|
|
struct spec *spec = descr;
|
|
formatstring_kde.free (spec->base);
|
|
free (spec);
|
|
}
|
|
|
|
static int
|
|
format_get_number_of_directives (void *descr)
|
|
{
|
|
struct spec *spec = descr;
|
|
return formatstring_kde.get_number_of_directives (spec->base);
|
|
}
|
|
|
|
static bool
|
|
format_check (void *msgid_descr, void *msgstr_descr, bool equality,
|
|
formatstring_error_logger_t error_logger,
|
|
const char *pretty_msgid, const char *pretty_msgstr)
|
|
{
|
|
struct spec *msgid_spec = msgid_descr;
|
|
struct spec *msgstr_spec = msgstr_descr;
|
|
|
|
return formatstring_kde.check (msgid_spec->base, msgstr_spec->base, equality,
|
|
error_logger,
|
|
pretty_msgid, pretty_msgstr);
|
|
}
|
|
|
|
struct formatstring_parser formatstring_kde_kuit =
|
|
{
|
|
format_parse,
|
|
format_free,
|
|
format_get_number_of_directives,
|
|
NULL,
|
|
format_check
|
|
};
|
|
|
|
|
|
#ifdef TEST
|
|
|
|
/* Test program: Print the argument list specification returned by
|
|
format_parse for strings read from standard input. */
|
|
|
|
#include <stdio.h>
|
|
|
|
static void
|
|
format_print (void *descr)
|
|
{
|
|
struct spec *spec = (struct spec *) descr;
|
|
unsigned int last;
|
|
unsigned int i;
|
|
|
|
if (spec == NULL)
|
|
{
|
|
printf ("INVALID");
|
|
return;
|
|
}
|
|
|
|
printf ("(");
|
|
last = 1;
|
|
for (i = 0; i < spec->numbered_arg_count; i++)
|
|
{
|
|
unsigned int number = spec->numbered[i].number;
|
|
|
|
if (i > 0)
|
|
printf (" ");
|
|
if (number < last)
|
|
abort ();
|
|
for (; last < number; last++)
|
|
printf ("_ ");
|
|
last = number + 1;
|
|
}
|
|
printf (")");
|
|
}
|
|
|
|
int
|
|
main ()
|
|
{
|
|
for (;;)
|
|
{
|
|
char *line = NULL;
|
|
size_t line_size = 0;
|
|
int line_len;
|
|
char *invalid_reason;
|
|
void *descr;
|
|
|
|
line_len = getline (&line, &line_size, stdin);
|
|
if (line_len < 0)
|
|
break;
|
|
if (line_len > 0 && line[line_len - 1] == '\n')
|
|
line[--line_len] = '\0';
|
|
|
|
invalid_reason = NULL;
|
|
descr = format_parse (line, false, NULL, &invalid_reason);
|
|
|
|
format_print (descr);
|
|
printf ("\n");
|
|
if (descr == NULL)
|
|
printf ("%s\n", invalid_reason);
|
|
|
|
free (invalid_reason);
|
|
free (line);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* For Emacs M-x compile
|
|
* Local Variables:
|
|
* compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../../gettext-runtime/intl -DHAVE_CONFIG_H -DTEST format-kde-kuit.c ../gnulib-lib/libgettextlib.la"
|
|
* End:
|
|
*/
|
|
|
|
#endif /* TEST */
|