diff options
Diffstat (limited to 'utils')
-rw-r--r-- | utils/fetch-transifex.pl | 124 | ||||
-rw-r--r-- | utils/filepath.c | 10 | ||||
-rw-r--r-- | utils/import-messages.pl | 301 | ||||
-rw-r--r-- | utils/utils.c | 38 |
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; } |