diff options
Diffstat (limited to 'utils')
-rw-r--r-- | utils/Makefile | 4 | ||||
-rw-r--r-- | utils/bloom.c | 163 | ||||
-rw-r--r-- | utils/bloom.h | 99 | ||||
-rw-r--r-- | utils/fetch-transifex.pl | 127 | ||||
-rw-r--r-- | utils/filepath.c | 10 | ||||
-rw-r--r-- | utils/import-messages.pl | 326 | ||||
-rwxr-xr-x | utils/jenkins-build.sh | 351 | ||||
-rw-r--r-- | utils/log.c | 24 | ||||
-rw-r--r-- | utils/memdebug.c | 381 | ||||
-rw-r--r-- | utils/memdebug.h | 106 | ||||
-rw-r--r-- | utils/nsoption.c | 896 | ||||
-rw-r--r-- | utils/nsoption.h | 343 | ||||
-rw-r--r-- | utils/nsurl.c | 65 | ||||
-rw-r--r-- | utils/nsurl.h | 9 | ||||
-rw-r--r--[-rwxr-xr-x] | utils/split-messages.pl | 244 | ||||
-rw-r--r-- | utils/utf8.c | 63 | ||||
-rw-r--r-- | utils/utils.c | 38 |
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; } |