facf0c92e0
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.
251 lines
8.1 KiB
C
251 lines
8.1 KiB
C
/* xgettext Ruby backend.
|
|
Copyright (C) 2020 Free Software Foundation, Inc.
|
|
Written by Bruno Haible <bruno@clisp.org>, 2020.
|
|
|
|
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
|
|
|
|
/* Specification. */
|
|
#include "x-ruby.h"
|
|
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "message.h"
|
|
#include "sh-quote.h"
|
|
#include "spawn-pipe.h"
|
|
#include "wait-process.h"
|
|
#include "xvasprintf.h"
|
|
#include "x-po.h"
|
|
#include "xgettext.h"
|
|
#include "xg-message.h"
|
|
#include "c-strstr.h"
|
|
#include "read-catalog-abstract.h"
|
|
#include "error.h"
|
|
#include "gettext.h"
|
|
|
|
/* A convenience macro. I don't like writing gettext() every time. */
|
|
#define _(str) gettext (str)
|
|
|
|
/* The Ruby syntax is defined in
|
|
https://ruby-doc.org/core-2.7.1/doc/syntax_rdoc.html
|
|
https://ruby-doc.org/core-2.7.1/doc/syntax/comments_rdoc.html
|
|
https://ruby-doc.org/core-2.7.1/doc/syntax/literals_rdoc.html
|
|
We don't parse Ruby directly, but instead rely on the 'rxgettext' program
|
|
from https://github.com/ruby-gettext/gettext . */
|
|
|
|
|
|
/* ====================== Keyword set customization. ====================== */
|
|
|
|
/* This function currently has no effect. */
|
|
void
|
|
x_ruby_extract_all (void)
|
|
{
|
|
}
|
|
|
|
/* This function currently has no effect. */
|
|
void
|
|
x_ruby_keyword (const char *keyword)
|
|
{
|
|
}
|
|
|
|
/* This function currently has no effect. */
|
|
void
|
|
init_flag_table_ruby (void)
|
|
{
|
|
}
|
|
|
|
|
|
/* ========================= Extracting strings. ========================== */
|
|
|
|
void
|
|
extract_ruby (const char *found_in_dir, const char *real_filename,
|
|
const char *logical_filename,
|
|
flag_context_list_table_ty *flag_table,
|
|
msgdomain_list_ty *mdlp)
|
|
{
|
|
const char *progname = "rxgettext";
|
|
char *dummy_filename;
|
|
msgdomain_list_ty *mdlp2;
|
|
int pass;
|
|
|
|
dummy_filename = xasprintf (_("(output from '%s')"), progname);
|
|
|
|
/* Invoke rgettext twice:
|
|
1. to get the messages, without ruby-format flags.
|
|
2. to get the 'xgettext:' comments that guide us while adding
|
|
[no-]ruby-format flags. */
|
|
mdlp2 = msgdomain_list_alloc (true);
|
|
for (pass = 0; pass < 2; pass++)
|
|
{
|
|
const char *argv[4];
|
|
unsigned int i;
|
|
pid_t child;
|
|
int fd[1];
|
|
FILE *fp;
|
|
int exitstatus;
|
|
|
|
/* Prepare arguments. */
|
|
argv[0] = progname;
|
|
i = 1;
|
|
|
|
if (pass > 0)
|
|
argv[i++] = "--add-comments=xgettext:";
|
|
else
|
|
{
|
|
if (add_all_comments)
|
|
argv[i++] = "--add-comments";
|
|
else if (comment_tag != NULL)
|
|
argv[i++] = xasprintf ("--add-comments=%s", comment_tag);
|
|
}
|
|
|
|
argv[i++] = logical_filename;
|
|
|
|
argv[i] = NULL;
|
|
|
|
if (verbose)
|
|
{
|
|
char *command = shell_quote_argv (argv);
|
|
error (0, 0, "%s", command);
|
|
free (command);
|
|
}
|
|
|
|
child = create_pipe_in (progname, progname, argv, found_in_dir,
|
|
DEV_NULL, false, true, true, fd);
|
|
|
|
fp = fdopen (fd[0], "r");
|
|
if (fp == NULL)
|
|
error (EXIT_FAILURE, errno, _("fdopen() failed"));
|
|
|
|
/* Read the resulting PO file. */
|
|
extract_po (fp, dummy_filename, dummy_filename, flag_table,
|
|
pass == 0 ? mdlp : mdlp2);
|
|
|
|
fclose (fp);
|
|
|
|
/* Remove zombie process from process list, and retrieve exit status. */
|
|
exitstatus =
|
|
wait_subprocess (child, progname, false, false, true, true, NULL);
|
|
if (exitstatus != 0)
|
|
error (EXIT_FAILURE, 0, _("%s subprocess failed with exit code %d"),
|
|
progname, exitstatus);
|
|
}
|
|
|
|
/* Add [no-]ruby-format flags and process 'xgettext:' comments.
|
|
This processing is similar to the one done in remember_a_message(). */
|
|
if (mdlp->nitems == 1 && mdlp2->nitems == 1)
|
|
{
|
|
message_list_ty *mlp = mdlp->item[0]->messages;
|
|
message_list_ty *mlp2 = mdlp2->item[0]->messages;
|
|
size_t j;
|
|
|
|
for (j = 0; j < mlp->nitems; j++)
|
|
{
|
|
message_ty *mp = mlp->item[j];
|
|
|
|
if (!is_header (mp))
|
|
{
|
|
/* Find 'xgettext:' comments and apply them to mp. */
|
|
message_ty *mp2 =
|
|
message_list_search (mlp2, mp->msgctxt, mp->msgid);
|
|
|
|
if (mp2 != NULL && mp2->comment_dot != NULL)
|
|
{
|
|
string_list_ty *mp2_comment_dot = mp2->comment_dot;
|
|
size_t k;
|
|
|
|
for (k = 0; k < mp2_comment_dot->nitems; k++)
|
|
{
|
|
const char *s = mp2_comment_dot->item[k];
|
|
|
|
/* To reduce the possibility of unwanted matches we do a
|
|
two step match: the line must contain 'xgettext:' and
|
|
one of the possible format description strings. */
|
|
const char *t = c_strstr (s, "xgettext:");
|
|
if (t != NULL)
|
|
{
|
|
bool tmp_fuzzy;
|
|
enum is_format tmp_format[NFORMATS];
|
|
struct argument_range tmp_range;
|
|
enum is_wrap tmp_wrap;
|
|
enum is_syntax_check tmp_syntax_check[NSYNTAXCHECKS];
|
|
bool interesting;
|
|
size_t i;
|
|
|
|
t += strlen ("xgettext:");
|
|
|
|
po_parse_comment_special (t, &tmp_fuzzy, tmp_format,
|
|
&tmp_range, &tmp_wrap,
|
|
tmp_syntax_check);
|
|
|
|
interesting = false;
|
|
for (i = 0; i < NFORMATS; i++)
|
|
if (tmp_format[i] != undecided)
|
|
{
|
|
mp->is_format[i] = tmp_format[i];
|
|
interesting = true;
|
|
}
|
|
if (has_range_p (tmp_range))
|
|
{
|
|
intersect_range (mp, &tmp_range);
|
|
interesting = true;
|
|
}
|
|
if (tmp_wrap != undecided)
|
|
{
|
|
mp->do_wrap = tmp_wrap;
|
|
interesting = true;
|
|
}
|
|
for (i = 0; i < NSYNTAXCHECKS; i++)
|
|
if (tmp_syntax_check[i] != undecided)
|
|
{
|
|
mp->do_syntax_check[i] = tmp_syntax_check[i];
|
|
interesting = true;
|
|
}
|
|
|
|
/* If the "xgettext:" marker was followed by an
|
|
interesting keyword, and we updated our
|
|
is_format/do_wrap variables, eliminate the comment
|
|
from the #. comments. */
|
|
if (interesting)
|
|
if (mp->comment_dot != NULL)
|
|
{
|
|
const char *removed =
|
|
string_list_remove (mp->comment_dot, s);
|
|
|
|
if (removed != NULL)
|
|
free ((char *) removed);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Now evaluate the consequences of the 'xgettext:' comments, */
|
|
decide_is_format (mp);
|
|
decide_do_wrap (mp);
|
|
decide_syntax_check (mp);
|
|
}
|
|
}
|
|
}
|
|
|
|
msgdomain_list_free (mdlp2);
|
|
|
|
free (dummy_filename);
|
|
}
|