summaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
Diffstat (limited to 'utils')
-rw-r--r--utils/fetch-transifex.pl124
-rw-r--r--utils/filepath.c10
-rw-r--r--utils/import-messages.pl301
-rw-r--r--utils/utils.c38
4 files changed, 449 insertions, 24 deletions
diff --git a/utils/fetch-transifex.pl b/utils/fetch-transifex.pl
new file mode 100644
index 000000000..d8d588285
--- /dev/null
+++ b/utils/fetch-transifex.pl
@@ -0,0 +1,124 @@
+#!/usr/bin/perl
+#
+# Copyright © 2013 Vincent Sanders <vince@netsurf-browser.org>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# * The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+=head1
+
+retrive resource from transifex service
+
+=cut
+
+use strict;
+use Getopt::Long ();
+use LWP::UserAgent;
+use JSON qw( decode_json );
+use Data::Dumper;
+use Fcntl qw( O_CREAT O_EXCL O_WRONLY O_APPEND O_RDONLY O_WRONLY );
+
+use constant GETOPT_OPTS => qw( auto_abbrev no_getopt_compat bundling );
+use constant GETOPT_SPEC =>
+ qw( output|o=s
+ lang|l=s
+ resource|res|r=s
+ project|prj|p=s
+ user|u=s
+ password|w=s
+ help|h|? );
+
+# default option values:
+my %opt = qw( resource messagesany project netsurf user netsurf );
+
+sub output_stream ();
+sub usage ();
+
+sub main ()
+{
+ my $output;
+ my $opt_ok;
+
+ # option parsing:
+ Getopt::Long::Configure( GETOPT_OPTS );
+ $opt_ok = Getopt::Long::GetOptions( \%opt, GETOPT_SPEC );
+
+ if( $opt_ok )
+ {
+ $output = output_stream();
+ }
+
+ # double check the options are sane (and we weren't asked for the help)
+ if( !$opt_ok || $opt{help} || $opt{lang} !~ /^[a-z]{2}$/ )
+ {
+ usage();
+ }
+
+ my $transifexurl = "https://www.transifex.com/api/2/project/" . $opt{project} . "/resource/" . $opt{resource} . "/translation/" . $opt{lang} . "/";
+
+ my $ua = LWP::UserAgent->new;
+ $ua->credentials(
+ 'www.transifex.com:443',
+ 'Transifex API',
+ $opt{user} => $opt{password}
+ );
+
+ my $response = $ua->get( $transifexurl );
+ if (!$response->is_success) {
+ die $response->status_line . " When fetching " . $transifexurl;
+ }
+
+ # Decode the entire JSON
+ my $decoded_json = decode_json( $response->decoded_content );
+
+ print ( $output $decoded_json->{'content'} );
+}
+
+main();
+
+sub usage ()
+{
+ print(STDERR <<TXT );
+usage:
+ $0 -l lang-code \
+ [-o output-file] [-r resource] [-p project] [-u user] [-w password]
+
+ lang-code : en fr ko ... (no default)
+ project : transifex project (default 'netsurf')
+ resource : transifex resource (default 'messagesany')
+ user : transifex resource (default 'netsurf')
+ password : transifex resource (no default)
+ output-file: defaults to standard output
+TXT
+ exit(1);
+}
+
+sub output_stream ()
+{
+ if( $opt{output} )
+ {
+ my $ofh;
+
+ sysopen( $ofh, $opt{output}, O_CREAT|O_EXCL|O_APPEND|O_WRONLY ) ||
+ die( "$0: Failed to open output file $opt{output}: $!\n" );
+
+ return $ofh;
+ }
+
+ return \*STDOUT;
+}
diff --git a/utils/filepath.c b/utils/filepath.c
index 21a965949..f0aa19585 100644
--- a/utils/filepath.c
+++ b/utils/filepath.c
@@ -89,21 +89,13 @@ char *filepath_sfindfile(char *str, const char *format, ...)
/* exported interface documented in filepath.h */
char *filepath_findfile(const char *format, ...)
{
- char *str;
char *ret;
va_list ap;
- str = malloc(PATH_MAX);
- if (str == NULL)
- return NULL; /* unable to allocate memory */
-
va_start(ap, format);
- ret = filepath_vsfindfile(str, format, ap);
+ ret = filepath_vsfindfile(NULL, format, ap);
va_end(ap);
- if (ret == NULL)
- free(str);
-
return ret;
}
diff --git a/utils/import-messages.pl b/utils/import-messages.pl
new file mode 100644
index 000000000..1c30e7fce
--- /dev/null
+++ b/utils/import-messages.pl
@@ -0,0 +1,301 @@
+#!/usr/bin/perl
+#
+# Copyright © 2013 Vivek Dasmohapatra <vivek@collabora.co.uk>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# * The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+=head1
+
+Take a single-language messages file and merge it back in to the
+NetSurf master messaged (i10n) file.
+
+=cut
+
+use strict;
+
+use Getopt::Long ();
+use Fcntl qw( O_CREAT O_EXCL O_WRONLY O_APPEND O_RDONLY O_WRONLY O_TRUNC );
+
+use constant GETOPT_OPTS => qw( auto_abbrev no_getopt_compat bundling );
+use constant GETOPT_SPEC =>
+ qw( output|o=s
+ input|i=s
+ lang|l=s
+ plat|platform|p=s
+ format|fmt|f=s
+ import|I=s
+ help|h|? );
+
+# default option values:
+my %opt = qw( plat any format messages );
+
+sub input_stream ($;$);
+sub output_stream ();
+sub usage ();
+sub parser ();
+
+sub main ()
+{
+ my $input;
+ my $output;
+ my $import;
+ my $parser;
+ my $opt_ok;
+ my @input;
+ my %message;
+
+ # option parsing:
+ Getopt::Long::Configure( GETOPT_OPTS );
+ $opt_ok = Getopt::Long::GetOptions( \%opt, GETOPT_SPEC );
+
+ # allow input, import & output to be specified as non-option arguments:
+ if( @ARGV ) { $opt{input } ||= shift( @ARGV ) }
+ if( @ARGV ) { $opt{import} ||= shift( @ARGV ) }
+ if( @ARGV ) { $opt{output} ||= shift( @ARGV ) }
+
+ # open the appropriate streams and get the formatter and headers:
+ if( $opt_ok )
+ {
+ $input = input_stream( $opt{input} );
+ $import = input_stream( $opt{import}, 'import-file' );
+ $parser = parser();
+ $opt{plat} ||= 'any';
+ }
+
+ # double check the options are sane (and we weren't asked for the help)
+ if( !$opt_ok || $opt{help} || $opt{lang} !~ /^[a-z]{2}$/ )
+ {
+ usage();
+ }
+
+ @input = <$input>;
+ $output = output_stream();
+
+ $parser->( \%message, $import );
+
+ foreach ( @input )
+ {
+ use bytes;
+
+ if( !/#/ &&
+ !/^\s*$/ &&
+ /^([a-z]{2})\.([^.]+)\.([^:]+):/ )
+ {
+ my( $lang, $plat, $key ) = ( $1, $2, $3 );
+
+ if( $lang eq $opt{lang} )
+ {
+ my $val = $message{ $key };
+ if( $val &&
+ ( $opt{plat} eq 'any' || # all platforms ok
+ $opt{plat} eq $plat ) ) # specified platform matched
+ {
+ print( $output $val ? qq|$1.$2.$3:$val\n| : $_ );
+ next;
+ }
+ }
+ }
+
+ print( $output $_ );
+ }
+}
+
+main();
+
+sub usage ()
+{
+ my @fmt = map { s/::$//; $_ } keys(%{$::{'msgfmt::'}});
+ print( STDERR <<TXT );
+usage:
+ $0 -l lang-code \
+ [-p platform] [-f format] \
+ [-o output-file] [-i input-file] [-I import-file]
+
+ $0 -l lang-code … [input-file [import-file [output-file]]]
+
+ lang-code : en fr ko … (no default)
+ platform : any gtk ami (default 'any')
+ format : @fmt (default 'messages')
+ input-file : defaults to standard input
+ output-file: defaults to standard output
+ import-file: no default
+
+ The input-file may be the same as the output-file, in which case
+ it will be altered in place.
+TXT
+ exit(1);
+}
+
+sub input_stream ($;$)
+{
+ my $file = shift();
+ my $must_exist = shift();
+
+ if( $file )
+ {
+ my $ifh;
+
+ sysopen( $ifh, $file, O_RDONLY ) ||
+ die( "$0: Failed to open input file $file: $!\n" );
+
+ return $ifh;
+ }
+
+ if( $must_exist )
+ {
+ print( STDERR "No file specified for $must_exist\n" );
+ usage();
+ }
+
+ return \*STDIN;
+}
+
+sub output_stream ()
+{
+ if( $opt{output} )
+ {
+ my $ofh;
+
+ sysopen( $ofh, $opt{output}, O_CREAT|O_TRUNC|O_WRONLY ) ||
+ die( "$0: Failed to open output file $opt{output}: $!\n" );
+
+ return $ofh;
+ }
+
+ return \*STDOUT;
+}
+
+sub parser ()
+{
+ my $name = $opt{format};
+ my $func = "msgfmt::$name"->UNIVERSAL::can("parse");
+
+ return $func || die( "No handler found for format '$name'\n" );
+}
+
+# format implementations:
+{
+ package msgfmt::java;
+
+ sub unescape { $_[0] =~ s/\\([\\':])/$1/g; $_[0] }
+ sub parse
+ {
+ my $cache = shift();
+ my $stream = shift();
+
+ while ( <$stream> )
+ {
+ if( /(\S+)\s*=\s?(.*)/ )
+ {
+ my $key = $1;
+ my $val = $2;
+ $cache->{ $key } = unescape( $val );
+ }
+ }
+ }
+}
+
+{
+ package msgfmt::messages; # native netsurf format
+
+ sub parse
+ {
+ my $cache = shift();
+ my $stream = shift();
+
+ while ( <$stream> )
+ {
+ if( /^([a-z]{2})\.([^.]+)\.([^:]+):(.*)/ )
+ {
+ my( $lang, $plat, $key, $val ) = ( $1, $2, $3, $4 );
+
+ if( $lang ne $opt{lang} ) { next }
+ if( $opt{plat} ne 'any' &&
+ $opt{plat} ne $plat &&
+ 'all' ne $plat ) { next }
+
+ $cache->{ $key } = $val;
+ }
+ }
+ }
+}
+
+{
+ package msgfmt::transifex;
+ use base 'msgfmt::java';
+
+ # the differences between transifex and java properties only matter in
+ # the outward direction: During import they can be treated the same way
+}
+
+{
+ package msgfmt::android;
+
+ ANDROID_XML:
+ {
+ package msgfmt::android::xml;
+
+ my @stack;
+ my $data;
+ my $key;
+ our $cache;
+
+ sub StartDocument ($) { @stack = (); $key = '' }
+ sub Text ($) { if( $key ) { $data .= $_ } }
+ sub PI ($$$) { }
+ sub EndDocument ($) { }
+
+ sub EndTag ($$)
+ {
+ pop( @stack );
+
+ if( !$key ) { return; }
+
+ $cache->{ $key } = $data;
+ $data = $key = '';
+ }
+
+ sub StartTag ($$)
+ {
+ push( @stack, $_[1] );
+
+ if( "@stack" eq "resources string" )
+ {
+ $data = '';
+ $key = $_{ name };
+ }
+ }
+ }
+
+ sub parse
+ {
+ require XML::Parser;
+
+ if( !$XML::Parser::VERSION )
+ {
+ die("XML::Parser required for android format support\n");
+ }
+
+ $msgfmt::android::xml::cache = shift();
+ my $stream = shift();
+ my $parser = XML::Parser->new( Style => 'Stream',
+ Pkg => 'msgfmt::android::xml' );
+ $parser->parse( $stream );
+ }
+}
diff --git a/utils/utils.c b/utils/utils.c
index 3398a7df8..8155f4af1 100644
--- a/utils/utils.c
+++ b/utils/utils.c
@@ -98,26 +98,34 @@ char *remove_underscores(const char *s, bool replacespace)
/**
* Replace consecutive whitespace with a single space.
*
+ * @todo determine if squash_whitespace utf-8 safe and that it needs to be
+ *
* \param s source string
- * \return heap allocated result, or 0 on memory exhaustion
+ * \return heap allocated result, or NULL on memory exhaustion
*/
-char * squash_whitespace(const char *s)
+char *squash_whitespace(const char *s)
{
- char *c = malloc(strlen(s) + 1);
+ char *c;
int i = 0, j = 0;
- if (!c)
- return 0;
- do {
- if (s[i] == ' ' || s[i] == '\n' || s[i] == '\r' ||
- s[i] == '\t') {
- c[j++] = ' ';
- while (s[i] == ' ' || s[i] == '\n' || s[i] == '\r' ||
- s[i] == '\t')
- i++;
- }
- c[j++] = s[i++];
- } while (s[i - 1] != 0);
+
+ c = malloc(strlen(s) + 1);
+ if (c != NULL) {
+ do {
+ if (s[i] == ' ' ||
+ s[i] == '\n' ||
+ s[i] == '\r' ||
+ s[i] == '\t') {
+ c[j++] = ' ';
+ while (s[i] == ' ' ||
+ s[i] == '\n' ||
+ s[i] == '\r' ||
+ s[i] == '\t')
+ i++;
+ }
+ c[j++] = s[i++];
+ } while (s[i - 1] != 0);
+ }
return c;
}