summaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
Diffstat (limited to 'utils')
-rw-r--r--utils/Makefile4
-rw-r--r--utils/bloom.c163
-rw-r--r--utils/bloom.h99
-rw-r--r--utils/fetch-transifex.pl127
-rw-r--r--utils/filepath.c10
-rw-r--r--utils/import-messages.pl326
-rwxr-xr-xutils/jenkins-build.sh351
-rw-r--r--utils/log.c24
-rw-r--r--utils/memdebug.c381
-rw-r--r--utils/memdebug.h106
-rw-r--r--utils/nsoption.c896
-rw-r--r--utils/nsoption.h343
-rw-r--r--utils/nsurl.c65
-rw-r--r--utils/nsurl.h9
-rw-r--r--[-rwxr-xr-x]utils/split-messages.pl244
-rw-r--r--utils/utf8.c63
-rw-r--r--utils/utils.c38
17 files changed, 2694 insertions, 555 deletions
diff --git a/utils/Makefile b/utils/Makefile
index ed34e9557..aef579948 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -2,6 +2,6 @@
S_UTILS := base64.c corestrings.c filename.c filepath.c hashtable.c \
libdom.c locale.c log.c messages.c nsurl.c talloc.c url.c \
- utf8.c utils.c useragent.c
+ utf8.c utils.c useragent.c bloom.c nsoption.c
-S_UTILS := $(addprefix utils/,$(S_UTILS)) \ No newline at end of file
+S_UTILS := $(addprefix utils/,$(S_UTILS))
diff --git a/utils/bloom.c b/utils/bloom.c
new file mode 100644
index 000000000..1b07d6f1b
--- /dev/null
+++ b/utils/bloom.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2013 Rob Kendrick <rjek@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Trivial bloom filter */
+
+#include <stdlib.h>
+#include "utils/bloom.h"
+
+/**
+ * Hash a string, returning a 32bit value. The hash algorithm used is
+ * Fowler Noll Vo - a very fast and simple hash, ideal for short strings.
+ * See http://en.wikipedia.org/wiki/Fowler_Noll_Vo_hash for more details.
+ *
+ * \param datum The string to hash.
+ * \param len size_t of data length.
+ * \return The calculated hash value for the datum.
+ */
+
+static inline uint32_t fnv(const char *datum, size_t len)
+{
+ uint32_t z = 0x811c9dc5;
+
+ if (datum == NULL)
+ return 0;
+
+ while (len--) {
+ z *= 0x01000193;
+ z ^= *datum++;
+ }
+
+ return z;
+}
+
+struct bloom_filter {
+ size_t size;
+ uint32_t items;
+ uint8_t filter[];
+};
+
+struct bloom_filter *bloom_create(size_t size)
+{
+ struct bloom_filter *r = calloc(sizeof(*r) + size, 1);
+
+ if (r == NULL)
+ return NULL;
+
+ r->size = size;
+
+ return r;
+}
+
+void bloom_destroy(struct bloom_filter *b)
+{
+ free(b);
+}
+
+void bloom_insert_str(struct bloom_filter *b, const char *s, size_t z)
+{
+ uint32_t hash = fnv(s, z);
+ bloom_insert_hash(b, hash);
+}
+
+void bloom_insert_hash(struct bloom_filter *b, uint32_t hash)
+{
+ unsigned int index = hash % (b->size << 3);
+ unsigned int byte_index = index >> 3;
+ unsigned int bit_index = index & 7;
+
+ b->filter[byte_index] |= (1 << bit_index);
+ b->items++;
+}
+
+bool bloom_search_str(struct bloom_filter *b, const char *s, size_t z)
+{
+ uint32_t hash = fnv(s, z);
+ return bloom_search_hash(b, hash);
+}
+
+bool bloom_search_hash(struct bloom_filter *b, uint32_t hash)
+{
+ unsigned int index = hash % (b->size << 3);
+ unsigned int byte_index = index >> 3;
+ unsigned int bit_index = index & 7;
+
+ return (b->filter[byte_index] & (1 << bit_index)) != 0;
+}
+
+uint32_t bloom_items(struct bloom_filter *b)
+{
+ return b->items;
+}
+
+#ifdef TEST_RIG
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+int main(int argc, char *arg[])
+{
+ struct bloom_filter *b = bloom_create(8192);
+ FILE *dict = fopen("/usr/share/dict/words", "r");
+ char buf[BUFSIZ];
+ int false_positives = 0, total = 0;
+
+ for (int i = 0; i < 8192; i++) {
+ fscanf(dict, "%s", buf);
+ printf("adding %s\n", buf);
+ bloom_insert_str(b, buf, strlen(buf));
+ }
+
+ printf("adding NetSurf\n");
+
+ bloom_insert_str(b, "NetSurf", 7);
+ printf("checking NetSurf (should be true)\n");
+ assert(bloom_search_str(b, "NetSurf", 7));
+
+ fseek(dict, 0, SEEK_SET);
+
+ for (int i = 0; i < 8192; i++) {
+ fscanf(dict, "%s", buf);
+ printf("checking %s (should be true)\n", buf);
+ assert(bloom_search_str(b, buf, strlen(buf)));
+
+ total++;
+ }
+
+ for (int i = 0; i < 8192; i++) {
+ fscanf(dict, "%s", buf);
+ printf("checking %s (should be false)\n", buf);
+ if (bloom_search_str(b, buf, strlen(buf)) == true)
+ false_positives++;
+ total++;
+ }
+
+ printf("false positives: %d of %d, %f%%\n",
+ false_positives, total,
+ ((float)false_positives / total) * 100);
+
+ fclose(dict);
+ bloom_destroy(b);
+
+ return 0;
+}
+
+#endif /* TEST_RIG */
+
diff --git a/utils/bloom.h b/utils/bloom.h
new file mode 100644
index 000000000..4a7bd3800
--- /dev/null
+++ b/utils/bloom.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2013 Rob Kendrick <rjek@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Trivial bloom filter */
+
+#ifndef _NETSURF_UTILS_BLOOM_H_
+#define _NETSURF_UTILS_BLOOM_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+struct bloom_filter;
+
+/**
+ * Create a new bloom filter.
+ *
+ * \param size Size of bloom filter in bytes
+ * \return Handle for newly-created bloom filter, or NULL
+ */
+struct bloom_filter *bloom_create(size_t size);
+
+/**
+ * Destroy a previously-created bloom filter
+ *
+ * \param b Bloom filter to destroy
+ */
+void bloom_destroy(struct bloom_filter *b);
+
+/**
+ * Insert a string of given length (may include NULs) into the filter,
+ * using an internal hash function.
+ *
+ * \param b Bloom filter to add to
+ * \param s Pointer to data
+ * \param z Length of data
+ */
+void bloom_insert_str(struct bloom_filter *b, const char *s, size_t z);
+
+/**
+ * Insert a given hash value into the filter, should you already have
+ * one to hand.
+ *
+ * \param b Bloom filter to add to
+ * \param hash Value to add
+ */
+void bloom_insert_hash(struct bloom_filter *b, uint32_t hash);
+
+/**
+ * Search the filter for the given string, assuming it was added by
+ * bloom_insert_str(). May return false-positives.
+ *
+ * \param b Bloom filter to search
+ * \param s Pointer to data to search for
+ * \param z Length of data
+ *
+ * \return False if never added, True if it might have been.
+ */
+bool bloom_search_str(struct bloom_filter *b, const char *s, size_t z);
+
+/**
+ * Search the filter for the given hash value, assuming it was added by
+ * bloom_insert_hash(). May return false-positives.
+ *
+ * \param b Bloom filter to search
+ * \param hash Hash value to search for
+ *
+ * \return False if never added, True if it might have been.
+ */
+bool bloom_search_hash(struct bloom_filter *b, uint32_t hash);
+
+/**
+ * Find out how many items have been added to this bloom filter. This
+ * is useful for deciding the size of a new bloom filter should you
+ * need to rehash it.
+ *
+ * \param b Bloom filter to examine
+ *
+ * \return Number of items that have been added
+ */
+uint32_t bloom_items(struct bloom_filter *b);
+
+#endif
diff --git a/utils/fetch-transifex.pl b/utils/fetch-transifex.pl
new file mode 100644
index 000000000..4d40062c9
--- /dev/null
+++ b/utils/fetch-transifex.pl
@@ -0,0 +1,127 @@
+#!/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|? );
+
+# ensure no locale translation is applied and leave it all in UTF-8
+use bytes;
+
+# 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..4c13a859e
--- /dev/null
+++ b/utils/import-messages.pl
@@ -0,0 +1,326 @@
+#!/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;
+ my $last_key;
+ my $last_plat;
+
+ # 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;
+
+ my( $lang, $plat, $key );
+
+ if( /^([a-z]{2})\.([^.]+)\.([^:]+):/ )
+ {
+ ( $lang, $plat, $key ) = ( $1, $2, $3 );
+ }
+
+ if( $key || $message{ $last_key } )
+ {
+ #print( $output "## $last_key -> $key\n" );
+ # the key changed but we have a message for it still pending:
+ if( $last_key && $message{ $last_key } && ($key ne $last_key) )
+ {
+ my $plt = $last_plat;
+ my $str = $message{ $last_key };
+ my $msg = qq|$opt{lang}.$last_plat.$last_key:$str\n|;
+
+ print( $output $msg );
+ delete( $message{ $last_key } );
+
+ # if the line following our new translation is not blank,
+ # generate a synthetic group-separator:
+ if( !/^\s*$/ ) { print( $output "\n") }
+ }
+
+ $last_key = $key;
+ $last_plat = $plat;
+
+ 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 qq|$1.$2.$3:$val\n| );
+ delete( $message{ $key } );
+ 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/\\([^abfnrtv])/$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/jenkins-build.sh b/utils/jenkins-build.sh
new file mode 100755
index 000000000..ea8a52040
--- /dev/null
+++ b/utils/jenkins-build.sh
@@ -0,0 +1,351 @@
+#!/bin/bash
+#
+# 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.
+
+# NetSurf continuous integration build script for jenkins
+#
+# This script is executed by jenkins to build netsurf itself
+#
+# Usage: jenkins-build.sh
+#
+
+# TARGET is set to the frontend target to build
+# label is set to the identifier of the toolchain doing the building
+
+################# Parameter and environment setup #####################
+
+#identifier for this specific build
+IDENTIFIER="$CC-${BUILD_JS}-${BUILD_NUMBER}"
+
+# default atari architecture - bletch
+ATARIARCH=68020-60
+
+# Ensure the combination of target and toolchain works and set build
+# specific parameters too
+case ${TARGET} in
+ "riscos")
+ case ${label} in
+ "arm-unknown-riscos")
+ ARTIFACT_TARGET=riscos
+ ;;
+
+ *)
+ echo "Target \"${TARGET}\" cannot be built on \"${label})\""
+ exit 1
+ ;;
+
+ esac
+
+ PKG_SRC=netsurf
+ PKG_SFX=.zip
+ ;;
+
+
+ "windows")
+ case ${label} in
+ "i686-w64-mingw32")
+ ARTIFACT_TARGET=windows
+ ;;
+
+ *)
+ echo "Target \"${TARGET}\" cannot be built on \"${label})\""
+ exit 1
+ ;;
+
+ esac
+
+ PKG_SRC=netsurf-installer
+ PKG_SFX=.exe
+ ;;
+
+
+ "cocoa")
+ case ${label} in
+ "i686-apple-darwin10")
+ ARTIFACT_TARGET=Darwin
+ IDENTIFIER="${label}-${IDENTIFIER}"
+ ;;
+
+ "powerpc-apple-darwin9")
+ ARTIFACT_TARGET=powerpc-apple-darwin9
+ IDENTIFIER="${ARTIFACT_TARGET}-${IDENTIFIER}"
+ ;;
+
+ *)
+ echo "Target \"${TARGET}\" cannot be built on \"${label})\""
+ exit 1
+ ;;
+
+ esac
+
+ PKG_SRC=NetSurf
+ PKG_SFX=.dmg
+ ;;
+
+
+ "amiga")
+ case ${label} in
+ "ppc-amigaos")
+ ARTIFACT_TARGET=amiga
+ ;;
+
+ *)
+ echo "Target \"${TARGET}\" cannot be built on \"${label})\""
+ exit 1
+ ;;
+
+ esac
+
+ PKG_SRC=NetSurf_Amiga/netsurf
+ PKG_SFX=.lha
+ ;;
+
+
+ "atari")
+ case ${label} in
+ "m68k-atari-mint")
+ ARTIFACT_TARGET=m68k-atari-mint
+ PKG_SRC=ns020
+ PKG_SFX=.zip
+ ;;
+
+ "m5475-atari-mint")
+ ARTIFACT_TARGET=m5475-atari-mint
+ export GCCSDK_INSTALL_ENV=/opt/netsurf/m5475-atari-mint/env
+ export GCCSDK_INSTALL_CROSSBIN=/opt/netsurf/m5475-atari-mint/cross/bin
+ ATARIARCH=v4e
+ PKG_SRC=nsv4e
+ PKG_SFX=.zip
+ ;;
+
+ *)
+ echo "Target \"${TARGET}\" cannot be built on \"${label})\""
+ exit 1
+ ;;
+
+ esac
+
+ IDENTIFIER="${ARTIFACT_TARGET}-${IDENTIFIER}"
+ ;;
+
+
+ "gtk")
+ case ${label} in
+ "x86_64-linux-gnu")
+ ARTIFACT_TARGET=Linux
+ ;;
+
+ *)
+ echo "Target \"${TARGET}\" cannot be built on \"${label})\""
+ exit 1
+ ;;
+
+ esac
+
+ PKG_SRC=nsgtk
+ PKG_SFX=
+ ;;
+
+
+ "framebuffer")
+ case ${label} in
+ "x86_64-linux-gnu")
+ ARTIFACT_TARGET=Linux
+ ;;
+
+ "i686-apple-darwin10")
+ ARTIFACT_TARGET=Darwin
+ ;;
+
+ "powerpc-apple-darwin9")
+ ARTIFACT_TARGET=powerpc-apple-darwin9
+ ;;
+
+ "arm-unknown-riscos")
+ ARTIFACT_TARGET=riscos
+ export GCCSDK_INSTALL_ENV=/opt/netsurf/${label}/env
+ export GCCSDK_INSTALL_CROSSBIN=/opt/netsurf/${label}/cross/bin
+ ;;
+
+ "m68k-atari-mint")
+ ARTIFACT_TARGET=m68k-atari-mint
+ export GCCSDK_INSTALL_ENV=/opt/netsurf/${label}/env
+ export GCCSDK_INSTALL_CROSSBIN=/opt/netsurf/${label}/cross/bin
+ ;;
+
+ "m5475-atari-mint")
+ ATARIARCH=v4e
+ ARTIFACT_TARGET=m5475-atari-mint
+ export GCCSDK_INSTALL_ENV=/opt/netsurf/${label}/env
+ export GCCSDK_INSTALL_CROSSBIN=/opt/netsurf/${label}/cross/bin
+ ;;
+
+ "i686-w64-mingw32")
+ ARTIFACT_TARGET=windows
+ export GCCSDK_INSTALL_ENV=/opt/netsurf/${label}/env
+ export GCCSDK_INSTALL_CROSSBIN=/opt/netsurf/${label}/cross/bin
+ ;;
+
+ "ppc-amigaos")
+ ARTIFACT_TARGET=amiga
+ export GCCSDK_INSTALL_ENV=/opt/netsurf/${label}/env
+ export GCCSDK_INSTALL_CROSSBIN=/opt/netsurf/${label}/cross/bin
+ ;;
+
+ *)
+ echo "Target \"${TARGET}\" cannot be built on \"${label})\""
+ exit 1
+ ;;
+
+ esac
+
+ PKG_SRC=nsfb
+ PKG_SFX=
+ ;;
+
+
+ "monkey")
+ # monkey target can be built on most of the supported architectures
+ case ${label} in
+ "x86_64-linux-gnu")
+ ARTIFACT_TARGET=Linux
+ ;;
+
+ "i686-apple-darwin10")
+ ARTIFACT_TARGET=Darwin
+ ;;
+
+ "powerpc-apple-darwin9")
+ ARTIFACT_TARGET=powerpc-apple-darwin9
+ ;;
+
+ "arm-unknown-riscos")
+ ARTIFACT_TARGET=riscos
+ export GCCSDK_INSTALL_ENV=/opt/netsurf/${label}/env
+ export GCCSDK_INSTALL_CROSSBIN=/opt/netsurf/${label}/cross/bin
+ ;;
+
+ "m68k-atari-mint")
+ ARTIFACT_TARGET=m68k-atari-mint
+ export GCCSDK_INSTALL_ENV=/opt/netsurf/${label}/env
+ export GCCSDK_INSTALL_CROSSBIN=/opt/netsurf/${label}/cross/bin
+ ;;
+
+ "m5475-atari-mint")
+ ATARIARCH=v4e
+ ARTIFACT_TARGET=m5475-atari-mint
+ export GCCSDK_INSTALL_ENV=/opt/netsurf/${label}/env
+ export GCCSDK_INSTALL_CROSSBIN=/opt/netsurf/${label}/cross/bin
+ ;;
+
+ "i686-w64-mingw32")
+ ARTIFACT_TARGET=windows
+ export GCCSDK_INSTALL_ENV=/opt/netsurf/${label}/env
+ export GCCSDK_INSTALL_CROSSBIN=/opt/netsurf/${label}/cross/bin
+ ;;
+
+ "ppc-amigaos")
+ ARTIFACT_TARGET=amiga
+ export GCCSDK_INSTALL_ENV=/opt/netsurf/${label}/env
+ export GCCSDK_INSTALL_CROSSBIN=/opt/netsurf/${label}/cross/bin
+ ;;
+
+ *)
+ echo "Target \"${TARGET}\" cannot be built on \"${label})\""
+ exit 1
+ ;;
+
+ esac
+
+ IDENTIFIER="${label}-${IDENTIFIER}"
+ PKG_SRC=nsmonkey
+ PKG_SFX=
+ ;;
+
+ *)
+ # TARGET must be in the environment and set correctly
+ echo "Unkown TARGET \"${TARGET}\""
+ exit 1
+ ;;
+
+esac
+
+# setup environment
+export PREFIX=${JENKINS_HOME}/artifacts-${ARTIFACT_TARGET}
+export PKG_CONFIG_PATH=${PREFIX}/lib/pkgconfig
+export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${PREFIX}/lib
+export PATH=${PATH}:${PREFIX}/bin
+
+# configure ccache for clang
+if [ "${CC}" = "clang" ];then
+ export CCACHE_CPP2=yes
+ export CC="clang -Qunused-arguments"
+fi
+
+# convert javascript parameters
+if [ "${BUILD_JS}" = "json" ];then
+ BUILD_MOZJS=NO
+ BUILD_JS=YES
+else
+ BUILD_JS=NO
+ BUILD_MOZJS=NO
+fi
+
+
+
+
+########### Build from source ##################
+
+# Clean first
+make NETSURF_USE_JS=${BUILD_JS} NETSURF_USE_MOZJS=${BUILD_MOZJS} clean
+
+# Do the Build
+make -k NETSURF_USE_JS=${BUILD_JS} NETSURF_USE_MOZJS=${BUILD_MOZJS} CI_BUILD=${BUILD_NUMBER} ATARIARCH=${ATARIARCH} Q=
+
+
+
+
+
+############ Package artifact construction ################
+
+#destination for package artifacts
+DESTDIR=/srv/ci.netsurf-browser.org/html/builds/${TARGET}/
+
+# build the package file
+make -k NETSURF_USE_JS=${BUILD_JS} NETSURF_USE_MOZJS=${BUILD_MOZJS} CI_BUILD=${BUILD_NUMBER} ATARIARCH=${ATARIARCH} package Q=
+
+if [ ! -f "${PKG_SRC}${PKG_SFX}" ]; then
+ # unable to find package file
+ exit 1
+fi
+
+
+
+############ Package artifact deployment ################
+
+# copy the file into the output - always use scp as it works local or remote
+scp "${PKG_SRC}${PKG_SFX}" netsurf@ci.netsurf-browser.org:${DESTDIR}/NetSurf-${IDENTIFIER}${PKG_SFX}
+
+# remove the package file
+rm -f "${PKG_SRC}${PKG_SFX}"
+
+# setup latest link
+ssh netsurf@ci.netsurf-browser.org "rm -f ${DESTDIR}/LATEST && echo "NetSurf-${IDENTIFIER}${PKG_SFX}" > ${DESTDIR}/LATEST"
diff --git a/utils/log.c b/utils/log.c
index 96a6d3c5b..2aa39ee41 100644
--- a/utils/log.c
+++ b/utils/log.c
@@ -31,9 +31,9 @@ nserror nslog_init(nslog_ensure_t *ensure, int *pargc, char **argv)
{
nserror ret = NSERROR_OK;
- if (((*pargc) > 1) &&
- (argv[1][0] == '-') &&
- (argv[1][1] == 'v') &&
+ if (((*pargc) > 1) &&
+ (argv[1][0] == '-') &&
+ (argv[1][1] == 'v') &&
(argv[1][2] == 0)) {
int argcmv;
for (argcmv = 2; argcmv < (*pargc); argcmv++) {
@@ -43,15 +43,17 @@ nserror nslog_init(nslog_ensure_t *ensure, int *pargc, char **argv)
/* ensure we actually show logging */
verbose_log = true;
-
- /* ensure stderr is available */
- if (ensure != NULL) {
- if (ensure(stderr) == false) {
- /* failed to ensure output */
- ret = NSERROR_INIT_FAILED;
- }
- }
}
+
+ /* ensure output file handle is correctly configured */
+ if ((verbose_log == true) &&
+ (ensure != NULL) &&
+ (ensure(stderr) == false)) {
+ /* failed to ensure output configuration */
+ ret = NSERROR_INIT_FAILED;
+ verbose_log = false;
+ }
+
return ret;
}
diff --git a/utils/memdebug.c b/utils/memdebug.c
deleted file mode 100644
index 0f94f6f13..000000000
--- a/utils/memdebug.c
+++ /dev/null
@@ -1,381 +0,0 @@
-/** \file
- * Heap debugging functions (implementation).
- *
- * Based on memdebug.c from curl (see below), with the following modifications:
- *
- * - renamed functions from curl_ to memdebug_
- * - added memdebug_strndup
- * - added guard bytes before and after each block to help detect overflows
- * - if a guard byte is corrupted during free, dumps the DA to file
- */
-
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * $Id: memdebug.c,v 1.1 2004/07/28 22:35:02 bursa Exp $
- ***************************************************************************/
-
-#include <assert.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/socket.h>
-#ifdef riscos
-#include <unixlib/local.h>
-
-#include "oslib/os.h"
-#include "oslib/osfile.h"
-#endif
-
-#include "memdebug.h"
-
-#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
-
-#define MAGIC 0x34343434
-#define GUARD 0x34
-
-#if defined(riscos) && !defined(__ELF__)
-extern int __dynamic_num;
-#endif
-
-struct memdebug {
- size_t size;
- unsigned int magic;
- double mem[1];
- /* I'm hoping this is the thing with the strictest alignment
- * requirements. That also means we waste some space :-( */
-};
-
-/*
- * Note that these debug functions are very simple and they are meant to
- * remain so. For advanced analysis, record a log file and write perl scripts
- * to analyze them!
- *
- * Don't use these with multithreaded test programs!
- */
-
-#define logfile memdebug_debuglogfile
-FILE *memdebug_debuglogfile;
-static bool memlimit; /* enable memory limit */
-static long memsize; /* set number of mallocs allowed */
-
-/* this sets the log file name */
-void memdebug_memdebug(const char *logname)
-{
- if(logname)
- logfile = fopen(logname, "w");
- else
- logfile = stderr;
-}
-
-/* This function sets the number of malloc() calls that should return
- successfully! */
-void memdebug_memlimit(long limit)
-{
- memlimit = true;
- memsize = limit;
-}
-
-/* returns true if this isn't allowed! */
-static bool countcheck(const char *func, int line, const char *source)
-{
- /* if source is NULL, then the call is made internally and this check
- should not be made */
- if(memlimit && source) {
- if(!memsize) {
- if(logfile && source)
- fprintf(logfile, "LIMIT %s:%d %s reached memlimit\n",
- source, line, func);
- if(source)
- fprintf(stderr, "LIMIT %s:%d %s reached memlimit\n",
- source, line, func);
- return true; /* RETURN ERROR! */
- }
- else
- memsize--; /* countdown */
-
- /* log the countdown */
- if(logfile && source)
- fprintf(logfile, "LIMIT %s:%d %ld ALLOCS left\n",
- source, line, memsize);
-
- }
-
- return false; /* allow this */
-}
-
-void *memdebug_malloc(size_t wantedsize, int line, const char *source)
-{
- struct memdebug *mem;
- size_t size;
-
- if(countcheck("malloc", line, source))
- return NULL;
-
- /* alloc at least 64 bytes */
- size = sizeof(struct memdebug)+wantedsize + 8;
-
- mem=(struct memdebug *)(malloc)(size);
- if(mem) {
- unsigned int i;
- /* fill memory with junk */
- memset(mem->mem, 0xA5, wantedsize);
- mem->size = wantedsize;
- mem->magic = MAGIC;
- for (i = 0; i != 8; i++)
- ((char *) mem->mem)[wantedsize + i] = GUARD;
- }
-
- if(logfile && source)
- fprintf(logfile, "MEM %s:%d malloc(%zu) = %p\n",
- source, line, wantedsize, mem ? mem->mem : 0);
- return (mem ? mem->mem : NULL);
-}
-
-void *memdebug_calloc(size_t wanted_elements, size_t wanted_size,
- int line, const char *source)
-{
- struct memdebug *mem;
- size_t size, user_size;
-
- if(countcheck("calloc", line, source))
- return NULL;
-
- /* alloc at least 64 bytes */
- user_size = wanted_size * wanted_elements;
- size = sizeof(struct memdebug) + user_size + 8;
-
- mem = (struct memdebug *)(malloc)(size);
- if(mem) {
- unsigned int i;
- /* fill memory with zeroes */
- memset(mem->mem, 0, user_size);
- mem->size = user_size;
- mem->magic = MAGIC;
- for (i = 0; i != 8; i++)
- ((char *) mem->mem)[mem->size + i] = GUARD;
- }
-
- if(logfile && source)
- fprintf(logfile, "MEM %s:%d calloc(%zu,%zu) = %p\n",
- source, line, wanted_elements, wanted_size, mem ? mem->mem : 0);
- return (mem ? mem->mem : NULL);
-}
-
-char *memdebug_strdup(const char *str, int line, const char *source)
-{
- char *mem;
- size_t len;
-
- assert(str != NULL);
-
- if(countcheck("strdup", line, source))
- return NULL;
-
- len=strlen(str)+1;
-
- mem=memdebug_malloc(len, 0, NULL); /* NULL prevents logging */
- if (mem)
- memcpy(mem, str, len);
-
- if(logfile)
- fprintf(logfile, "MEM %s:%d strdup(%p) (%zu) = %p\n",
- source, line, str, len, mem);
-
- return mem;
-}
-
-char *memdebug_strndup(const char *str, size_t size, int line, const char *source)
-{
- char *mem;
- size_t len;
-
- assert(str != NULL);
-
- if(countcheck("strndup", line, source))
- return NULL;
-
- len=strlen(str)+1;
- if (size < len - 1)
- len = size + 1;
-
- mem=memdebug_malloc(len, 0, NULL); /* NULL prevents logging */
- if (mem) {
- memcpy(mem, str, len);
- mem[len - 1] = 0;
- }
-
- if(logfile)
- fprintf(logfile, "MEM %s:%d strndup(%p, %zd) (%zu) = %p\n",
- source, line, str, size, len, mem);
-
- return mem;
-}
-
-/* We provide a realloc() that accepts a NULL as pointer, which then
- performs a malloc(). In order to work with ares. */
-void *memdebug_realloc(void *ptr, size_t wantedsize,
- int line, const char *source)
-{
- unsigned int i;
- struct memdebug *mem=NULL;
-
- size_t size = sizeof(struct memdebug)+wantedsize+8;
-
- if(countcheck("realloc", line, source))
- return NULL;
-
- if(ptr) {
- mem = (struct memdebug *)(void *)
- ((char *)ptr - offsetof(struct memdebug, mem));
- }
-
- if(logfile) {
- if (mem && mem->magic != MAGIC)
- fprintf(logfile, "MAGIC match failed!\n");
- for (i = 0; mem && i != 8; i++)
- if (((char *) mem->mem)[mem->size + i] != GUARD)
- fprintf(logfile, "GUARD %u match failed!\n", i);
- fprintf(logfile, "MEM %s:%d realloc(%p, %zu) = ",
- source, line, ptr, wantedsize);
- fflush(logfile);
- }
-
- mem=(struct memdebug *)(realloc)(mem, size);
- if(logfile)
- fprintf(logfile, "%p\n", mem?mem->mem:NULL);
-
- if(mem) {
- mem->size = wantedsize;
- mem->magic = MAGIC;
- for (i = 0; i != 8; i++)
- ((char *) mem->mem)[wantedsize + i] = GUARD;
- return mem->mem;
- }
-
- return NULL;
-}
-
-void memdebug_free(void *ptr, int line, const char *source)
-{
- unsigned int i;
- struct memdebug *mem;
-
- if (!ptr)
- return;
-
- assert(ptr != NULL);
-
- mem = (struct memdebug *)(void *)
- ((char *)ptr - offsetof(struct memdebug, mem));
- if(logfile) {
- fprintf(logfile, "MEM %s:%d free(%p)\n", source, line, ptr);
- if (mem->magic != MAGIC) {
- fprintf(logfile, "MAGIC match failed!\n");
-#ifdef riscos
- #ifndef __ELF__
- if (__dynamic_num != -1) {
- int size;
- byte *base_address;
- xosdynamicarea_read(__dynamic_num, &size, &base_address,
- 0, 0, 0, 0, 0);
- fprintf(logfile, "saving DA %i %p %x\n", __dynamic_num, base_address,
- size);
- xosfile_save("core", (bits) base_address, 0, base_address,
- base_address + size);
- }
- #else
- __unixlib_write_coredump(NULL);
- #endif
-#endif
- }
- fflush(logfile);
- for (i = 0; i != 8; i++)
- if (((char *) mem->mem)[mem->size + i] != GUARD)
- fprintf(logfile, "GUARD %u match failed!\n", i);
- fflush(logfile);
- }
-
- /* destroy */
- memset(mem->mem, 0x13, mem->size);
- mem->magic = 0x13131313;
- for (i = 0; i != 8; i++)
- ((char *) mem->mem)[mem->size + i] = 0x13;
-
- /* free for real */
- (free)(mem);
-}
-
-int memdebug_socket(int domain, int type, int protocol, int line,
- const char *source)
-{
- int sockfd=(socket)(domain, type, protocol);
- if(logfile && (sockfd!=-1))
- fprintf(logfile, "FD %s:%d socket() = %d\n",
- source, line, sockfd);
- return sockfd;
-}
-
-int memdebug_accept(int s, void *saddr, void *saddrlen,
- int line, const char *source)
-{
- struct sockaddr *addr = (struct sockaddr *)saddr;
- socklen_t *addrlen = (socklen_t *)saddrlen;
- int sockfd=(accept)(s, addr, addrlen);
- if(logfile)
- fprintf(logfile, "FD %s:%d accept() = %d\n",
- source, line, sockfd);
- return sockfd;
-}
-
-/* this is our own defined way to close sockets on *ALL* platforms */
-int memdebug_sclose(int sockfd, int line, const char *source)
-{
- int res=sclose(sockfd);
- if(logfile)
- fprintf(logfile, "FD %s:%d sclose(%d)\n",
- source, line, sockfd);
- return res;
-}
-
-FILE *memdebug_fopen(const char *file, const char *mode,
- int line, const char *source)
-{
- FILE *res=(fopen)(file, mode);
- if(logfile)
- fprintf(logfile, "FILE %s:%d fopen(\"%s\",\"%s\") = %p\n",
- source, line, file, mode, res);
- return res;
-}
-
-int memdebug_fclose(FILE *file, int line, const char *source)
-{
- int res;
-
- assert(file != NULL);
-
- res=(fclose)(file);
- if(logfile)
- fprintf(logfile, "FILE %s:%d fclose(%p)\n",
- source, line, file);
- return res;
-}
diff --git a/utils/memdebug.h b/utils/memdebug.h
deleted file mode 100644
index bdf933cc3..000000000
--- a/utils/memdebug.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/** \file
- * Heap debugging functions (interface).
- *
- * Based on memdebug.h from curl (see below), with the following modifications:
- *
- * - renamed functions from curl_ to memdebug_
- * - added memdebug_strndup
- * - added guard bytes before and after each block to help detect overflows
- * - if a guard byte is corrupted during free, dumps the DA to file
- */
-
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * $Id: memdebug.h,v 1.1 2004/07/28 22:35:02 bursa Exp $
- ***************************************************************************/
-
-#ifndef _MEMDEBUG_H_
-#define _MEMDEBUG_H_
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-
-#define logfile memdebug_debuglogfile
-
-extern FILE *logfile;
-
-/* memory functions */
-void *memdebug_malloc(size_t size, int line, const char *source);
-void *memdebug_calloc(size_t elements, size_t size, int line, const char *source);
-void *memdebug_realloc(void *ptr, size_t size, int line, const char *source);
-void memdebug_free(void *ptr, int line, const char *source);
-char *memdebug_strdup(const char *str, int line, const char *source);
-char *memdebug_strndup(const char *str, size_t size, int line, const char *source);
-void memdebug_memdebug(const char *logname);
-void memdebug_memlimit(long limit);
-
-/* file descriptor manipulators */
-int memdebug_socket(int domain, int type, int protocol, int line , const char *);
-int memdebug_sclose(int sockfd, int, const char *source);
-int memdebug_accept(int s, void *addr, void *addrlen,
- int line, const char *source);
-
-/* FILE functions */
-FILE *memdebug_fopen(const char *file, const char *mode, int line,
- const char *source);
-int memdebug_fclose(FILE *file, int line, const char *source);
-
-#ifndef MEMDEBUG_NODEFINES
-
-#undef strdup
-#define strdup(ptr) memdebug_strdup(ptr, __LINE__, __FILE__)
-#define strndup(ptr,size) memdebug_strndup(ptr, size, __LINE__, __FILE__)
-#define malloc(size) memdebug_malloc(size, __LINE__, __FILE__)
-#define calloc(nbelem,size) memdebug_calloc(nbelem, size, __LINE__, __FILE__)
-#define realloc(ptr,size) memdebug_realloc(ptr, size, __LINE__, __FILE__)
-#define free(ptr) memdebug_free(ptr, __LINE__, __FILE__)
-
-#define socket(domain,type,protocol)\
- memdebug_socket(domain,type,protocol,__LINE__,__FILE__)
-#undef accept /* for those with accept as a macro */
-#define accept(sock,addr,len)\
- memdebug_accept(sock,addr,len,__LINE__,__FILE__)
-
-#define getaddrinfo(host,serv,hint,res) \
- memdebug_getaddrinfo(host,serv,hint,res,__LINE__,__FILE__)
-#define getnameinfo(sa,salen,host,hostlen,serv,servlen,flags) \
- memdebug_getnameinfo(sa,salen,host,hostlen,serv,servlen,flags, __LINE__, \
- __FILE__)
-#define freeaddrinfo(data) \
- memdebug_freeaddrinfo(data,__LINE__,__FILE__)
-
-/* sclose is probably already defined, redefine it! */
-#undef sclose
-#define sclose(sockfd) memdebug_sclose(sockfd,__LINE__,__FILE__)
-/* ares-adjusted define: */
-#undef closesocket
-#define closesocket(sockfd) memdebug_sclose(sockfd,__LINE__,__FILE__)
-
-#undef fopen
-#define fopen(file,mode) memdebug_fopen(file,mode,__LINE__,__FILE__)
-#define fclose(file) memdebug_fclose(file,__LINE__,__FILE__)
-
-#endif /* MEMDEBUG_NODEFINES */
-
-#endif
diff --git a/utils/nsoption.c b/utils/nsoption.c
new file mode 100644
index 000000000..f6244cd48
--- /dev/null
+++ b/utils/nsoption.c
@@ -0,0 +1,896 @@
+/*
+ * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Option reading and saving (implementation).
+ *
+ * Options are stored in the format key:value, one per line.
+ *
+ * For bool options, value is "0" or "1".
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "desktop/plot_style.h"
+#include "utils/errors.h"
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "utils/nsoption.h"
+
+struct nsoption_s *nsoptions = NULL;
+struct nsoption_s *nsoptions_default = NULL;
+
+#define NSOPTION_BOOL(NAME, DEFAULT) \
+ { #NAME, sizeof(#NAME) - 1, OPTION_BOOL, { .b = DEFAULT } },
+
+#define NSOPTION_STRING(NAME, DEFAULT) \
+ { #NAME, sizeof(#NAME) - 1, OPTION_STRING, { .cs = DEFAULT } },
+
+#define NSOPTION_INTEGER(NAME, DEFAULT) \
+ { #NAME, sizeof(#NAME) - 1, OPTION_INTEGER, { .i = DEFAULT } },
+
+#define NSOPTION_UINT(NAME, DEFAULT) \
+ { #NAME, sizeof(#NAME) - 1, OPTION_UINT, { .u = DEFAULT } },
+
+#define NSOPTION_COLOUR(NAME, DEFAULT) \
+ { #NAME, sizeof(#NAME) - 1, OPTION_COLOUR, { .c = DEFAULT } },
+
+/** The table of compiled in default options */
+static struct nsoption_s defaults[] = {
+#include "desktop/options.h"
+
+#if defined(riscos)
+#include "riscos/options.h"
+#elif defined(nsgtk)
+#include "gtk/options.h"
+#elif defined(nsbeos)
+#include "beos/options.h"
+#elif defined(nsamiga)
+#include "amiga/options.h"
+#elif defined(nsframebuffer)
+#include "framebuffer/options.h"
+#elif defined(nsatari)
+#include "atari/options.h"
+#elif defined(nsmonkey)
+#include "monkey/options.h"
+#endif
+ { NULL, 0, OPTION_INTEGER, { 0 } }
+};
+
+#undef NSOPTION_BOOL
+#undef NSOPTION_STRING
+#undef NSOPTION_INTEGER
+#undef NSOPTION_UINT
+#undef NSOPTION_COLOUR
+
+/**
+ * Set an option value based on a string
+ */
+static bool
+strtooption(const char *value, struct nsoption_s *option)
+{
+ bool ret = true;
+ colour rgbcolour; /* RRGGBB */
+
+ switch (option->type) {
+ case OPTION_BOOL:
+ option->value.b = (value[0] == '1');
+ break;
+
+ case OPTION_INTEGER:
+ option->value.i = atoi(value);
+ break;
+
+ case OPTION_UINT:
+ option->value.u = strtoul(value, NULL, 0);
+ break;
+
+ case OPTION_COLOUR:
+ if (sscanf(value, "%x", &rgbcolour) == 1) {
+ option->value.c = (((0x000000FF & rgbcolour) << 16) |
+ ((0x0000FF00 & rgbcolour) << 0) |
+ ((0x00FF0000 & rgbcolour) >> 16));
+ }
+ break;
+
+ case OPTION_STRING:
+ if (option->value.s != NULL) {
+ free(option->value.s);
+ }
+
+ if (*value == 0) {
+ /* do not allow empty strings in text options */
+ option->value.s = NULL;
+ } else {
+ option->value.s = strdup(value);
+ }
+ break;
+
+ default:
+ ret = false;
+ break;
+ }
+
+ return ret;
+}
+
+/* validate options to sane values */
+static void nsoption_validate(struct nsoption_s *opts, struct nsoption_s *defs)
+{
+ int cloop;
+ bool black = true;
+
+ if (opts[NSOPTION_font_size].value.i < 50) {
+ opts[NSOPTION_font_size].value.i = 50;
+ }
+
+ if (opts[NSOPTION_font_size].value.i > 1000) {
+ opts[NSOPTION_font_size].value.i = 1000;
+ }
+
+ if (opts[NSOPTION_font_min_size].value.i < 10) {
+ opts[NSOPTION_font_min_size].value.i = 10;
+ }
+
+ if (opts[NSOPTION_font_min_size].value.i > 500) {
+ opts[NSOPTION_font_min_size].value.i = 500;
+ }
+
+ if (opts[NSOPTION_memory_cache_size].value.i < 0) {
+ opts[NSOPTION_memory_cache_size].value.i = 0;
+ }
+
+ /* to aid migration from old, broken, configuration files this
+ * checks to see if all the system colours are set to black
+ * and returns them to defaults instead
+ */
+
+ for (cloop = NSOPTION_SYS_COLOUR_START;
+ cloop <= NSOPTION_SYS_COLOUR_END;
+ cloop++) {
+ if (opts[cloop].value.c != 0) {
+ black = false;
+ break;
+ }
+ }
+ if (black == true) {
+ for (cloop = NSOPTION_SYS_COLOUR_START;
+ cloop <= NSOPTION_SYS_COLOUR_END;
+ cloop++) {
+ opts[cloop].value.c = defs[cloop].value.c;
+ }
+ }
+}
+
+/**
+ * Determines if an option is different between two option tables.
+ *
+ * @param opts The first table to compare.
+ * @param defs The second table to compare.
+ * @param entry The option to compare.
+ * @return true if the option differs false if not.
+ */
+static bool
+nsoption_is_set(const struct nsoption_s *opts,
+ const struct nsoption_s *defs,
+ const enum nsoption_e entry)
+{
+ bool ret = false;
+
+ switch (opts[entry].type) {
+ case OPTION_BOOL:
+ if (opts[entry].value.b != defs[entry].value.b) {
+ ret = true;
+ }
+ break;
+
+ case OPTION_INTEGER:
+ if (opts[entry].value.i != defs[entry].value.i) {
+ ret = true;
+ }
+ break;
+
+ case OPTION_UINT:
+ if (opts[entry].value.u != defs[entry].value.u) {
+ ret = true;
+ }
+ break;
+
+ case OPTION_COLOUR:
+ if (opts[entry].value.c != defs[entry].value.c) {
+ ret = true;
+ }
+ break;
+
+ case OPTION_STRING:
+ /* set if:
+ * - defs is null.
+ * - default is null but value is not.
+ * - default and value pointers are different
+ * (acts as a null check because of previous check)
+ * and the strings content differ.
+ */
+ if (((defs[entry].value.s == NULL) &&
+ (opts[entry].value.s != NULL)) ||
+ ((defs[entry].value.s != opts[entry].value.s) &&
+ (strcmp(opts[entry].value.s, defs[entry].value.s) != 0))) {
+ ret = true;
+ }
+ break;
+
+ }
+ return ret;
+}
+
+/**
+ * Output choices to file stream
+ *
+ * @param fp The file stream to write to.
+ * @param opts The options table to write.
+ * @param defs The default value table to compare with.
+ * @param all Output all entries not just ones changed from defaults
+ */
+static nserror
+nsoption_output(FILE *fp,
+ struct nsoption_s *opts,
+ struct nsoption_s *defs,
+ bool all)
+{
+ unsigned int entry; /* index to option being output */
+ colour rgbcolour; /* RRGGBB */
+
+ for (entry = 0; entry < NSOPTION_LISTEND; entry++) {
+ if ((all == false) &&
+ (nsoption_is_set(opts, defs, entry) == false)) {
+ continue;
+ }
+
+ switch (opts[entry].type) {
+ case OPTION_BOOL:
+ fprintf(fp, "%s:%c\n",
+ opts[entry].key,
+ opts[entry].value.b ? '1' : '0');
+ break;
+
+ case OPTION_INTEGER:
+ fprintf(fp, "%s:%i\n",
+ opts[entry].key,
+ opts[entry].value.i);
+
+ break;
+
+ case OPTION_UINT:
+ fprintf(fp, "%s:%u\n",
+ opts[entry].key,
+ opts[entry].value.u);
+ break;
+
+ case OPTION_COLOUR:
+ rgbcolour = (((0x000000FF & opts[entry].value.c) << 16) |
+ ((0x0000FF00 & opts[entry].value.c) << 0) |
+ ((0x00FF0000 & opts[entry].value.c) >> 16));
+ fprintf(fp, "%s:%06x\n",
+ opts[entry].key,
+ rgbcolour);
+
+ break;
+
+ case OPTION_STRING:
+ fprintf(fp, "%s:%s\n",
+ opts[entry].key,
+ ((opts[entry].value.s == NULL) ||
+ (*opts[entry].value.s == 0)) ? "" : opts[entry].value.s);
+
+ break;
+ }
+ }
+
+ return NSERROR_OK;
+}
+
+/**
+ * Output an option value into a string, in HTML format.
+ *
+ * @param option The option to output the value of.
+ * @param size The size of the string buffer.
+ * @param pos The current position in string
+ * @param string The string in which to output the value.
+ * @return The number of bytes written to string or -1 on error
+ */
+static size_t
+nsoption_output_value_html(struct nsoption_s *option,
+ size_t size,
+ size_t pos,
+ char *string)
+{
+ size_t slen = 0; /* length added to string */
+ colour rgbcolour; /* RRGGBB */
+
+ switch (option->type) {
+ case OPTION_BOOL:
+ slen = snprintf(string + pos,
+ size - pos,
+ "%s",
+ option->value.b ? "true" : "false");
+ break;
+
+ case OPTION_INTEGER:
+ slen = snprintf(string + pos,
+ size - pos,
+ "%i",
+ option->value.i);
+ break;
+
+ case OPTION_UINT:
+ slen = snprintf(string + pos,
+ size - pos,
+ "%u",
+ option->value.u);
+ break;
+
+ case OPTION_COLOUR:
+ rgbcolour = (((0x000000FF & option->value.c) << 16) |
+ ((0x0000FF00 & option->value.c) << 0) |
+ ((0x00FF0000 & option->value.c) >> 16));
+ slen = snprintf(string + pos,
+ size - pos,
+ "<span style=\"background-color: #%06x; "
+ "color: #%06x; "
+ "font-family:Monospace; \">#%06X</span>",
+ rgbcolour,
+ colour_to_bw_furthest(rgbcolour),
+ rgbcolour);
+ break;
+
+ case OPTION_STRING:
+ if (option->value.s != NULL) {
+ slen = snprintf(string + pos, size - pos, "%s",
+ option->value.s);
+ } else {
+ slen = snprintf(string + pos, size - pos,
+ "<span class=\"null-content\">NULL"
+ "</span>");
+ }
+ break;
+ }
+
+ return slen;
+}
+
+
+/**
+ * Output an option value into a string, in plain text format.
+ *
+ * @param option The option to output the value of.
+ * @param size The size of the string buffer.
+ * @param pos The current position in string
+ * @param string The string in which to output the value.
+ * @return The number of bytes written to string or -1 on error
+ */
+static size_t
+nsoption_output_value_text(struct nsoption_s *option,
+ size_t size,
+ size_t pos,
+ char *string)
+{
+ size_t slen = 0; /* length added to string */
+ colour rgbcolour; /* RRGGBB */
+
+ switch (option->type) {
+ case OPTION_BOOL:
+ slen = snprintf(string + pos,
+ size - pos,
+ "%c",
+ option->value.b ? '1' : '0');
+ break;
+
+ case OPTION_INTEGER:
+ slen = snprintf(string + pos,
+ size - pos,
+ "%i",
+ option->value.i);
+ break;
+
+ case OPTION_UINT:
+ slen = snprintf(string + pos,
+ size - pos,
+ "%u",
+ option->value.u);
+ break;
+
+ case OPTION_COLOUR:
+ rgbcolour = (((0x000000FF & option->value.c) << 16) |
+ ((0x0000FF00 & option->value.c) << 0) |
+ ((0x00FF0000 & option->value.c) >> 16));
+ slen = snprintf(string + pos, size - pos, "%06x", rgbcolour);
+ break;
+
+ case OPTION_STRING:
+ if (option->value.s != NULL) {
+ slen = snprintf(string + pos,
+ size - pos,
+ "%s",
+ option->value.s);
+ }
+ break;
+ }
+
+ return slen;
+}
+
+/**
+ * Duplicates an option table.
+ *
+ * Allocates a new option table and copies an existing one into it.
+ *
+ * @param src The source table to copy
+ */
+static nserror
+nsoption_dup(struct nsoption_s *src, struct nsoption_s **pdst)
+{
+ struct nsoption_s *dst;
+ dst = malloc(sizeof(defaults));
+ if (dst == NULL) {
+ return NSERROR_NOMEM;
+ }
+ *pdst = dst;
+
+ /* copy the source table into the destination table */
+ memcpy(dst, src, sizeof(defaults));
+
+ while (src->key != NULL) {
+ if ((src->type == OPTION_STRING) &&
+ (src->value.s != NULL)) {
+ dst->value.s = strdup(src->value.s);
+ }
+ src++;
+ dst++;
+ }
+
+ return NSERROR_OK;
+}
+
+/**
+ * frees an option table.
+ *
+ * Iterates through an option table a freeing resources as required
+ * finally freeing the option table itself.
+ *
+ * @param opts The option table to free.
+ */
+static nserror
+nsoption_free(struct nsoption_s *opts)
+{
+ struct nsoption_s *cur; /* option being freed */
+
+ if (opts == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ cur = opts;
+
+ while (cur->key != NULL) {
+ if ((cur->type == OPTION_STRING) && (cur->value.s != NULL)) {
+ free(cur->value.s);
+ }
+ cur++;
+ }
+ free(opts);
+
+ return NSERROR_OK;
+}
+
+
+/* exported interface documented in utils/nsoption.h */
+nserror
+nsoption_init(nsoption_set_default_t *set_defaults,
+ struct nsoption_s **popts,
+ struct nsoption_s **pdefs)
+{
+ nserror ret;
+ struct nsoption_s *defs;
+ struct nsoption_s *opts;
+
+ ret = nsoption_dup(&defaults[0], &defs);
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+
+ /* update the default table */
+ if (set_defaults != NULL) {
+ /** @todo it would be better if the frontends actually
+ * set values in the passed in table instead of
+ * assuming the global one.
+ */
+ opts = nsoptions;
+ nsoptions = defs;
+
+ ret = set_defaults(defs);
+
+ if (ret != NSERROR_OK) {
+ nsoptions = opts;
+ nsoption_free(defs);
+ return ret;
+ }
+ }
+
+ /* copy the default values into the working set */
+ ret = nsoption_dup(defs, &opts);
+ if (ret != NSERROR_OK) {
+ nsoption_free(defs);
+ return ret;
+ }
+
+ /* return values if wanted */
+ if (popts != NULL) {
+ *popts = opts;
+ } else {
+ nsoptions = opts;
+ }
+
+ if (pdefs != NULL) {
+ *pdefs = defs;
+ } else {
+ nsoptions_default = defs;
+ }
+
+ return NSERROR_OK;
+}
+
+/* exported interface documented in utils/nsoption.h */
+nserror nsoption_finalise(struct nsoption_s *opts, struct nsoption_s *defs)
+{
+ /* check to see if global table selected */
+ if (opts == NULL) {
+ opts = nsoptions;
+ }
+
+ nsoption_free(opts);
+
+ /* check to see if global table selected */
+ if (defs == NULL) {
+ defs = nsoptions_default;
+ }
+
+ nsoption_free(defs);
+
+ return NSERROR_OK;
+}
+
+/* exported interface documented in utils/nsoption.h */
+nserror
+nsoption_read(const char *path, struct nsoption_s *opts)
+{
+ char s[100];
+ FILE *fp;
+ struct nsoption_s *defs;
+
+ if (path == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ /* check to see if global table selected */
+ if (opts == NULL) {
+ opts = nsoptions;
+ }
+
+ /** @todo is this and API bug not being a parameter */
+ defs = nsoptions_default;
+
+ fp = fopen(path, "r");
+ if (!fp) {
+ LOG(("Failed to open file '%s'", path));
+ return NSERROR_NOT_FOUND;
+ }
+
+ LOG(("Sucessfully opened '%s' for Options file", path));
+
+ while (fgets(s, 100, fp)) {
+ char *colon, *value;
+ unsigned int idx;
+
+ if ((s[0] == 0) || (s[0] == '#')) {
+ continue;
+ }
+
+ colon = strchr(s, ':');
+ if (colon == 0) {
+ continue;
+ }
+
+ s[strlen(s) - 1] = 0; /* remove \n at end */
+ *colon = 0; /* terminate key */
+ value = colon + 1;
+
+ for (idx = 0; opts[idx].key != NULL; idx++) {
+ if (strcasecmp(s, opts[idx].key) != 0) {
+ continue;
+ }
+
+ strtooption(value, &opts[idx]);
+ break;
+ }
+ }
+
+ fclose(fp);
+
+ nsoption_validate(opts, defs);
+
+ return NSERROR_OK;
+}
+
+
+/* exported interface documented in utils/nsoption.h */
+nserror
+nsoption_write(const char *path,
+ struct nsoption_s *opts,
+ struct nsoption_s *defs)
+{
+ FILE *fp;
+ nserror ret;
+
+ if (path == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ /* check to see if global table selected */
+ if (opts == NULL) {
+ opts = nsoptions;
+ }
+
+ /* check to see if global table selected */
+ if (defs == NULL) {
+ defs = nsoptions_default;
+ }
+
+ fp = fopen(path, "w");
+ if (!fp) {
+ LOG(("failed to open file '%s' for writing", path));
+ return NSERROR_NOT_FOUND;
+ }
+
+ ret = nsoption_output(fp, opts, defs, false);
+
+ fclose(fp);
+
+ return ret;
+}
+
+/* exported interface documented in utils/nsoption.h */
+nserror
+nsoption_dump(FILE *outf, struct nsoption_s *opts)
+{
+ if (outf == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ /* check to see if global table selected */
+ if (opts == NULL) {
+ opts = nsoptions;
+ }
+
+ return nsoption_output(outf, opts, NULL, true);
+}
+
+
+/* exported interface documented in utils/nsoption.h */
+nserror
+nsoption_commandline(int *pargc, char **argv, struct nsoption_s *opts)
+{
+ char *arg;
+ char *val;
+ int arglen;
+ int idx = 1;
+ int mv_loop;
+ unsigned int entry_loop;
+
+ /* check to see if global table selected */
+ if (opts == NULL) {
+ opts = nsoptions;
+ }
+
+ while (idx < *pargc) {
+ arg = argv[idx];
+ arglen = strlen(arg);
+
+ /* check we have an option */
+ /* option must start -- and be as long as the shortest option*/
+ if ((arglen < (2+5) ) || (arg[0] != '-') || (arg[1] != '-'))
+ break;
+
+ arg += 2; /* skip -- */
+
+ val = strchr(arg, '=');
+ if (val == NULL) {
+ /* no equals sign - next parameter is val */
+ idx++;
+ if (idx >= *pargc)
+ break;
+ val = argv[idx];
+ } else {
+ /* equals sign */
+ arglen = val - arg ;
+ val++;
+ }
+
+ /* arg+arglen is the option to set, val is the value */
+
+ LOG(("%.*s = %s", arglen, arg, val));
+
+ for (entry_loop = 0;
+ entry_loop < NSOPTION_LISTEND;
+ entry_loop++) {
+ if (strncmp(arg, opts[entry_loop].key, arglen) == 0) {
+ strtooption(val, opts + entry_loop);
+ break;
+ }
+ }
+
+ idx++;
+ }
+
+ /* remove processed options from argv */
+ for (mv_loop=0; mv_loop < (*pargc - idx); mv_loop++) {
+ argv[mv_loop + 1] = argv[mv_loop + idx];
+ }
+ *pargc -= (idx - 1);
+
+ return NSERROR_OK;
+}
+
+/* exported interface documented in options.h */
+int
+nsoption_snoptionf(char *string,
+ size_t size,
+ enum nsoption_e option_idx,
+ const char *fmt)
+{
+ size_t slen = 0; /* current output string length */
+ int fmtc = 0; /* current index into format string */
+ struct nsoption_s *option;
+
+ if (option_idx >= NSOPTION_LISTEND) {
+ return -1;
+ }
+
+ option = &nsoptions[option_idx]; /* assume the global table */
+ if (option == NULL || option->key == NULL)
+ return -1;
+
+
+ while ((slen < size) && (fmt[fmtc] != 0)) {
+ if (fmt[fmtc] == '%') {
+ fmtc++;
+ switch (fmt[fmtc]) {
+ case 'k':
+ slen += snprintf(string + slen,
+ size - slen,
+ "%s",
+ option->key);
+ break;
+
+ case 'p':
+ if (nsoption_is_set(nsoptions,
+ nsoptions_default,
+ option_idx)) {
+ slen += snprintf(string + slen,
+ size - slen,
+ "user");
+ } else {
+ slen += snprintf(string + slen,
+ size - slen,
+ "default");
+ }
+ break;
+
+ case 't':
+ switch (option->type) {
+ case OPTION_BOOL:
+ slen += snprintf(string + slen,
+ size - slen,
+ "boolean");
+ break;
+
+ case OPTION_INTEGER:
+ slen += snprintf(string + slen,
+ size - slen,
+ "integer");
+ break;
+
+ case OPTION_UINT:
+ slen += snprintf(string + slen,
+ size - slen,
+ "unsigned integer");
+ break;
+
+ case OPTION_COLOUR:
+ slen += snprintf(string + slen,
+ size - slen,
+ "colour");
+ break;
+
+ case OPTION_STRING:
+ slen += snprintf(string + slen,
+ size - slen,
+ "string");
+ break;
+
+ }
+ break;
+
+
+ case 'V':
+ slen += nsoption_output_value_html(option,
+ size,
+ slen,
+ string);
+ break;
+ case 'v':
+ slen += nsoption_output_value_text(option,
+ size,
+ slen,
+ string);
+ break;
+ }
+ fmtc++;
+ } else {
+ string[slen] = fmt[fmtc];
+ slen++;
+ fmtc++;
+ }
+ }
+
+ /* Ensure that we NUL-terminate the output */
+ string[min(slen, size - 1)] = '\0';
+
+ return slen;
+}
+
+/* exported interface documented in options.h */
+nserror
+nsoption_set_tbl_charp(struct nsoption_s *opts,
+ enum nsoption_e option_idx,
+ char *s)
+{
+ struct nsoption_s *option;
+
+ option = &opts[option_idx];
+
+ /* ensure it is a string option */
+ if (option->type != OPTION_STRING) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ /* free any existing string */
+ if (option->value.s != NULL) {
+ free(option->value.s);
+ }
+
+ option->value.s = s;
+
+ /* check for empty string */
+ if ((option->value.s != NULL) && (*option->value.s == 0)) {
+ free(option->value.s);
+ option->value.s = NULL;
+ }
+ return NSERROR_OK;
+}
diff --git a/utils/nsoption.h b/utils/nsoption.h
new file mode 100644
index 000000000..d111729aa
--- /dev/null
+++ b/utils/nsoption.h
@@ -0,0 +1,343 @@
+/*
+ * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Option reading and saving (interface).
+ *
+ * Global options are defined in desktop/options.h
+ * Distinct target options are defined in <TARGET>/options.h
+ *
+ * The implementation API is slightly compromised because it still has
+ * "global" tables for both the default and current option tables.
+ *
+ * The initialisation and read/write interfaces take pointers to an
+ * option table which would let us to make the option structure
+ * opaque.
+ *
+ * All the actual acessors assume direct access to a global option
+ * table (nsoptions). To avoid this the acessors would have to take a
+ * pointer to the active options table and be implemented as functions
+ * within nsoptions.c
+ *
+ * Indirect access would have an impact on performance of NetSurf as
+ * the expected option lookup cost is currently that of a simple
+ * dereference (which this current implementation keeps).
+ */
+
+#ifndef _NETSURF_UTILS_NSOPTION_H_
+#define _NETSURF_UTILS_NSOPTION_H_
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "utils/errors.h"
+
+/* allow targets to include any necessary headers of their own */
+#define NSOPTION_BOOL(NAME, DEFAULT)
+#define NSOPTION_STRING(NAME, DEFAULT)
+#define NSOPTION_INTEGER(NAME, DEFAULT)
+#define NSOPTION_UINT(NAME, DEFAULT)
+#define NSOPTION_COLOUR(NAME, DEFAULT)
+
+#include "desktop/options.h"
+#if defined(riscos)
+#include "riscos/options.h"
+#elif defined(nsgtk)
+#include "gtk/options.h"
+#elif defined(nsbeos)
+#include "beos/options.h"
+#elif defined(nsamiga)
+#include "amiga/options.h"
+#elif defined(nsframebuffer)
+#include "framebuffer/options.h"
+#elif defined(nsatari)
+#include "atari/options.h"
+#elif defined(nsmonkey)
+#include "monkey/options.h"
+#endif
+
+#undef NSOPTION_BOOL
+#undef NSOPTION_STRING
+#undef NSOPTION_INTEGER
+#undef NSOPTION_UINT
+#undef NSOPTION_COLOUR
+
+
+
+enum { OPTION_HTTP_PROXY_AUTH_NONE = 0,
+ OPTION_HTTP_PROXY_AUTH_BASIC = 1,
+ OPTION_HTTP_PROXY_AUTH_NTLM = 2 };
+
+#define DEFAULT_MARGIN_TOP_MM 10
+#define DEFAULT_MARGIN_BOTTOM_MM 10
+#define DEFAULT_MARGIN_LEFT_MM 10
+#define DEFAULT_MARGIN_RIGHT_MM 10
+#define DEFAULT_EXPORT_SCALE 0.7
+
+#ifndef DEFAULT_REFLOW_PERIOD
+/** Default reflow time in cs */
+#define DEFAULT_REFLOW_PERIOD 25
+#endif
+
+/** The options type. */
+enum nsoption_type_e {
+ OPTION_BOOL, /**< Option is a boolean. */
+ OPTION_INTEGER, /**< Option is an integer. */
+ OPTION_UINT, /**< Option is an unsigned integer */
+ OPTION_STRING, /**< option is a heap allocated string. */
+ OPTION_COLOUR /**< Option is a netsurf colour. */
+};
+
+struct nsoption_s {
+ const char *key;
+ int key_len;
+ enum nsoption_type_e type;
+ union {
+ bool b;
+ int i;
+ unsigned int u;
+ char *s;
+ const char *cs;
+ colour c;
+ } value;
+};
+
+/* construct the option enumeration */
+#define NSOPTION_BOOL(NAME, DEFAULT) NSOPTION_##NAME,
+#define NSOPTION_STRING(NAME, DEFAULT) NSOPTION_##NAME,
+#define NSOPTION_INTEGER(NAME, DEFAULT) NSOPTION_##NAME,
+#define NSOPTION_UINT(NAME, DEFAULT) NSOPTION_##NAME,
+#define NSOPTION_COLOUR(NAME, DEFAULT) NSOPTION_##NAME,
+
+enum nsoption_e {
+#include "desktop/options.h"
+#if defined(riscos)
+#include "riscos/options.h"
+#elif defined(nsgtk)
+#include "gtk/options.h"
+#elif defined(nsbeos)
+#include "beos/options.h"
+#elif defined(nsamiga)
+#include "amiga/options.h"
+#elif defined(nsframebuffer)
+#include "framebuffer/options.h"
+#elif defined(nsatari)
+#include "atari/options.h"
+#elif defined(nsmonkey)
+#include "monkey/options.h"
+#endif
+ NSOPTION_LISTEND /* end of list */
+};
+
+#undef NSOPTION_BOOL
+#undef NSOPTION_STRING
+#undef NSOPTION_INTEGER
+#undef NSOPTION_UINT
+#undef NSOPTION_COLOUR
+
+/**
+ * global active option table.
+ */
+extern struct nsoption_s *nsoptions;
+
+/**
+ * global default option table.
+ */
+extern struct nsoption_s *nsoptions_default;
+
+/**
+ * default setting callback.
+ */
+typedef nserror(nsoption_set_default_t)(struct nsoption_s *defaults);
+
+
+/**
+ * Initialise option system.
+ *
+ * @param set_default callback to allow the customisation of the default
+ * options.
+ * @param ppots pointer to update to get options table or NULL.
+ * @param pdefs pointer to update to get default options table or NULL.
+ * @return The error status
+ */
+nserror nsoption_init(nsoption_set_default_t *set_default, struct nsoption_s **popts, struct nsoption_s **pdefs);
+
+
+/**
+ * Finalise option system
+ *
+ * Releases all resources allocated in the initialisation.
+ *
+ * @param opts the options table or NULL to use global table.
+ * @param defs the default options table to use or NULL to use global table
+ * return The error status
+ */
+nserror nsoption_finalise(struct nsoption_s *opts, struct nsoption_s *defs);
+
+
+/**
+ * Read choices file and set them in the passed table
+ *
+ * @param path The path to read the file from
+ * @param opts The options table to enerate values from or NULL to use global
+ * @return The error status
+ */
+nserror nsoption_read(const char *path, struct nsoption_s *opts);
+
+
+/**
+ * Write options that have changed from the defaults to a file.
+ *
+ * The \a nsoption_dump can be used to output all entries not just
+ * changed ones.
+ *
+ * @param path The path to read the file from
+ * @param opts The options table to enerate values from or NULL to use global
+ * @param defs The default table to use or NULL to use global
+ * @return The error status
+ */
+nserror nsoption_write(const char *path, struct nsoption_s *opts, struct nsoption_s *defs);
+
+
+/**
+ * Write all options to a stream.
+ *
+ * @param outf The stream to write to
+ * @param opts The options table to enerate values from or NULL to use global
+ * @return The error status
+ */
+nserror nsoption_dump(FILE *outf, struct nsoption_s *opts);
+
+
+/**
+ * Process commandline and set options approriately.
+ *
+ * @param pargc Pointer to the size of the argument vector.
+ * @param argv The argument vector.
+ * @param opts The options table to enerate values from or NULL to use global
+ * @return The error status
+ */
+nserror nsoption_commandline(int *pargc, char **argv, struct nsoption_s *opts);
+
+
+/**
+ * Fill a buffer with an option using a format.
+ *
+ * The format string is copied into the output buffer with the
+ * following replaced:
+ * %k - The options key
+ * %t - The options type
+ * %V - value (HTML formatting)
+ * %v - value (plain formatting)
+ * %p - provenance either "user" or "default"
+ *
+ * @param string The buffer in which to place the results.
+ * @param size The size of the string buffer.
+ * @param option The option .
+ * @param fmt The format string.
+ * @return The number of bytes written to \a string or -1 on error
+ */
+int nsoption_snoptionf(char *string, size_t size, enum nsoption_e option, const char *fmt);
+
+
+/**
+ * Get the value of a boolean option.
+ *
+ * Gets the value of an option assuming it is a boolean type.
+ * @note option type is unchecked so care must be taken in caller.
+ */
+#define nsoption_bool(OPTION) (nsoptions[NSOPTION_##OPTION].value.b)
+
+
+/**
+ * Get the value of an integer option.
+ *
+ * Gets the value of an option assuming it is a integer type.
+ * @note option type is unchecked so care must be taken in caller.
+ */
+#define nsoption_int(OPTION) (nsoptions[NSOPTION_##OPTION].value.i)
+
+
+/**
+ * Get the value of an unsigned integer option.
+ *
+ * Gets the value of an option assuming it is a integer type.
+ * @note option type is unchecked so care must be taken in caller.
+ */
+#define nsoption_uint(OPTION) (nsoptions[NSOPTION_##OPTION].value.u)
+
+
+/**
+ * Get the value of a string option.
+ *
+ * Gets the value of an option assuming it is a string type.
+ * @note option type is unchecked so care must be taken in caller.
+ */
+#define nsoption_charp(OPTION) (nsoptions[NSOPTION_##OPTION].value.s)
+
+
+/**
+ * Get the value of a netsurf colour option.
+ *
+ * Gets the value of an option assuming it is a colour type.
+ * @note option type is unchecked so care must be taken in caller.
+ */
+#define nsoption_colour(OPTION) (nsoptions[NSOPTION_##OPTION].value.c)
+
+
+/** set a boolean option in the default table */
+#define nsoption_set_bool(OPTION, VALUE) nsoptions[NSOPTION_##OPTION].value.b = VALUE
+
+
+/** set an integer option in the default table */
+#define nsoption_set_int(OPTION, VALUE) nsoptions[NSOPTION_##OPTION].value.i = VALUE
+
+
+/** set a colour option in the default table */
+#define nsoption_set_colour(OPTION, VALUE) nsoptions[NSOPTION_##OPTION].value.c = VALUE
+
+
+/**
+ * Set string option in specified table.
+ *
+ * Sets the string option to the value given freeing any resources
+ * currently allocated to the option. If the passed string is empty it
+ * is converted to the NULL value.
+ *
+ * @param opts The table to set option in
+ * @param option_idx The option
+ * @param s The string to set. This is used directly and not copied.
+ */
+nserror nsoption_set_tbl_charp(struct nsoption_s *opts, enum nsoption_e option_idx, char *s);
+
+/** set string option in default table */
+#define nsoption_set_charp(OPTION, VALUE) \
+ nsoption_set_tbl_charp(nsoptions, NSOPTION_##OPTION, VALUE)
+
+/** set string option in default table if currently unset */
+#define nsoption_setnull_charp(OPTION, VALUE) \
+ do { \
+ if (nsoptions[NSOPTION_##OPTION].value.s == NULL) { \
+ nsoption_set_tbl_charp(nsoptions, NSOPTION_##OPTION, VALUE); \
+ } else { \
+ free(VALUE); \
+ } \
+ } while (0)
+
+#endif
diff --git a/utils/nsurl.c b/utils/nsurl.c
index 23e177e05..61f849e5f 100644
--- a/utils/nsurl.c
+++ b/utils/nsurl.c
@@ -154,6 +154,7 @@ struct nsurl {
struct nsurl_components components;
int count; /* Number of references to NetSurf URL object */
+ uint32_t hash; /* Hash value for nsurl identification */
size_t length; /* Length of string */
char string[FLEX_ARRAY_LEN_DECL]; /* Full URL as a string */
@@ -1185,6 +1186,43 @@ static void nsurl_get_string(const struct nsurl_components *url, char *url_s,
}
+/**
+ * Calculate hash value
+ *
+ * \param url NetSurf URL object to set hash value for
+ */
+static void nsurl_calc_hash(nsurl *url)
+{
+ uint32_t hash = 0;
+
+ if (url->components.scheme)
+ hash ^= lwc_string_hash_value(url->components.scheme);
+
+ if (url->components.username)
+ hash ^= lwc_string_hash_value(url->components.username);
+
+ if (url->components.password)
+ hash ^= lwc_string_hash_value(url->components.password);
+
+ if (url->components.host)
+ hash ^= lwc_string_hash_value(url->components.host);
+
+ if (url->components.port)
+ hash ^= lwc_string_hash_value(url->components.port);
+
+ if (url->components.path)
+ hash ^= lwc_string_hash_value(url->components.path);
+
+ if (url->components.query)
+ hash ^= lwc_string_hash_value(url->components.query);
+
+ if (url->components.fragment)
+ hash ^= lwc_string_hash_value(url->components.fragment);
+
+ url->hash = hash;
+}
+
+
#ifdef NSURL_DEBUG
/**
* Dump a NetSurf URL's internal components
@@ -1282,6 +1320,9 @@ nserror nsurl_create(const char * const url_s, nsurl **url)
/* Fill out the url string */
nsurl_get_string(&c, (*url)->string, &str_len, str_flags);
+ /* Get the nsurl's hash */
+ nsurl_calc_hash(*url);
+
/* Give the URL a reference */
(*url)->count = 1;
@@ -1615,6 +1656,15 @@ size_t nsurl_length(const nsurl *url)
/* exported interface, documented in nsurl.h */
+uint32_t nsurl_hash(const nsurl *url)
+{
+ assert(url != NULL);
+
+ return url->hash;
+}
+
+
+/* exported interface, documented in nsurl.h */
nserror nsurl_join(const nsurl *base, const char *rel, nsurl **joined)
{
struct url_markers m;
@@ -1819,6 +1869,9 @@ nserror nsurl_join(const nsurl *base, const char *rel, nsurl **joined)
/* Fill out the url string */
nsurl_get_string(&c, (*joined)->string, &str_len, str_flags);
+ /* Get the nsurl's hash */
+ nsurl_calc_hash(*joined);
+
/* Give the URL a reference */
(*joined)->count = 1;
@@ -1871,6 +1924,9 @@ nserror nsurl_defragment(const nsurl *url, nsurl **no_frag)
pos += length;
*pos = '\0';
+ /* Get the nsurl's hash */
+ nsurl_calc_hash(*no_frag);
+
/* Give the URL a reference */
(*no_frag)->count = 1;
@@ -1936,6 +1992,9 @@ nserror nsurl_refragment(const nsurl *url, lwc_string *frag, nsurl **new_url)
(*new_url)->components.scheme_type = url->components.scheme_type;
+ /* Get the nsurl's hash */
+ nsurl_calc_hash(*new_url);
+
/* Give the URL a reference */
(*new_url)->count = 1;
@@ -2019,6 +2078,9 @@ nserror nsurl_replace_query(const nsurl *url, const char *query,
(*new_url)->components.scheme_type = url->components.scheme_type;
+ /* Get the nsurl's hash */
+ nsurl_calc_hash(*new_url);
+
/* Give the URL a reference */
(*new_url)->count = 1;
@@ -2114,6 +2176,9 @@ nserror nsurl_parent(const nsurl *url, nsurl **new_url)
(*new_url)->components.scheme_type = url->components.scheme_type;
+ /* Get the nsurl's hash */
+ nsurl_calc_hash(*new_url);
+
/* Give the URL a reference */
(*new_url)->count = 1;
diff --git a/utils/nsurl.h b/utils/nsurl.h
index b075c42a1..435df73bd 100644
--- a/utils/nsurl.h
+++ b/utils/nsurl.h
@@ -208,6 +208,15 @@ size_t nsurl_length(const nsurl *url);
/**
+ * Get a URL's hash value
+ *
+ * \param url NetSurf URL get hash value for.
+ * \return the hash value
+ */
+uint32_t nsurl_hash(const nsurl *url);
+
+
+/**
* Join a base url to a relative link part, creating a new NetSurf URL object
*
* \param base NetSurf URL containing the base to join rel to
diff --git a/utils/split-messages.pl b/utils/split-messages.pl
index 2bbe79a43..45e55391b 100755..100644
--- a/utils/split-messages.pl
+++ b/utils/split-messages.pl
@@ -1,23 +1,239 @@
-#!/usr/bin/perl -w
+#!/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
+
+Filter the NetSurf combined messages (i10n) file according to language
+and platform and generate output in a selection of formats for use
+both internally within netsurf and externally for translation
+services.
+
+=cut
use strict;
-die "usage: split-messages <langname> <platname> < FatMessages > ThinMessages" if ($#ARGV != 1);
+use Getopt::Long ();
+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
+ input|i=s
+ lang|l=s
+ plat|platform|p=s
+ format|fmt|f=s
+ help|h|? );
+
+# default option values:
+my %opt = qw( plat any format messages );
+
+sub input_stream ();
+sub output_stream ();
+sub formatter ();
+sub static_section($);
+sub usage ();
+
+sub main ()
+{
+ my $input;
+ my $output;
+ my $format;
+ my $header;
+ my $footer;
+ my $opt_ok;
+
+ # option parsing:
+ Getopt::Long::Configure( GETOPT_OPTS );
+ $opt_ok = Getopt::Long::GetOptions( \%opt, GETOPT_SPEC );
+
+ # allow input and output to be specified as non-option arguments:
+ if( @ARGV ) { $opt{input } ||= shift( @ARGV ) }
+ if( @ARGV ) { $opt{output} ||= shift( @ARGV ) }
+
+ # open the appropriate streams and get the formatter and headers:
+ if( $opt_ok )
+ {
+ $input = input_stream();
+ $output = output_stream();
+ $format = formatter();
+ $header = static_section('header');
+ $footer = static_section('footer');
+ }
+
+ # 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();
+ }
+
+ # we are good to go:
+ print( $output $header );
+
+ while (<$input>)
+ {
+ /^#/ && next;
+ /^\s*$/ && next;
+ # only parsing thinsg that look like message lines:
+ if( /^([a-z]{2}).([^.]+).([^:]+):(.*)/ )
+ {
+ my( $lang, $plat, $key, $val ) = ( $1, $2, $3, $4 );
+
+ if( $lang ne $opt{lang} ) { next };
+ if( $opt{plat} eq 'any' ||
+ $opt{plat} eq $plat ||
+ 'all' eq $plat )
+ {
+ print( $output $format->( $key, $val ) );
+ }
+ }
+ else
+ {
+ warn( "Malformed entry: $_" );
+ }
+ }
+
+ print( $output $footer );
+}
+
+main();
+
+sub usage ()
+{
+ my @fmt = map { s/::$//; $_ } keys(%{$::{'msgfmt::'}});
+ print(STDERR <<TXT );
+usage:
+ $0 -l lang-code \
+ [-o output-file] [-i input-file] [-p platform] [-f format]
+
+ $0 -l lang-code ... [input-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
+TXT
+ exit(1);
+}
+
+sub input_stream ()
+{
+ if( $opt{input} )
+ {
+ my $ifh;
+
+ sysopen( $ifh, $opt{input}, O_RDONLY ) ||
+ die( "$0: Failed to open input file $opt{input}: $!\n" );
-my $langname = $ARGV[0];
-my $platname = $ARGV[1];
+ return $ifh;
+ }
+
+ return \*STDIN;
+}
+
+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;
+}
+
+sub formatter ()
+{
+ my $name = $opt{format};
+ my $func = "msgfmt::$name"->UNIVERSAL::can("format");
+
+ return $func || die( "No handler found for format '$name'\n" );
+}
+
+sub static_section ($)
+{
+ my $name = $opt{format};
+ my $sect = shift();
+ my $func = "msgfmt::$name"->UNIVERSAL::can( $sect );
+
+ return $func ? $func->() : "";
+}
+
+# format implementations:
+{
+ package msgfmt::java;
+
+ sub escape { $_[0] =~ s/([:'\\])/\\$1/g; $_[0] }
+ sub format { return join(' = ', $_[0], escape( $_[1] ) ) . "\n" }
+ sub header { "# autogenerated from " . ($opt{input} || '-stdin-') . "\n" }
+}
+
+{
+ package msgfmt::messages; # native netsurf format
+
+ sub format { return join( ":", @_ ) . "\n" }
+ sub header
+ {
+ my $in = $opt{input} || '-stdin-';
+ return <<TXT;
+# This messages file is automatically generated from $in
+# at build-time. Please go and edit that instead of this.\n
+TXT
+ }
+}
+
+{
+ package msgfmt::transifex;
+ use base 'msgfmt::java';
+
+ # transifex has the following quirks:
+ # \ processing is buggy - they re-process every \\ as a \
+ # so \\n, instead or producing literal '\n', is interpreted as \ ^J
+ # Additionally, although the java properties format specifies
+ # that ' should be \ escaped, transifex does not allow/support this:
+ sub escape { $_[0] =~ s/(:|\\(?![abfnrtv]))/\\$1/g; $_[0] }
+ sub format { return join(' = ', $_[0], escape( $_[1] ) ) . "\n" }
+}
-my $allprefix = $langname . ".all.";
-my $platprefix = $langname . "." . $platname . ".";
+########### YAML ###########
+#{
+# package msgfmt::yaml;
+# use YAML qw(Dump Bless);
+# print Dump %data;
+#}
-print "# This messages file is automatically generated from FatMessages\n";
-print "# at build-time. Please go and edit that instead of this.\n\n";
+{
+ package msgfmt::android;
-foreach (<STDIN>) {
- if (not /^#/ and not /^\s*$/) {
- if (/^$allprefix/ or /^$platprefix/) {
- s/^$langname\.(all|$platname)\.//;
- print "$_";
- }
+ sub header { qq|<?xml version="1.0" encoding="utf-8"?>\n<resources>\n| }
+ sub footer { qq|</resources>| }
+ sub format
+ {
+ use HTML::Entities qw(encode_entities);
+ my $escaped = encode_entities( $_[1], '<>&"' );
+ qq| <string name="$_[0]">$escaped</string>\n|;
}
}
diff --git a/utils/utf8.c b/utils/utf8.c
index 885ca94ee..127ffe642 100644
--- a/utils/utf8.c
+++ b/utils/utf8.c
@@ -198,6 +198,13 @@ static struct {
iconv_t cd; /**< Iconv conversion descriptor */
} last_cd;
+static inline void utf8_clear_cd_cache(void)
+{
+ last_cd.from[0] = '\0';
+ last_cd.to[0] = '\0';
+ last_cd.cd = 0;
+}
+
/**
* Finalise the UTF-8 library
*/
@@ -207,9 +214,7 @@ void utf8_finalise(void)
iconv_close(last_cd.cd);
/* paranoia follows */
- last_cd.from[0] = '\0';
- last_cd.to[0] = '\0';
- last_cd.cd = 0;
+ utf8_clear_cd_cache();
}
/**
@@ -331,9 +336,7 @@ utf8_convert_ret utf8_convert(const char *string, size_t len,
/* clear the cached conversion descriptor as it's invalid */
if (last_cd.cd)
iconv_close(last_cd.cd);
- last_cd.from[0] = '\0';
- last_cd.to[0] = '\0';
- last_cd.cd = 0;
+ utf8_clear_cd_cache();
/** \todo handle the various cases properly
* There are 3 possible error cases:
* a) Insufficiently large output buffer
@@ -411,21 +414,43 @@ utf8_convert_ret utf8_to_html(const char *string, const char *encname,
if (len == 0)
len = strlen(string);
- cd = iconv_open(encname, "UTF-8");
- if (cd == (iconv_t) -1) {
- if (errno == EINVAL)
- return UTF8_CONVERT_BADENC;
- /* default to no memory */
- return UTF8_CONVERT_NOMEM;
+ /* we cache the last used conversion descriptor,
+ * so check if we're trying to use it here */
+ if (strncasecmp(last_cd.from, "UTF-8", sizeof(last_cd.from)) == 0 &&
+ strncasecmp(last_cd.to, encname,
+ sizeof(last_cd.to)) == 0 &&
+ last_cd.cd != 0) {
+ cd = last_cd.cd;
+ }
+ else {
+ /* no match, so create a new cd */
+ cd = iconv_open(encname, "UTF-8");
+ if (cd == (iconv_t) -1) {
+ if (errno == EINVAL)
+ return UTF8_CONVERT_BADENC;
+ /* default to no memory */
+ return UTF8_CONVERT_NOMEM;
+ }
+
+ /* close the last cd - we don't care if this fails */
+ if (last_cd.cd)
+ iconv_close(last_cd.cd);
+
+ /* and copy the to/from/cd data into last_cd */
+ strncpy(last_cd.from, "UTF-8", sizeof(last_cd.from));
+ strncpy(last_cd.to, encname, sizeof(last_cd.to));
+ last_cd.cd = cd;
}
/* Worst case is ASCII -> UCS4, with all characters escaped:
* "&#xYYYYYY;", thus each input character may become a string
- * of 10 UCS4 characters, each 4 bytes in length */
- origoutlen = outlen = len * 10 * 4;
+ * of 10 UCS4 characters, each 4 bytes in length, plus four for
+ * terminating the string */
+ origoutlen = outlen = len * 10 * 4 + 4;
origout = out = malloc(outlen);
if (out == NULL) {
iconv_close(cd);
+ utf8_clear_cd_cache();
return UTF8_CONVERT_NOMEM;
}
@@ -444,6 +469,7 @@ utf8_convert_ret utf8_to_html(const char *string, const char *encname,
if (ret != UTF8_CONVERT_OK) {
free(origout);
iconv_close(cd);
+ utf8_clear_cd_cache();
return ret;
}
}
@@ -457,6 +483,7 @@ utf8_convert_ret utf8_to_html(const char *string, const char *encname,
if (ret != UTF8_CONVERT_OK) {
free(origout);
iconv_close(cd);
+ utf8_clear_cd_cache();
return ret;
}
@@ -474,19 +501,21 @@ utf8_convert_ret utf8_to_html(const char *string, const char *encname,
if (ret != UTF8_CONVERT_OK) {
free(origout);
iconv_close(cd);
+ utf8_clear_cd_cache();
return ret;
}
}
- iconv_close(cd);
+ /* Terminate string */
+ memset(out, 0, 4);
+ outlen -= 4;
/* Shrink-wrap */
- *result = realloc(origout, origoutlen - outlen + 4);
+ *result = realloc(origout, origoutlen - outlen);
if (*result == NULL) {
free(origout);
return UTF8_CONVERT_NOMEM;
}
- memset(*result + (origoutlen - outlen), 0, 4);
return UTF8_CONVERT_OK;
}
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;
}